Coverage Report

Created: 2026-09-14 07:04

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 "progress.h"
48
#include "transfer.h"
49
#include "escape.h"
50
#include "ftp.h"
51
#include "ftp-int.h"
52
#include "ftplistparser.h"
53
#include "curl_range.h"
54
#include "strcase.h"
55
#include "vtls/vtls.h"
56
#include "cfilters.h"
57
#include "cf-socket.h"
58
#include "connect.h"
59
#include "curlx/inet_ntop.h"
60
#include "curlx/inet_pton.h"
61
#include "select.h"
62
#include "parsedate.h" /* for the week day and month names */
63
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
64
#include "multiif.h"
65
#include "url.h"
66
#include "http_proxy.h"
67
#include "vdns/hostip.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
                             int8_t 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
  CURLcode result;
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
    result = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, hbuflen);
1037
0
    break;
1038
0
#endif
1039
0
  default:
1040
0
    result = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, hbuflen);
1041
0
    break;
1042
0
  }
1043
0
  if(result)
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 = CURL_EASY_STR(data, 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_expire(data, (data->set.accepttimeout > 0) ?
1402
0
                data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
1403
0
                EXPIRE_FTP_ACCEPT);
1404
0
  }
1405
0
  if(portsock != CURL_SOCKET_BAD)
1406
0
    Curl_socket_close(data, conn, portsock);
1407
0
  return result;
1408
0
}
1409
1410
static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
1411
                                   struct ftp_conn *ftpc,
1412
                                   struct connectdata *conn)
1413
0
{
1414
0
  CURLcode result = CURLE_OK;
1415
  /* Here's the executive summary on what to do:
1416
1417
     PASV is RFC959, expect:
1418
     227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1419
1420
     LPSV is RFC1639, expect:
1421
     228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1422
1423
     EPSV is RFC2428, expect:
1424
     229 Entering Extended Passive Mode (|||port|)
1425
   */
1426
1427
0
  static const char mode[][5] = { "EPSV", "PASV" };
1428
0
  int modeoff;
1429
1430
0
#ifdef PF_INET6
1431
0
  if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1432
    /* EPSV is disabled but we are connected to an IPv6 host, so we ignore the
1433
       request and enable EPSV again! */
1434
0
    conn->bits.ftp_use_epsv = TRUE;
1435
0
#endif
1436
1437
0
  modeoff = conn->bits.ftp_use_epsv ? 0 : 1;
1438
1439
0
  result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
1440
0
  if(!result) {
1441
0
    ftpc->count1 = modeoff;
1442
0
    ftp_state(data, ftpc, FTP_PASV);
1443
0
    infof(data, "Connect data stream passively");
1444
0
  }
1445
0
  return result;
1446
0
}
1447
1448
/*
1449
 * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
1450
 *
1451
 * REST is the last command in the chain of commands when a "head"-like
1452
 * request is made. Thus, if an actual transfer is to be made this is where we
1453
 * take off for real.
1454
 */
1455
static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data,
1456
                                           struct ftp_conn *ftpc,
1457
                                           struct FTP *ftp)
1458
0
{
1459
0
  CURLcode result = CURLE_OK;
1460
0
  struct connectdata *conn = data->conn;
1461
1462
0
  if(ftp->transfer != PPTRANSFER_BODY) {
1463
    /* does not transfer any data */
1464
1465
    /* still possibly do PRE QUOTE jobs */
1466
0
    ftp_state(data, ftpc, FTP_RETR_PREQUOTE);
1467
0
    result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE);
1468
0
  }
1469
0
  else if(data->set.ftp_use_port) {
1470
    /* We have chosen to use the PORT (or similar) command */
1471
0
    result = ftp_state_use_port(data, ftpc, EPRT);
1472
0
  }
1473
0
  else {
1474
    /* We have chosen (this is default) to use the PASV (or similar) command */
1475
0
    if(data->set.ftp_use_pret) {
1476
      /* The user has requested that we send a PRET command
1477
         to prepare the server for the upcoming PASV */
1478
0
      if(!ftpc->file)
1479
0
        result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s",
1480
0
                               CURL_EASY_STR(data, STRING_CUSTOMREQUEST) ?
1481
0
                               CURL_EASY_STR(data, STRING_CUSTOMREQUEST) :
1482
0
                               (data->state.list_only ? "NLST" : "LIST"));
1483
0
      else if(data->state.upload)
1484
0
        result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", ftpc->file);
1485
0
      else
1486
0
        result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", ftpc->file);
1487
0
      if(!result)
1488
0
        ftp_state(data, ftpc, FTP_PRET);
1489
0
    }
1490
0
    else
1491
0
      result = ftp_state_use_pasv(data, ftpc, conn);
1492
0
  }
1493
0
  return result;
1494
0
}
1495
1496
static CURLcode ftp_state_rest(struct Curl_easy *data,
1497
                               struct ftp_conn *ftpc,
1498
                               struct FTP *ftp)
1499
0
{
1500
0
  CURLcode result = CURLE_OK;
1501
1502
0
  if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) {
1503
    /* if a "head"-like request is being made (on a file) */
1504
1505
    /* Determine if server can respond to REST command and therefore
1506
       whether it supports range */
1507
0
    result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
1508
0
    if(!result)
1509
0
      ftp_state(data, ftpc, FTP_REST);
1510
0
  }
1511
0
  else
1512
0
    result = ftp_state_prepare_transfer(data, ftpc, ftp);
1513
1514
0
  return result;
1515
0
}
1516
1517
static CURLcode ftp_state_size(struct Curl_easy *data,
1518
                               struct ftp_conn *ftpc,
1519
                               struct FTP *ftp)
1520
0
{
1521
0
  CURLcode result = CURLE_OK;
1522
1523
0
  if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) {
1524
    /* if a "head"-like request is being made (on a file) */
1525
1526
    /* we know ftpc->file is a valid pointer to a filename */
1527
0
    result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1528
0
    if(!result)
1529
0
      ftp_state(data, ftpc, FTP_SIZE);
1530
0
  }
1531
0
  else
1532
0
    result = ftp_state_rest(data, ftpc, ftp);
1533
1534
0
  return result;
1535
0
}
1536
1537
static CURLcode ftp_state_list(struct Curl_easy *data,
1538
                               struct ftp_conn *ftpc,
1539
                               struct FTP *ftp)
1540
0
{
1541
0
  CURLcode result = CURLE_OK;
1542
1543
  /* If this output is to be machine-parsed, the NLST command might be better
1544
     to use, since the LIST command output is not specified or standard in any
1545
     way. It has turned out that the NLST list output is not the same on all
1546
     servers either... */
1547
1548
  /* if FTPFILE_NOCWD was specified, we should add the path
1549
     as argument for the LIST / NLST / or custom command.
1550
     Whether the server will support this, is uncertain.
1551
1552
     The other ftp_filemethods will CWD into dir/dir/ first and
1553
     then do LIST (in that case: nothing to do here) */
1554
0
  const char *lstArg = NULL;
1555
0
  int lstArglen = 0;
1556
0
  char *cmd;
1557
1558
0
  if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
1559
    /* URL-decode before evaluation: e.g. paths starting/ending with %2f */
1560
0
    const char *rawPath = ftpc->rawpath;
1561
0
    const char *slashPos = strrchr(rawPath, '/');
1562
0
    if(slashPos) {
1563
      /* chop off the file part if format is dir/file otherwise remove
1564
         the trailing slash for dir/dir/ except for absolute path / */
1565
0
      size_t n = slashPos - rawPath;
1566
0
      if(n == 0)
1567
0
        ++n;
1568
1569
0
      lstArg = rawPath;
1570
0
      lstArglen = (int)n;
1571
0
    }
1572
0
  }
1573
1574
0
  cmd = curl_maprintf("%s%s%.*s",
1575
0
                      CURL_EASY_STR(data, STRING_CUSTOMREQUEST) ?
1576
0
                      CURL_EASY_STR(data, STRING_CUSTOMREQUEST) :
1577
0
                      (data->state.list_only ? "NLST" : "LIST"),
1578
0
                      lstArg ? " " : "",
1579
0
                      lstArglen, lstArg ? lstArg : "");
1580
1581
0
  if(!cmd)
1582
0
    return CURLE_OUT_OF_MEMORY;
1583
1584
0
  result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
1585
0
  curlx_free(cmd);
1586
1587
0
  if(!result)
1588
0
    ftp_state(data, ftpc, FTP_LIST);
1589
1590
0
  return result;
1591
0
}
1592
1593
static CURLcode ftp_state_list_prequote(struct Curl_easy *data,
1594
                                        struct ftp_conn *ftpc,
1595
                                        struct FTP *ftp)
1596
0
{
1597
  /* We have sent the TYPE, now we must send the list of prequote strings */
1598
0
  return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_LIST_PREQUOTE);
1599
0
}
1600
1601
static CURLcode ftp_state_retr_prequote(struct Curl_easy *data,
1602
                                        struct ftp_conn *ftpc,
1603
                                        struct FTP *ftp)
1604
0
{
1605
  /* We have sent the TYPE, now we must send the list of prequote strings */
1606
0
  return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE);
1607
0
}
1608
1609
static CURLcode ftp_state_stor_prequote(struct Curl_easy *data,
1610
                                        struct ftp_conn *ftpc,
1611
                                        struct FTP *ftp)
1612
0
{
1613
  /* We have sent the TYPE, now we must send the list of prequote strings */
1614
0
  return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_STOR_PREQUOTE);
1615
0
}
1616
1617
static CURLcode ftp_state_type(struct Curl_easy *data,
1618
                               struct ftp_conn *ftpc,
1619
                               struct FTP *ftp)
1620
0
{
1621
0
  CURLcode result = CURLE_OK;
1622
1623
  /* If we have selected NOBODY and HEADER, it means that we only want file
1624
     information. Which in FTP cannot be much more than the file size and
1625
     date. */
1626
0
  if(data->req.no_body && ftpc->file &&
1627
0
     ftp_need_type(ftpc, (bool)data->state.prefer_ascii)) {
1628
    /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
1629
       may not support it! It is however the only way we have to get a file's
1630
       size! */
1631
1632
0
    ftp->transfer = PPTRANSFER_INFO;
1633
    /* this means no actual transfer will be made */
1634
1635
    /* Some servers return different sizes for different modes, and thus we
1636
       must set the proper type before we check the size */
1637
0
    result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii,
1638
0
                         FTP_TYPE);
1639
0
    if(result)
1640
0
      return result;
1641
0
  }
1642
0
  else
1643
0
    result = ftp_state_size(data, ftpc, ftp);
1644
1645
0
  return result;
1646
0
}
1647
1648
/* This is called after the CWD commands have been done in the beginning of
1649
   the DO phase */
1650
static CURLcode ftp_state_mdtm(struct Curl_easy *data,
1651
                               struct ftp_conn *ftpc,
1652
                               struct FTP *ftp)
1653
0
{
1654
0
  CURLcode result = CURLE_OK;
1655
1656
  /* Requested time of file or time-depended transfer? */
1657
0
  if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1658
1659
    /* we have requested to get the modified-time of the file, this is a white
1660
       spot as the MDTM is not mentioned in RFC959 */
1661
0
    result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
1662
1663
0
    if(!result)
1664
0
      ftp_state(data, ftpc, FTP_MDTM);
1665
0
  }
1666
0
  else
1667
0
    result = ftp_state_type(data, ftpc, ftp);
1668
1669
0
  return result;
1670
0
}
1671
1672
/* This is called after the TYPE and possible quote commands have been sent */
1673
static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
1674
                                   struct ftp_conn *ftpc,
1675
                                   struct FTP *ftp,
1676
                                   bool sizechecked)
1677
0
{
1678
0
  CURLcode result = CURLE_OK;
1679
0
  curl_bit append = data->set.remote_append;
1680
1681
0
  if((data->state.resume_from && !sizechecked) ||
1682
0
     ((data->state.resume_from > 0) && sizechecked)) {
1683
    /* we are about to continue the uploading of a file
1684
       1. get already existing file's size. We use the SIZE command for this
1685
          which may not exist in the server!  The SIZE command is not in
1686
          RFC959.
1687
1688
       2. This used to set REST, but since we can do append, we issue no
1689
          another ftp command. Skip the source file offset and APPEND the rest
1690
          on the file instead
1691
1692
       3. pass file-size number of bytes in the source file
1693
       4. lower the infilesize counter */
1694
    /* => transfer as usual */
1695
0
    int seekerr = CURL_SEEKFUNC_OK;
1696
1697
0
    if(data->state.resume_from < 0) {
1698
      /* Got no given size to start from, figure it out */
1699
0
      result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1700
0
      if(!result)
1701
0
        ftp_state(data, ftpc, FTP_STOR_SIZE);
1702
0
      return result;
1703
0
    }
1704
1705
    /* enable append */
1706
0
    append = TRUE;
1707
1708
    /* Let's read off the proper amount of bytes from the input. */
1709
0
    if(data->set.seek_func) {
1710
0
      struct Curl_mapi_guard guard;
1711
0
      CURL_CBAPI_START(&guard, data, easy_seek_func);
1712
0
      seekerr = data->set.seek_func(data->set.seek_client,
1713
0
                                    data->state.resume_from, SEEK_SET);
1714
0
      CURL_CBAPI_END(&guard);
1715
0
    }
1716
1717
0
    if(seekerr != CURL_SEEKFUNC_OK) {
1718
0
      curl_off_t passed = 0;
1719
0
      if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1720
0
        failf(data, "Could not seek stream");
1721
0
        return CURLE_FTP_COULDNT_USE_REST;
1722
0
      }
1723
      /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
1724
0
      do {
1725
0
        char scratch[4 * 1024];
1726
0
        size_t readthisamountnow =
1727
0
          (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
1728
0
          sizeof(scratch) :
1729
0
          curlx_sotouz(data->state.resume_from - passed);
1730
1731
0
        size_t actuallyread =
1732
0
          data->state.fread_func(scratch, 1, readthisamountnow,
1733
0
                                 data->state.in);
1734
1735
0
        passed += actuallyread;
1736
0
        if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1737
          /* this checks for greater-than only to make sure that the
1738
             CURL_READFUNC_ABORT return code still aborts */
1739
0
          failf(data, "Failed to read data");
1740
0
          return CURLE_FTP_COULDNT_USE_REST;
1741
0
        }
1742
0
      } while(passed < data->state.resume_from);
1743
0
    }
1744
    /* now, decrease the size of the read */
1745
0
    if(data->state.infilesize > 0) {
1746
0
      data->state.infilesize -= data->state.resume_from;
1747
1748
0
      if(data->state.infilesize <= 0) {
1749
0
        infof(data, "File already completely uploaded");
1750
1751
        /* no data to transfer */
1752
0
        Curl_xfer_setup_nop(data);
1753
1754
        /* Set ->transfer so that we will not get any error in
1755
         * ftp_done() because we did not transfer anything! */
1756
0
        ftp->transfer = PPTRANSFER_NONE;
1757
1758
0
        ftp_state(data, ftpc, FTP_STOP);
1759
0
        return CURLE_OK;
1760
0
      }
1761
0
    }
1762
    /* we have passed, proceed as normal */
1763
0
  } /* resume_from */
1764
1765
0
  result = Curl_pp_sendf(data, &ftpc->pp, append ? "APPE %s" : "STOR %s",
1766
0
                         ftpc->file);
1767
0
  if(!result)
1768
0
    ftp_state(data, ftpc, FTP_STOR);
1769
1770
0
  return result;
1771
0
}
1772
1773
static CURLcode ftp_state_retr(struct Curl_easy *data,
1774
                               struct ftp_conn *ftpc,
1775
                               struct FTP *ftp,
1776
                               curl_off_t filesize)
1777
0
{
1778
0
  CURLcode result = CURLE_OK;
1779
1780
0
  CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_CSTATE(ftpc));
1781
0
  if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
1782
0
    failf(data, "Maximum file size exceeded");
1783
0
    return CURLE_FILESIZE_EXCEEDED;
1784
0
  }
1785
0
  ftp->downloadsize = filesize;
1786
1787
0
  if(data->state.resume_from) {
1788
    /* We always (attempt to) get the size of downloads, so it is done before
1789
       this even when not doing resumes. */
1790
0
    if(filesize == -1) {
1791
0
      infof(data, "ftp server does not support SIZE");
1792
      /* We could not get the size and therefore we cannot know if there
1793
         really is a part of the file left to get, although the server will
1794
         close the connection when we start the connection so it will not
1795
         cause us any harm, not make us exit as nicely. */
1796
0
    }
1797
0
    else {
1798
      /* We got a file size report, so we check that there actually is a
1799
         part of the file left to get, or else we go home. */
1800
0
      if(data->state.resume_from < 0) {
1801
        /* We are supposed to download the last abs(from) bytes */
1802
0
        if(filesize < -data->state.resume_from) {
1803
0
          failf(data, "Offset (%" FMT_OFF_T
1804
0
                ") was beyond file size (%" FMT_OFF_T ")",
1805
0
                data->state.resume_from, filesize);
1806
0
          return CURLE_BAD_DOWNLOAD_RESUME;
1807
0
        }
1808
        /* convert to size to download */
1809
0
        ftp->downloadsize = -data->state.resume_from;
1810
        /* download from where? */
1811
0
        data->state.resume_from = filesize - ftp->downloadsize;
1812
0
      }
1813
0
      else {
1814
0
        if(filesize < data->state.resume_from) {
1815
0
          failf(data, "Offset (%" FMT_OFF_T
1816
0
                ") was beyond file size (%" FMT_OFF_T ")",
1817
0
                data->state.resume_from, filesize);
1818
0
          return CURLE_BAD_DOWNLOAD_RESUME;
1819
0
        }
1820
        /* Now store the number of bytes we are expected to download */
1821
0
        ftp->downloadsize = filesize - data->state.resume_from;
1822
0
      }
1823
0
    }
1824
1825
0
    if(ftp->downloadsize == 0) {
1826
      /* no data to transfer */
1827
0
      Curl_xfer_setup_nop(data);
1828
0
      infof(data, "File already completely downloaded");
1829
1830
      /* Set ->transfer so that we will not get any error in ftp_done()
1831
       * because we did not transfer the any file */
1832
0
      ftp->transfer = PPTRANSFER_NONE;
1833
0
      ftp_state(data, ftpc, FTP_STOP);
1834
0
      return CURLE_OK;
1835
0
    }
1836
1837
    /* Set resume file transfer offset */
1838
0
    infof(data, "Instructs server to resume from offset %" FMT_OFF_T,
1839
0
          data->state.resume_from);
1840
1841
0
    result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T,
1842
0
                           data->state.resume_from);
1843
0
    if(!result)
1844
0
      ftp_state(data, ftpc, FTP_RETR_REST);
1845
0
  }
1846
0
  else {
1847
    /* no resume */
1848
0
    result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
1849
0
    if(!result)
1850
0
      ftp_state(data, ftpc, FTP_RETR);
1851
0
  }
1852
1853
0
  return result;
1854
0
}
1855
1856
static CURLcode ftp_state_quote(struct Curl_easy *data,
1857
                                struct ftp_conn *ftpc,
1858
                                struct FTP *ftp,
1859
                                bool init,
1860
                                ftpstate instate)
1861
0
{
1862
0
  CURLcode result = CURLE_OK;
1863
0
  bool quote = FALSE;
1864
0
  struct curl_slist *item;
1865
1866
0
  switch(instate) {
1867
0
  case FTP_QUOTE:
1868
0
  default:
1869
0
    item = data->set.quote;
1870
0
    break;
1871
0
  case FTP_RETR_PREQUOTE:
1872
0
  case FTP_STOR_PREQUOTE:
1873
0
  case FTP_LIST_PREQUOTE:
1874
0
    item = data->set.prequote;
1875
0
    break;
1876
0
  case FTP_POSTQUOTE:
1877
0
    item = data->set.postquote;
1878
0
    break;
1879
0
  }
1880
1881
  /*
1882
   * This state uses:
1883
   * 'count1' to iterate over the commands to send
1884
   * 'count2' to store whether to allow commands to fail
1885
   */
1886
1887
0
  if(init)
1888
0
    ftpc->count1 = 0;
1889
0
  else
1890
0
    ftpc->count1++;
1891
1892
0
  if(item) {
1893
0
    int i = 0;
1894
1895
    /* Skip count1 items in the linked list */
1896
0
    while((i < ftpc->count1) && item) {
1897
0
      item = item->next;
1898
0
      i++;
1899
0
    }
1900
0
    if(item) {
1901
0
      const char *cmd = item->data;
1902
0
      if(cmd[0] == '*') {
1903
0
        cmd++;
1904
0
        ftpc->count2 = 1; /* the sent command is allowed to fail */
1905
0
      }
1906
0
      else
1907
0
        ftpc->count2 = 0; /* failure means cancel operation */
1908
1909
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
1910
0
      if(result)
1911
0
        return result;
1912
0
      ftp_state(data, ftpc, instate);
1913
0
      quote = TRUE;
1914
0
    }
1915
0
  }
1916
1917
0
  if(!quote) {
1918
    /* No more quote to send, continue to ... */
1919
0
    switch(instate) {
1920
0
    case FTP_QUOTE:
1921
0
    default:
1922
0
      result = ftp_state_cwd(data, ftpc, ftp);
1923
0
      break;
1924
0
    case FTP_RETR_PREQUOTE:
1925
0
      if(ftp->transfer != PPTRANSFER_BODY)
1926
0
        ftp_state(data, ftpc, FTP_STOP);
1927
0
      else {
1928
0
        if(ftpc->known_filesize != -1) {
1929
0
          Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1930
0
          result = ftp_state_retr(data, ftpc, ftp, ftpc->known_filesize);
1931
0
        }
1932
0
        else {
1933
0
          if(data->set.ignorecl || data->state.prefer_ascii) {
1934
            /* 'ignorecl' is used to support download of growing files. It
1935
               prevents the state machine from requesting the file size from
1936
               the server. With an unknown file size the download continues
1937
               until the server terminates it, otherwise the client stops if
1938
               the received byte count exceeds the reported file size. Set
1939
               option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this
1940
               behavior.
1941
1942
               In addition: asking for the size for 'TYPE A' transfers is not
1943
               constructive since servers do not report the converted size.
1944
               Thus, skip it. */
1945
0
            result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
1946
0
            if(!result)
1947
0
              ftp_state(data, ftpc, FTP_RETR);
1948
0
          }
1949
0
          else {
1950
0
            result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1951
0
            if(!result)
1952
0
              ftp_state(data, ftpc, FTP_RETR_SIZE);
1953
0
          }
1954
0
        }
1955
0
      }
1956
0
      break;
1957
0
    case FTP_STOR_PREQUOTE:
1958
0
      result = ftp_state_ul_setup(data, ftpc, ftp, FALSE);
1959
0
      break;
1960
0
    case FTP_POSTQUOTE:
1961
0
      break;
1962
0
    case FTP_LIST_PREQUOTE:
1963
0
      ftp_state(data, ftpc, FTP_LIST_TYPE);
1964
0
      result = ftp_state_list(data, ftpc, ftp);
1965
0
      break;
1966
0
    }
1967
0
  }
1968
1969
0
  return result;
1970
0
}
1971
1972
/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
1973
   problems */
1974
static CURLcode ftp_epsv_disable(struct Curl_easy *data,
1975
                                 struct ftp_conn *ftpc,
1976
                                 struct connectdata *conn)
1977
0
{
1978
0
  CURLcode result = CURLE_OK;
1979
1980
0
  if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) {
1981
    /* We cannot disable EPSV when doing IPv6, so this is instead a fail */
1982
0
    failf(data, "Failed EPSV attempt, exiting");
1983
0
    return CURLE_WEIRD_SERVER_REPLY;
1984
0
  }
1985
1986
0
  infof(data, "Failed EPSV attempt. Disabling EPSV");
1987
  /* disable it for next transfer */
1988
0
  conn->bits.ftp_use_epsv = FALSE;
1989
0
  close_secondarysocket(data, ftpc);
1990
0
  data->state.errorbuf = FALSE; /* allow error message to get
1991
                                         rewritten */
1992
0
  result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PASV");
1993
0
  if(!result) {
1994
0
    ftpc->count1++;
1995
    /* remain in/go to the FTP_PASV state */
1996
0
    ftp_state(data, ftpc, FTP_PASV);
1997
0
  }
1998
0
  return result;
1999
0
}
2000
2001
static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp)
2002
0
{
2003
0
  struct connectdata *conn = data->conn;
2004
0
  struct ip_quadruple ipquad;
2005
0
  bool is_ipv6;
2006
2007
  /* Returns the control connection IP address.
2008
     If a proxy tunnel is used, returns the original hostname instead, because
2009
     the effective control connection address is the proxy address,
2010
     not the ftp host. */
2011
0
#ifndef CURL_DISABLE_PROXY
2012
0
  if(Curl_conn_is_tunneling(conn, FIRSTSOCKET))
2013
0
    *newhostp = curlx_strdup(conn->origin->hostname);
2014
0
  else
2015
0
#endif
2016
0
  if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) &&
2017
0
     *ipquad.remote_ip)
2018
0
    *newhostp = curlx_strdup(ipquad.remote_ip);
2019
0
  else {
2020
    /* failed to get the remote_ip of the DATA connection */
2021
0
    failf(data, "unable to get peername of DATA connection");
2022
0
    *newhostp = NULL;
2023
0
    return CURLE_FTP_CANT_GET_HOST;
2024
0
  }
2025
0
  return *newhostp ? CURLE_OK : CURLE_OUT_OF_MEMORY;
2026
0
}
2027
2028
static bool match_pasv_6nums(const char *p,
2029
                             unsigned int *array) /* 6 numbers */
2030
0
{
2031
0
  int i;
2032
0
  for(i = 0; i < 6; i++) {
2033
0
    curl_off_t num;
2034
0
    if(i) {
2035
0
      if(*p != ',')
2036
0
        return FALSE;
2037
0
      p++;
2038
0
    }
2039
0
    if(curlx_str_number(&p, &num, 0xff))
2040
0
      return FALSE;
2041
0
    array[i] = (unsigned int)num;
2042
0
  }
2043
0
  return TRUE;
2044
0
}
2045
2046
static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
2047
                                    struct ftp_conn *ftpc,
2048
                                    int ftpcode)
2049
0
{
2050
0
  struct connectdata *conn = data->conn;
2051
0
  CURLcode result;
2052
0
  const struct pingpong *pp = &ftpc->pp;
2053
0
  char *newhost = NULL;
2054
0
  unsigned short newport = 0;
2055
0
  const char *str = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
2056
                                                        letter */
2057
0
  if((ftpc->count1 == 0) &&
2058
0
     (ftpcode == 229)) {
2059
    /* positive EPSV response */
2060
0
    const char *ptr = strchr(str, '(');
2061
0
    if(ptr) {
2062
0
      char sep;
2063
0
      ptr++;
2064
      /* |||12345| */
2065
0
      sep = ptr[0];
2066
0
      if(sep && (ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
2067
0
        const char *p = &ptr[3];
2068
0
        curl_off_t num;
2069
0
        if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) {
2070
0
          failf(data, "Illegal port number in EPSV reply");
2071
0
          return CURLE_FTP_WEIRD_PASV_REPLY;
2072
0
        }
2073
0
        newport = (unsigned short)num;
2074
0
        result = ftp_control_addr_dup(data, &newhost);
2075
0
        if(result)
2076
0
          return result;
2077
0
      }
2078
0
      else
2079
0
        ptr = NULL;
2080
0
    }
2081
0
    if(!ptr) {
2082
0
      failf(data, "Weirdly formatted EPSV reply");
2083
0
      return CURLE_FTP_WEIRD_PASV_REPLY;
2084
0
    }
2085
0
  }
2086
0
  else if((ftpc->count1 == 1) &&
2087
0
          (ftpcode == 227)) {
2088
    /* positive PASV response */
2089
0
    unsigned int ip[6];
2090
2091
    /*
2092
     * Scan for a sequence of six comma-separated numbers and use them as
2093
     * IP+port indicators.
2094
     *
2095
     * Found reply-strings include:
2096
     * "227 Entering Passive Mode (127,0,0,1,4,51)"
2097
     * "227 Data transfer will passively listen to 127,0,0,1,4,51"
2098
     * "227 Entering passive mode. 127,0,0,1,4,51"
2099
     */
2100
0
    while(*str) {
2101
0
      if(match_pasv_6nums(str, ip))
2102
0
        break;
2103
0
      str++;
2104
0
    }
2105
2106
0
    if(!*str) {
2107
0
      failf(data, "Could not interpret the 227-response");
2108
0
      return CURLE_FTP_WEIRD_227_FORMAT;
2109
0
    }
2110
2111
    /* we got OK from server */
2112
0
    if(data->set.ftp_skip_ip) {
2113
      /* told to ignore the remotely given IP but instead use the host we used
2114
         for the control connection */
2115
0
      infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead",
2116
0
            ip[0], ip[1], ip[2], ip[3], conn->origin->hostname);
2117
0
      result = ftp_control_addr_dup(data, &newhost);
2118
0
      if(result)
2119
0
        return result;
2120
0
    }
2121
0
    else
2122
0
      newhost = curl_maprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
2123
2124
0
    if(!newhost)
2125
0
      return CURLE_OUT_OF_MEMORY;
2126
2127
0
    newport = (unsigned short)(((ip[4] << 8) + ip[5]) & 0xffff);
2128
0
  }
2129
0
  else if(ftpc->count1 == 0) {
2130
    /* EPSV failed, move on to PASV */
2131
0
    return ftp_epsv_disable(data, ftpc, conn);
2132
0
  }
2133
0
  else {
2134
0
    failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
2135
0
    return CURLE_FTP_WEIRD_PASV_REPLY;
2136
0
  }
2137
2138
0
  DEBUGASSERT(newhost);
2139
0
  Curl_peer_unlink(&conn->origin2);
2140
0
  result = Curl_peer_create(data, conn->scheme, newhost, newport,
2141
0
                            &conn->origin2);
2142
0
  if(result)
2143
0
    goto error;
2144
2145
  /* If FIRSTSOCKET goes via another peer, SECONDARY needs as well,
2146
   * but with its new port. */
2147
0
  if(conn->via_peer) {
2148
0
    Curl_peer_unlink(&conn->via_peer2);
2149
0
    result = Curl_peer_create(data, conn->via_peer->scheme,
2150
0
                              conn->via_peer->hostname, newport,
2151
0
                              &conn->via_peer2);
2152
0
    if(result)
2153
0
      goto error;
2154
0
  }
2155
2156
0
  result = Curl_conn_setup(data, conn, SECONDARYSOCKET,
2157
0
                           conn->bits.ftp_use_data_ssl ?
2158
0
                           CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
2159
2160
0
  if(result) {
2161
0
    if((result != CURLE_OUT_OF_MEMORY) &&
2162
0
       (ftpc->count1 == 0) && (ftpcode == 229)) {
2163
0
      result = ftp_epsv_disable(data, ftpc, conn);
2164
0
    }
2165
0
    goto error;
2166
0
  }
2167
2168
  /*
2169
   * When this is used from the multi interface, this might have returned with
2170
   * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
2171
   * connect to connect.
2172
   */
2173
2174
0
#ifdef CURLVERBOSE
2175
0
  if(data->set.verbose) {
2176
    /* Dump information about this second connection when we have issued
2177
     * a PASV command. */
2178
0
    infof(data, "Connecting to %s port %d",
2179
0
          conn->origin2->hostname, conn->origin2->port);
2180
0
  }
2181
0
#endif
2182
2183
0
  conn->bits.do_more = TRUE;
2184
0
  ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */
2185
2186
0
error:
2187
0
  curlx_free(newhost);
2188
0
  return result;
2189
0
}
2190
2191
/* called repeatedly until done from multi.c */
2192
static CURLcode ftp_statemach(struct Curl_easy *data,
2193
                              struct ftp_conn *ftpc,
2194
                              bool *done)
2195
0
{
2196
0
  CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
2197
2198
  /* Check for the state outside of the Curl_socket_check() return code checks
2199
     since at times we are in fact already in this state when this function
2200
     gets called. */
2201
0
  *done = (ftpc->state == FTP_STOP);
2202
2203
0
  return result;
2204
0
}
2205
2206
/*
2207
 * ftp_do_more()
2208
 *
2209
 * This function shall be called when the second FTP (data) connection is
2210
 * connected.
2211
 *
2212
 * 'more' can return DOMORE_INCOMPLETE, DOMORE_DONE or DOMORE_GOBACK
2213
 * (which is for when PASV is being sent to retry a failed EPSV).
2214
 */
2215
static CURLcode ftp_do_more(struct Curl_easy *data, domore *more)
2216
0
{
2217
0
  struct connectdata *conn = data->conn;
2218
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
2219
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
2220
0
  CURLcode result = CURLE_OK;
2221
0
  bool connected = FALSE;
2222
0
  bool complete = FALSE;
2223
  /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
2224
   * proxy then the state will not be valid until after that connection is
2225
   * complete */
2226
2227
0
  if(!ftpc || !ftp)
2228
0
    return CURLE_FAILED_INIT;
2229
2230
0
  *more = DOMORE_INCOMPLETE; /* default to stay in the state */
2231
2232
  /* if the second connection has been set up, try to connect it fully
2233
   * to the remote host. This may not complete at this time, for several
2234
   * reasons:
2235
   * - we do EPTR and the server will not connect to our listen socket
2236
   *   until we send more FTP commands
2237
   * - an SSL filter is in place and the server will not start the TLS
2238
   *   handshake until we send more FTP commands
2239
   */
2240
0
  if(conn->cfilter[SECONDARYSOCKET]) {
2241
0
    bool is_eptr = Curl_conn_is_tcp_listen(data, SECONDARYSOCKET);
2242
0
    result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
2243
0
    if(result == CURLE_OUT_OF_MEMORY)
2244
0
      return result;
2245
0
    if(result || (!connected && !is_eptr &&
2246
0
                  !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) {
2247
0
      if(result && !is_eptr && (ftpc->count1 == 0)) {
2248
0
        *more = DOMORE_GOBACK; /* go back to DOING please */
2249
        /* this is a EPSV connect failing, try PASV instead */
2250
0
        return ftp_epsv_disable(data, ftpc, conn);
2251
0
      }
2252
0
      return result;
2253
0
    }
2254
0
  }
2255
2256
0
  if(ftpc->state) {
2257
    /* already in a state so skip the initial commands.
2258
       They are only done to kickstart the do_more state */
2259
0
    result = ftp_statemach(data, ftpc, &complete);
2260
2261
0
    if(complete)
2262
0
      *more = DOMORE_DONE;
2263
2264
    /* if we got an error or if we do not wait for a data connection return
2265
       immediately */
2266
0
    if(result || !ftpc->wait_data_conn)
2267
0
      return result;
2268
2269
    /* if we reach the end of the FTP state machine here, *complete will be
2270
       TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
2271
       data connection and therefore we are not actually complete */
2272
0
    *more = DOMORE_INCOMPLETE;
2273
0
  }
2274
2275
0
  if(ftp->transfer <= PPTRANSFER_INFO) {
2276
    /* a transfer is about to take place, or if not a filename was given so we
2277
       will do a SIZE on it later and then we need the right TYPE first */
2278
2279
0
    if(ftpc->wait_data_conn) {
2280
0
      bool serv_conned;
2281
2282
0
      result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned);
2283
0
      if(result)
2284
0
        return result; /* Failed to accept data connection */
2285
2286
0
      if(serv_conned) {
2287
        /* It looks data connection is established */
2288
0
        ftpc->wait_data_conn = FALSE;
2289
0
        result = ftp_initiate_transfer(data, ftpc);
2290
2291
0
        if(result)
2292
0
          return result;
2293
2294
0
        *more = DOMORE_DONE; /* this state is now complete when the server has
2295
                                connected back to us */
2296
0
      }
2297
0
      else {
2298
0
        result = ftp_check_ctrl_on_data_wait(data, ftpc);
2299
0
        if(result)
2300
0
          return result;
2301
0
      }
2302
0
    }
2303
0
    else if(data->state.upload) {
2304
0
      result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii,
2305
0
                           FTP_STOR_TYPE);
2306
0
      if(result)
2307
0
        return result;
2308
2309
0
      result = ftp_statemach(data, ftpc, &complete);
2310
      /* ftp_nb_type() might have skipped sending `TYPE A|I` when not
2311
       * deemed necessary and directly sent `STORE name`. If this was
2312
       * then complete, but we are still waiting on the data connection,
2313
       * the transfer has not been initiated yet. */
2314
0
      *more = (!ftpc->wait_data_conn && complete) ?
2315
0
        DOMORE_DONE : DOMORE_INCOMPLETE;
2316
0
    }
2317
0
    else {
2318
      /* download */
2319
0
      ftp->downloadsize = -1; /* unknown as of yet */
2320
2321
0
      result = Curl_range(data);
2322
2323
0
      if(result == CURLE_OK && data->req.maxdownload >= 0) {
2324
        /* Do not check for successful transfer */
2325
0
        ftpc->dont_check = TRUE;
2326
0
      }
2327
2328
0
      if(result)
2329
0
        ;
2330
0
      else if((data->state.list_only || !ftpc->file) &&
2331
0
              !data->set.prequote) {
2332
        /* The specified path ends with a slash, and therefore we think this
2333
           is a directory that is requested, use LIST. Before that, we also
2334
           need to set ASCII transfer mode. */
2335
2336
        /* Only if a body transfer was requested. */
2337
0
        if(ftp->transfer == PPTRANSFER_BODY) {
2338
0
          result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE);
2339
0
          if(result)
2340
0
            return result;
2341
0
        }
2342
        /* otherwise fall through */
2343
0
      }
2344
0
      else {
2345
0
        if(data->set.prequote && !ftpc->file) {
2346
0
          result = ftp_nb_type(data, ftpc, ftp, TRUE,
2347
0
                               FTP_RETR_LIST_TYPE);
2348
0
        }
2349
0
        else {
2350
0
          result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii,
2351
0
                               FTP_RETR_TYPE);
2352
0
        }
2353
0
        if(result)
2354
0
          return result;
2355
0
      }
2356
2357
0
      result = ftp_statemach(data, ftpc, &complete);
2358
0
      if(complete)
2359
0
        *more = DOMORE_DONE;
2360
0
    }
2361
0
    return result;
2362
0
  }
2363
2364
  /* no data to transfer */
2365
0
  Curl_xfer_setup_nop(data);
2366
2367
0
  if(!ftpc->wait_data_conn) {
2368
    /* no waiting for the data connection so this is now complete */
2369
0
    *more = DOMORE_DONE;
2370
0
    CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc),
2371
0
                 (int)result);
2372
0
  }
2373
2374
0
  return result;
2375
0
}
2376
2377
/* call this when the DO phase has completed */
2378
static CURLcode ftp_dophase_done(struct Curl_easy *data,
2379
                                 struct ftp_conn *ftpc,
2380
                                 struct FTP *ftp,
2381
                                 bool connected)
2382
0
{
2383
0
  if(connected) {
2384
0
    domore completed;
2385
0
    CURLcode result = ftp_do_more(data, &completed);
2386
2387
0
    if(result) {
2388
0
      close_secondarysocket(data, ftpc);
2389
0
      return result;
2390
0
    }
2391
0
  }
2392
2393
0
  if(ftp->transfer != PPTRANSFER_BODY)
2394
    /* no data to transfer */
2395
0
    Curl_xfer_setup_nop(data);
2396
0
  else if(!connected)
2397
    /* since we did not connect now, we want do_more to get called */
2398
0
    data->conn->bits.do_more = TRUE;
2399
2400
0
  ftpc->ctl_valid = TRUE; /* seems good */
2401
2402
0
  return CURLE_OK;
2403
0
}
2404
2405
static CURLcode ftp_state_port_resp(struct Curl_easy *data,
2406
                                    struct ftp_conn *ftpc,
2407
                                    struct FTP *ftp,
2408
                                    int ftpcode)
2409
0
{
2410
0
  struct connectdata *conn = data->conn;
2411
0
  ftpport fcmd = (ftpport)ftpc->count1;
2412
0
  CURLcode result = CURLE_OK;
2413
2414
  /* The FTP spec tells a positive response should have code 200.
2415
     Be more permissive here to tolerate deviant servers. */
2416
0
  if(ftpcode / 100 != 2) {
2417
    /* the command failed */
2418
2419
0
    if(EPRT == fcmd) {
2420
0
      infof(data, "disabling EPRT usage");
2421
0
      conn->bits.ftp_use_eprt = FALSE;
2422
0
    }
2423
0
    fcmd++;
2424
2425
0
    if(fcmd == DONE) {
2426
0
      failf(data, "Failed to do PORT");
2427
0
      result = CURLE_FTP_PORT_FAILED;
2428
0
    }
2429
0
    else
2430
      /* try next */
2431
0
      result = ftp_state_use_port(data, ftpc, fcmd);
2432
0
  }
2433
0
  else {
2434
0
    infof(data, "Connect data stream actively");
2435
0
    ftp_state(data, ftpc, FTP_STOP); /* end of DO phase */
2436
0
    result = ftp_dophase_done(data, ftpc, ftp, FALSE);
2437
0
  }
2438
2439
0
  return result;
2440
0
}
2441
2442
/* return TRUE on error, FALSE on success */
2443
static bool twodigit(const char *p, int *val)
2444
0
{
2445
0
  if(!ISDIGIT(p[0]) || !ISDIGIT(p[1]))
2446
0
    return TRUE;
2447
  /* curlx_hexval() works fine here since we make sure it is decimal above */
2448
0
  *val = (curlx_hexval(p[0]) * 10) + curlx_hexval(p[1]);
2449
0
  return FALSE;
2450
0
}
2451
2452
/*
2453
 * @unittest 1668
2454
 */
2455
UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day,
2456
                           int *hour, int *minute, int *second);
2457
UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day,
2458
                           int *hour, int *minute, int *second)
2459
0
{
2460
0
  int century;
2461
0
  if((strlen(p) < 14) || twodigit(&p[0], &century) || twodigit(&p[2], year) ||
2462
0
     twodigit(&p[4], month) || twodigit(&p[6], day) ||
2463
0
     twodigit(&p[8], hour) || twodigit(&p[10], minute) ||
2464
0
     twodigit(&p[12], second))
2465
0
    return FALSE;
2466
2467
0
  *year += century * 100;
2468
0
  if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) ||
2469
0
     (*second > 60))
2470
0
    return FALSE;
2471
0
  return TRUE;
2472
0
}
2473
2474
static CURLcode client_write_header(struct Curl_easy *data,
2475
                                    char *buf, size_t blen)
2476
0
{
2477
  /* Some replies from an FTP server are written to the client
2478
   * as CLIENTWRITE_HEADER, formatted as if they came from a
2479
   * HTTP conversation.
2480
   * In all protocols, CLIENTWRITE_HEADER data is only passed to
2481
   * the body write callback when data->set.include_header is set
2482
   * via CURLOPT_HEADER.
2483
   * For historic reasons, FTP never played this game and expects
2484
   * all its headers to do that always. Set that flag during the
2485
   * call to Curl_client_write() so it does the right thing.
2486
   *
2487
   * Notice that we cannot enable this flag for FTP in general,
2488
   * as an FTP transfer might involve an HTTP proxy connection and
2489
   * headers from CONNECT should not automatically be part of the
2490
   * output. */
2491
0
  CURLcode result;
2492
0
  bool save = (bool)data->set.include_header;
2493
0
  data->set.include_header = TRUE;
2494
0
  result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen);
2495
0
  data->set.include_header = save;
2496
0
  return result;
2497
0
}
2498
2499
static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
2500
                                    struct ftp_conn *ftpc,
2501
                                    struct FTP *ftp,
2502
                                    int ftpcode)
2503
0
{
2504
0
  CURLcode result = CURLE_OK;
2505
2506
0
  switch(ftpcode) {
2507
0
  case 213: {
2508
    /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2509
       last .sss part is optional and means fractions of a second */
2510
0
    int year, month, day, hour, minute, second;
2511
0
    struct pingpong *pp = &ftpc->pp;
2512
0
    const char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4;
2513
0
    bool showtime = FALSE;
2514
0
    if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
2515
      /* we have a time, reformat it */
2516
0
      char timebuf[24];
2517
0
      curl_msnprintf(timebuf, sizeof(timebuf),
2518
0
                     "%04d%02d%02d %02d:%02d:%02d GMT",
2519
0
                     year, month, day, hour, minute, second);
2520
      /* now, convert this into a time() value: */
2521
0
      if(!Curl_getdate_capped(timebuf, &data->info.filetime))
2522
0
        showtime = TRUE;
2523
0
    }
2524
2525
    /* If we asked for a time of the file and we actually got one as well,
2526
       we "emulate" an HTTP-style header in our output. */
2527
2528
#if defined(CURL_HAVE_DIAG) && (defined(__DJGPP__) || defined(__AMIGA__))
2529
#pragma GCC diagnostic push
2530
/* 'time_t' is unsigned in MSDOS and AmigaOS. Silence:
2531
   warning: comparison of unsigned expression in '>= 0' is always true */
2532
#pragma GCC diagnostic ignored "-Wtype-limits"
2533
#endif
2534
0
    if(data->req.no_body && ftpc->file &&
2535
0
       data->set.get_filetime && showtime) {
2536
#if defined(CURL_HAVE_DIAG) && (defined(__DJGPP__) || defined(__AMIGA__))
2537
#pragma GCC diagnostic pop
2538
#endif
2539
0
      char headerbuf[128];
2540
0
      int headerbuflen;
2541
0
      time_t filetime = data->info.filetime;
2542
0
      struct tm buffer;
2543
0
      const struct tm *tm = &buffer;
2544
2545
0
      result = curlx_gmtime(filetime, &buffer);
2546
0
      if(result)
2547
0
        return result;
2548
2549
      /* format: "Tue, 15 Nov 1994 12:45:26" */
2550
0
      headerbuflen =
2551
0
        curl_msnprintf(headerbuf, sizeof(headerbuf),
2552
0
                       "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d "
2553
0
                       "GMT\r\n",
2554
0
                       Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6],
2555
0
                       tm->tm_mday,
2556
0
                       Curl_month[tm->tm_mon],
2557
0
                       tm->tm_year + 1900,
2558
0
                       tm->tm_hour,
2559
0
                       tm->tm_min,
2560
0
                       tm->tm_sec);
2561
0
      result = client_write_header(data, headerbuf, headerbuflen);
2562
0
      if(result)
2563
0
        return result;
2564
0
    } /* end of a ridiculous amount of conditionals */
2565
0
  }
2566
0
    break;
2567
0
  default:
2568
0
    infof(data, "unsupported MDTM reply format");
2569
0
    break;
2570
0
  case 550: /* 550 is used for several different problems, e.g.
2571
               "No such file or directory" or "Permission denied".
2572
               It does not mean that the file does not exist at all. */
2573
0
    infof(data, "MDTM failed: file does not exist or permission problem,"
2574
0
          " continuing");
2575
0
    break;
2576
0
  }
2577
2578
0
  if(data->set.timecondition) {
2579
0
    if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2580
0
      switch(data->set.timecondition) {
2581
0
      case CURL_TIMECOND_IFMODSINCE:
2582
0
      default:
2583
0
        if(data->info.filetime <= data->set.timevalue) {
2584
0
          infof(data, "The requested document is not new enough");
2585
0
          ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
2586
0
          data->info.timecond = TRUE;
2587
0
          ftp_state(data, ftpc, FTP_STOP);
2588
0
          return CURLE_OK;
2589
0
        }
2590
0
        break;
2591
0
      case CURL_TIMECOND_IFUNMODSINCE:
2592
0
        if(data->info.filetime > data->set.timevalue) {
2593
0
          infof(data, "The requested document is not old enough");
2594
0
          ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
2595
0
          data->info.timecond = TRUE;
2596
0
          ftp_state(data, ftpc, FTP_STOP);
2597
0
          return CURLE_OK;
2598
0
        }
2599
0
        break;
2600
0
      } /* switch */
2601
0
    }
2602
0
    else {
2603
0
      infof(data, "Skipping time comparison");
2604
0
    }
2605
0
  }
2606
2607
0
  if(!result)
2608
0
    result = ftp_state_type(data, ftpc, ftp);
2609
2610
0
  return result;
2611
0
}
2612
2613
static CURLcode ftp_state_type_resp(struct Curl_easy *data,
2614
                                    struct ftp_conn *ftpc,
2615
                                    struct FTP *ftp,
2616
                                    int ftpcode,
2617
                                    ftpstate instate)
2618
0
{
2619
0
  CURLcode result = CURLE_OK;
2620
2621
0
  if(ftpcode / 100 != 2) {
2622
    /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2623
       successful 'TYPE I'. While that is not as RFC959 says, it is still a
2624
       positive response code and we allow that. */
2625
0
    failf(data, "Could not set desired mode");
2626
0
    return CURLE_FTP_COULDNT_SET_TYPE;
2627
0
  }
2628
0
  if(ftpcode != 200)
2629
0
    infof(data, "Got a %03d response code instead of the assumed 200",
2630
0
          ftpcode);
2631
2632
0
  if(instate == FTP_TYPE)
2633
0
    result = ftp_state_size(data, ftpc, ftp);
2634
0
  else if(instate == FTP_LIST_TYPE)
2635
0
    result = ftp_state_list(data, ftpc, ftp);
2636
0
  else if(instate == FTP_RETR_TYPE)
2637
0
    result = ftp_state_retr_prequote(data, ftpc, ftp);
2638
0
  else if(instate == FTP_STOR_TYPE)
2639
0
    result = ftp_state_stor_prequote(data, ftpc, ftp);
2640
0
  else if(instate == FTP_RETR_LIST_TYPE)
2641
0
    result = ftp_state_list_prequote(data, ftpc, ftp);
2642
2643
0
  return result;
2644
0
}
2645
2646
static CURLcode ftp_state_size_resp(struct Curl_easy *data,
2647
                                    struct ftp_conn *ftpc,
2648
                                    struct FTP *ftp,
2649
                                    int ftpcode,
2650
                                    ftpstate instate)
2651
0
{
2652
0
  CURLcode result = CURLE_OK;
2653
0
  curl_off_t filesize = -1;
2654
0
  const char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
2655
0
  size_t len = ftpc->pp.nfinal;
2656
2657
  /* get the size from the ascii string: */
2658
0
  if(ftpcode == 213) {
2659
    /* To allow servers to prepend "rubbish" in the response string, we scan
2660
       for all the digits at the end of the response and parse only those as a
2661
       number. */
2662
0
    const char *start = &buf[4];
2663
0
    const char *fdigit = memchr(start, '\r', len - 4);
2664
0
    if(fdigit) {
2665
0
      fdigit--;
2666
0
      if(*fdigit == '\n')
2667
0
        fdigit--;
2668
0
      while(ISDIGIT(fdigit[-1]) && (fdigit > start))
2669
0
        fdigit--;
2670
0
    }
2671
0
    else
2672
0
      fdigit = start;
2673
0
    if(curlx_str_number(&fdigit, &filesize, CURL_OFF_T_MAX))
2674
0
      filesize = -1; /* size remain unknown */
2675
0
  }
2676
0
  else if(ftpcode == 550 && /* "No such file or directory" */
2677
          /* allow a SIZE failure for (resumed) uploads, when probing what
2678
             command to use */
2679
0
          instate != FTP_STOR_SIZE) {
2680
0
    failf(data, "The file does not exist");
2681
0
    return CURLE_REMOTE_FILE_NOT_FOUND;
2682
0
  }
2683
2684
0
  if(instate == FTP_SIZE) {
2685
0
    if(filesize != -1) {
2686
0
      char clbuf[128];
2687
0
      int clbuflen = curl_msnprintf(clbuf, sizeof(clbuf),
2688
0
                                    "Content-Length: %" FMT_OFF_T "\r\n",
2689
0
                                    filesize);
2690
0
      result = client_write_header(data, clbuf, clbuflen);
2691
0
      if(result)
2692
0
        return result;
2693
0
    }
2694
0
    Curl_pgrsSetDownloadSize(data, filesize);
2695
0
    result = ftp_state_rest(data, ftpc, ftp);
2696
0
  }
2697
0
  else if(instate == FTP_RETR_SIZE) {
2698
0
    Curl_pgrsSetDownloadSize(data, filesize);
2699
0
    result = ftp_state_retr(data, ftpc, ftp, filesize);
2700
0
  }
2701
0
  else if(instate == FTP_STOR_SIZE) {
2702
0
    data->state.resume_from = filesize;
2703
0
    result = ftp_state_ul_setup(data, ftpc, ftp, TRUE);
2704
0
  }
2705
2706
0
  return result;
2707
0
}
2708
2709
static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
2710
                                    struct ftp_conn *ftpc,
2711
                                    struct FTP *ftp,
2712
                                    int ftpcode,
2713
                                    ftpstate instate)
2714
0
{
2715
0
  CURLcode result = CURLE_OK;
2716
2717
0
  switch(instate) {
2718
0
  case FTP_REST:
2719
0
  default:
2720
0
    if(ftpcode == 350) {
2721
0
      char buffer[24] = { "Accept-ranges: bytes\r\n" };
2722
0
      result = client_write_header(data, buffer, strlen(buffer));
2723
0
      if(result)
2724
0
        return result;
2725
0
    }
2726
0
    result = ftp_state_prepare_transfer(data, ftpc, ftp);
2727
0
    break;
2728
2729
0
  case FTP_RETR_REST:
2730
0
    if(ftpcode != 350) {
2731
0
      failf(data, "Could not use REST");
2732
0
      result = CURLE_FTP_COULDNT_USE_REST;
2733
0
    }
2734
0
    else {
2735
0
      result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
2736
0
      if(!result)
2737
0
        ftp_state(data, ftpc, FTP_RETR);
2738
0
    }
2739
0
    break;
2740
0
  }
2741
2742
0
  return result;
2743
0
}
2744
2745
static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
2746
                                    struct ftp_conn *ftpc,
2747
                                    int ftpcode)
2748
0
{
2749
0
  CURLcode result = CURLE_OK;
2750
2751
0
  if(ftpcode >= 400) {
2752
0
    failf(data, "Failed FTP upload: %0d", ftpcode);
2753
0
    ftp_state(data, ftpc, FTP_STOP);
2754
0
    return CURLE_UPLOAD_FAILED;
2755
0
  }
2756
2757
  /* PORT means we are now awaiting the server to connect to us. */
2758
0
  if(data->set.ftp_use_port) {
2759
0
    bool connected;
2760
2761
0
    ftp_state(data, ftpc, FTP_STOP); /* no longer in STOR state */
2762
2763
0
    result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
2764
0
    if(result)
2765
0
      return result;
2766
2767
0
    if(!connected) {
2768
0
      infof(data, "Data conn was not available immediately");
2769
0
      ftpc->wait_data_conn = TRUE;
2770
0
      return ftp_check_ctrl_on_data_wait(data, ftpc);
2771
0
    }
2772
0
    ftpc->wait_data_conn = FALSE;
2773
0
  }
2774
0
  return ftp_initiate_transfer(data, ftpc);
2775
0
}
2776
2777
/* for LIST and RETR responses */
2778
static CURLcode ftp_state_get_resp(struct Curl_easy *data,
2779
                                   struct ftp_conn *ftpc,
2780
                                   struct FTP *ftp,
2781
                                   int ftpcode,
2782
                                   ftpstate instate)
2783
0
{
2784
0
  CURLcode result = CURLE_OK;
2785
2786
0
  if((ftpcode == 150) || (ftpcode == 125)) {
2787
2788
    /*
2789
      A;
2790
      150 Opening BINARY mode data connection for /etc/passwd (2241
2791
      bytes).  (ok, the file is being transferred)
2792
2793
      B:
2794
      150 Opening ASCII mode data connection for /bin/ls
2795
2796
      C:
2797
      150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2798
2799
      D:
2800
      150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2801
2802
      E:
2803
      125 Data connection already open; Transfer starting. */
2804
2805
0
    data->req.size = -1; /* default unknown size */
2806
2807
    /*
2808
     * It appears that there are FTP-servers that return size 0 for files when
2809
     * SIZE is used on the file while being in BINARY mode. To work around
2810
     * that (stupid) behavior, we attempt to parse the RETR response even if
2811
     * the SIZE returned size zero.
2812
     *
2813
     * Debugging help from Salvatore Sorrentino on February 26, 2003.
2814
     */
2815
2816
0
    if((instate != FTP_LIST) &&
2817
0
       !data->state.prefer_ascii &&
2818
0
       !data->set.ignorecl &&
2819
0
       (ftp->downloadsize < 1)) {
2820
      /*
2821
       * It seems directory listings either do not show the size or often uses
2822
       * size 0 anyway. ASCII transfers may cause that the transferred amount
2823
       * of data is not the same as this line tells, why using this number in
2824
       * those cases only confuses us.
2825
       *
2826
       * Example D above makes this parsing a little tricky */
2827
0
      size_t len = curlx_dyn_len(&ftpc->pp.recvbuf);
2828
0
      if(len >= 7) { /* "1 bytes" is 7 characters */
2829
0
        size_t i;
2830
0
        for(i = 0; i < len - 7; i++) {
2831
0
          curl_off_t what;
2832
0
          const char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
2833
0
          const char *c = &buf[i];
2834
0
          if(!curlx_str_number(&c, &what, CURL_OFF_T_MAX) &&
2835
0
             !curlx_str_single(&c, ' ') &&
2836
0
             !strncmp(c, "bytes", 5)) {
2837
0
            data->req.size = what;
2838
0
            break;
2839
0
          }
2840
0
        }
2841
0
      }
2842
0
    }
2843
0
    else if(ftp->downloadsize > -1)
2844
0
      data->req.size = ftp->downloadsize;
2845
2846
0
    if(data->req.size > data->req.maxdownload && data->req.maxdownload > 0)
2847
0
      data->req.size = data->req.maxdownload;
2848
0
    else if((instate != FTP_LIST) && data->state.prefer_ascii)
2849
0
      data->req.size = -1; /* for servers that understate ASCII mode file
2850
                              size */
2851
2852
0
    infof(data, "Maxdownload = %" FMT_OFF_T, data->req.maxdownload);
2853
2854
0
    if(instate != FTP_LIST)
2855
0
      infof(data, "Getting file with size: %" FMT_OFF_T, data->req.size);
2856
2857
0
    if(data->set.ftp_use_port) {
2858
0
      bool connected;
2859
2860
0
      result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
2861
0
      if(result)
2862
0
        return result;
2863
2864
0
      if(!connected) {
2865
0
        infof(data, "Data conn was not available immediately");
2866
0
        ftp_state(data, ftpc, FTP_STOP);
2867
0
        ftpc->wait_data_conn = TRUE;
2868
0
        return ftp_check_ctrl_on_data_wait(data, ftpc);
2869
0
      }
2870
0
      ftpc->wait_data_conn = FALSE;
2871
0
    }
2872
0
    return ftp_initiate_transfer(data, ftpc);
2873
0
  }
2874
0
  else {
2875
0
    if((instate == FTP_LIST) && (ftpcode == 450)) {
2876
      /* no matching files in the directory listing */
2877
0
      ftp->transfer = PPTRANSFER_NONE; /* do not download anything */
2878
0
      ftp_state(data, ftpc, FTP_STOP); /* this phase is over */
2879
0
    }
2880
0
    else {
2881
0
      failf(data, "RETR response: %03d", ftpcode);
2882
0
      return instate == FTP_RETR && ftpcode == 550 ?
2883
0
        CURLE_REMOTE_FILE_NOT_FOUND :
2884
0
        CURLE_FTP_COULDNT_RETR_FILE;
2885
0
    }
2886
0
  }
2887
2888
0
  return result;
2889
0
}
2890
2891
/* after USER, PASS and ACCT */
2892
static CURLcode ftp_state_loggedin(struct Curl_easy *data,
2893
                                   struct ftp_conn *ftpc)
2894
0
{
2895
0
  CURLcode result = CURLE_OK;
2896
2897
0
  if(Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
2898
    /* PBSZ = PROTECTION BUFFER SIZE.
2899
2900
    The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2901
2902
    Specifically, the PROT command MUST be preceded by a PBSZ
2903
    command and a PBSZ command MUST be preceded by a successful
2904
    security data exchange (the TLS negotiation in this case)
2905
2906
    ... (and on page 8):
2907
2908
    Thus the PBSZ command must still be issued, but must have a
2909
    parameter of '0' to indicate that no buffering is taking place
2910
    and the data connection should not be encapsulated.
2911
    */
2912
0
    result = Curl_pp_sendf(data, &ftpc->pp, "PBSZ %d", 0);
2913
0
    if(!result)
2914
0
      ftp_state(data, ftpc, FTP_PBSZ);
2915
0
  }
2916
0
  else {
2917
0
    result = ftp_state_pwd(data, ftpc);
2918
0
  }
2919
0
  return result;
2920
0
}
2921
2922
/* A value that becomes part of an FTP control command must not carry a
2923
   control byte: a CR or LF would end the command line and let a second
2924
   command be smuggled onto the control connection. */
2925
static bool ftp_has_ctrl(const char *string)
2926
0
{
2927
0
  const unsigned char *s = (const unsigned char *)string;
2928
0
  while(*s) {
2929
0
    if(*s < 0x20)
2930
0
      return TRUE;
2931
0
    s++;
2932
0
  }
2933
0
  return FALSE;
2934
0
}
2935
2936
/* for USER and PASS responses */
2937
static CURLcode ftp_state_user_resp(struct Curl_easy *data,
2938
                                    struct ftp_conn *ftpc,
2939
                                    int ftpcode)
2940
0
{
2941
0
  CURLcode result = CURLE_OK;
2942
2943
  /* some need password anyway, and others return 2xx ignored */
2944
0
  if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2945
    /* 331 Password required for ...
2946
       (the server requires to send the user's password too) */
2947
0
    result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
2948
0
                           Curl_creds_passwd(data->conn->creds));
2949
0
    if(!result)
2950
0
      ftp_state(data, ftpc, FTP_PASS);
2951
0
  }
2952
0
  else if(ftpcode / 100 == 2) {
2953
    /* 230 User ... logged in.
2954
       (the user logged in with or without password) */
2955
0
    result = ftp_state_loggedin(data, ftpc);
2956
0
  }
2957
0
  else if(ftpcode == 332) {
2958
0
    const char *account = CURL_EASY_STR(data, STRING_FTP_ACCOUNT);
2959
0
    if(!account) {
2960
0
      failf(data, "ACCT requested but none available");
2961
0
      result = CURLE_LOGIN_DENIED;
2962
0
    }
2963
0
    else if(ftp_has_ctrl(account)) {
2964
0
      failf(data, "Control byte in FTP account");
2965
0
      result = CURLE_BAD_FUNCTION_ARGUMENT;
2966
0
    }
2967
0
    else {
2968
0
      result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", account);
2969
0
      if(!result)
2970
0
        ftp_state(data, ftpc, FTP_ACCT);
2971
0
    }
2972
0
  }
2973
0
  else {
2974
    /* All other response codes, like:
2975
2976
    530 User ... access denied
2977
    (the server denies to log the specified user) */
2978
2979
0
    const char *alt = CURL_EASY_STR(data, STRING_FTP_ALTERNATIVE_TO_USER);
2980
0
    if(alt && !ftpc->ftp_trying_alternative) {
2981
      /* Ok, USER failed. Let's try the supplied command. */
2982
0
      if(ftp_has_ctrl(alt)) {
2983
0
        failf(data, "Control byte in FTP alternative-to-user command");
2984
0
        result = CURLE_BAD_FUNCTION_ARGUMENT;
2985
0
      }
2986
0
      else {
2987
0
        result = Curl_pp_sendf(data, &ftpc->pp, "%s", alt);
2988
0
        if(!result) {
2989
0
          ftpc->ftp_trying_alternative = TRUE;
2990
0
          ftp_state(data, ftpc, FTP_USER);
2991
0
        }
2992
0
      }
2993
0
    }
2994
0
    else {
2995
0
      failf(data, "Access denied: %03d", ftpcode);
2996
0
      result = CURLE_LOGIN_DENIED;
2997
0
    }
2998
0
  }
2999
0
  return result;
3000
0
}
3001
3002
/* for ACCT response */
3003
static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
3004
                                    struct ftp_conn *ftpc,
3005
                                    int ftpcode)
3006
0
{
3007
0
  CURLcode result = CURLE_OK;
3008
0
  if(ftpcode != 230) {
3009
0
    failf(data, "ACCT rejected by server: %03d", ftpcode);
3010
0
    result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
3011
0
  }
3012
0
  else
3013
0
    result = ftp_state_loggedin(data, ftpc);
3014
3015
0
  return result;
3016
0
}
3017
3018
static CURLcode ftp_pwd_resp(struct Curl_easy *data,
3019
                             struct ftp_conn *ftpc,
3020
                             int ftpcode)
3021
0
{
3022
0
  struct pingpong *pp = &ftpc->pp;
3023
0
  CURLcode result;
3024
3025
0
  if(ftpcode == 257) {
3026
0
    const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
3027
                                                          letter */
3028
0
    bool entry_extracted = FALSE;
3029
0
    struct dynbuf out;
3030
0
    curlx_dyn_init(&out, 1000);
3031
3032
    /* Reply format is like
3033
       257<space>[rubbish]"<directory-name>"<space><commentary> and the
3034
       RFC959 says
3035
3036
       The directory name can contain any character; embedded
3037
       double-quotes should be escaped by double-quotes (the
3038
       "quote-doubling" convention).
3039
     */
3040
3041
    /* scan for the first double-quote for non-standard responses */
3042
0
    while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
3043
0
      ptr++;
3044
3045
0
    if('\"' == *ptr) {
3046
      /* it started good */
3047
0
      for(ptr++; *ptr; ptr++) {
3048
0
        if('\"' == *ptr) {
3049
0
          if('\"' == ptr[1]) {
3050
            /* "quote-doubling" */
3051
0
            result = curlx_dyn_addn(&out, &ptr[1], 1);
3052
0
            ptr++;
3053
0
          }
3054
0
          else {
3055
            /* end of path */
3056
0
            if(curlx_dyn_len(&out))
3057
0
              entry_extracted = TRUE;
3058
0
            break; /* get out of this loop */
3059
0
          }
3060
0
        }
3061
0
        else {
3062
0
          if(ISCNTRL(*ptr)) {
3063
            /* control characters have no business in a path */
3064
0
            curlx_dyn_free(&out);
3065
0
            return CURLE_WEIRD_SERVER_REPLY;
3066
0
          }
3067
0
          result = curlx_dyn_addn(&out, ptr, 1);
3068
0
        }
3069
0
        if(result) {
3070
0
          curlx_dyn_free(&out);
3071
0
          return result;
3072
0
        }
3073
0
      }
3074
0
    }
3075
0
    if(entry_extracted) {
3076
      /* If the path name does not look like an absolute path (i.e.: it
3077
         does not start with a '/'), we probably need some server-dependent
3078
         adjustments. For example, this is the case when connecting to
3079
         an OS400 FTP server: this server supports two name syntaxes,
3080
         the default one being incompatible with standard paths. In
3081
         addition, this server switches automatically to the regular path
3082
         syntax when one is encountered in a command: this results in
3083
         having an entrypath in the wrong syntax when later used in CWD.
3084
         The method used here is to check the server OS: we do it only
3085
         if the path name looks strange to minimize overhead on other
3086
         systems. */
3087
0
      char *dir = curlx_dyn_ptr(&out);
3088
3089
0
      if(!ftpc->server_os && dir[0] != '/') {
3090
0
        result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
3091
0
        if(result) {
3092
0
          curlx_dyn_free(&out);
3093
0
          return result;
3094
0
        }
3095
0
      }
3096
3097
0
      curlx_free(ftpc->entrypath);
3098
0
      ftpc->entrypath = dir; /* remember this */
3099
0
      infof(data, "Entry path is '%s'", ftpc->entrypath);
3100
      /* also save it where getinfo can access it: */
3101
0
      curlx_free(data->state.most_recent_ftp_entrypath);
3102
0
      data->state.most_recent_ftp_entrypath = curlx_strdup(ftpc->entrypath);
3103
0
      if(!data->state.most_recent_ftp_entrypath)
3104
0
        return CURLE_OUT_OF_MEMORY;
3105
3106
0
      if(!ftpc->server_os && dir[0] != '/') {
3107
0
        ftp_state(data, ftpc, FTP_SYST);
3108
0
        return CURLE_OK;
3109
0
      }
3110
0
    }
3111
0
    else {
3112
      /* could not get the path */
3113
0
      curlx_dyn_free(&out);
3114
0
      infof(data, "Failed to figure out path");
3115
0
    }
3116
0
  }
3117
0
  ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
3118
0
  CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
3119
0
  return CURLE_OK;
3120
0
}
3121
3122
static const char * const ftpauth[] = { "SSL", "TLS" };
3123
3124
static CURLcode ftp_wait_resp(struct Curl_easy *data,
3125
                              struct connectdata *conn,
3126
                              struct ftp_conn *ftpc,
3127
                              int ftpcode)
3128
0
{
3129
0
  CURLcode result = CURLE_OK;
3130
0
  if(ftpcode == 230) {
3131
    /* 230 User logged in - already! Take as 220 if TLS required. */
3132
0
    if(ftpc->use_ssl <= CURLUSESSL_TRY ||
3133
0
       Curl_conn_is_ssl(conn, FIRSTSOCKET))
3134
0
      return ftp_state_user_resp(data, ftpc, ftpcode);
3135
0
  }
3136
0
  else if(ftpcode != 220) {
3137
0
    failf(data, "Got a %03d ftp-server response when 220 was expected",
3138
0
          ftpcode);
3139
0
    return CURLE_WEIRD_SERVER_REPLY;
3140
0
  }
3141
3142
0
  if(ftpc->use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
3143
    /* We do not have an SSL/TLS control connection yet, but FTPS is
3144
       requested. Try an FTPS connection now */
3145
3146
0
    ftpc->count3 = 0;
3147
0
    switch((long)data->set.ftpsslauth) {
3148
0
    case CURLFTPAUTH_DEFAULT:
3149
0
    case CURLFTPAUTH_SSL:
3150
0
      ftpc->count2 = 1; /* add one to get next */
3151
0
      ftpc->count1 = 0;
3152
0
      break;
3153
0
    case CURLFTPAUTH_TLS:
3154
0
      ftpc->count2 = -1; /* subtract one to get next */
3155
0
      ftpc->count1 = 1;
3156
0
      break;
3157
0
    default:
3158
0
      failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
3159
0
            (int)data->set.ftpsslauth);
3160
0
      return CURLE_UNKNOWN_OPTION; /* we do not know what to do */
3161
0
    }
3162
0
    result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
3163
0
    if(!result)
3164
0
      ftp_state(data, ftpc, FTP_AUTH);
3165
0
  }
3166
0
  else
3167
0
    result = ftp_state_user(data, ftpc, conn);
3168
0
  return result;
3169
0
}
3170
3171
static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
3172
                                    struct connectdata *conn)
3173
0
{
3174
0
  CURLcode result;
3175
0
  int ftpcode;
3176
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
3177
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
3178
0
  struct pingpong *pp;
3179
0
  size_t nread = 0;
3180
3181
0
  if(!ftpc || !ftp)
3182
0
    return CURLE_FAILED_INIT;
3183
0
  pp = &ftpc->pp;
3184
0
  if(pp->sendleft)
3185
0
    return Curl_pp_flushsend(data, pp);
3186
3187
0
  result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, &ftpcode, &nread);
3188
0
  if(result || !ftpcode)
3189
0
    return result;
3190
3191
  /* we have now received a full FTP server response */
3192
0
  switch(ftpc->state) {
3193
0
  case FTP_WAIT220:
3194
0
    result = ftp_wait_resp(data, conn, ftpc, ftpcode);
3195
0
    break;
3196
3197
0
  case FTP_AUTH:
3198
    /* we have gotten the response to a previous AUTH command */
3199
3200
0
    if(pp->overflow)
3201
0
      return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
3202
3203
    /* RFC2228 (page 5) says:
3204
     *
3205
     * If the server is willing to accept the named security mechanism,
3206
     * and does not require any security data, it must respond with
3207
     * reply code 234/334.
3208
     */
3209
3210
0
    if((ftpcode == 234) || (ftpcode == 334)) {
3211
      /* this was BLOCKING, keep it so for now */
3212
0
      bool done;
3213
0
      if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
3214
0
        result = Curl_ssl_cfilter_add(
3215
0
          data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
3216
0
        if(result) {
3217
          /* we failed and bail out */
3218
0
          return CURLE_USE_SSL_FAILED;
3219
0
        }
3220
0
      }
3221
      /* BLOCKING */
3222
0
      result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
3223
0
      if(!result) {
3224
0
        conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
3225
0
        result = ftp_state_user(data, ftpc, conn);
3226
0
      }
3227
0
    }
3228
0
    else if(ftpc->count3 < 1) {
3229
0
      ftpc->count3++;
3230
0
      ftpc->count1 += ftpc->count2; /* get next attempt */
3231
0
      result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
3232
0
                             ftpauth[ftpc->count1]);
3233
      /* remain in this same state */
3234
0
    }
3235
0
    else {
3236
0
      if(ftpc->use_ssl > CURLUSESSL_TRY)
3237
        /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
3238
0
        result = CURLE_USE_SSL_FAILED;
3239
0
      else
3240
        /* ignore the failure and continue */
3241
0
        result = ftp_state_user(data, ftpc, conn);
3242
0
    }
3243
0
    break;
3244
3245
0
  case FTP_USER:
3246
0
  case FTP_PASS:
3247
0
    result = ftp_state_user_resp(data, ftpc, ftpcode);
3248
0
    break;
3249
3250
0
  case FTP_ACCT:
3251
0
    result = ftp_state_acct_resp(data, ftpc, ftpcode);
3252
0
    break;
3253
3254
0
  case FTP_PBSZ:
3255
0
    result =
3256
0
      Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
3257
0
                    ftpc->use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
3258
0
    if(!result)
3259
0
      ftp_state(data, ftpc, FTP_PROT);
3260
0
    break;
3261
3262
0
  case FTP_PROT:
3263
0
    if(ftpcode / 100 == 2)
3264
      /* We have enabled SSL for the data connection! */
3265
0
      conn->bits.ftp_use_data_ssl = (ftpc->use_ssl != CURLUSESSL_CONTROL);
3266
    /* FTP servers typically responds with 500 if they decide to reject
3267
       our 'P' request */
3268
0
    else if(ftpc->use_ssl > CURLUSESSL_CONTROL)
3269
      /* we failed and bails out */
3270
0
      return CURLE_USE_SSL_FAILED;
3271
3272
0
    if(data->set.ftp_ccc) {
3273
      /* CCC - Clear Command Channel
3274
       */
3275
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
3276
0
      if(!result)
3277
0
        ftp_state(data, ftpc, FTP_CCC);
3278
0
    }
3279
0
    else
3280
0
      result = ftp_state_pwd(data, ftpc);
3281
0
    break;
3282
3283
0
  case FTP_CCC:
3284
0
    if(ftpcode < 500) {
3285
      /* First shut down the SSL layer (note: this call will block) */
3286
      /* This has only been tested on the proftpd server, and the mod_tls
3287
       * code sends a close notify alert without waiting for a close notify
3288
       * alert in response. Thus we wait for a close notify alert from the
3289
       * server, but we do not send one. Let's hope other servers do
3290
       * the same... */
3291
0
      result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET,
3292
0
                                       (data->set.ftp_ccc ==
3293
0
                                        (unsigned char)CURLFTPSSL_CCC_ACTIVE));
3294
0
      if(result)
3295
0
        failf(data, "Failed to clear the command channel (CCC)");
3296
0
    }
3297
0
    if(!result)
3298
      /* Then continue as normal */
3299
0
      result = ftp_state_pwd(data, ftpc);
3300
0
    break;
3301
3302
0
  case FTP_PWD:
3303
0
    result = ftp_pwd_resp(data, ftpc, ftpcode);
3304
0
    break;
3305
3306
0
  case FTP_SYST:
3307
0
    if(ftpcode == 215) {
3308
0
      const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
3309
                                                            letter */
3310
0
      const char *start;
3311
0
      char *os;
3312
3313
      /* Reply format is like
3314
         215<space><OS-name><space><commentary>
3315
       */
3316
0
      while(*ptr == ' ')
3317
0
        ptr++;
3318
0
      for(start = ptr; *ptr && *ptr != ' '; ptr++)
3319
0
        ;
3320
0
      os = curlx_memdup0(start, ptr - start);
3321
0
      if(!os)
3322
0
        return CURLE_OUT_OF_MEMORY;
3323
3324
      /* Check for special servers here. */
3325
0
      if(curl_strequal(os, "OS/400")) {
3326
        /* Force OS400 name format 1. */
3327
0
        result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
3328
0
        if(result) {
3329
0
          curlx_free(os);
3330
0
          return result;
3331
0
        }
3332
        /* remember target server OS */
3333
0
        curlx_free(ftpc->server_os);
3334
0
        ftpc->server_os = os;
3335
0
        ftp_state(data, ftpc, FTP_NAMEFMT);
3336
0
        break;
3337
0
      }
3338
      /* Nothing special for the target server. */
3339
      /* remember target server OS */
3340
0
      curlx_free(ftpc->server_os);
3341
0
      ftpc->server_os = os;
3342
0
    }
3343
0
    else {
3344
      /* Cannot identify server OS. Continue anyway and cross fingers. */
3345
0
    }
3346
3347
0
    ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
3348
0
    CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
3349
0
    break;
3350
3351
0
  case FTP_NAMEFMT:
3352
0
    if(ftpcode == 250) {
3353
      /* Name format change successful: reload initial path. */
3354
0
      ftp_state_pwd(data, ftpc);
3355
0
      break;
3356
0
    }
3357
3358
0
    ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
3359
0
    CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
3360
0
    break;
3361
3362
0
  case FTP_QUOTE:
3363
0
  case FTP_POSTQUOTE:
3364
0
  case FTP_RETR_PREQUOTE:
3365
0
  case FTP_STOR_PREQUOTE:
3366
0
  case FTP_LIST_PREQUOTE:
3367
0
    if((ftpcode >= 400) && !ftpc->count2) {
3368
      /* failure response code, and not allowed to fail */
3369
0
      failf(data, "QUOT command failed with %03d", ftpcode);
3370
0
      result = CURLE_QUOTE_ERROR;
3371
0
    }
3372
0
    else
3373
0
      result = ftp_state_quote(data, ftpc, ftp, FALSE, ftpc->state);
3374
0
    break;
3375
3376
0
  case FTP_CWD:
3377
0
    if(ftpcode / 100 != 2) {
3378
      /* failure to CWD there */
3379
0
      if(data->set.ftp_create_missing_dirs &&
3380
0
         ftpc->cwdcount && !ftpc->count2) {
3381
        /* try making it */
3382
0
        ftpc->count2++; /* counter to prevent CWD-MKD loops */
3383
3384
        /* count3 is set to allow MKD to fail once per dir. In the case when
3385
           CWD fails and then MKD fails (due to another session raced it to
3386
           create the dir) this then allows for a second try to CWD to it. */
3387
0
        ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
3388
3389
0
        result = Curl_pp_sendf(data, &ftpc->pp, "MKD %.*s",
3390
0
                               pathlen(ftpc, ftpc->cwdcount - 1),
3391
0
                               pathpiece(ftpc, ftpc->cwdcount - 1));
3392
0
        if(!result)
3393
0
          ftp_state(data, ftpc, FTP_MKD);
3394
0
      }
3395
0
      else {
3396
        /* return failure */
3397
0
        failf(data, "Server denied you to change to the given directory");
3398
0
        ftpc->cwdfail = TRUE; /* do not remember this path as we failed
3399
                                 to enter it */
3400
0
        result = CURLE_REMOTE_ACCESS_DENIED;
3401
0
      }
3402
0
    }
3403
0
    else {
3404
      /* success */
3405
0
      ftpc->count2 = 0;
3406
0
      if(ftpc->cwdcount >= ftpc->dirdepth)
3407
0
        result = ftp_state_mdtm(data, ftpc, ftp);
3408
0
      else {
3409
0
        ftpc->cwdcount++;
3410
        /* send next CWD */
3411
0
        result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s",
3412
0
                               pathlen(ftpc, ftpc->cwdcount - 1),
3413
0
                               pathpiece(ftpc, ftpc->cwdcount - 1));
3414
0
      }
3415
0
    }
3416
0
    break;
3417
3418
0
  case FTP_MKD:
3419
0
    if((ftpcode / 100 != 2) && !ftpc->count3--) {
3420
      /* failure to MKD the directory */
3421
0
      failf(data, "Failed to MKD dir: %03d", ftpcode);
3422
0
      result = CURLE_REMOTE_ACCESS_DENIED;
3423
0
    }
3424
0
    else {
3425
0
      ftp_state(data, ftpc, FTP_CWD);
3426
      /* send CWD */
3427
0
      result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s",
3428
0
                             pathlen(ftpc, ftpc->cwdcount - 1),
3429
0
                             pathpiece(ftpc, ftpc->cwdcount - 1));
3430
0
    }
3431
0
    break;
3432
3433
0
  case FTP_MDTM:
3434
0
    result = ftp_state_mdtm_resp(data, ftpc, ftp, ftpcode);
3435
0
    break;
3436
3437
0
  case FTP_TYPE:
3438
0
  case FTP_LIST_TYPE:
3439
0
  case FTP_RETR_TYPE:
3440
0
  case FTP_STOR_TYPE:
3441
0
  case FTP_RETR_LIST_TYPE:
3442
0
    result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
3443
0
    break;
3444
3445
0
  case FTP_SIZE:
3446
0
  case FTP_RETR_SIZE:
3447
0
  case FTP_STOR_SIZE:
3448
0
    result = ftp_state_size_resp(data, ftpc, ftp, ftpcode, ftpc->state);
3449
0
    break;
3450
3451
0
  case FTP_REST:
3452
0
  case FTP_RETR_REST:
3453
0
    result = ftp_state_rest_resp(data, ftpc, ftp, ftpcode, ftpc->state);
3454
0
    break;
3455
3456
0
  case FTP_PRET:
3457
0
    if(ftpcode != 200) {
3458
      /* there only is this one standard OK return code. */
3459
0
      failf(data, "PRET command not accepted: %03d", ftpcode);
3460
0
      return CURLE_FTP_PRET_FAILED;
3461
0
    }
3462
0
    result = ftp_state_use_pasv(data, ftpc, conn);
3463
0
    break;
3464
3465
0
  case FTP_PASV:
3466
0
    result = ftp_state_pasv_resp(data, ftpc, ftpcode);
3467
0
    break;
3468
3469
0
  case FTP_PORT:
3470
0
    result = ftp_state_port_resp(data, ftpc, ftp, ftpcode);
3471
0
    break;
3472
3473
0
  case FTP_LIST:
3474
0
  case FTP_RETR:
3475
0
    result = ftp_state_get_resp(data, ftpc, ftp, ftpcode, ftpc->state);
3476
0
    break;
3477
3478
0
  case FTP_STOR:
3479
0
    result = ftp_state_stor_resp(data, ftpc, ftpcode);
3480
0
    break;
3481
3482
0
  case FTP_QUIT:
3483
0
  default:
3484
    /* internal error */
3485
0
    ftp_state(data, ftpc, FTP_STOP);
3486
0
    break;
3487
0
  }
3488
3489
0
  return result;
3490
0
}
3491
3492
/* called repeatedly until done from multi.c */
3493
static CURLcode ftp_multi_statemach(struct Curl_easy *data,
3494
                                    bool *done)
3495
0
{
3496
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
3497
0
  return ftpc ? ftp_statemach(data, ftpc, done) : CURLE_FAILED_INIT;
3498
0
}
3499
3500
static CURLcode ftp_block_statemach(struct Curl_easy *data,
3501
                                    struct ftp_conn *ftpc)
3502
0
{
3503
0
  struct pingpong *pp = &ftpc->pp;
3504
0
  CURLcode result = CURLE_OK;
3505
3506
0
  while(ftpc->state != FTP_STOP) {
3507
0
    if(ftpc->shutdown)
3508
0
      CURL_TRC_FTP(data, "in shutdown, waiting for server response");
3509
0
    result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
3510
0
    if(result)
3511
0
      break;
3512
0
  }
3513
3514
0
  return result;
3515
0
}
3516
3517
/*
3518
 * ftp_connect() should do everything that is to be considered a part of
3519
 * the connection phase.
3520
 *
3521
 * The variable 'done' points to will be TRUE if the protocol-layer connect
3522
 * phase is done when this function returns, or FALSE if not.
3523
 *
3524
 */
3525
static CURLcode ftp_connect(struct Curl_easy *data,
3526
                            bool *done) /* see description above */
3527
0
{
3528
0
  CURLcode result;
3529
0
  struct connectdata *conn = data->conn;
3530
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
3531
0
  struct pingpong *pp;
3532
3533
0
  *done = FALSE; /* default to not done yet */
3534
0
  if(!ftpc)
3535
0
    return CURLE_FAILED_INIT;
3536
0
  pp = &ftpc->pp;
3537
0
  PINGPONG_SETUP(pp, ftp_pp_statemachine, ftp_endofresp);
3538
3539
0
  if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
3540
    /* BLOCKING */
3541
0
    result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
3542
0
    if(result)
3543
0
      return result;
3544
0
  }
3545
3546
0
  Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */
3547
3548
  /* When we connect, we start in the state where we await the 220
3549
     response */
3550
0
  ftp_state(data, ftpc, FTP_WAIT220);
3551
3552
0
  result = ftp_statemach(data, ftpc, done);
3553
3554
0
  return result;
3555
0
}
3556
3557
/***********************************************************************
3558
 *
3559
 * ftp_sendquote()
3560
 *
3561
 * Where a 'quote' means a list of custom commands to send to the server.
3562
 * The quote list is passed as an argument.
3563
 *
3564
 * BLOCKING
3565
 */
3566
static CURLcode ftp_sendquote(struct Curl_easy *data,
3567
                              struct ftp_conn *ftpc,
3568
                              struct curl_slist *quote)
3569
0
{
3570
0
  struct curl_slist *item;
3571
0
  struct pingpong *pp = &ftpc->pp;
3572
3573
0
  item = quote;
3574
0
  while(item) {
3575
0
    if(item->data) {
3576
0
      size_t nread;
3577
0
      const char *cmd = item->data;
3578
0
      bool acceptfail = FALSE;
3579
0
      CURLcode result;
3580
0
      int ftpcode = 0;
3581
3582
      /* if a command starts with an asterisk, which a legal FTP command never
3583
         can, the command will be allowed to fail without it causing any
3584
         aborts or cancels etc. It will cause libcurl to act as if the command
3585
         is successful, whatever the server responds. */
3586
3587
0
      if(cmd[0] == '*') {
3588
0
        cmd++;
3589
0
        acceptfail = TRUE;
3590
0
      }
3591
3592
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
3593
0
      if(!result) {
3594
0
        pp->response = *Curl_pgrs_now(data); /* timeout relative now */
3595
0
        result = getftpresponse(data, &nread, &ftpcode);
3596
0
      }
3597
0
      if(result)
3598
0
        return result;
3599
3600
0
      if(!acceptfail && (ftpcode >= 400)) {
3601
0
        failf(data, "QUOT string not accepted: %s", cmd);
3602
0
        return CURLE_QUOTE_ERROR;
3603
0
      }
3604
0
    }
3605
3606
0
    item = item->next;
3607
0
  }
3608
3609
0
  return CURLE_OK;
3610
0
}
3611
3612
static CURLcode ftp_done_status(struct Curl_easy *data,
3613
                                struct ftp_conn *ftpc, CURLcode status,
3614
                                bool premature)
3615
0
{
3616
0
  switch(status) {
3617
0
  case CURLE_BAD_DOWNLOAD_RESUME:
3618
0
  case CURLE_FTP_WEIRD_PASV_REPLY:
3619
0
  case CURLE_FTP_PORT_FAILED:
3620
0
  case CURLE_FTP_ACCEPT_FAILED:
3621
0
  case CURLE_FTP_ACCEPT_TIMEOUT:
3622
0
  case CURLE_FTP_COULDNT_SET_TYPE:
3623
0
  case CURLE_FTP_COULDNT_RETR_FILE:
3624
0
  case CURLE_PARTIAL_FILE:
3625
0
  case CURLE_UPLOAD_FAILED:
3626
0
  case CURLE_REMOTE_ACCESS_DENIED:
3627
0
  case CURLE_FILESIZE_EXCEEDED:
3628
0
  case CURLE_REMOTE_FILE_NOT_FOUND:
3629
0
  case CURLE_WRITE_ERROR:
3630
    /* the connection stays alive fine even though this happened */
3631
0
  case CURLE_OK: /* does not affect the control connection's status */
3632
0
    if(!premature)
3633
0
      break;
3634
3635
    /* until we cope better with prematurely ended requests, let them
3636
     * fallback as if in complete failure */
3637
0
    FALLTHROUGH();
3638
0
  default:       /* by default, an error means the control connection is
3639
                    wedged and should not be used anymore */
3640
0
    ftpc->ctl_valid = FALSE;
3641
0
    ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3642
                             current path, as this connection is going */
3643
0
    CURL_TRC_FTP(data, "FTP ended with bad error code");
3644
0
    connclose(data->conn);
3645
0
    return status;      /* use the already set error code */
3646
0
  }
3647
0
  return CURLE_OK;
3648
0
}
3649
3650
static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc)
3651
0
{
3652
0
  if(data->state.wildcardmatch) {
3653
0
    if(data->set.chunk_end && ftpc->file) {
3654
0
      struct Curl_mapi_guard guard;
3655
0
      CURL_CBAPI_START(&guard, data, easy_chunk_end);
3656
0
      data->set.chunk_end(data->set.wildcardptr);
3657
0
      CURL_CBAPI_END(&guard);
3658
0
      freedirs(ftpc);
3659
0
    }
3660
0
    ftpc->known_filesize = -1;
3661
0
  }
3662
0
}
3663
3664
static void ftp_done_path(struct Curl_easy *data, struct ftp_conn *ftpc,
3665
                          CURLcode result)
3666
0
{
3667
0
  struct connectdata *conn = data->conn;
3668
0
  if(result) {
3669
    /* We can limp along anyway (and should try to since we may already be in
3670
     * the error path) */
3671
0
    ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3672
0
    connclose(conn); /* mark for connection closure */
3673
0
    curlx_safefree(ftpc->prevpath); /* no path remembering */
3674
0
  }
3675
0
  else { /* remember working directory for connection reuse */
3676
0
    const char *rawPath = ftpc->rawpath;
3677
0
    if(rawPath) {
3678
0
      if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
3679
0
        ; /* full path => no CWDs happened => keep ftpc->prevpath */
3680
0
      else {
3681
0
        size_t pathLen = strlen(ftpc->rawpath);
3682
3683
0
        curlx_free(ftpc->prevpath);
3684
3685
0
        if(!ftpc->cwdfail) {
3686
0
          if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3687
0
            pathLen = 0; /* relative path => working directory is FTP home */
3688
0
          else
3689
            /* file is URL-decoded */
3690
0
            pathLen -= ftpc->file ? strlen(ftpc->file) : 0;
3691
0
          ftpc->prevpath = curlx_memdup0(rawPath, pathLen);
3692
0
        }
3693
0
        else
3694
0
          ftpc->prevpath = NULL; /* no path */
3695
0
      }
3696
0
    }
3697
0
    if(ftpc->prevpath)
3698
0
      infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath);
3699
0
  }
3700
0
}
3701
3702
static CURLcode ftp_done_secondary_socket(struct Curl_easy *data,
3703
                                          struct ftp_conn *ftpc,
3704
                                          CURLcode result)
3705
0
{
3706
0
  struct connectdata *conn = data->conn;
3707
0
  if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) {
3708
0
    if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
3709
      /* partial download completed */
3710
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s", "ABOR");
3711
0
      if(result) {
3712
0
        failf(data, "Failure sending ABOR command: %s",
3713
0
              curl_easy_strerror(result));
3714
0
        ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3715
0
        connclose(conn); /* connection closure */
3716
0
      }
3717
0
    }
3718
3719
0
    close_secondarysocket(data, ftpc);
3720
0
  }
3721
0
  return result;
3722
0
}
3723
3724
static CURLcode ftp_done_control_reply(struct Curl_easy *data,
3725
                                       struct ftp_conn *ftpc,
3726
                                       struct FTP *ftp, CURLcode result,
3727
                                       bool premature)
3728
0
{
3729
0
  struct connectdata *conn = data->conn;
3730
0
  size_t nread;
3731
0
  int ftpcode;
3732
3733
0
  if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
3734
0
     ftpc->pp.pending_resp && !premature) {
3735
    /*
3736
     * Let's see what the server says about the transfer we performed, but
3737
     * lower the timeout as sometimes this connection has died while the data
3738
     * has been transferred. This happens when doing through NATs etc that
3739
     * abandon old silent connections.
3740
     */
3741
0
    ftpc->pp.response = *Curl_pgrs_now(data); /* timeout relative now */
3742
0
    result = getftpresponse(data, &nread, &ftpcode);
3743
3744
0
    if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) {
3745
0
      failf(data, "control connection looks dead");
3746
0
      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3747
0
      connclose(conn); /* close */
3748
0
    }
3749
3750
0
    if(result)
3751
0
      return result;
3752
3753
0
    if(ftpc->dont_check && data->req.maxdownload > 0) {
3754
      /* we have sent ABOR and there is no reliable way to check if it was
3755
       * successful or not; we have to close the connection now */
3756
0
      infof(data, "partial download completed, closing connection");
3757
0
      connclose(conn);
3758
0
      return result;
3759
0
    }
3760
3761
0
    if(!ftpc->dont_check) {
3762
      /* 226 Transfer complete, 250 Requested file action okay, completed. */
3763
0
      switch(ftpcode) {
3764
0
      case 226:
3765
0
      case 250:
3766
0
        break;
3767
0
      case 552:
3768
0
        failf(data, "Exceeded storage allocation");
3769
0
        result = CURLE_REMOTE_DISK_FULL;
3770
0
        break;
3771
0
      default:
3772
0
        failf(data, "server did not report OK, got %d", ftpcode);
3773
0
        result = CURLE_PARTIAL_FILE;
3774
0
        break;
3775
0
      }
3776
0
    }
3777
0
  }
3778
0
  return result;
3779
0
}
3780
3781
static CURLcode ftp_done_check_partial(struct Curl_easy *data,
3782
                                       struct ftp_conn *ftpc,
3783
                                       struct FTP *ftp, CURLcode result,
3784
                                       bool premature)
3785
0
{
3786
0
  if(result || premature)
3787
    /* the response code from the transfer showed an error already so no
3788
       use checking further */
3789
0
    ;
3790
0
  else if(data->state.upload) {
3791
0
    if((ftp->transfer == PPTRANSFER_BODY) &&
3792
0
       (data->state.infilesize != -1) && /* upload with known size */
3793
0
       ((!data->set.crlf && !data->state.prefer_ascii && /* no conversion */
3794
0
         (data->state.infilesize != data->req.writebytecount)) ||
3795
0
        ((data->set.crlf || data->state.prefer_ascii) && /* maybe CRLF conv */
3796
0
         (data->state.infilesize > data->req.writebytecount))
3797
0
       )) {
3798
0
      failf(data, "Uploaded unaligned file size (%" FMT_OFF_T
3799
0
            " out of %" FMT_OFF_T " bytes)",
3800
0
            data->req.writebytecount, data->state.infilesize);
3801
0
      result = CURLE_PARTIAL_FILE;
3802
0
    }
3803
0
  }
3804
0
  else {
3805
0
    if((data->req.size != -1) &&
3806
0
       (data->req.size != data->req.bytecount) &&
3807
0
       (data->req.maxdownload != data->req.bytecount)) {
3808
0
      failf(data, "Received only partial file: %" FMT_OFF_T " bytes",
3809
0
            data->req.bytecount);
3810
0
      result = CURLE_PARTIAL_FILE;
3811
0
    }
3812
0
    else if(!ftpc->dont_check &&
3813
0
            !data->req.bytecount &&
3814
0
            (data->req.size > 0)) {
3815
0
      failf(data, "No data was received");
3816
0
      result = CURLE_FTP_COULDNT_RETR_FILE;
3817
0
    }
3818
0
  }
3819
0
  return result;
3820
0
}
3821
3822
/***********************************************************************
3823
 *
3824
 * ftp_done()
3825
 *
3826
 * The DONE function. This does what needs to be done after a single DO has
3827
 * performed.
3828
 *
3829
 * Input argument is already checked for validity.
3830
 */
3831
static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
3832
                         bool premature)
3833
0
{
3834
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
3835
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
3836
0
  CURLcode result;
3837
3838
0
  if(!ftp || !ftpc)
3839
0
    return CURLE_OK;
3840
3841
0
  result = ftp_done_status(data, ftpc, status, premature);
3842
3843
0
  ftp_done_wildcard(data, ftpc);
3844
0
  ftp_done_path(data, ftpc, result);
3845
0
  result = ftp_done_secondary_socket(data, ftpc, result);
3846
0
  result = ftp_done_control_reply(data, ftpc, ftp, result, premature);
3847
0
  result = ftp_done_check_partial(data, ftpc, ftp, result, premature);
3848
3849
  /* clear these for next connection */
3850
0
  ftp->transfer = PPTRANSFER_BODY;
3851
0
  ftpc->dont_check = FALSE;
3852
3853
  /* Send any post-transfer QUOTE strings? */
3854
0
  if(!status && !result && !premature && data->set.postquote)
3855
0
    result = ftp_sendquote(data, ftpc, data->set.postquote);
3856
0
  CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), (int)result);
3857
0
  return result;
3858
0
}
3859
3860
/***********************************************************************
3861
 *
3862
 * ftp_nb_type()
3863
 *
3864
 * Set TYPE. We only deal with ASCII or BINARY so this function
3865
 * sets one of them.
3866
 * If the transfer type is not sent, simulate on OK response in newstate
3867
 */
3868
static CURLcode ftp_nb_type(struct Curl_easy *data,
3869
                            struct ftp_conn *ftpc,
3870
                            struct FTP *ftp,
3871
                            bool ascii, ftpstate newstate)
3872
0
{
3873
0
  CURLcode result;
3874
0
  char want = (char)(ascii ? 'A' : 'I');
3875
3876
0
  if(ftpc->transfertype == want) {
3877
0
    ftp_state(data, ftpc, newstate);
3878
0
    return ftp_state_type_resp(data, ftpc, ftp, 200, newstate);
3879
0
  }
3880
3881
0
  result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
3882
0
  if(!result) {
3883
0
    ftp_state(data, ftpc, newstate);
3884
3885
    /* keep track of our current transfer type */
3886
0
    ftpc->transfertype = want;
3887
0
  }
3888
0
  return result;
3889
0
}
3890
3891
/***********************************************************************
3892
 *
3893
 * ftp_perform()
3894
 *
3895
 * This is the actual DO function for FTP. Get a file/directory according to
3896
 * the options previously setup.
3897
 */
3898
static CURLcode ftp_perform(
3899
  struct Curl_easy *data,
3900
  struct ftp_conn *ftpc,
3901
  struct FTP *ftp,
3902
  bool *connected,  /* connect status after PASV / PORT */
3903
  bool *dophase_done)
3904
0
{
3905
  /* this is FTP and no proxy */
3906
0
  CURLcode result = CURLE_OK;
3907
3908
0
  CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_CSTATE(ftpc));
3909
3910
0
  if(data->req.no_body) {
3911
    /* requested no body means no transfer... */
3912
0
    ftp->transfer = PPTRANSFER_INFO;
3913
0
  }
3914
3915
0
  *dophase_done = FALSE; /* not done yet */
3916
3917
  /* start the first command in the DO phase */
3918
0
  result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_QUOTE);
3919
0
  if(result)
3920
0
    return result;
3921
3922
  /* run the state-machine */
3923
0
  result = ftp_statemach(data, ftpc, dophase_done);
3924
3925
0
  *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
3926
3927
0
  if(*connected)
3928
0
    infof(data, "[FTP] [%s] perform, DATA connection established",
3929
0
          FTP_CSTATE(ftpc));
3930
0
  else
3931
0
    CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect",
3932
0
                 FTP_CSTATE(ftpc));
3933
3934
0
  if(*dophase_done)
3935
0
    CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_CSTATE(ftpc));
3936
3937
0
  return result;
3938
0
}
3939
3940
static void wc_data_dtor(void *ptr)
3941
0
{
3942
0
  struct ftp_wc *ftpwc = ptr;
3943
0
  if(ftpwc && ftpwc->parser)
3944
0
    Curl_ftp_parselist_data_free(&ftpwc->parser);
3945
0
  curlx_free(ftpwc);
3946
0
}
3947
3948
static CURLcode init_wc_data(struct Curl_easy *data,
3949
                             struct ftp_conn *ftpc,
3950
                             struct FTP *ftp)
3951
0
{
3952
0
  char *last_slash;
3953
0
  char *path = ftp->path;
3954
0
  struct WildcardData *wildcard = data->wildcard;
3955
0
  CURLcode result = CURLE_OK;
3956
0
  struct ftp_wc *ftpwc = NULL;
3957
3958
0
  last_slash = strrchr(ftp->path, '/');
3959
0
  if(last_slash) {
3960
0
    last_slash++;
3961
0
    if(last_slash[0] == '\0') {
3962
0
      wildcard->state = CURLWC_CLEAN;
3963
0
      return ftp_parse_url_path(data, ftpc, ftp);
3964
0
    }
3965
0
    wildcard->pattern = curlx_strdup(last_slash);
3966
0
    if(!wildcard->pattern)
3967
0
      return CURLE_OUT_OF_MEMORY;
3968
0
    last_slash[0] = '\0'; /* cut file from path */
3969
0
  }
3970
0
  else { /* there is only 'wildcard pattern' or nothing */
3971
0
    if(path[0]) {
3972
0
      wildcard->pattern = curlx_strdup(path);
3973
0
      if(!wildcard->pattern)
3974
0
        return CURLE_OUT_OF_MEMORY;
3975
0
      path[0] = '\0';
3976
0
    }
3977
0
    else { /* only list */
3978
0
      wildcard->state = CURLWC_CLEAN;
3979
0
      return ftp_parse_url_path(data, ftpc, ftp);
3980
0
    }
3981
0
  }
3982
3983
  /* program continues only if URL is not ending with slash, allocate needed
3984
     resources for wildcard transfer */
3985
3986
  /* allocate ftp protocol specific wildcard data */
3987
0
  ftpwc = curlx_calloc(1, sizeof(struct ftp_wc));
3988
0
  if(!ftpwc) {
3989
0
    result = CURLE_OUT_OF_MEMORY;
3990
0
    goto fail;
3991
0
  }
3992
3993
  /* INITIALIZE parselist structure */
3994
0
  ftpwc->parser = Curl_ftp_parselist_data_alloc();
3995
0
  if(!ftpwc->parser) {
3996
0
    result = CURLE_OUT_OF_MEMORY;
3997
0
    goto fail;
3998
0
  }
3999
4000
0
  wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */
4001
0
  wildcard->dtor = wc_data_dtor;
4002
4003
  /* wildcard does not support NOCWD option (assert it?) */
4004
0
  if(data->set.ftp_filemethod == FTPFILE_NOCWD)
4005
0
    data->set.ftp_filemethod = FTPFILE_MULTICWD;
4006
4007
  /* try to parse ftp URL */
4008
0
  result = ftp_parse_url_path(data, ftpc, ftp);
4009
0
  if(result) {
4010
0
    goto fail;
4011
0
  }
4012
4013
0
  wildcard->path = curlx_strdup(ftp->path);
4014
0
  if(!wildcard->path) {
4015
0
    result = CURLE_OUT_OF_MEMORY;
4016
0
    goto fail;
4017
0
  }
4018
4019
  /* backup old write_function */
4020
0
  ftpwc->backup.write_function = data->set.fwrite_func;
4021
  /* parsing write function */
4022
0
  data->set.fwrite_func = Curl_ftp_parselist;
4023
  /* backup old file descriptor */
4024
0
  ftpwc->backup.file_descriptor = data->set.out;
4025
  /* let the writefunc callback know the transfer */
4026
0
  data->set.out = data;
4027
4028
0
  infof(data, "Wildcard - Parsing started");
4029
0
  return CURLE_OK;
4030
4031
0
fail:
4032
0
  if(ftpwc) {
4033
0
    Curl_ftp_parselist_data_free(&ftpwc->parser);
4034
0
    curlx_free(ftpwc);
4035
0
  }
4036
0
  curlx_safefree(wildcard->pattern);
4037
0
  wildcard->dtor = ZERO_NULL;
4038
0
  wildcard->ftpwc = NULL;
4039
0
  return result;
4040
0
}
4041
4042
static CURLcode wc_statemach(struct Curl_easy *data,
4043
                             struct ftp_conn *ftpc,
4044
                             struct FTP *ftp)
4045
0
{
4046
0
  struct WildcardData * const wildcard = data->wildcard;
4047
0
  CURLcode result = CURLE_OK;
4048
4049
0
  for(;;) {
4050
0
    switch(wildcard->state) {
4051
0
    case CURLWC_INIT:
4052
0
      result = init_wc_data(data, ftpc, ftp);
4053
0
      if(wildcard->state == CURLWC_CLEAN)
4054
        /* only listing! */
4055
0
        return result;
4056
0
      wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
4057
0
      return result;
4058
4059
0
    case CURLWC_MATCHING: {
4060
      /* In this state is LIST response successfully parsed, so lets restore
4061
         previous WRITEFUNCTION callback and WRITEDATA pointer */
4062
0
      struct ftp_wc *ftpwc = wildcard->ftpwc;
4063
0
      data->set.fwrite_func = ftpwc->backup.write_function;
4064
0
      data->set.out = ftpwc->backup.file_descriptor;
4065
0
      ftpwc->backup.write_function = ZERO_NULL;
4066
0
      ftpwc->backup.file_descriptor = NULL;
4067
0
      wildcard->state = CURLWC_DOWNLOADING;
4068
4069
0
      if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
4070
        /* error found in LIST parsing */
4071
0
        wildcard->state = CURLWC_CLEAN;
4072
0
        continue;
4073
0
      }
4074
0
      if(Curl_llist_count(&wildcard->filelist) == 0) {
4075
        /* no corresponding file */
4076
0
        wildcard->state = CURLWC_CLEAN;
4077
0
        return CURLE_REMOTE_FILE_NOT_FOUND;
4078
0
      }
4079
0
      continue;
4080
0
    }
4081
4082
0
    case CURLWC_DOWNLOADING: {
4083
      /* filelist has at least one file, lets get first one */
4084
0
      struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist);
4085
0
      struct curl_fileinfo *finfo = Curl_node_elem(head);
4086
0
      char *tmp_path;
4087
0
      char *enc = curl_easy_escape(NULL, finfo->filename, 0);
4088
0
      if(!enc)
4089
0
        return CURLE_OUT_OF_MEMORY;
4090
4091
0
      tmp_path = curl_maprintf("%s%s", wildcard->path, enc);
4092
0
      curl_free(enc);
4093
0
      if(!tmp_path)
4094
0
        return CURLE_OUT_OF_MEMORY;
4095
4096
      /* switch default ftp->path and tmp_path */
4097
0
      curlx_free(ftp->pathalloc);
4098
0
      ftp->pathalloc = ftp->path = tmp_path;
4099
4100
0
      infof(data, "Wildcard - START of \"%s\"", finfo->filename);
4101
0
      if(data->set.chunk_bgn) {
4102
0
        long userresponse;
4103
0
        struct Curl_mapi_guard guard;
4104
0
        CURL_CBAPI_START(&guard, data, easy_chunk_bgn);
4105
0
        userresponse = data->set.chunk_bgn(
4106
0
          finfo, data->set.wildcardptr,
4107
0
          (int)Curl_llist_count(&wildcard->filelist));
4108
0
        CURL_CBAPI_END(&guard);
4109
0
        switch(userresponse) {
4110
0
        case CURL_CHUNK_BGN_FUNC_SKIP:
4111
0
          infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename);
4112
0
          wildcard->state = CURLWC_SKIP;
4113
0
          continue;
4114
0
        case CURL_CHUNK_BGN_FUNC_FAIL:
4115
0
          return CURLE_CHUNK_FAILED;
4116
0
        }
4117
0
      }
4118
4119
0
      if(finfo->filetype != CURLFILETYPE_FILE) {
4120
0
        wildcard->state = CURLWC_SKIP;
4121
0
        continue;
4122
0
      }
4123
4124
0
      if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
4125
0
        ftpc->known_filesize = finfo->size;
4126
4127
0
      result = ftp_parse_url_path(data, ftpc, ftp);
4128
0
      if(result)
4129
0
        return result;
4130
4131
      /* we do not need the Curl_fileinfo of first file anymore */
4132
0
      Curl_node_remove(Curl_llist_head(&wildcard->filelist));
4133
4134
0
      if(Curl_llist_count(&wildcard->filelist) == 0) {
4135
        /* remains only one file to down. */
4136
0
        wildcard->state = CURLWC_CLEAN;
4137
        /* after that will be ftp_do called once again and no transfer
4138
           will be done because of CURLWC_CLEAN state */
4139
0
        return CURLE_OK;
4140
0
      }
4141
0
      return result;
4142
0
    }
4143
4144
0
    case CURLWC_SKIP: {
4145
0
      if(data->set.chunk_end) {
4146
0
        struct Curl_mapi_guard guard;
4147
0
        CURL_CBAPI_START(&guard, data, easy_chunk_end);
4148
0
        data->set.chunk_end(data->set.wildcardptr);
4149
0
        CURL_CBAPI_END(&guard);
4150
0
      }
4151
0
      Curl_node_remove(Curl_llist_head(&wildcard->filelist));
4152
0
      wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ?
4153
0
        CURLWC_CLEAN : CURLWC_DOWNLOADING;
4154
0
      continue;
4155
0
    }
4156
4157
0
    case CURLWC_CLEAN: {
4158
0
      struct ftp_wc *ftpwc = wildcard->ftpwc;
4159
0
      result = CURLE_OK;
4160
0
      if(ftpwc)
4161
0
        result = Curl_ftp_parselist_geterror(ftpwc->parser);
4162
4163
0
      wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
4164
0
      return result;
4165
0
    }
4166
4167
0
    case CURLWC_DONE:
4168
0
    case CURLWC_ERROR:
4169
0
    case CURLWC_CLEAR:
4170
0
      if(wildcard->dtor) {
4171
0
        wildcard->dtor(wildcard->ftpwc);
4172
0
        wildcard->ftpwc = NULL;
4173
0
      }
4174
0
      return result;
4175
0
    }
4176
0
  }
4177
  /* UNREACHABLE */
4178
0
}
4179
4180
/***********************************************************************
4181
 *
4182
 * ftp_regular_transfer()
4183
 *
4184
 * The input argument is already checked for validity.
4185
 *
4186
 * Performs all commands done before a regular transfer between a local and a
4187
 * remote host.
4188
 *
4189
 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4190
 * ftp_done() function without finding any major problem.
4191
 */
4192
static CURLcode ftp_regular_transfer(struct Curl_easy *data,
4193
                                     struct ftp_conn *ftpc,
4194
                                     struct FTP *ftp,
4195
                                     bool *dophase_done)
4196
0
{
4197
0
  CURLcode result = CURLE_OK;
4198
0
  bool connected = FALSE;
4199
0
  data->req.size = -1; /* make sure this is unknown at this point */
4200
4201
0
  Curl_pgrsReset(data);
4202
4203
0
  ftpc->ctl_valid = TRUE; /* starts good */
4204
4205
0
  result = ftp_perform(data, ftpc, ftp,
4206
0
                       &connected, /* have we connected after PASV/PORT */
4207
0
                       dophase_done); /* all commands in the DO-phase done? */
4208
4209
0
  if(!result) {
4210
4211
0
    if(!*dophase_done)
4212
      /* the DO phase has not completed yet */
4213
0
      return CURLE_OK;
4214
4215
0
    result = ftp_dophase_done(data, ftpc, ftp, connected);
4216
4217
0
    if(result)
4218
0
      return result;
4219
0
  }
4220
0
  else
4221
0
    freedirs(ftpc);
4222
4223
0
  return result;
4224
0
}
4225
4226
/***********************************************************************
4227
 *
4228
 * ftp_do()
4229
 *
4230
 * This function is registered as 'curl_do' function. It decodes the path
4231
 * parts etc as a wrapper to the actual DO function (ftp_perform).
4232
 *
4233
 * The input argument is already checked for validity.
4234
 */
4235
static CURLcode ftp_do(struct Curl_easy *data, bool *done)
4236
0
{
4237
0
  CURLcode result = CURLE_OK;
4238
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
4239
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
4240
4241
0
  *done = FALSE; /* default to false */
4242
0
  if(!ftpc || !ftp)
4243
0
    return CURLE_FAILED_INIT;
4244
0
  ftpc->wait_data_conn = FALSE; /* default to no such wait */
4245
4246
0
#ifdef CURL_PREFER_LF_LINEENDS
4247
0
  {
4248
    /* FTP data may need conversion. */
4249
0
    struct Curl_cwriter *ftp_lc_writer;
4250
4251
0
    result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc,
4252
0
                                 CURL_CW_CONTENT_DECODE);
4253
0
    if(result)
4254
0
      return result;
4255
4256
0
    result = Curl_cwriter_add(data, ftp_lc_writer);
4257
0
    if(result) {
4258
0
      Curl_cwriter_free(data, ftp_lc_writer);
4259
0
      return result;
4260
0
    }
4261
0
  }
4262
0
#endif /* CURL_PREFER_LF_LINEENDS */
4263
4264
0
  if(data->state.wildcardmatch) {
4265
0
    result = wc_statemach(data, ftpc, ftp);
4266
0
    if(data->wildcard->state == CURLWC_SKIP ||
4267
0
       data->wildcard->state == CURLWC_DONE) {
4268
      /* do not call ftp_regular_transfer */
4269
0
      return CURLE_OK;
4270
0
    }
4271
0
    if(result) /* error, loop or skipping the file */
4272
0
      return result;
4273
0
  }
4274
0
  else { /* no wildcard FSM needed */
4275
0
    result = ftp_parse_url_path(data, ftpc, ftp);
4276
0
    if(result)
4277
0
      return result;
4278
0
  }
4279
4280
0
  result = ftp_regular_transfer(data, ftpc, ftp, done);
4281
4282
0
  return result;
4283
0
}
4284
4285
/***********************************************************************
4286
 *
4287
 * ftp_quit()
4288
 *
4289
 * This should be called before calling sclose() on an ftp control connection
4290
 * (not data connections). We should then wait for the response from the
4291
 * server before returning. The calling code should then try to close the
4292
 * connection.
4293
 *
4294
 */
4295
static CURLcode ftp_quit(struct Curl_easy *data,
4296
                         struct ftp_conn *ftpc)
4297
0
{
4298
0
  CURLcode result = CURLE_OK;
4299
4300
0
  if(ftpc->ctl_valid) {
4301
0
    CURL_TRC_FTP(data, "sending QUIT to close session");
4302
0
    result = Curl_pp_sendf(data, &ftpc->pp, "%s", "QUIT");
4303
0
    if(result) {
4304
0
      failf(data, "Failure sending QUIT command: %s",
4305
0
            curl_easy_strerror(result));
4306
0
      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
4307
0
      connclose(data->conn); /* mark for closure */
4308
0
      ftp_state(data, ftpc, FTP_STOP);
4309
0
      return result;
4310
0
    }
4311
4312
0
    ftp_state(data, ftpc, FTP_QUIT);
4313
4314
0
    result = ftp_block_statemach(data, ftpc);
4315
0
  }
4316
4317
0
  return result;
4318
0
}
4319
4320
/***********************************************************************
4321
 *
4322
 * ftp_disconnect()
4323
 *
4324
 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4325
 * resources. BLOCKING.
4326
 */
4327
static CURLcode ftp_disconnect(struct Curl_easy *data,
4328
                               struct connectdata *conn,
4329
                               bool dead_connection)
4330
0
{
4331
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
4332
4333
0
  if(!ftpc)
4334
0
    return CURLE_FAILED_INIT;
4335
  /* We cannot send quit unconditionally. If this connection is stale or
4336
     bad in any way, sending quit and waiting around here will make the
4337
     disconnect wait in vain and cause more problems than we need to.
4338
4339
     ftp_quit() will check the state of ftp->ctl_valid. If it is ok it
4340
     will try to send the QUIT command, otherwise it will return. */
4341
0
  ftpc->shutdown = TRUE;
4342
0
  if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp))
4343
0
    ftpc->ctl_valid = FALSE;
4344
4345
  /* The FTP session may or may not have been allocated/setup at this point! */
4346
0
  (void)ftp_quit(data, ftpc); /* ignore errors on the QUIT */
4347
0
  return CURLE_OK;
4348
0
}
4349
4350
/* called from multi.c while DOing */
4351
static CURLcode ftp_doing(struct Curl_easy *data,
4352
                          bool *dophase_done)
4353
0
{
4354
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
4355
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
4356
0
  CURLcode result;
4357
4358
0
  if(!ftpc || !ftp)
4359
0
    return CURLE_FAILED_INIT;
4360
0
  result = ftp_statemach(data, ftpc, dophase_done);
4361
4362
0
  if(result)
4363
0
    CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_CSTATE(ftpc));
4364
0
  else if(*dophase_done) {
4365
0
    result = ftp_dophase_done(data, ftpc, ftp, FALSE /* not connected */);
4366
4367
0
    CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_CSTATE(ftpc));
4368
0
  }
4369
0
  return result;
4370
0
}
4371
4372
static void ftp_easy_dtor(const void *key, size_t klen, void *entry)
4373
0
{
4374
0
  struct FTP *ftp = entry;
4375
0
  (void)key;
4376
0
  (void)klen;
4377
0
  curlx_safefree(ftp->pathalloc);
4378
0
  curlx_free(ftp);
4379
0
}
4380
4381
static void ftp_conn_dtor(const void *key, size_t klen, void *entry)
4382
0
{
4383
0
  struct ftp_conn *ftpc = entry;
4384
0
  (void)key;
4385
0
  (void)klen;
4386
0
  freedirs(ftpc);
4387
0
  curlx_safefree(ftpc->account);
4388
0
  curlx_safefree(ftpc->alternative_to_user);
4389
0
  curlx_safefree(ftpc->entrypath);
4390
0
  curlx_safefree(ftpc->prevpath);
4391
0
  curlx_safefree(ftpc->server_os);
4392
0
  Curl_pp_disconnect(&ftpc->pp);
4393
0
  curlx_free(ftpc);
4394
0
}
4395
4396
static void type_url_check(struct Curl_easy *data, struct FTP *ftp)
4397
0
{
4398
0
  size_t len = strlen(ftp->path);
4399
  /* FTP URLs support an extension like ";type=<typecode>" that
4400
   * we will try to get now! */
4401
0
  if((len >= 7) && !memcmp(&ftp->path[len - 7], ";type=", 6)) {
4402
0
    char *type = &ftp->path[len - 7];
4403
0
    char command = Curl_raw_toupper(type[6]);
4404
4405
0
    *type = 0; /* cut it off */
4406
4407
0
    switch(command) {
4408
0
    case 'A': /* ASCII mode */
4409
0
      data->state.prefer_ascii = TRUE;
4410
0
      break;
4411
4412
0
    case 'D': /* directory mode */
4413
0
      data->state.list_only = TRUE;
4414
0
      break;
4415
4416
0
    case 'I': /* binary mode */
4417
0
    default:
4418
      /* switch off ASCII */
4419
0
      data->state.prefer_ascii = FALSE;
4420
0
      break;
4421
0
    }
4422
0
  }
4423
0
}
4424
4425
static CURLcode ftp_setup_connection(struct Curl_easy *data,
4426
                                     struct connectdata *conn)
4427
0
{
4428
0
  struct FTP *ftp;
4429
0
  CURLcode result = CURLE_OK;
4430
0
  struct ftp_conn *ftpc;
4431
4432
0
  ftp = curlx_calloc(1, sizeof(*ftp));
4433
0
  if(!ftp ||
4434
0
     Curl_meta_set(data, CURL_META_FTP_EASY, ftp, ftp_easy_dtor))
4435
0
    return CURLE_OUT_OF_MEMORY;
4436
4437
0
  ftpc = curlx_calloc(1, sizeof(*ftpc));
4438
0
  if(!ftpc ||
4439
0
     Curl_conn_meta_set(conn, CURL_META_FTP_CONN, ftpc, ftp_conn_dtor))
4440
0
    return CURLE_OUT_OF_MEMORY;
4441
4442
  /* clone connection related data that is FTP specific */
4443
0
  if(CURL_EASY_STR(data, STRING_FTP_ACCOUNT)) {
4444
0
    ftpc->account = curlx_strdup(CURL_EASY_STR(data, STRING_FTP_ACCOUNT));
4445
0
    if(!ftpc->account) {
4446
0
      Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
4447
0
      return CURLE_OUT_OF_MEMORY;
4448
0
    }
4449
0
  }
4450
0
  if(CURL_EASY_STR(data, STRING_FTP_ALTERNATIVE_TO_USER)) {
4451
0
    ftpc->alternative_to_user =
4452
0
      curlx_strdup(CURL_EASY_STR(data, STRING_FTP_ALTERNATIVE_TO_USER));
4453
0
    if(!ftpc->alternative_to_user) {
4454
0
      curlx_safefree(ftpc->account);
4455
0
      Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
4456
0
      return CURLE_OUT_OF_MEMORY;
4457
0
    }
4458
0
  }
4459
4460
0
  ftp->path = &data->state.up.path[1]; /* do not include the initial slash */
4461
4462
0
  type_url_check(data, ftp);
4463
4464
  /* get some initial data into the ftp struct */
4465
0
  ftp->transfer = PPTRANSFER_BODY;
4466
0
  ftp->downloadsize = 0;
4467
0
  ftpc->known_filesize = -1; /* unknown size for now */
4468
0
  ftpc->use_ssl = data->set.use_ssl;
4469
0
  ftpc->ccc = data->set.ftp_ccc;
4470
4471
0
  CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc),
4472
0
               (int)result);
4473
0
  return result;
4474
0
}
4475
4476
bool Curl_ftp_conns_match(struct connectdata *needle, struct connectdata *conn)
4477
0
{
4478
0
  struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN);
4479
0
  struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
4480
  /* Also match ACCOUNT, ALTERNATIVE-TO-USER and CCC options */
4481
0
  if(!nftpc || !cftpc ||
4482
0
     Curl_timestrcmp(nftpc->account, cftpc->account) ||
4483
0
     Curl_timestrcmp(nftpc->alternative_to_user,
4484
0
                     cftpc->alternative_to_user) ||
4485
0
     (nftpc->ccc != cftpc->ccc))
4486
0
    return FALSE;
4487
4488
0
  switch(nftpc->use_ssl) {
4489
0
  case CURLUSESSL_TRY:
4490
    /* A transfer that only "tries" SSL, is compatible with a connection
4491
     * of similar or stricter SSL use setting. */
4492
0
    return (cftpc->use_ssl != CURLUSESSL_NONE);
4493
0
  default:
4494
0
    return (nftpc->use_ssl == cftpc->use_ssl);
4495
0
  }
4496
0
}
4497
4498
/*
4499
 * FTP protocol.
4500
 */
4501
const struct Curl_protocol Curl_protocol_ftp = {
4502
  ftp_setup_connection,            /* setup_connection */
4503
  ftp_do,                          /* do_it */
4504
  ftp_done,                        /* done */
4505
  ftp_do_more,                     /* do_more */
4506
  ftp_connect,                     /* connect_it */
4507
  ftp_multi_statemach,             /* connecting */
4508
  ftp_doing,                       /* doing */
4509
  ftp_pollset,                     /* proto_pollset */
4510
  ftp_pollset,                     /* doing_pollset */
4511
  ftp_domore_pollset,              /* domore_pollset */
4512
  ZERO_NULL,                       /* perform_pollset */
4513
  ftp_disconnect,                  /* disconnect */
4514
  ZERO_NULL,                       /* write_resp */
4515
  ZERO_NULL,                       /* write_resp_hd */
4516
  ZERO_NULL,                       /* connection_is_dead */
4517
  ZERO_NULL,                       /* attach connection */
4518
  ZERO_NULL,                       /* follow */
4519
};
4520
4521
#endif /* CURL_DISABLE_FTP */