Coverage Report

Created: 2026-08-13 07:18

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
                             int sockindex,
594
                             struct pingpong *pp,
595
                             int *ftpcodep, /* return the ftp-code if done */
596
                             size_t *size) /* size of the response */
597
0
{
598
0
  int code;
599
0
  CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size);
600
0
  DEBUGASSERT(ftpcodep);
601
602
  /* store the latest code for later retrieval, except during shutdown */
603
0
  if(!ftpc->shutdown)
604
0
    data->info.httpcode = code;
605
606
0
  *ftpcodep = code;
607
608
0
  if(code == 421) {
609
    /* 421 means "Service not available, closing control connection." and FTP
610
     * servers use it to signal that idle session timeout has been exceeded.
611
     * If we ignored the response, it could end up hanging in some cases.
612
     *
613
     * This response code can come at any point so having it treated
614
     * generically is a good idea.
615
     */
616
0
    infof(data, "We got a 421 - timeout");
617
0
    ftp_state(data, ftpc, FTP_STOP);
618
0
    return CURLE_OPERATION_TIMEDOUT;
619
0
  }
620
621
0
  return result;
622
0
}
623
624
/* --- parse FTP server responses --- */
625
626
/*
627
 * getftpresponse() is a BLOCKING function to read the full response from a
628
 * server after a command.
629
 *
630
 */
631
static CURLcode getftpresponse(struct Curl_easy *data,
632
                               size_t *nreadp, /* return number of bytes
633
                                                  read */
634
                               int *ftpcodep) /* return the ftp-code */
635
0
{
636
  /*
637
   * We cannot read one byte per read() and then go back to select() as the
638
   * OpenSSL read() does not grok that properly.
639
   *
640
   * Alas, read as much as possible, split up into lines, use the ending
641
   * line in a response or continue reading. */
642
643
0
  struct connectdata *conn = data->conn;
644
0
  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
645
0
  CURLcode result = CURLE_OK;
646
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
647
0
  struct pingpong *pp = &ftpc->pp;
648
0
  size_t nread;
649
0
  int cache_skip = 0;
650
0
  DEBUGASSERT(ftpcodep);
651
652
0
  CURL_TRC_FTP(data, "getftpresponse start");
653
0
  *nreadp = 0;
654
0
  *ftpcodep = 0; /* 0 for errors */
655
656
0
  if(!ftpc)
657
0
    return CURLE_FAILED_INIT;
658
659
0
  while(!*ftpcodep && !result) {
660
    /* check and reset timeout value every lap */
661
0
    timediff_t timeout = Curl_pp_state_timeleft_ms(data, pp);
662
0
    timediff_t interval_ms;
663
664
0
    if(timeout <= 0) {
665
0
      failf(data, "FTP response timeout");
666
0
      return CURLE_OPERATION_TIMEDOUT; /* already too little time */
667
0
    }
668
669
0
    interval_ms = 1000;  /* use 1 second timeout intervals */
670
0
    if(timeout < interval_ms)
671
0
      interval_ms = timeout;
672
673
    /*
674
     * Since this function is blocking, we need to wait here for input on the
675
     * connection and only then we call the response reading function. We do
676
     * timeout at least every second to make the timeout check run.
677
     *
678
     * A caution here is that the ftp_readresp() function has a cache that may
679
     * contain pieces of a response from the previous invoke and we need to
680
     * make sure we do not wait for input while there is unhandled data in
681
     * that cache. Also, if the cache is there, we call ftp_readresp() and
682
     * the cache was not good enough to continue we must not busy-loop around
683
     * this function.
684
     *
685
     */
686
687
0
    if(curlx_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
688
      /*
689
       * There is a cache left since before. We then skipping the wait for
690
       * socket action, unless this is the same cache like the previous round
691
       * as then the cache was deemed not enough to act on and we then need to
692
       * wait for more data anyway.
693
       */
694
0
    }
695
0
    else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) {
696
0
      curl_socket_t wsock = Curl_pp_needs_flush(data, pp) ?
697
0
        sockfd : CURL_SOCKET_BAD;
698
0
      int ev = Curl_socket_check(sockfd, CURL_SOCKET_BAD, wsock, interval_ms);
699
0
      if(ev < 0) {
700
0
        failf(data, "FTP response aborted due to select/poll error: %d",
701
0
              SOCKERRNO);
702
0
        return CURLE_RECV_ERROR;
703
0
      }
704
0
      else if(ev == 0) {
705
0
        result = Curl_pgrsUpdate(data);
706
0
        continue; /* continue in our loop for the timeout duration */
707
0
      }
708
0
    }
709
710
0
    if(Curl_pp_needs_flush(data, pp)) {
711
0
      result = Curl_pp_flushsend(data, pp);
712
0
      if(result)
713
0
        break;
714
0
    }
715
716
0
    result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, ftpcodep, &nread);
717
0
    if(result)
718
0
      break;
719
720
0
    if(!nread && curlx_dyn_len(&pp->recvbuf))
721
      /* bump cache skip counter as on repeated skips we must wait for more
722
         data */
723
0
      cache_skip++;
724
0
    else
725
      /* when we got data or there is no cache left, we reset the cache skip
726
         counter */
727
0
      cache_skip = 0;
728
729
0
    *nreadp += nread;
730
731
0
  } /* while there is buffer left and loop is requested */
732
733
0
  pp->pending_resp = FALSE;
734
0
  CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zu, ftpcode=%d",
735
0
               (int)result, *nreadp, *ftpcodep);
736
737
0
  return result;
738
0
}
739
740
static CURLcode ftp_state_user(struct Curl_easy *data,
741
                               struct ftp_conn *ftpc,
742
                               struct connectdata *conn)
743
0
{
744
0
  CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s",
745
0
                                  Curl_creds_user(conn->creds));
746
0
  if(!result) {
747
0
    ftpc->ftp_trying_alternative = FALSE;
748
0
    ftp_state(data, ftpc, FTP_USER);
749
0
  }
750
0
  return result;
751
0
}
752
753
static CURLcode ftp_state_pwd(struct Curl_easy *data,
754
                              struct ftp_conn *ftpc)
755
0
{
756
0
  CURLcode result;
757
0
#ifdef DEBUGBUILD
758
0
  if(!data->id && getenv("CURL_FTP_PWD_STOP"))
759
0
    return CURLE_OK;
760
0
#endif
761
0
  result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD");
762
0
  if(!result)
763
0
    ftp_state(data, ftpc, FTP_PWD);
764
765
0
  return result;
766
0
}
767
768
/* For the FTP "protocol connect" and "doing" phases only */
769
static CURLcode ftp_pollset(struct Curl_easy *data,
770
                            struct easy_pollset *ps)
771
0
{
772
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
773
0
  return ftpc ? Curl_pp_pollset(data, &ftpc->pp, ps) : CURLE_OK;
774
0
}
775
776
/* For the FTP "DO_MORE" phase only */
777
static CURLcode ftp_domore_pollset(struct Curl_easy *data,
778
                                   struct easy_pollset *ps)
779
0
{
780
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
781
782
0
  if(!ftpc)
783
0
    return CURLE_OK;
784
785
  /* When in DO_MORE state, we could be either waiting for us to connect to a
786
   * remote site, or we could wait for that site to connect to us. Or handle
787
   * ordinary commands.
788
   */
789
0
  CURL_TRC_FTP(data, "[%s] ftp_domore_pollset()", FTP_CSTATE(ftpc));
790
791
0
  if(FTP_STOP == ftpc->state) {
792
    /* if stopped and still in this state, then we are also waiting for a
793
       connect on the secondary connection */
794
0
    DEBUGASSERT(data->conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD ||
795
0
                (data->conn->cfilter[SECONDARYSOCKET] &&
796
0
                 !Curl_conn_is_connected(data->conn, SECONDARYSOCKET)));
797
    /* An unconnected SECONDARY will add its socket by itself
798
     * via its adjust_pollset() */
799
0
    return Curl_pollset_add_in(data, ps, data->conn->sock[FIRSTSOCKET]);
800
0
  }
801
0
  return Curl_pp_pollset(data, &ftpc->pp, ps);
802
0
}
803
804
static int pathlen(struct ftp_conn *ftpc, int num)
805
0
{
806
0
  DEBUGASSERT(ftpc->dirs);
807
0
  DEBUGASSERT(ftpc->dirdepth > num);
808
0
  return ftpc->dirs[num].len;
809
0
}
810
811
static const char *pathpiece(struct ftp_conn *ftpc, int num)
812
0
{
813
0
  DEBUGASSERT(ftpc->dirs);
814
0
  DEBUGASSERT(ftpc->dirdepth > num);
815
0
  return &ftpc->rawpath[ftpc->dirs[num].start];
816
0
}
817
818
/* This is called after the FTP_QUOTE state is passed.
819
820
   ftp_state_cwd() sends the range of CWD commands to the server to change to
821
   the correct directory. It may also need to send MKD commands to create
822
   missing ones, if that option is enabled. */
823
static CURLcode ftp_state_cwd(struct Curl_easy *data,
824
                              struct ftp_conn *ftpc,
825
                              struct FTP *ftp)
826
0
{
827
0
  CURLcode result = CURLE_OK;
828
829
0
  if(ftpc->cwddone)
830
    /* already done and fine */
831
0
    result = ftp_state_mdtm(data, ftpc, ftp);
832
0
  else {
833
    /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
834
0
    DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) ||
835
0
                !(ftpc->dirdepth && ftpc->rawpath[0] == '/'));
836
837
0
    ftpc->count2 = 0; /* count2 counts failed CWDs */
838
839
0
    if(data->conn->bits.reuse && ftpc->entrypath &&
840
       /* no need to go to entrypath when we have an absolute path */
841
0
       !(ftpc->dirdepth && ftpc->rawpath[0] == '/')) {
842
      /* This is a reused connection. Since we change directory to where the
843
         transfer is taking place, we must first get back to the original dir
844
         where we ended up after login: */
845
0
      ftpc->cwdcount = 0; /* we count this as the first path, then we add one
846
                             for all upcoming ones in the ftp->dirs[] array */
847
0
      result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
848
0
      if(!result)
849
0
        ftp_state(data, ftpc, FTP_CWD);
850
0
    }
851
0
    else {
852
0
      if(ftpc->dirdepth) {
853
0
        ftpc->cwdcount = 1;
854
        /* issue the first CWD, the rest is sent when the CWD responses are
855
           received... */
856
0
        result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s",
857
0
                               pathlen(ftpc, 0), pathpiece(ftpc, 0));
858
0
        if(!result)
859
0
          ftp_state(data, ftpc, FTP_CWD);
860
0
      }
861
0
      else {
862
        /* No CWD necessary */
863
0
        result = ftp_state_mdtm(data, ftpc, ftp);
864
0
      }
865
0
    }
866
0
  }
867
0
  return result;
868
0
}
869
870
typedef enum {
871
  EPRT,
872
  PORT,
873
  DONE
874
} ftpport;
875
876
/*
877
 * Parse the CURLOPT_FTPPORT string
878
 * "(ipv4|ipv6|domain|interface)?(:port(-range)?)?"
879
 * and extract addr/addrlen and port_min/port_max.
880
 */
881
static CURLcode ftp_port_parse_string(struct Curl_easy *data,
882
                                      struct connectdata *conn,
883
                                      const char *string_ftpport,
884
                                      struct Curl_sockaddr_storage *ss,
885
                                      unsigned short *port_minp,
886
                                      unsigned short *port_maxp,
887
                                      const char **hostp,
888
                                      char *hbuf, size_t hbuflen)
889
0
{
890
0
  const char *ip_end = NULL;
891
0
  const char *addr = NULL;
892
0
  size_t addrlen = 0;
893
0
  unsigned short port_min = 0;
894
0
  unsigned short port_max = 0;
895
0
  char ipstr[50];
896
#ifndef USE_IPV6
897
  (void)conn;
898
  (void)ss;
899
#endif
900
901
  /* default to nothing */
902
0
  *hostp = NULL;
903
0
  *port_minp = *port_maxp = 0;
904
905
0
  if(!string_ftpport || (strlen(string_ftpport) <= 1))
906
0
    goto done;
907
908
0
#ifdef USE_IPV6
909
0
  if(*string_ftpport == '[') {
910
    /* [ipv6]:port(-range) */
911
0
    const char *ip_start = string_ftpport + 1;
912
0
    ip_end = strchr(ip_start, ']');
913
0
    if(ip_end) {
914
0
      addrlen = ip_end - ip_start;
915
0
      addr = ip_start;
916
0
    }
917
0
  }
918
0
  else
919
0
#endif
920
0
    if(*string_ftpport == ':') {
921
      /* :port */
922
0
      ip_end = string_ftpport;
923
0
    }
924
0
    else {
925
0
      ip_end = strchr(string_ftpport, ':');
926
0
      addr = string_ftpport;
927
0
      if(ip_end) {
928
0
#ifdef USE_IPV6
929
0
        struct sockaddr_in6 * const sa6 = (void *)ss;
930
0
#endif
931
        /* either IPv6 or (ipv4|domain|interface):port(-range) */
932
0
        addrlen = ip_end - string_ftpport;
933
0
#ifdef USE_IPV6
934
0
        if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
935
          /* IPv6 */
936
0
          addrlen = strlen(string_ftpport);
937
0
          ip_end = NULL; /* this got no port ! */
938
0
        }
939
0
#endif
940
0
      }
941
0
      else
942
        /* ipv4|interface */
943
0
        addrlen = strlen(string_ftpport);
944
0
    }
945
946
  /* parse the port */
947
0
  if(ip_end) {
948
0
    const char *portp = strchr(ip_end, ':');
949
0
    if(portp) {
950
0
      curl_off_t start;
951
0
      curl_off_t end;
952
0
      portp++;
953
0
      if(!curlx_str_number(&portp, &start, 0xffff)) {
954
0
        port_min = (unsigned short)start;
955
0
        if(!curlx_str_single(&portp, '-') &&
956
0
           !curlx_str_number(&portp, &end, 0xffff))
957
0
          port_max = (unsigned short)end;
958
0
        else
959
0
          port_max = port_min;
960
0
      }
961
0
    }
962
0
  }
963
964
  /* correct errors like :1234-1230 or :-4711 */
965
0
  if(port_min > port_max)
966
0
    port_min = port_max = 0;
967
968
0
  if(addrlen) {
969
0
    const struct Curl_sockaddr_ex *remote_addr =
970
0
      Curl_conn_get_remote_addr(data, FIRSTSOCKET);
971
972
0
    DEBUGASSERT(remote_addr);
973
0
    DEBUGASSERT(addr);
974
0
    if(!remote_addr || (addrlen >= sizeof(ipstr)) || (addrlen >= hbuflen))
975
0
      return CURLE_FTP_PORT_FAILED;
976
0
    memcpy(ipstr, addr, addrlen);
977
0
    ipstr[addrlen] = 0;
978
979
    /* attempt to get the address of the given interface name */
980
0
    switch(Curl_if2ip(remote_addr->family,
981
0
#ifdef USE_IPV6
982
0
                      Curl_ipv6_scope(&remote_addr->curl_sa_addr),
983
0
                      conn->scope_id,
984
0
#endif
985
0
                      ipstr, hbuf, hbuflen)) {
986
0
    case IF2IP_NOT_FOUND:
987
      /* not an interface, use the string as hostname instead */
988
0
      memcpy(hbuf, addr, addrlen);
989
0
      hbuf[addrlen] = 0;
990
0
      *hostp = hbuf;
991
0
      break;
992
0
    case IF2IP_AF_NOT_SUPPORTED:
993
0
      return CURLE_FTP_PORT_FAILED;
994
0
    case IF2IP_FOUND:
995
0
      *hostp = hbuf; /* use the hbuf for hostname */
996
0
      break;
997
0
    }
998
0
  }
999
  /* else: only a port(-range) given, leave host as NULL */
1000
1001
0
done:
1002
0
  *port_minp = port_min;
1003
0
  *port_maxp = port_max;
1004
0
  return CURLE_OK;
1005
0
}
1006
1007
/*
1008
 * If no host was derived from the FTPPORT string, fall back to the IP address
1009
 * of the control connection's local socket.
1010
 */
1011
static CURLcode ftp_port_default_host(struct Curl_easy *data,
1012
                                      struct connectdata *conn,
1013
                                      struct Curl_sockaddr_storage *ss,
1014
                                      curl_socklen_t *sslenp,
1015
                                      const char **hostp,
1016
                                      char *hbuf, size_t hbuflen,
1017
                                      bool *non_localp)
1018
0
{
1019
0
  struct sockaddr *sa = (struct sockaddr *)ss;
1020
0
  struct sockaddr_in * const sa4 = (void *)sa;
1021
0
#ifdef USE_IPV6
1022
0
  struct sockaddr_in6 * const sa6 = (void *)sa;
1023
0
#endif
1024
0
  char buffer[STRERROR_LEN];
1025
0
  const char *r;
1026
1027
0
  *sslenp = sizeof(*ss);
1028
0
  if(getsockname(conn->sock[FIRSTSOCKET], sa, sslenp)) {
1029
0
    failf(data, "getsockname() failed: %s",
1030
0
          curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1031
0
    return CURLE_FTP_PORT_FAILED;
1032
0
  }
1033
0
  switch(sa->sa_family) {
1034
0
#ifdef USE_IPV6
1035
0
  case AF_INET6:
1036
0
    r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, hbuflen);
1037
0
    break;
1038
0
#endif
1039
0
  default:
1040
0
    r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, hbuflen);
1041
0
    break;
1042
0
  }
1043
0
  if(!r)
1044
0
    return CURLE_FTP_PORT_FAILED;
1045
1046
0
  *hostp = hbuf;
1047
0
  *non_localp = FALSE; /* we know it is local now */
1048
0
  return CURLE_OK;
1049
0
}
1050
1051
/*
1052
 * Resolve the host string to a list of addresses.
1053
 */
1054
static CURLcode ftp_port_resolve_host(struct Curl_easy *data,
1055
                                      struct connectdata *conn,
1056
                                      const char *host,
1057
                                      struct Curl_dns_entry **dns_entryp,
1058
                                      const struct Curl_addrinfo **resp)
1059
0
{
1060
0
  CURLcode result;
1061
1062
0
  *resp = NULL;
1063
0
  result = Curl_resolv_blocking(
1064
0
    data, Curl_resolv_dns_queries(data, conn->ip_version),
1065
0
    host, 0, Curl_conn_get_transport(data, conn), dns_entryp);
1066
0
  if(result)
1067
0
    failf(data, "failed to resolve the address provided to PORT: %s", host);
1068
0
  else {
1069
0
    DEBUGASSERT(*dns_entryp);
1070
0
    *resp = (*dns_entryp)->addr;
1071
0
  }
1072
0
  return result;
1073
0
}
1074
1075
/*
1076
 * Open a TCP socket for the resolved address family.
1077
 */
1078
static CURLcode ftp_port_open_socket(struct Curl_easy *data,
1079
                                     struct connectdata *conn,
1080
                                     const struct Curl_addrinfo *res,
1081
                                     const struct Curl_addrinfo **aip,
1082
                                     curl_socket_t *portsockp)
1083
0
{
1084
0
  char buffer[STRERROR_LEN];
1085
0
  int sockerr = 0;
1086
0
  const struct Curl_addrinfo *ai;
1087
0
  CURLcode result = CURLE_FTP_PORT_FAILED;
1088
1089
0
  for(ai = res; ai; ai = ai->ai_next) {
1090
0
    result =
1091
0
      Curl_socket_open(data, ai, NULL,
1092
0
                       Curl_conn_get_transport(data, conn), portsockp);
1093
0
    if(result) {
1094
0
      if(result == CURLE_OUT_OF_MEMORY)
1095
0
        return result;
1096
0
      result = CURLE_FTP_PORT_FAILED;
1097
0
      sockerr = SOCKERRNO;
1098
0
      continue;
1099
0
    }
1100
0
    break;
1101
0
  }
1102
0
  if(!ai) {
1103
0
    failf(data, "socket failure: %s",
1104
0
          curlx_strerror(sockerr, buffer, sizeof(buffer)));
1105
0
    return CURLE_FTP_PORT_FAILED;
1106
0
  }
1107
0
  *aip = ai;
1108
0
  return result;
1109
0
}
1110
1111
/*
1112
 * Bind the socket to a local address and port within the requested range.
1113
 * Falls back to the control-connection address if the user-requested address
1114
 * is non-local.
1115
 */
1116
static CURLcode ftp_port_bind_socket(struct Curl_easy *data,
1117
                                     struct connectdata *conn,
1118
                                     curl_socket_t portsock,
1119
                                     const struct Curl_addrinfo *ai,
1120
                                     struct Curl_sockaddr_storage *ss,
1121
                                     curl_socklen_t *sslen_io,
1122
                                     unsigned short port_min,
1123
                                     unsigned short port_max,
1124
                                     bool non_local)
1125
0
{
1126
0
  struct sockaddr *sa = (struct sockaddr *)ss;
1127
0
  struct sockaddr_in * const sa4 = (void *)sa;
1128
0
#ifdef USE_IPV6
1129
0
  struct sockaddr_in6 * const sa6 = (void *)sa;
1130
0
#endif
1131
0
  char buffer[STRERROR_LEN];
1132
0
  unsigned short port;
1133
0
  int sockerr;
1134
1135
0
  memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1136
0
  *sslen_io = ai->ai_addrlen;
1137
1138
0
  for(port = port_min; port <= port_max;) {
1139
0
    if(sa->sa_family == AF_INET)
1140
0
      sa4->sin_port = htons(port);
1141
0
#ifdef USE_IPV6
1142
0
    else
1143
0
      sa6->sin6_port = htons(port);
1144
0
#endif
1145
0
    if(bind(portsock, sa, *sslen_io)) {
1146
0
      sockerr = SOCKERRNO;
1147
0
      if(non_local && (sockerr == SOCKEADDRNOTAVAIL)) {
1148
        /* The requested bind address is not local. Use the address used for
1149
         * the control connection instead and restart the port loop.
1150
         */
1151
0
        infof(data, "bind(port=%hu) on non-local address failed: %s", port,
1152
0
              curlx_strerror(sockerr, buffer, sizeof(buffer)));
1153
1154
0
        *sslen_io = sizeof(*ss);
1155
0
        if(getsockname(conn->sock[FIRSTSOCKET], sa, sslen_io)) {
1156
0
          failf(data, "getsockname() failed: %s",
1157
0
                curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1158
0
          return CURLE_FTP_PORT_FAILED;
1159
0
        }
1160
0
        port = port_min;
1161
0
        non_local = FALSE; /* do not try this again */
1162
0
        continue;
1163
0
      }
1164
0
      if(sockerr != SOCKEADDRINUSE && sockerr != SOCKEACCES) {
1165
0
        failf(data, "bind(port=%hu) failed: %s", port,
1166
0
              curlx_strerror(sockerr, buffer, sizeof(buffer)));
1167
0
        return CURLE_FTP_PORT_FAILED;
1168
0
      }
1169
0
    }
1170
0
    else
1171
0
      break;
1172
1173
    /* check if port is the maximum value here, because it might be 0xffff
1174
       and then the increment below will wrap the 16-bit counter */
1175
0
    if(port == port_max) {
1176
0
      failf(data, "bind() failed, ran out of ports");
1177
0
      return CURLE_FTP_PORT_FAILED;
1178
0
    }
1179
0
    port++;
1180
0
  }
1181
1182
  /* re-read the name so we can extract the actual port chosen */
1183
0
  *sslen_io = sizeof(*ss);
1184
0
  if(getsockname(portsock, sa, sslen_io)) {
1185
0
    failf(data, "getsockname() failed: %s",
1186
0
          curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1187
0
    return CURLE_FTP_PORT_FAILED;
1188
0
  }
1189
0
  CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", port);
1190
0
  return CURLE_OK;
1191
0
}
1192
1193
/*
1194
 * Start listening on the data socket.
1195
 */
1196
static CURLcode ftp_port_listen(struct Curl_easy *data, curl_socket_t portsock)
1197
0
{
1198
0
  char buffer[STRERROR_LEN];
1199
1200
0
  if(listen(portsock, 1)) {
1201
0
    failf(data, "socket failure: %s",
1202
0
          curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1203
0
    return CURLE_FTP_PORT_FAILED;
1204
0
  }
1205
0
  CURL_TRC_FTP(data, "ftp_port_listen(), listening on port");
1206
0
  return CURLE_OK;
1207
0
}
1208
1209
/*
1210
 * Send the EPRT or PORT command to the server.
1211
 */
1212
static CURLcode ftp_port_send_command(struct Curl_easy *data,
1213
                                      struct ftp_conn *ftpc,
1214
                                      struct connectdata *conn,
1215
                                      struct Curl_sockaddr_storage *ss,
1216
                                      const struct Curl_addrinfo *ai,
1217
                                      ftpport fcmd)
1218
0
{
1219
0
  static const char mode[][5] = { "EPRT", "PORT" };
1220
0
  struct sockaddr *sa = (struct sockaddr *)ss;
1221
0
  struct sockaddr_in * const sa4 = (void *)sa;
1222
0
#ifdef USE_IPV6
1223
0
  struct sockaddr_in6 * const sa6 = (void *)sa;
1224
0
#endif
1225
0
  char myhost[MAX_IPADR_LEN + 1] = "";
1226
0
  unsigned short port;
1227
0
  CURLcode result;
1228
1229
  /* Get a plain printable version of the numerical address to work with. This
1230
     logic uses the address provided by the FTPPORT option, which at times
1231
     might differ from the address in 'ss' used to bind to: when a user asks
1232
     the server to connect to a specific address knowing that it works, but
1233
     curl instead selects to listen to the local address because it cannot use
1234
     the provided address. FTP is strange. */
1235
0
  Curl_printable_address(ai, myhost, sizeof(myhost));
1236
1237
0
#ifdef USE_IPV6
1238
0
  if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1239
    /* EPRT is disabled but we are connected to an IPv6 host, so we ignore the
1240
       request and enable EPRT again! */
1241
0
    conn->bits.ftp_use_eprt = TRUE;
1242
0
#endif
1243
1244
0
  for(; fcmd != DONE; fcmd++) {
1245
1246
0
    if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1247
      /* if disabled, goto next */
1248
0
      continue;
1249
1250
0
    if((PORT == fcmd) && sa->sa_family != AF_INET)
1251
      /* PORT is IPv4 only */
1252
0
      continue;
1253
1254
0
    switch(sa->sa_family) {
1255
0
    case AF_INET:
1256
0
      port = ntohs(sa4->sin_port);
1257
0
      break;
1258
0
#ifdef USE_IPV6
1259
0
    case AF_INET6:
1260
0
      port = ntohs(sa6->sin6_port);
1261
0
      break;
1262
0
#endif
1263
0
    default:
1264
0
      continue; /* might as well skip this */
1265
0
    }
1266
1267
0
    if(EPRT == fcmd) {
1268
      /*
1269
       * Two fine examples from RFC2428;
1270
       *
1271
       * EPRT |1|132.235.1.2|6275|
1272
       *
1273
       * EPRT |2|1080::8:800:200C:417A|5282|
1274
       */
1275
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1276
0
                             sa->sa_family == AF_INET ? 1 : 2, myhost, port);
1277
0
      if(result) {
1278
0
        failf(data, "Failure sending EPRT command: %s",
1279
0
              curl_easy_strerror(result));
1280
0
        return result;
1281
0
      }
1282
0
      break;
1283
0
    }
1284
0
    if(PORT == fcmd) {
1285
      /* large enough for [IP address],[num],[num] */
1286
0
      char target[sizeof(myhost) + 20];
1287
0
      const char *source = myhost;
1288
0
      char *dest = target;
1289
1290
      /* translate x.x.x.x to x,x,x,x */
1291
0
      while(*source) {
1292
0
        if(*source == '.')
1293
0
          *dest = ',';
1294
0
        else
1295
0
          *dest = *source;
1296
0
        dest++;
1297
0
        source++;
1298
0
      }
1299
0
      *dest = 0;
1300
0
      curl_msnprintf(dest, 20, ",%d,%d", (int)(port >> 8), (int)(port & 0xff));
1301
1302
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target);
1303
0
      if(result) {
1304
0
        failf(data, "Failure sending PORT command: %s",
1305
0
              curl_easy_strerror(result));
1306
0
        return result;
1307
0
      }
1308
0
      break;
1309
0
    }
1310
0
  }
1311
1312
  /* store which command was sent */
1313
0
  ftpc->count1 = fcmd;
1314
0
  ftp_state(data, ftpc, FTP_PORT);
1315
0
  return CURLE_OK;
1316
0
}
1317
1318
/*
1319
 * ftp_state_use_port()
1320
 *
1321
 * Set up an active-mode FTP data connection (using PORT or EPRT) and start
1322
 * listening for the server's incoming connection on SECONDARYSOCKET.
1323
 */
1324
static CURLcode ftp_state_use_port(struct Curl_easy *data,
1325
                                   struct ftp_conn *ftpc,
1326
                                   ftpport fcmd) /* start with this */
1327
0
{
1328
0
  CURLcode result = CURLE_FTP_PORT_FAILED;
1329
0
  struct connectdata *conn = data->conn;
1330
0
  curl_socket_t portsock = CURL_SOCKET_BAD;
1331
1332
0
  struct Curl_sockaddr_storage ss;
1333
0
  curl_socklen_t sslen;
1334
0
  char hbuf[NI_MAXHOST];
1335
0
  const char *host = NULL;
1336
0
  const char *string_ftpport = data->set.str[STRING_FTPPORT];
1337
0
  struct Curl_dns_entry *dns_entry = NULL;
1338
0
  const struct Curl_addrinfo *res = NULL;
1339
0
  const struct Curl_addrinfo *ai = NULL;
1340
0
  unsigned short port_min = 0;
1341
0
  unsigned short port_max = 0;
1342
0
  bool non_local = TRUE;
1343
1344
  /* parse the FTPPORT string for address and port range */
1345
0
  result = ftp_port_parse_string(data, conn, string_ftpport,
1346
0
                                 &ss, &port_min, &port_max,
1347
0
                                 &host, hbuf, sizeof(hbuf));
1348
0
  if(!result && !host)
1349
    /* if no host was specified, use the control connection's local IP */
1350
0
    result = ftp_port_default_host(data, conn, &ss, &sslen, &host,
1351
0
                                   hbuf, sizeof(hbuf), &non_local);
1352
1353
  /* resolve host string to address list */
1354
0
  if(!result)
1355
0
    result = ftp_port_resolve_host(data, conn, host, &dns_entry, &res);
1356
1357
  /* Open a TCP socket for the data connection */
1358
0
  if(!result)
1359
0
    result = ftp_port_open_socket(data, conn, res, &ai, &portsock);
1360
0
  if(!result) {
1361
0
    CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket",
1362
0
                 FTP_CSTATE(ftpc));
1363
1364
    /* bind to a suitable local address / port */
1365
0
    result = ftp_port_bind_socket(data, conn, portsock, ai, &ss, &sslen,
1366
0
                                  port_min, port_max, non_local);
1367
0
  }
1368
1369
  /* listen */
1370
0
  if(!result)
1371
0
    result = ftp_port_listen(data, portsock);
1372
1373
  /* send the PORT / EPRT command */
1374
0
  if(!result)
1375
0
    result = ftp_port_send_command(data, ftpc, conn, &ss, ai, fcmd);
1376
1377
  /* replace any filter on SECONDARY with one listening on this socket */
1378
0
  if(!result)
1379
0
    result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
1380
1381
0
  if(!result)
1382
0
    portsock = CURL_SOCKET_BAD; /* now held in filter */
1383
1384
  /* cleanup */
1385
1386
0
  if(dns_entry)
1387
0
    Curl_dns_entry_unlink(data, &dns_entry);
1388
0
  if(result) {
1389
0
    ftp_state(data, ftpc, FTP_STOP);
1390
0
  }
1391
0
  else {
1392
    /* successfully set up the listen socket filter. SSL needed?
1393
     * Use the control connections origin for cert verification. */
1394
0
    if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
1395
0
       !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
1396
0
      result = Curl_ssl_cfilter_add(
1397
0
        data, Curl_conn_get_origin(conn, FIRSTSOCKET),
1398
0
        conn, SECONDARYSOCKET);
1399
0
    }
1400
0
    conn->bits.do_more = FALSE;
1401
0
    Curl_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
                               data->set.str[STRING_CUSTOMREQUEST] ?
1481
0
                               data->set.str[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
                      data->set.str[STRING_CUSTOMREQUEST] ?
1576
0
                      data->set.str[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 command
2678
       to use */
2679
0
    if(instate != FTP_STOR_SIZE) {
2680
0
      failf(data, "The file does not exist");
2681
0
      return CURLE_REMOTE_FILE_NOT_FOUND;
2682
0
    }
2683
0
  }
2684
2685
0
  if(instate == FTP_SIZE) {
2686
0
    if(filesize != -1) {
2687
0
      char clbuf[128];
2688
0
      int clbuflen = curl_msnprintf(clbuf, sizeof(clbuf),
2689
0
                                    "Content-Length: %" FMT_OFF_T "\r\n",
2690
0
                                    filesize);
2691
0
      result = client_write_header(data, clbuf, clbuflen);
2692
0
      if(result)
2693
0
        return result;
2694
0
    }
2695
0
    Curl_pgrsSetDownloadSize(data, filesize);
2696
0
    result = ftp_state_rest(data, ftpc, ftp);
2697
0
  }
2698
0
  else if(instate == FTP_RETR_SIZE) {
2699
0
    Curl_pgrsSetDownloadSize(data, filesize);
2700
0
    result = ftp_state_retr(data, ftpc, ftp, filesize);
2701
0
  }
2702
0
  else if(instate == FTP_STOR_SIZE) {
2703
0
    data->state.resume_from = filesize;
2704
0
    result = ftp_state_ul_setup(data, ftpc, ftp, TRUE);
2705
0
  }
2706
2707
0
  return result;
2708
0
}
2709
2710
static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
2711
                                    struct ftp_conn *ftpc,
2712
                                    struct FTP *ftp,
2713
                                    int ftpcode,
2714
                                    ftpstate instate)
2715
0
{
2716
0
  CURLcode result = CURLE_OK;
2717
2718
0
  switch(instate) {
2719
0
  case FTP_REST:
2720
0
  default:
2721
0
    if(ftpcode == 350) {
2722
0
      char buffer[24] = { "Accept-ranges: bytes\r\n" };
2723
0
      result = client_write_header(data, buffer, strlen(buffer));
2724
0
      if(result)
2725
0
        return result;
2726
0
    }
2727
0
    result = ftp_state_prepare_transfer(data, ftpc, ftp);
2728
0
    break;
2729
2730
0
  case FTP_RETR_REST:
2731
0
    if(ftpcode != 350) {
2732
0
      failf(data, "Could not use REST");
2733
0
      result = CURLE_FTP_COULDNT_USE_REST;
2734
0
    }
2735
0
    else {
2736
0
      result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
2737
0
      if(!result)
2738
0
        ftp_state(data, ftpc, FTP_RETR);
2739
0
    }
2740
0
    break;
2741
0
  }
2742
2743
0
  return result;
2744
0
}
2745
2746
static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
2747
                                    struct ftp_conn *ftpc,
2748
                                    int ftpcode)
2749
0
{
2750
0
  CURLcode result = CURLE_OK;
2751
2752
0
  if(ftpcode >= 400) {
2753
0
    failf(data, "Failed FTP upload: %0d", ftpcode);
2754
0
    ftp_state(data, ftpc, FTP_STOP);
2755
0
    return CURLE_UPLOAD_FAILED;
2756
0
  }
2757
2758
  /* PORT means we are now awaiting the server to connect to us. */
2759
0
  if(data->set.ftp_use_port) {
2760
0
    bool connected;
2761
2762
0
    ftp_state(data, ftpc, FTP_STOP); /* no longer in STOR state */
2763
2764
0
    result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
2765
0
    if(result)
2766
0
      return result;
2767
2768
0
    if(!connected) {
2769
0
      infof(data, "Data conn was not available immediately");
2770
0
      ftpc->wait_data_conn = TRUE;
2771
0
      return ftp_check_ctrl_on_data_wait(data, ftpc);
2772
0
    }
2773
0
    ftpc->wait_data_conn = FALSE;
2774
0
  }
2775
0
  return ftp_initiate_transfer(data, ftpc);
2776
0
}
2777
2778
/* for LIST and RETR responses */
2779
static CURLcode ftp_state_get_resp(struct Curl_easy *data,
2780
                                   struct ftp_conn *ftpc,
2781
                                   struct FTP *ftp,
2782
                                   int ftpcode,
2783
                                   ftpstate instate)
2784
0
{
2785
0
  CURLcode result = CURLE_OK;
2786
2787
0
  if((ftpcode == 150) || (ftpcode == 125)) {
2788
2789
    /*
2790
      A;
2791
      150 Opening BINARY mode data connection for /etc/passwd (2241
2792
      bytes).  (ok, the file is being transferred)
2793
2794
      B:
2795
      150 Opening ASCII mode data connection for /bin/ls
2796
2797
      C:
2798
      150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2799
2800
      D:
2801
      150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2802
2803
      E:
2804
      125 Data connection already open; Transfer starting. */
2805
2806
0
    data->req.size = -1; /* default unknown size */
2807
2808
    /*
2809
     * It appears that there are FTP-servers that return size 0 for files when
2810
     * SIZE is used on the file while being in BINARY mode. To work around
2811
     * that (stupid) behavior, we attempt to parse the RETR response even if
2812
     * the SIZE returned size zero.
2813
     *
2814
     * Debugging help from Salvatore Sorrentino on February 26, 2003.
2815
     */
2816
2817
0
    if((instate != FTP_LIST) &&
2818
0
       !data->state.prefer_ascii &&
2819
0
       !data->set.ignorecl &&
2820
0
       (ftp->downloadsize < 1)) {
2821
      /*
2822
       * It seems directory listings either do not show the size or often uses
2823
       * size 0 anyway. ASCII transfers may cause that the transferred amount
2824
       * of data is not the same as this line tells, why using this number in
2825
       * those cases only confuses us.
2826
       *
2827
       * Example D above makes this parsing a little tricky */
2828
0
      size_t len = curlx_dyn_len(&ftpc->pp.recvbuf);
2829
0
      if(len >= 7) { /* "1 bytes" is 7 characters */
2830
0
        size_t i;
2831
0
        for(i = 0; i < len - 7; i++) {
2832
0
          curl_off_t what;
2833
0
          const char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
2834
0
          const char *c = &buf[i];
2835
0
          if(!curlx_str_number(&c, &what, CURL_OFF_T_MAX) &&
2836
0
             !curlx_str_single(&c, ' ') &&
2837
0
             !strncmp(c, "bytes", 5)) {
2838
0
            data->req.size = what;
2839
0
            break;
2840
0
          }
2841
0
        }
2842
0
      }
2843
0
    }
2844
0
    else if(ftp->downloadsize > -1)
2845
0
      data->req.size = ftp->downloadsize;
2846
2847
0
    if(data->req.size > data->req.maxdownload && data->req.maxdownload > 0)
2848
0
      data->req.size = data->req.maxdownload;
2849
0
    else if((instate != FTP_LIST) && (data->state.prefer_ascii))
2850
0
      data->req.size = -1; /* for servers that understate ASCII mode file
2851
                              size */
2852
2853
0
    infof(data, "Maxdownload = %" FMT_OFF_T, data->req.maxdownload);
2854
2855
0
    if(instate != FTP_LIST)
2856
0
      infof(data, "Getting file with size: %" FMT_OFF_T, data->req.size);
2857
2858
0
    if(data->set.ftp_use_port) {
2859
0
      bool connected;
2860
2861
0
      result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
2862
0
      if(result)
2863
0
        return result;
2864
2865
0
      if(!connected) {
2866
0
        infof(data, "Data conn was not available immediately");
2867
0
        ftp_state(data, ftpc, FTP_STOP);
2868
0
        ftpc->wait_data_conn = TRUE;
2869
0
        return ftp_check_ctrl_on_data_wait(data, ftpc);
2870
0
      }
2871
0
      ftpc->wait_data_conn = FALSE;
2872
0
    }
2873
0
    return ftp_initiate_transfer(data, ftpc);
2874
0
  }
2875
0
  else {
2876
0
    if((instate == FTP_LIST) && (ftpcode == 450)) {
2877
      /* no matching files in the directory listing */
2878
0
      ftp->transfer = PPTRANSFER_NONE; /* do not download anything */
2879
0
      ftp_state(data, ftpc, FTP_STOP); /* this phase is over */
2880
0
    }
2881
0
    else {
2882
0
      failf(data, "RETR response: %03d", ftpcode);
2883
0
      return instate == FTP_RETR && ftpcode == 550 ?
2884
0
        CURLE_REMOTE_FILE_NOT_FOUND :
2885
0
        CURLE_FTP_COULDNT_RETR_FILE;
2886
0
    }
2887
0
  }
2888
2889
0
  return result;
2890
0
}
2891
2892
/* after USER, PASS and ACCT */
2893
static CURLcode ftp_state_loggedin(struct Curl_easy *data,
2894
                                   struct ftp_conn *ftpc)
2895
0
{
2896
0
  CURLcode result = CURLE_OK;
2897
2898
0
  if(Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
2899
    /* PBSZ = PROTECTION BUFFER SIZE.
2900
2901
    The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2902
2903
    Specifically, the PROT command MUST be preceded by a PBSZ
2904
    command and a PBSZ command MUST be preceded by a successful
2905
    security data exchange (the TLS negotiation in this case)
2906
2907
    ... (and on page 8):
2908
2909
    Thus the PBSZ command must still be issued, but must have a
2910
    parameter of '0' to indicate that no buffering is taking place
2911
    and the data connection should not be encapsulated.
2912
    */
2913
0
    result = Curl_pp_sendf(data, &ftpc->pp, "PBSZ %d", 0);
2914
0
    if(!result)
2915
0
      ftp_state(data, ftpc, FTP_PBSZ);
2916
0
  }
2917
0
  else {
2918
0
    result = ftp_state_pwd(data, ftpc);
2919
0
  }
2920
0
  return result;
2921
0
}
2922
2923
/* A value that becomes part of an FTP control command must not carry a
2924
   control byte: a CR or LF would end the command line and let a second
2925
   command be smuggled onto the control connection. */
2926
static bool ftp_has_ctrl(const char *string)
2927
0
{
2928
0
  const unsigned char *s = (const unsigned char *)string;
2929
0
  while(*s) {
2930
0
    if(*s < 0x20)
2931
0
      return TRUE;
2932
0
    s++;
2933
0
  }
2934
0
  return FALSE;
2935
0
}
2936
2937
/* for USER and PASS responses */
2938
static CURLcode ftp_state_user_resp(struct Curl_easy *data,
2939
                                    struct ftp_conn *ftpc,
2940
                                    int ftpcode)
2941
0
{
2942
0
  CURLcode result = CURLE_OK;
2943
2944
  /* some need password anyway, and others return 2xx ignored */
2945
0
  if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2946
    /* 331 Password required for ...
2947
       (the server requires to send the user's password too) */
2948
0
    result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
2949
0
                           Curl_creds_passwd(data->conn->creds));
2950
0
    if(!result)
2951
0
      ftp_state(data, ftpc, FTP_PASS);
2952
0
  }
2953
0
  else if(ftpcode / 100 == 2) {
2954
    /* 230 User ... logged in.
2955
       (the user logged in with or without password) */
2956
0
    result = ftp_state_loggedin(data, ftpc);
2957
0
  }
2958
0
  else if(ftpcode == 332) {
2959
0
    const char *account = data->set.str[STRING_FTP_ACCOUNT];
2960
0
    if(!account) {
2961
0
      failf(data, "ACCT requested but none available");
2962
0
      result = CURLE_LOGIN_DENIED;
2963
0
    }
2964
0
    else if(ftp_has_ctrl(account)) {
2965
0
      failf(data, "Control byte in FTP account");
2966
0
      result = CURLE_BAD_FUNCTION_ARGUMENT;
2967
0
    }
2968
0
    else {
2969
0
      result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", account);
2970
0
      if(!result)
2971
0
        ftp_state(data, ftpc, FTP_ACCT);
2972
0
    }
2973
0
  }
2974
0
  else {
2975
    /* All other response codes, like:
2976
2977
    530 User ... access denied
2978
    (the server denies to log the specified user) */
2979
2980
0
    const char *alt = data->set.str[STRING_FTP_ALTERNATIVE_TO_USER];
2981
0
    if(alt && !ftpc->ftp_trying_alternative) {
2982
      /* Ok, USER failed. Let's try the supplied command. */
2983
0
      if(ftp_has_ctrl(alt)) {
2984
0
        failf(data, "Control byte in FTP alternative-to-user command");
2985
0
        result = CURLE_BAD_FUNCTION_ARGUMENT;
2986
0
      }
2987
0
      else {
2988
0
        result = Curl_pp_sendf(data, &ftpc->pp, "%s", alt);
2989
0
        if(!result) {
2990
0
          ftpc->ftp_trying_alternative = TRUE;
2991
0
          ftp_state(data, ftpc, FTP_USER);
2992
0
        }
2993
0
      }
2994
0
    }
2995
0
    else {
2996
0
      failf(data, "Access denied: %03d", ftpcode);
2997
0
      result = CURLE_LOGIN_DENIED;
2998
0
    }
2999
0
  }
3000
0
  return result;
3001
0
}
3002
3003
/* for ACCT response */
3004
static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
3005
                                    struct ftp_conn *ftpc,
3006
                                    int ftpcode)
3007
0
{
3008
0
  CURLcode result = CURLE_OK;
3009
0
  if(ftpcode != 230) {
3010
0
    failf(data, "ACCT rejected by server: %03d", ftpcode);
3011
0
    result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
3012
0
  }
3013
0
  else
3014
0
    result = ftp_state_loggedin(data, ftpc);
3015
3016
0
  return result;
3017
0
}
3018
3019
static CURLcode ftp_pwd_resp(struct Curl_easy *data,
3020
                             struct ftp_conn *ftpc,
3021
                             int ftpcode)
3022
0
{
3023
0
  struct pingpong *pp = &ftpc->pp;
3024
0
  CURLcode result;
3025
3026
0
  if(ftpcode == 257) {
3027
0
    const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
3028
                                                          letter */
3029
0
    bool entry_extracted = FALSE;
3030
0
    struct dynbuf out;
3031
0
    curlx_dyn_init(&out, 1000);
3032
3033
    /* Reply format is like
3034
       257<space>[rubbish]"<directory-name>"<space><commentary> and the
3035
       RFC959 says
3036
3037
       The directory name can contain any character; embedded
3038
       double-quotes should be escaped by double-quotes (the
3039
       "quote-doubling" convention).
3040
     */
3041
3042
    /* scan for the first double-quote for non-standard responses */
3043
0
    while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
3044
0
      ptr++;
3045
3046
0
    if('\"' == *ptr) {
3047
      /* it started good */
3048
0
      for(ptr++; *ptr; ptr++) {
3049
0
        if('\"' == *ptr) {
3050
0
          if('\"' == ptr[1]) {
3051
            /* "quote-doubling" */
3052
0
            result = curlx_dyn_addn(&out, &ptr[1], 1);
3053
0
            ptr++;
3054
0
          }
3055
0
          else {
3056
            /* end of path */
3057
0
            if(curlx_dyn_len(&out))
3058
0
              entry_extracted = TRUE;
3059
0
            break; /* get out of this loop */
3060
0
          }
3061
0
        }
3062
0
        else {
3063
0
          if(ISCNTRL(*ptr)) {
3064
            /* control characters have no business in a path */
3065
0
            curlx_dyn_free(&out);
3066
0
            return CURLE_WEIRD_SERVER_REPLY;
3067
0
          }
3068
0
          result = curlx_dyn_addn(&out, ptr, 1);
3069
0
        }
3070
0
        if(result) {
3071
0
          curlx_dyn_free(&out);
3072
0
          return result;
3073
0
        }
3074
0
      }
3075
0
    }
3076
0
    if(entry_extracted) {
3077
      /* If the path name does not look like an absolute path (i.e.: it
3078
         does not start with a '/'), we probably need some server-dependent
3079
         adjustments. For example, this is the case when connecting to
3080
         an OS400 FTP server: this server supports two name syntaxes,
3081
         the default one being incompatible with standard paths. In
3082
         addition, this server switches automatically to the regular path
3083
         syntax when one is encountered in a command: this results in
3084
         having an entrypath in the wrong syntax when later used in CWD.
3085
         The method used here is to check the server OS: we do it only
3086
         if the path name looks strange to minimize overhead on other
3087
         systems. */
3088
0
      char *dir = curlx_dyn_ptr(&out);
3089
3090
0
      if(!ftpc->server_os && dir[0] != '/') {
3091
0
        result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
3092
0
        if(result) {
3093
0
          curlx_dyn_free(&out);
3094
0
          return result;
3095
0
        }
3096
0
      }
3097
3098
0
      curlx_free(ftpc->entrypath);
3099
0
      ftpc->entrypath = dir; /* remember this */
3100
0
      infof(data, "Entry path is '%s'", ftpc->entrypath);
3101
      /* also save it where getinfo can access it: */
3102
0
      curlx_free(data->state.most_recent_ftp_entrypath);
3103
0
      data->state.most_recent_ftp_entrypath = curlx_strdup(ftpc->entrypath);
3104
0
      if(!data->state.most_recent_ftp_entrypath)
3105
0
        return CURLE_OUT_OF_MEMORY;
3106
3107
0
      if(!ftpc->server_os && dir[0] != '/') {
3108
0
        ftp_state(data, ftpc, FTP_SYST);
3109
0
        return CURLE_OK;
3110
0
      }
3111
0
    }
3112
0
    else {
3113
      /* could not get the path */
3114
0
      curlx_dyn_free(&out);
3115
0
      infof(data, "Failed to figure out path");
3116
0
    }
3117
0
  }
3118
0
  ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
3119
0
  CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
3120
0
  return CURLE_OK;
3121
0
}
3122
3123
static const char * const ftpauth[] = { "SSL", "TLS" };
3124
3125
static CURLcode ftp_wait_resp(struct Curl_easy *data,
3126
                              struct connectdata *conn,
3127
                              struct ftp_conn *ftpc,
3128
                              int ftpcode)
3129
0
{
3130
0
  CURLcode result = CURLE_OK;
3131
0
  if(ftpcode == 230) {
3132
    /* 230 User logged in - already! Take as 220 if TLS required. */
3133
0
    if(ftpc->use_ssl <= CURLUSESSL_TRY ||
3134
0
       Curl_conn_is_ssl(conn, FIRSTSOCKET))
3135
0
      return ftp_state_user_resp(data, ftpc, ftpcode);
3136
0
  }
3137
0
  else if(ftpcode != 220) {
3138
0
    failf(data, "Got a %03d ftp-server response when 220 was expected",
3139
0
          ftpcode);
3140
0
    return CURLE_WEIRD_SERVER_REPLY;
3141
0
  }
3142
3143
0
  if(ftpc->use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
3144
    /* We do not have an SSL/TLS control connection yet, but FTPS is
3145
       requested. Try an FTPS connection now */
3146
3147
0
    ftpc->count3 = 0;
3148
0
    switch((long)data->set.ftpsslauth) {
3149
0
    case CURLFTPAUTH_DEFAULT:
3150
0
    case CURLFTPAUTH_SSL:
3151
0
      ftpc->count2 = 1; /* add one to get next */
3152
0
      ftpc->count1 = 0;
3153
0
      break;
3154
0
    case CURLFTPAUTH_TLS:
3155
0
      ftpc->count2 = -1; /* subtract one to get next */
3156
0
      ftpc->count1 = 1;
3157
0
      break;
3158
0
    default:
3159
0
      failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
3160
0
            (int)data->set.ftpsslauth);
3161
0
      return CURLE_UNKNOWN_OPTION; /* we do not know what to do */
3162
0
    }
3163
0
    result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
3164
0
    if(!result)
3165
0
      ftp_state(data, ftpc, FTP_AUTH);
3166
0
  }
3167
0
  else
3168
0
    result = ftp_state_user(data, ftpc, conn);
3169
0
  return result;
3170
0
}
3171
3172
static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
3173
                                    struct connectdata *conn)
3174
0
{
3175
0
  CURLcode result;
3176
0
  int ftpcode;
3177
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
3178
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
3179
0
  struct pingpong *pp;
3180
0
  size_t nread = 0;
3181
3182
0
  if(!ftpc || !ftp)
3183
0
    return CURLE_FAILED_INIT;
3184
0
  pp = &ftpc->pp;
3185
0
  if(pp->sendleft)
3186
0
    return Curl_pp_flushsend(data, pp);
3187
3188
0
  result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, &ftpcode, &nread);
3189
0
  if(result || !ftpcode)
3190
0
    return result;
3191
3192
  /* we have now received a full FTP server response */
3193
0
  switch(ftpc->state) {
3194
0
  case FTP_WAIT220:
3195
0
    result = ftp_wait_resp(data, conn, ftpc, ftpcode);
3196
0
    break;
3197
3198
0
  case FTP_AUTH:
3199
    /* we have gotten the response to a previous AUTH command */
3200
3201
0
    if(pp->overflow)
3202
0
      return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
3203
3204
    /* RFC2228 (page 5) says:
3205
     *
3206
     * If the server is willing to accept the named security mechanism,
3207
     * and does not require any security data, it must respond with
3208
     * reply code 234/334.
3209
     */
3210
3211
0
    if((ftpcode == 234) || (ftpcode == 334)) {
3212
      /* this was BLOCKING, keep it so for now */
3213
0
      bool done;
3214
0
      if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
3215
0
        result = Curl_ssl_cfilter_add(
3216
0
          data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
3217
0
        if(result) {
3218
          /* we failed and bail out */
3219
0
          return CURLE_USE_SSL_FAILED;
3220
0
        }
3221
0
      }
3222
      /* BLOCKING */
3223
0
      result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
3224
0
      if(!result) {
3225
0
        conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
3226
0
        result = ftp_state_user(data, ftpc, conn);
3227
0
      }
3228
0
    }
3229
0
    else if(ftpc->count3 < 1) {
3230
0
      ftpc->count3++;
3231
0
      ftpc->count1 += ftpc->count2; /* get next attempt */
3232
0
      result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
3233
0
                             ftpauth[ftpc->count1]);
3234
      /* remain in this same state */
3235
0
    }
3236
0
    else {
3237
0
      if(ftpc->use_ssl > CURLUSESSL_TRY)
3238
        /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
3239
0
        result = CURLE_USE_SSL_FAILED;
3240
0
      else
3241
        /* ignore the failure and continue */
3242
0
        result = ftp_state_user(data, ftpc, conn);
3243
0
    }
3244
0
    break;
3245
3246
0
  case FTP_USER:
3247
0
  case FTP_PASS:
3248
0
    result = ftp_state_user_resp(data, ftpc, ftpcode);
3249
0
    break;
3250
3251
0
  case FTP_ACCT:
3252
0
    result = ftp_state_acct_resp(data, ftpc, ftpcode);
3253
0
    break;
3254
3255
0
  case FTP_PBSZ:
3256
0
    result =
3257
0
      Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
3258
0
                    ftpc->use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
3259
0
    if(!result)
3260
0
      ftp_state(data, ftpc, FTP_PROT);
3261
0
    break;
3262
3263
0
  case FTP_PROT:
3264
0
    if(ftpcode / 100 == 2)
3265
      /* We have enabled SSL for the data connection! */
3266
0
      conn->bits.ftp_use_data_ssl = (ftpc->use_ssl != CURLUSESSL_CONTROL);
3267
    /* FTP servers typically responds with 500 if they decide to reject
3268
       our 'P' request */
3269
0
    else if(ftpc->use_ssl > CURLUSESSL_CONTROL)
3270
      /* we failed and bails out */
3271
0
      return CURLE_USE_SSL_FAILED;
3272
3273
0
    if(data->set.ftp_ccc) {
3274
      /* CCC - Clear Command Channel
3275
       */
3276
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
3277
0
      if(!result)
3278
0
        ftp_state(data, ftpc, FTP_CCC);
3279
0
    }
3280
0
    else
3281
0
      result = ftp_state_pwd(data, ftpc);
3282
0
    break;
3283
3284
0
  case FTP_CCC:
3285
0
    if(ftpcode < 500) {
3286
      /* First shut down the SSL layer (note: this call will block) */
3287
      /* This has only been tested on the proftpd server, and the mod_tls
3288
       * code sends a close notify alert without waiting for a close notify
3289
       * alert in response. Thus we wait for a close notify alert from the
3290
       * server, but we do not send one. Let's hope other servers do
3291
       * the same... */
3292
0
      result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET,
3293
0
                                       (data->set.ftp_ccc ==
3294
0
                                        (unsigned char)CURLFTPSSL_CCC_ACTIVE));
3295
0
      if(result)
3296
0
        failf(data, "Failed to clear the command channel (CCC)");
3297
0
    }
3298
0
    if(!result)
3299
      /* Then continue as normal */
3300
0
      result = ftp_state_pwd(data, ftpc);
3301
0
    break;
3302
3303
0
  case FTP_PWD:
3304
0
    result = ftp_pwd_resp(data, ftpc, ftpcode);
3305
0
    break;
3306
3307
0
  case FTP_SYST:
3308
0
    if(ftpcode == 215) {
3309
0
      const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
3310
                                                            letter */
3311
0
      const char *start;
3312
0
      char *os;
3313
3314
      /* Reply format is like
3315
         215<space><OS-name><space><commentary>
3316
       */
3317
0
      while(*ptr == ' ')
3318
0
        ptr++;
3319
0
      for(start = ptr; *ptr && *ptr != ' '; ptr++)
3320
0
        ;
3321
0
      os = curlx_memdup0(start, ptr - start);
3322
0
      if(!os)
3323
0
        return CURLE_OUT_OF_MEMORY;
3324
3325
      /* Check for special servers here. */
3326
0
      if(curl_strequal(os, "OS/400")) {
3327
        /* Force OS400 name format 1. */
3328
0
        result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
3329
0
        if(result) {
3330
0
          curlx_free(os);
3331
0
          return result;
3332
0
        }
3333
        /* remember target server OS */
3334
0
        curlx_free(ftpc->server_os);
3335
0
        ftpc->server_os = os;
3336
0
        ftp_state(data, ftpc, FTP_NAMEFMT);
3337
0
        break;
3338
0
      }
3339
      /* Nothing special for the target server. */
3340
      /* remember target server OS */
3341
0
      curlx_free(ftpc->server_os);
3342
0
      ftpc->server_os = os;
3343
0
    }
3344
0
    else {
3345
      /* Cannot identify server OS. Continue anyway and cross fingers. */
3346
0
    }
3347
3348
0
    ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
3349
0
    CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
3350
0
    break;
3351
3352
0
  case FTP_NAMEFMT:
3353
0
    if(ftpcode == 250) {
3354
      /* Name format change successful: reload initial path. */
3355
0
      ftp_state_pwd(data, ftpc);
3356
0
      break;
3357
0
    }
3358
3359
0
    ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
3360
0
    CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
3361
0
    break;
3362
3363
0
  case FTP_QUOTE:
3364
0
  case FTP_POSTQUOTE:
3365
0
  case FTP_RETR_PREQUOTE:
3366
0
  case FTP_STOR_PREQUOTE:
3367
0
  case FTP_LIST_PREQUOTE:
3368
0
    if((ftpcode >= 400) && !ftpc->count2) {
3369
      /* failure response code, and not allowed to fail */
3370
0
      failf(data, "QUOT command failed with %03d", ftpcode);
3371
0
      result = CURLE_QUOTE_ERROR;
3372
0
    }
3373
0
    else
3374
0
      result = ftp_state_quote(data, ftpc, ftp, FALSE, ftpc->state);
3375
0
    break;
3376
3377
0
  case FTP_CWD:
3378
0
    if(ftpcode / 100 != 2) {
3379
      /* failure to CWD there */
3380
0
      if(data->set.ftp_create_missing_dirs &&
3381
0
         ftpc->cwdcount && !ftpc->count2) {
3382
        /* try making it */
3383
0
        ftpc->count2++; /* counter to prevent CWD-MKD loops */
3384
3385
        /* count3 is set to allow MKD to fail once per dir. In the case when
3386
           CWD fails and then MKD fails (due to another session raced it to
3387
           create the dir) this then allows for a second try to CWD to it. */
3388
0
        ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
3389
3390
0
        result = Curl_pp_sendf(data, &ftpc->pp, "MKD %.*s",
3391
0
                               pathlen(ftpc, ftpc->cwdcount - 1),
3392
0
                               pathpiece(ftpc, ftpc->cwdcount - 1));
3393
0
        if(!result)
3394
0
          ftp_state(data, ftpc, FTP_MKD);
3395
0
      }
3396
0
      else {
3397
        /* return failure */
3398
0
        failf(data, "Server denied you to change to the given directory");
3399
0
        ftpc->cwdfail = TRUE; /* do not remember this path as we failed
3400
                                 to enter it */
3401
0
        result = CURLE_REMOTE_ACCESS_DENIED;
3402
0
      }
3403
0
    }
3404
0
    else {
3405
      /* success */
3406
0
      ftpc->count2 = 0;
3407
0
      if(ftpc->cwdcount >= ftpc->dirdepth)
3408
0
        result = ftp_state_mdtm(data, ftpc, ftp);
3409
0
      else {
3410
0
        ftpc->cwdcount++;
3411
        /* send next CWD */
3412
0
        result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s",
3413
0
                               pathlen(ftpc, ftpc->cwdcount - 1),
3414
0
                               pathpiece(ftpc, ftpc->cwdcount - 1));
3415
0
      }
3416
0
    }
3417
0
    break;
3418
3419
0
  case FTP_MKD:
3420
0
    if((ftpcode / 100 != 2) && !ftpc->count3--) {
3421
      /* failure to MKD the directory */
3422
0
      failf(data, "Failed to MKD dir: %03d", ftpcode);
3423
0
      result = CURLE_REMOTE_ACCESS_DENIED;
3424
0
    }
3425
0
    else {
3426
0
      ftp_state(data, ftpc, FTP_CWD);
3427
      /* send CWD */
3428
0
      result = Curl_pp_sendf(data, &ftpc->pp, "CWD %.*s",
3429
0
                             pathlen(ftpc, ftpc->cwdcount - 1),
3430
0
                             pathpiece(ftpc, ftpc->cwdcount - 1));
3431
0
    }
3432
0
    break;
3433
3434
0
  case FTP_MDTM:
3435
0
    result = ftp_state_mdtm_resp(data, ftpc, ftp, ftpcode);
3436
0
    break;
3437
3438
0
  case FTP_TYPE:
3439
0
  case FTP_LIST_TYPE:
3440
0
  case FTP_RETR_TYPE:
3441
0
  case FTP_STOR_TYPE:
3442
0
  case FTP_RETR_LIST_TYPE:
3443
0
    result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
3444
0
    break;
3445
3446
0
  case FTP_SIZE:
3447
0
  case FTP_RETR_SIZE:
3448
0
  case FTP_STOR_SIZE:
3449
0
    result = ftp_state_size_resp(data, ftpc, ftp, ftpcode, ftpc->state);
3450
0
    break;
3451
3452
0
  case FTP_REST:
3453
0
  case FTP_RETR_REST:
3454
0
    result = ftp_state_rest_resp(data, ftpc, ftp, ftpcode, ftpc->state);
3455
0
    break;
3456
3457
0
  case FTP_PRET:
3458
0
    if(ftpcode != 200) {
3459
      /* there only is this one standard OK return code. */
3460
0
      failf(data, "PRET command not accepted: %03d", ftpcode);
3461
0
      return CURLE_FTP_PRET_FAILED;
3462
0
    }
3463
0
    result = ftp_state_use_pasv(data, ftpc, conn);
3464
0
    break;
3465
3466
0
  case FTP_PASV:
3467
0
    result = ftp_state_pasv_resp(data, ftpc, ftpcode);
3468
0
    break;
3469
3470
0
  case FTP_PORT:
3471
0
    result = ftp_state_port_resp(data, ftpc, ftp, ftpcode);
3472
0
    break;
3473
3474
0
  case FTP_LIST:
3475
0
  case FTP_RETR:
3476
0
    result = ftp_state_get_resp(data, ftpc, ftp, ftpcode, ftpc->state);
3477
0
    break;
3478
3479
0
  case FTP_STOR:
3480
0
    result = ftp_state_stor_resp(data, ftpc, ftpcode);
3481
0
    break;
3482
3483
0
  case FTP_QUIT:
3484
0
  default:
3485
    /* internal error */
3486
0
    ftp_state(data, ftpc, FTP_STOP);
3487
0
    break;
3488
0
  }
3489
3490
0
  return result;
3491
0
}
3492
3493
/* called repeatedly until done from multi.c */
3494
static CURLcode ftp_multi_statemach(struct Curl_easy *data,
3495
                                    bool *done)
3496
0
{
3497
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
3498
0
  return ftpc ? ftp_statemach(data, ftpc, done) : CURLE_FAILED_INIT;
3499
0
}
3500
3501
static CURLcode ftp_block_statemach(struct Curl_easy *data,
3502
                                    struct ftp_conn *ftpc)
3503
0
{
3504
0
  struct pingpong *pp = &ftpc->pp;
3505
0
  CURLcode result = CURLE_OK;
3506
3507
0
  while(ftpc->state != FTP_STOP) {
3508
0
    if(ftpc->shutdown)
3509
0
      CURL_TRC_FTP(data, "in shutdown, waiting for server response");
3510
0
    result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
3511
0
    if(result)
3512
0
      break;
3513
0
  }
3514
3515
0
  return result;
3516
0
}
3517
3518
/*
3519
 * ftp_connect() should do everything that is to be considered a part of
3520
 * the connection phase.
3521
 *
3522
 * The variable 'done' points to will be TRUE if the protocol-layer connect
3523
 * phase is done when this function returns, or FALSE if not.
3524
 *
3525
 */
3526
static CURLcode ftp_connect(struct Curl_easy *data,
3527
                            bool *done) /* see description above */
3528
0
{
3529
0
  CURLcode result;
3530
0
  struct connectdata *conn = data->conn;
3531
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
3532
0
  struct pingpong *pp;
3533
3534
0
  *done = FALSE; /* default to not done yet */
3535
0
  if(!ftpc)
3536
0
    return CURLE_FAILED_INIT;
3537
0
  pp = &ftpc->pp;
3538
0
  PINGPONG_SETUP(pp, ftp_pp_statemachine, ftp_endofresp);
3539
3540
0
  if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
3541
    /* BLOCKING */
3542
0
    result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
3543
0
    if(result)
3544
0
      return result;
3545
0
  }
3546
3547
0
  Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */
3548
3549
  /* When we connect, we start in the state where we await the 220
3550
     response */
3551
0
  ftp_state(data, ftpc, FTP_WAIT220);
3552
3553
0
  result = ftp_statemach(data, ftpc, done);
3554
3555
0
  return result;
3556
0
}
3557
3558
/***********************************************************************
3559
 *
3560
 * ftp_sendquote()
3561
 *
3562
 * Where a 'quote' means a list of custom commands to send to the server.
3563
 * The quote list is passed as an argument.
3564
 *
3565
 * BLOCKING
3566
 */
3567
static CURLcode ftp_sendquote(struct Curl_easy *data,
3568
                              struct ftp_conn *ftpc,
3569
                              struct curl_slist *quote)
3570
0
{
3571
0
  struct curl_slist *item;
3572
0
  struct pingpong *pp = &ftpc->pp;
3573
3574
0
  item = quote;
3575
0
  while(item) {
3576
0
    if(item->data) {
3577
0
      size_t nread;
3578
0
      const char *cmd = item->data;
3579
0
      bool acceptfail = FALSE;
3580
0
      CURLcode result;
3581
0
      int ftpcode = 0;
3582
3583
      /* if a command starts with an asterisk, which a legal FTP command never
3584
         can, the command will be allowed to fail without it causing any
3585
         aborts or cancels etc. It will cause libcurl to act as if the command
3586
         is successful, whatever the server responds. */
3587
3588
0
      if(cmd[0] == '*') {
3589
0
        cmd++;
3590
0
        acceptfail = TRUE;
3591
0
      }
3592
3593
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
3594
0
      if(!result) {
3595
0
        pp->response = *Curl_pgrs_now(data); /* timeout relative now */
3596
0
        result = getftpresponse(data, &nread, &ftpcode);
3597
0
      }
3598
0
      if(result)
3599
0
        return result;
3600
3601
0
      if(!acceptfail && (ftpcode >= 400)) {
3602
0
        failf(data, "QUOT string not accepted: %s", cmd);
3603
0
        return CURLE_QUOTE_ERROR;
3604
0
      }
3605
0
    }
3606
3607
0
    item = item->next;
3608
0
  }
3609
3610
0
  return CURLE_OK;
3611
0
}
3612
3613
static CURLcode ftp_done_status(struct Curl_easy *data,
3614
                                struct ftp_conn *ftpc, CURLcode status,
3615
                                bool premature)
3616
0
{
3617
0
  switch(status) {
3618
0
  case CURLE_BAD_DOWNLOAD_RESUME:
3619
0
  case CURLE_FTP_WEIRD_PASV_REPLY:
3620
0
  case CURLE_FTP_PORT_FAILED:
3621
0
  case CURLE_FTP_ACCEPT_FAILED:
3622
0
  case CURLE_FTP_ACCEPT_TIMEOUT:
3623
0
  case CURLE_FTP_COULDNT_SET_TYPE:
3624
0
  case CURLE_FTP_COULDNT_RETR_FILE:
3625
0
  case CURLE_PARTIAL_FILE:
3626
0
  case CURLE_UPLOAD_FAILED:
3627
0
  case CURLE_REMOTE_ACCESS_DENIED:
3628
0
  case CURLE_FILESIZE_EXCEEDED:
3629
0
  case CURLE_REMOTE_FILE_NOT_FOUND:
3630
0
  case CURLE_WRITE_ERROR:
3631
    /* the connection stays alive fine even though this happened */
3632
0
  case CURLE_OK: /* does not affect the control connection's status */
3633
0
    if(!premature)
3634
0
      break;
3635
3636
    /* until we cope better with prematurely ended requests, let them
3637
     * fallback as if in complete failure */
3638
0
    FALLTHROUGH();
3639
0
  default:       /* by default, an error means the control connection is
3640
                    wedged and should not be used anymore */
3641
0
    ftpc->ctl_valid = FALSE;
3642
0
    ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3643
                             current path, as this connection is going */
3644
0
    CURL_TRC_FTP(data, "FTP ended with bad error code");
3645
0
    connclose(data->conn);
3646
0
    return status;      /* use the already set error code */
3647
0
  }
3648
0
  return CURLE_OK;
3649
0
}
3650
3651
static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc)
3652
0
{
3653
0
  if(data->state.wildcardmatch) {
3654
0
    if(data->set.chunk_end && ftpc->file) {
3655
0
      struct Curl_mapi_guard guard;
3656
0
      CURL_CBAPI_START(&guard, data, easy_chunk_end);
3657
0
      data->set.chunk_end(data->set.wildcardptr);
3658
0
      CURL_CBAPI_END(&guard);
3659
0
      freedirs(ftpc);
3660
0
    }
3661
0
    ftpc->known_filesize = -1;
3662
0
  }
3663
0
}
3664
3665
static void ftp_done_path(struct Curl_easy *data, struct ftp_conn *ftpc,
3666
                          CURLcode result)
3667
0
{
3668
0
  struct connectdata *conn = data->conn;
3669
0
  if(result) {
3670
    /* We can limp along anyway (and should try to since we may already be in
3671
     * the error path) */
3672
0
    ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3673
0
    connclose(conn); /* mark for connection closure */
3674
0
    curlx_safefree(ftpc->prevpath); /* no path remembering */
3675
0
  }
3676
0
  else { /* remember working directory for connection reuse */
3677
0
    const char *rawPath = ftpc->rawpath;
3678
0
    if(rawPath) {
3679
0
      if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
3680
0
        ; /* full path => no CWDs happened => keep ftpc->prevpath */
3681
0
      else {
3682
0
        size_t pathLen = strlen(ftpc->rawpath);
3683
3684
0
        curlx_free(ftpc->prevpath);
3685
3686
0
        if(!ftpc->cwdfail) {
3687
0
          if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3688
0
            pathLen = 0; /* relative path => working directory is FTP home */
3689
0
          else
3690
            /* file is URL-decoded */
3691
0
            pathLen -= ftpc->file ? strlen(ftpc->file) : 0;
3692
0
          ftpc->prevpath = curlx_memdup0(rawPath, pathLen);
3693
0
        }
3694
0
        else
3695
0
          ftpc->prevpath = NULL; /* no path */
3696
0
      }
3697
0
    }
3698
0
    if(ftpc->prevpath)
3699
0
      infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath);
3700
0
  }
3701
0
}
3702
3703
static CURLcode ftp_done_secondary_socket(struct Curl_easy *data,
3704
                                          struct ftp_conn *ftpc,
3705
                                          CURLcode result)
3706
0
{
3707
0
  struct connectdata *conn = data->conn;
3708
0
  if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) {
3709
0
    if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
3710
      /* partial download completed */
3711
0
      result = Curl_pp_sendf(data, &ftpc->pp, "%s", "ABOR");
3712
0
      if(result) {
3713
0
        failf(data, "Failure sending ABOR command: %s",
3714
0
              curl_easy_strerror(result));
3715
0
        ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3716
0
        connclose(conn); /* connection closure */
3717
0
      }
3718
0
    }
3719
3720
0
    close_secondarysocket(data, ftpc);
3721
0
  }
3722
0
  return result;
3723
0
}
3724
3725
static CURLcode ftp_done_control_reply(struct Curl_easy *data,
3726
                                       struct ftp_conn *ftpc,
3727
                                       struct FTP *ftp, CURLcode result,
3728
                                       bool premature)
3729
0
{
3730
0
  struct connectdata *conn = data->conn;
3731
0
  size_t nread;
3732
0
  int ftpcode;
3733
3734
0
  if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
3735
0
     ftpc->pp.pending_resp && !premature) {
3736
    /*
3737
     * Let's see what the server says about the transfer we performed, but
3738
     * lower the timeout as sometimes this connection has died while the data
3739
     * has been transferred. This happens when doing through NATs etc that
3740
     * abandon old silent connections.
3741
     */
3742
0
    ftpc->pp.response = *Curl_pgrs_now(data); /* timeout relative now */
3743
0
    result = getftpresponse(data, &nread, &ftpcode);
3744
3745
0
    if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) {
3746
0
      failf(data, "control connection looks dead");
3747
0
      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3748
0
      connclose(conn); /* close */
3749
0
    }
3750
3751
0
    if(result)
3752
0
      return result;
3753
3754
0
    if(ftpc->dont_check && data->req.maxdownload > 0) {
3755
      /* we have sent ABOR and there is no reliable way to check if it was
3756
       * successful or not; we have to close the connection now */
3757
0
      infof(data, "partial download completed, closing connection");
3758
0
      connclose(conn);
3759
0
      return result;
3760
0
    }
3761
3762
0
    if(!ftpc->dont_check) {
3763
      /* 226 Transfer complete, 250 Requested file action okay, completed. */
3764
0
      switch(ftpcode) {
3765
0
      case 226:
3766
0
      case 250:
3767
0
        break;
3768
0
      case 552:
3769
0
        failf(data, "Exceeded storage allocation");
3770
0
        result = CURLE_REMOTE_DISK_FULL;
3771
0
        break;
3772
0
      default:
3773
0
        failf(data, "server did not report OK, got %d", ftpcode);
3774
0
        result = CURLE_PARTIAL_FILE;
3775
0
        break;
3776
0
      }
3777
0
    }
3778
0
  }
3779
0
  return result;
3780
0
}
3781
3782
static CURLcode ftp_done_check_partial(struct Curl_easy *data,
3783
                                       struct ftp_conn *ftpc,
3784
                                       struct FTP *ftp, CURLcode result,
3785
                                       bool premature)
3786
0
{
3787
0
  if(result || premature)
3788
    /* the response code from the transfer showed an error already so no
3789
       use checking further */
3790
0
    ;
3791
0
  else if(data->state.upload) {
3792
0
    if((ftp->transfer == PPTRANSFER_BODY) &&
3793
0
       (data->state.infilesize != -1) && /* upload with known size */
3794
0
       ((!data->set.crlf && !data->state.prefer_ascii && /* no conversion */
3795
0
         (data->state.infilesize != data->req.writebytecount)) ||
3796
0
        ((data->set.crlf || data->state.prefer_ascii) && /* maybe CRLF conv */
3797
0
         (data->state.infilesize > data->req.writebytecount))
3798
0
       )) {
3799
0
      failf(data, "Uploaded unaligned file size (%" FMT_OFF_T
3800
0
            " out of %" FMT_OFF_T " bytes)",
3801
0
            data->req.writebytecount, data->state.infilesize);
3802
0
      result = CURLE_PARTIAL_FILE;
3803
0
    }
3804
0
  }
3805
0
  else {
3806
0
    if((data->req.size != -1) &&
3807
0
       (data->req.size != data->req.bytecount) &&
3808
0
       (data->req.maxdownload != data->req.bytecount)) {
3809
0
      failf(data, "Received only partial file: %" FMT_OFF_T " bytes",
3810
0
            data->req.bytecount);
3811
0
      result = CURLE_PARTIAL_FILE;
3812
0
    }
3813
0
    else if(!ftpc->dont_check &&
3814
0
            !data->req.bytecount &&
3815
0
            (data->req.size > 0)) {
3816
0
      failf(data, "No data was received");
3817
0
      result = CURLE_FTP_COULDNT_RETR_FILE;
3818
0
    }
3819
0
  }
3820
0
  return result;
3821
0
}
3822
3823
/***********************************************************************
3824
 *
3825
 * ftp_done()
3826
 *
3827
 * The DONE function. This does what needs to be done after a single DO has
3828
 * performed.
3829
 *
3830
 * Input argument is already checked for validity.
3831
 */
3832
static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
3833
                         bool premature)
3834
0
{
3835
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
3836
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
3837
0
  CURLcode result;
3838
3839
0
  if(!ftp || !ftpc)
3840
0
    return CURLE_OK;
3841
3842
0
  result = ftp_done_status(data, ftpc, status, premature);
3843
3844
0
  ftp_done_wildcard(data, ftpc);
3845
0
  ftp_done_path(data, ftpc, result);
3846
0
  result = ftp_done_secondary_socket(data, ftpc, result);
3847
0
  result = ftp_done_control_reply(data, ftpc, ftp, result, premature);
3848
0
  result = ftp_done_check_partial(data, ftpc, ftp, result, premature);
3849
3850
  /* clear these for next connection */
3851
0
  ftp->transfer = PPTRANSFER_BODY;
3852
0
  ftpc->dont_check = FALSE;
3853
3854
  /* Send any post-transfer QUOTE strings? */
3855
0
  if(!status && !result && !premature && data->set.postquote)
3856
0
    result = ftp_sendquote(data, ftpc, data->set.postquote);
3857
0
  CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), (int)result);
3858
0
  return result;
3859
0
}
3860
3861
/***********************************************************************
3862
 *
3863
 * ftp_nb_type()
3864
 *
3865
 * Set TYPE. We only deal with ASCII or BINARY so this function
3866
 * sets one of them.
3867
 * If the transfer type is not sent, simulate on OK response in newstate
3868
 */
3869
static CURLcode ftp_nb_type(struct Curl_easy *data,
3870
                            struct ftp_conn *ftpc,
3871
                            struct FTP *ftp,
3872
                            bool ascii, ftpstate newstate)
3873
0
{
3874
0
  CURLcode result;
3875
0
  char want = (char)(ascii ? 'A' : 'I');
3876
3877
0
  if(ftpc->transfertype == want) {
3878
0
    ftp_state(data, ftpc, newstate);
3879
0
    return ftp_state_type_resp(data, ftpc, ftp, 200, newstate);
3880
0
  }
3881
3882
0
  result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
3883
0
  if(!result) {
3884
0
    ftp_state(data, ftpc, newstate);
3885
3886
    /* keep track of our current transfer type */
3887
0
    ftpc->transfertype = want;
3888
0
  }
3889
0
  return result;
3890
0
}
3891
3892
/***********************************************************************
3893
 *
3894
 * ftp_perform()
3895
 *
3896
 * This is the actual DO function for FTP. Get a file/directory according to
3897
 * the options previously setup.
3898
 */
3899
static CURLcode ftp_perform(
3900
  struct Curl_easy *data,
3901
  struct ftp_conn *ftpc,
3902
  struct FTP *ftp,
3903
  bool *connected,  /* connect status after PASV / PORT */
3904
  bool *dophase_done)
3905
0
{
3906
  /* this is FTP and no proxy */
3907
0
  CURLcode result = CURLE_OK;
3908
3909
0
  CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_CSTATE(ftpc));
3910
3911
0
  if(data->req.no_body) {
3912
    /* requested no body means no transfer... */
3913
0
    ftp->transfer = PPTRANSFER_INFO;
3914
0
  }
3915
3916
0
  *dophase_done = FALSE; /* not done yet */
3917
3918
  /* start the first command in the DO phase */
3919
0
  result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_QUOTE);
3920
0
  if(result)
3921
0
    return result;
3922
3923
  /* run the state-machine */
3924
0
  result = ftp_statemach(data, ftpc, dophase_done);
3925
3926
0
  *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
3927
3928
0
  if(*connected)
3929
0
    infof(data, "[FTP] [%s] perform, DATA connection established",
3930
0
          FTP_CSTATE(ftpc));
3931
0
  else
3932
0
    CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect",
3933
0
                 FTP_CSTATE(ftpc));
3934
3935
0
  if(*dophase_done)
3936
0
    CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_CSTATE(ftpc));
3937
3938
0
  return result;
3939
0
}
3940
3941
static void wc_data_dtor(void *ptr)
3942
0
{
3943
0
  struct ftp_wc *ftpwc = ptr;
3944
0
  if(ftpwc && ftpwc->parser)
3945
0
    Curl_ftp_parselist_data_free(&ftpwc->parser);
3946
0
  curlx_free(ftpwc);
3947
0
}
3948
3949
static CURLcode init_wc_data(struct Curl_easy *data,
3950
                             struct ftp_conn *ftpc,
3951
                             struct FTP *ftp)
3952
0
{
3953
0
  char *last_slash;
3954
0
  char *path = ftp->path;
3955
0
  struct WildcardData *wildcard = data->wildcard;
3956
0
  CURLcode result = CURLE_OK;
3957
0
  struct ftp_wc *ftpwc = NULL;
3958
3959
0
  last_slash = strrchr(ftp->path, '/');
3960
0
  if(last_slash) {
3961
0
    last_slash++;
3962
0
    if(last_slash[0] == '\0') {
3963
0
      wildcard->state = CURLWC_CLEAN;
3964
0
      return ftp_parse_url_path(data, ftpc, ftp);
3965
0
    }
3966
0
    wildcard->pattern = curlx_strdup(last_slash);
3967
0
    if(!wildcard->pattern)
3968
0
      return CURLE_OUT_OF_MEMORY;
3969
0
    last_slash[0] = '\0'; /* cut file from path */
3970
0
  }
3971
0
  else { /* there is only 'wildcard pattern' or nothing */
3972
0
    if(path[0]) {
3973
0
      wildcard->pattern = curlx_strdup(path);
3974
0
      if(!wildcard->pattern)
3975
0
        return CURLE_OUT_OF_MEMORY;
3976
0
      path[0] = '\0';
3977
0
    }
3978
0
    else { /* only list */
3979
0
      wildcard->state = CURLWC_CLEAN;
3980
0
      return ftp_parse_url_path(data, ftpc, ftp);
3981
0
    }
3982
0
  }
3983
3984
  /* program continues only if URL is not ending with slash, allocate needed
3985
     resources for wildcard transfer */
3986
3987
  /* allocate ftp protocol specific wildcard data */
3988
0
  ftpwc = curlx_calloc(1, sizeof(struct ftp_wc));
3989
0
  if(!ftpwc) {
3990
0
    result = CURLE_OUT_OF_MEMORY;
3991
0
    goto fail;
3992
0
  }
3993
3994
  /* INITIALIZE parselist structure */
3995
0
  ftpwc->parser = Curl_ftp_parselist_data_alloc();
3996
0
  if(!ftpwc->parser) {
3997
0
    result = CURLE_OUT_OF_MEMORY;
3998
0
    goto fail;
3999
0
  }
4000
4001
0
  wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */
4002
0
  wildcard->dtor = wc_data_dtor;
4003
4004
  /* wildcard does not support NOCWD option (assert it?) */
4005
0
  if(data->set.ftp_filemethod == FTPFILE_NOCWD)
4006
0
    data->set.ftp_filemethod = FTPFILE_MULTICWD;
4007
4008
  /* try to parse ftp URL */
4009
0
  result = ftp_parse_url_path(data, ftpc, ftp);
4010
0
  if(result) {
4011
0
    goto fail;
4012
0
  }
4013
4014
0
  wildcard->path = curlx_strdup(ftp->path);
4015
0
  if(!wildcard->path) {
4016
0
    result = CURLE_OUT_OF_MEMORY;
4017
0
    goto fail;
4018
0
  }
4019
4020
  /* backup old write_function */
4021
0
  ftpwc->backup.write_function = data->set.fwrite_func;
4022
  /* parsing write function */
4023
0
  data->set.fwrite_func = Curl_ftp_parselist;
4024
  /* backup old file descriptor */
4025
0
  ftpwc->backup.file_descriptor = data->set.out;
4026
  /* let the writefunc callback know the transfer */
4027
0
  data->set.out = data;
4028
4029
0
  infof(data, "Wildcard - Parsing started");
4030
0
  return CURLE_OK;
4031
4032
0
fail:
4033
0
  if(ftpwc) {
4034
0
    Curl_ftp_parselist_data_free(&ftpwc->parser);
4035
0
    curlx_free(ftpwc);
4036
0
  }
4037
0
  curlx_safefree(wildcard->pattern);
4038
0
  wildcard->dtor = ZERO_NULL;
4039
0
  wildcard->ftpwc = NULL;
4040
0
  return result;
4041
0
}
4042
4043
static CURLcode wc_statemach(struct Curl_easy *data,
4044
                             struct ftp_conn *ftpc,
4045
                             struct FTP *ftp)
4046
0
{
4047
0
  struct WildcardData * const wildcard = data->wildcard;
4048
0
  CURLcode result = CURLE_OK;
4049
4050
0
  for(;;) {
4051
0
    switch(wildcard->state) {
4052
0
    case CURLWC_INIT:
4053
0
      result = init_wc_data(data, ftpc, ftp);
4054
0
      if(wildcard->state == CURLWC_CLEAN)
4055
        /* only listing! */
4056
0
        return result;
4057
0
      wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
4058
0
      return result;
4059
4060
0
    case CURLWC_MATCHING: {
4061
      /* In this state is LIST response successfully parsed, so lets restore
4062
         previous WRITEFUNCTION callback and WRITEDATA pointer */
4063
0
      struct ftp_wc *ftpwc = wildcard->ftpwc;
4064
0
      data->set.fwrite_func = ftpwc->backup.write_function;
4065
0
      data->set.out = ftpwc->backup.file_descriptor;
4066
0
      ftpwc->backup.write_function = ZERO_NULL;
4067
0
      ftpwc->backup.file_descriptor = NULL;
4068
0
      wildcard->state = CURLWC_DOWNLOADING;
4069
4070
0
      if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
4071
        /* error found in LIST parsing */
4072
0
        wildcard->state = CURLWC_CLEAN;
4073
0
        continue;
4074
0
      }
4075
0
      if(Curl_llist_count(&wildcard->filelist) == 0) {
4076
        /* no corresponding file */
4077
0
        wildcard->state = CURLWC_CLEAN;
4078
0
        return CURLE_REMOTE_FILE_NOT_FOUND;
4079
0
      }
4080
0
      continue;
4081
0
    }
4082
4083
0
    case CURLWC_DOWNLOADING: {
4084
      /* filelist has at least one file, lets get first one */
4085
0
      struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist);
4086
0
      struct curl_fileinfo *finfo = Curl_node_elem(head);
4087
4088
0
      char *tmp_path = curl_maprintf("%s%s", wildcard->path, finfo->filename);
4089
0
      if(!tmp_path)
4090
0
        return CURLE_OUT_OF_MEMORY;
4091
4092
      /* switch default ftp->path and tmp_path */
4093
0
      curlx_free(ftp->pathalloc);
4094
0
      ftp->pathalloc = ftp->path = tmp_path;
4095
4096
0
      infof(data, "Wildcard - START of \"%s\"", finfo->filename);
4097
0
      if(data->set.chunk_bgn) {
4098
0
        long userresponse;
4099
0
        struct Curl_mapi_guard guard;
4100
0
        CURL_CBAPI_START(&guard, data, easy_chunk_bgn);
4101
0
        userresponse = data->set.chunk_bgn(
4102
0
          finfo, data->set.wildcardptr,
4103
0
          (int)Curl_llist_count(&wildcard->filelist));
4104
0
        CURL_CBAPI_END(&guard);
4105
0
        switch(userresponse) {
4106
0
        case CURL_CHUNK_BGN_FUNC_SKIP:
4107
0
          infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename);
4108
0
          wildcard->state = CURLWC_SKIP;
4109
0
          continue;
4110
0
        case CURL_CHUNK_BGN_FUNC_FAIL:
4111
0
          return CURLE_CHUNK_FAILED;
4112
0
        }
4113
0
      }
4114
4115
0
      if(finfo->filetype != CURLFILETYPE_FILE) {
4116
0
        wildcard->state = CURLWC_SKIP;
4117
0
        continue;
4118
0
      }
4119
4120
0
      if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
4121
0
        ftpc->known_filesize = finfo->size;
4122
4123
0
      result = ftp_parse_url_path(data, ftpc, ftp);
4124
0
      if(result)
4125
0
        return result;
4126
4127
      /* we do not need the Curl_fileinfo of first file anymore */
4128
0
      Curl_node_remove(Curl_llist_head(&wildcard->filelist));
4129
4130
0
      if(Curl_llist_count(&wildcard->filelist) == 0) {
4131
        /* remains only one file to down. */
4132
0
        wildcard->state = CURLWC_CLEAN;
4133
        /* after that will be ftp_do called once again and no transfer
4134
           will be done because of CURLWC_CLEAN state */
4135
0
        return CURLE_OK;
4136
0
      }
4137
0
      return result;
4138
0
    }
4139
4140
0
    case CURLWC_SKIP: {
4141
0
      if(data->set.chunk_end) {
4142
0
        struct Curl_mapi_guard guard;
4143
0
        CURL_CBAPI_START(&guard, data, easy_chunk_end);
4144
0
        data->set.chunk_end(data->set.wildcardptr);
4145
0
        CURL_CBAPI_END(&guard);
4146
0
      }
4147
0
      Curl_node_remove(Curl_llist_head(&wildcard->filelist));
4148
0
      wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ?
4149
0
        CURLWC_CLEAN : CURLWC_DOWNLOADING;
4150
0
      continue;
4151
0
    }
4152
4153
0
    case CURLWC_CLEAN: {
4154
0
      struct ftp_wc *ftpwc = wildcard->ftpwc;
4155
0
      result = CURLE_OK;
4156
0
      if(ftpwc)
4157
0
        result = Curl_ftp_parselist_geterror(ftpwc->parser);
4158
4159
0
      wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
4160
0
      return result;
4161
0
    }
4162
4163
0
    case CURLWC_DONE:
4164
0
    case CURLWC_ERROR:
4165
0
    case CURLWC_CLEAR:
4166
0
      if(wildcard->dtor) {
4167
0
        wildcard->dtor(wildcard->ftpwc);
4168
0
        wildcard->ftpwc = NULL;
4169
0
      }
4170
0
      return result;
4171
0
    }
4172
0
  }
4173
  /* UNREACHABLE */
4174
0
}
4175
4176
/***********************************************************************
4177
 *
4178
 * ftp_regular_transfer()
4179
 *
4180
 * The input argument is already checked for validity.
4181
 *
4182
 * Performs all commands done before a regular transfer between a local and a
4183
 * remote host.
4184
 *
4185
 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4186
 * ftp_done() function without finding any major problem.
4187
 */
4188
static CURLcode ftp_regular_transfer(struct Curl_easy *data,
4189
                                     struct ftp_conn *ftpc,
4190
                                     struct FTP *ftp,
4191
                                     bool *dophase_done)
4192
0
{
4193
0
  CURLcode result = CURLE_OK;
4194
0
  bool connected = FALSE;
4195
0
  data->req.size = -1; /* make sure this is unknown at this point */
4196
4197
0
  Curl_pgrsReset(data);
4198
4199
0
  ftpc->ctl_valid = TRUE; /* starts good */
4200
4201
0
  result = ftp_perform(data, ftpc, ftp,
4202
0
                       &connected, /* have we connected after PASV/PORT */
4203
0
                       dophase_done); /* all commands in the DO-phase done? */
4204
4205
0
  if(!result) {
4206
4207
0
    if(!*dophase_done)
4208
      /* the DO phase has not completed yet */
4209
0
      return CURLE_OK;
4210
4211
0
    result = ftp_dophase_done(data, ftpc, ftp, connected);
4212
4213
0
    if(result)
4214
0
      return result;
4215
0
  }
4216
0
  else
4217
0
    freedirs(ftpc);
4218
4219
0
  return result;
4220
0
}
4221
4222
/***********************************************************************
4223
 *
4224
 * ftp_do()
4225
 *
4226
 * This function is registered as 'curl_do' function. It decodes the path
4227
 * parts etc as a wrapper to the actual DO function (ftp_perform).
4228
 *
4229
 * The input argument is already checked for validity.
4230
 */
4231
static CURLcode ftp_do(struct Curl_easy *data, bool *done)
4232
0
{
4233
0
  CURLcode result = CURLE_OK;
4234
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
4235
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
4236
4237
0
  *done = FALSE; /* default to false */
4238
0
  if(!ftpc || !ftp)
4239
0
    return CURLE_FAILED_INIT;
4240
0
  ftpc->wait_data_conn = FALSE; /* default to no such wait */
4241
4242
0
#ifdef CURL_PREFER_LF_LINEENDS
4243
0
  {
4244
    /* FTP data may need conversion. */
4245
0
    struct Curl_cwriter *ftp_lc_writer;
4246
4247
0
    result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc,
4248
0
                                 CURL_CW_CONTENT_DECODE);
4249
0
    if(result)
4250
0
      return result;
4251
4252
0
    result = Curl_cwriter_add(data, ftp_lc_writer);
4253
0
    if(result) {
4254
0
      Curl_cwriter_free(data, ftp_lc_writer);
4255
0
      return result;
4256
0
    }
4257
0
  }
4258
0
#endif /* CURL_PREFER_LF_LINEENDS */
4259
4260
0
  if(data->state.wildcardmatch) {
4261
0
    result = wc_statemach(data, ftpc, ftp);
4262
0
    if(data->wildcard->state == CURLWC_SKIP ||
4263
0
       data->wildcard->state == CURLWC_DONE) {
4264
      /* do not call ftp_regular_transfer */
4265
0
      return CURLE_OK;
4266
0
    }
4267
0
    if(result) /* error, loop or skipping the file */
4268
0
      return result;
4269
0
  }
4270
0
  else { /* no wildcard FSM needed */
4271
0
    result = ftp_parse_url_path(data, ftpc, ftp);
4272
0
    if(result)
4273
0
      return result;
4274
0
  }
4275
4276
0
  result = ftp_regular_transfer(data, ftpc, ftp, done);
4277
4278
0
  return result;
4279
0
}
4280
4281
/***********************************************************************
4282
 *
4283
 * ftp_quit()
4284
 *
4285
 * This should be called before calling sclose() on an ftp control connection
4286
 * (not data connections). We should then wait for the response from the
4287
 * server before returning. The calling code should then try to close the
4288
 * connection.
4289
 *
4290
 */
4291
static CURLcode ftp_quit(struct Curl_easy *data,
4292
                         struct ftp_conn *ftpc)
4293
0
{
4294
0
  CURLcode result = CURLE_OK;
4295
4296
0
  if(ftpc->ctl_valid) {
4297
0
    CURL_TRC_FTP(data, "sending QUIT to close session");
4298
0
    result = Curl_pp_sendf(data, &ftpc->pp, "%s", "QUIT");
4299
0
    if(result) {
4300
0
      failf(data, "Failure sending QUIT command: %s",
4301
0
            curl_easy_strerror(result));
4302
0
      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
4303
0
      connclose(data->conn); /* mark for closure */
4304
0
      ftp_state(data, ftpc, FTP_STOP);
4305
0
      return result;
4306
0
    }
4307
4308
0
    ftp_state(data, ftpc, FTP_QUIT);
4309
4310
0
    result = ftp_block_statemach(data, ftpc);
4311
0
  }
4312
4313
0
  return result;
4314
0
}
4315
4316
/***********************************************************************
4317
 *
4318
 * ftp_disconnect()
4319
 *
4320
 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4321
 * resources. BLOCKING.
4322
 */
4323
static CURLcode ftp_disconnect(struct Curl_easy *data,
4324
                               struct connectdata *conn,
4325
                               bool dead_connection)
4326
0
{
4327
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
4328
4329
0
  if(!ftpc)
4330
0
    return CURLE_FAILED_INIT;
4331
  /* We cannot send quit unconditionally. If this connection is stale or
4332
     bad in any way, sending quit and waiting around here will make the
4333
     disconnect wait in vain and cause more problems than we need to.
4334
4335
     ftp_quit() will check the state of ftp->ctl_valid. If it is ok it
4336
     will try to send the QUIT command, otherwise it will return. */
4337
0
  ftpc->shutdown = TRUE;
4338
0
  if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp))
4339
0
    ftpc->ctl_valid = FALSE;
4340
4341
  /* The FTP session may or may not have been allocated/setup at this point! */
4342
0
  (void)ftp_quit(data, ftpc); /* ignore errors on the QUIT */
4343
0
  return CURLE_OK;
4344
0
}
4345
4346
/* called from multi.c while DOing */
4347
static CURLcode ftp_doing(struct Curl_easy *data,
4348
                          bool *dophase_done)
4349
0
{
4350
0
  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
4351
0
  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
4352
0
  CURLcode result;
4353
4354
0
  if(!ftpc || !ftp)
4355
0
    return CURLE_FAILED_INIT;
4356
0
  result = ftp_statemach(data, ftpc, dophase_done);
4357
4358
0
  if(result)
4359
0
    CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_CSTATE(ftpc));
4360
0
  else if(*dophase_done) {
4361
0
    result = ftp_dophase_done(data, ftpc, ftp, FALSE /* not connected */);
4362
4363
0
    CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_CSTATE(ftpc));
4364
0
  }
4365
0
  return result;
4366
0
}
4367
4368
static void ftp_easy_dtor(void *key, size_t klen, void *entry)
4369
0
{
4370
0
  struct FTP *ftp = entry;
4371
0
  (void)key;
4372
0
  (void)klen;
4373
0
  curlx_safefree(ftp->pathalloc);
4374
0
  curlx_free(ftp);
4375
0
}
4376
4377
static void ftp_conn_dtor(void *key, size_t klen, void *entry)
4378
0
{
4379
0
  struct ftp_conn *ftpc = entry;
4380
0
  (void)key;
4381
0
  (void)klen;
4382
0
  freedirs(ftpc);
4383
0
  curlx_safefree(ftpc->account);
4384
0
  curlx_safefree(ftpc->alternative_to_user);
4385
0
  curlx_safefree(ftpc->entrypath);
4386
0
  curlx_safefree(ftpc->prevpath);
4387
0
  curlx_safefree(ftpc->server_os);
4388
0
  Curl_pp_disconnect(&ftpc->pp);
4389
0
  curlx_free(ftpc);
4390
0
}
4391
4392
static void type_url_check(struct Curl_easy *data, struct FTP *ftp)
4393
0
{
4394
0
  size_t len = strlen(ftp->path);
4395
  /* FTP URLs support an extension like ";type=<typecode>" that
4396
   * we will try to get now! */
4397
0
  if((len >= 7) && !memcmp(&ftp->path[len - 7], ";type=", 6)) {
4398
0
    char *type = &ftp->path[len - 7];
4399
0
    char command = Curl_raw_toupper(type[6]);
4400
4401
0
    *type = 0; /* cut it off */
4402
4403
0
    switch(command) {
4404
0
    case 'A': /* ASCII mode */
4405
0
      data->state.prefer_ascii = TRUE;
4406
0
      break;
4407
4408
0
    case 'D': /* directory mode */
4409
0
      data->state.list_only = TRUE;
4410
0
      break;
4411
4412
0
    case 'I': /* binary mode */
4413
0
    default:
4414
      /* switch off ASCII */
4415
0
      data->state.prefer_ascii = FALSE;
4416
0
      break;
4417
0
    }
4418
0
  }
4419
0
}
4420
4421
static CURLcode ftp_setup_connection(struct Curl_easy *data,
4422
                                     struct connectdata *conn)
4423
0
{
4424
0
  struct FTP *ftp;
4425
0
  CURLcode result = CURLE_OK;
4426
0
  struct ftp_conn *ftpc;
4427
4428
0
  ftp = curlx_calloc(1, sizeof(*ftp));
4429
0
  if(!ftp ||
4430
0
     Curl_meta_set(data, CURL_META_FTP_EASY, ftp, ftp_easy_dtor))
4431
0
    return CURLE_OUT_OF_MEMORY;
4432
4433
0
  ftpc = curlx_calloc(1, sizeof(*ftpc));
4434
0
  if(!ftpc ||
4435
0
     Curl_conn_meta_set(conn, CURL_META_FTP_CONN, ftpc, ftp_conn_dtor))
4436
0
    return CURLE_OUT_OF_MEMORY;
4437
4438
  /* clone connection related data that is FTP specific */
4439
0
  if(data->set.str[STRING_FTP_ACCOUNT]) {
4440
0
    ftpc->account = curlx_strdup(data->set.str[STRING_FTP_ACCOUNT]);
4441
0
    if(!ftpc->account) {
4442
0
      Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
4443
0
      return CURLE_OUT_OF_MEMORY;
4444
0
    }
4445
0
  }
4446
0
  if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) {
4447
0
    ftpc->alternative_to_user =
4448
0
      curlx_strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
4449
0
    if(!ftpc->alternative_to_user) {
4450
0
      curlx_safefree(ftpc->account);
4451
0
      Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
4452
0
      return CURLE_OUT_OF_MEMORY;
4453
0
    }
4454
0
  }
4455
4456
0
  ftp->path = &data->state.up.path[1]; /* do not include the initial slash */
4457
4458
0
  type_url_check(data, ftp);
4459
4460
  /* get some initial data into the ftp struct */
4461
0
  ftp->transfer = PPTRANSFER_BODY;
4462
0
  ftp->downloadsize = 0;
4463
0
  ftpc->known_filesize = -1; /* unknown size for now */
4464
0
  ftpc->use_ssl = data->set.use_ssl;
4465
0
  ftpc->ccc = data->set.ftp_ccc;
4466
4467
0
  CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc),
4468
0
               (int)result);
4469
0
  return result;
4470
0
}
4471
4472
bool Curl_ftp_conns_match(struct connectdata *needle, struct connectdata *conn)
4473
0
{
4474
0
  struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN);
4475
0
  struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
4476
  /* Also match ACCOUNT, ALTERNATIVE-TO-USER and CCC options */
4477
0
  if(!nftpc || !cftpc ||
4478
0
     Curl_timestrcmp(nftpc->account, cftpc->account) ||
4479
0
     Curl_timestrcmp(nftpc->alternative_to_user,
4480
0
                     cftpc->alternative_to_user) ||
4481
0
     (nftpc->ccc != cftpc->ccc))
4482
0
    return FALSE;
4483
  /* A mismatch on `use_ssl` MUST have been found in connection matching
4484
   * before we come here. This is a check on MAYBE/MUST use of STARTTLS and
4485
   * it only works on FTP. But IMAP/SMTP etc have the same `use_ssl` and
4486
   * no extra match like FTP. We lack tests in this area, so let FTP fail
4487
   * loudly here to help other cases. */
4488
0
  if(nftpc->use_ssl > cftpc->use_ssl) {
4489
0
    DEBUGASSERT(0);
4490
0
    return FALSE;
4491
0
  }
4492
0
  return TRUE;
4493
0
}
4494
4495
/*
4496
 * FTP protocol.
4497
 */
4498
const struct Curl_protocol Curl_protocol_ftp = {
4499
  ftp_setup_connection,            /* setup_connection */
4500
  ftp_do,                          /* do_it */
4501
  ftp_done,                        /* done */
4502
  ftp_do_more,                     /* do_more */
4503
  ftp_connect,                     /* connect_it */
4504
  ftp_multi_statemach,             /* connecting */
4505
  ftp_doing,                       /* doing */
4506
  ftp_pollset,                     /* proto_pollset */
4507
  ftp_pollset,                     /* doing_pollset */
4508
  ftp_domore_pollset,              /* domore_pollset */
4509
  ZERO_NULL,                       /* perform_pollset */
4510
  ftp_disconnect,                  /* disconnect */
4511
  ZERO_NULL,                       /* write_resp */
4512
  ZERO_NULL,                       /* write_resp_hd */
4513
  ZERO_NULL,                       /* connection_is_dead */
4514
  ZERO_NULL,                       /* attach connection */
4515
  ZERO_NULL,                       /* follow */
4516
};
4517
4518
#endif /* CURL_DISABLE_FTP */