FtpClient.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 09 File Transfer Protocol (FTP) */
10 
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "base/AsyncJobCalls.h"
14 #include "base/Range.h"
15 #include "client_side.h"
16 #include "clients/FtpClient.h"
17 #include "comm/ConnOpener.h"
18 #include "comm/Read.h"
19 #include "comm/TcpAcceptor.h"
20 #include "comm/Write.h"
21 #include "error/SysErrorDetail.h"
22 #include "errorpage.h"
23 #include "fd.h"
24 #include "ftp/Parsing.h"
25 #include "http/Stream.h"
26 #include "ip/tools.h"
27 #include "sbuf/SBuf.h"
28 #include "sbuf/Stream.h"
29 #include "SquidConfig.h"
30 #include "SquidString.h"
31 #include "StatCounters.h"
32 #include "tools.h"
33 #include "wordlist.h"
34 
35 #include <set>
36 
37 namespace Ftp
38 {
39 
40 const char *const crlf = "\r\n";
41 
42 static char *
43 escapeIAC(const char *buf)
44 {
45  int n;
46  char *ret;
47  unsigned const char *p;
48  unsigned char *r;
49 
50  for (p = (unsigned const char *)buf, n = 1; *p; ++n, ++p)
51  if (*p == 255)
52  ++n;
53 
54  ret = (char *)xmalloc(n);
55 
56  for (p = (unsigned const char *)buf, r=(unsigned char *)ret; *p; ++p) {
57  *r = *p;
58  ++r;
59 
60  if (*p == 255) {
61  *r = 255;
62  ++r;
63  }
64  }
65 
66  *r = '\0';
67  ++r;
68  assert((r - (unsigned char *)ret) == n );
69  return ret;
70 }
71 
72 /* Ftp::ErrorDetail */
73 
74 SBuf
76 {
77  return ToSBuf("FTP_REPLY_CODE=", completionCode);
78 }
79 
80 SBuf
82 {
83  return ToSBuf("FTP reply with completion code ", completionCode);
84 }
85 
86 /* Ftp::Channel */
87 
89 void
91  const AsyncCall::Pointer &aCloser)
92 {
93  assert(!Comm::IsConnOpen(conn));
94  assert(closer == nullptr);
95 
96  assert(Comm::IsConnOpen(newConn));
97  assert(aCloser != nullptr);
98 
99  conn = newConn;
100  conn->leaveOrphanage();
101  closer = aCloser;
102  comm_add_close_handler(conn->fd, closer);
103 }
104 
106 void
108 {
109  // channels with active listeners will be closed when the listener handler dies.
110  if (Comm::IsConnOpen(conn)) {
111  comm_remove_close_handler(conn->fd, closer);
112  conn->close(); // we do not expect to be called back
113  }
114  clear();
115 }
116 
117 void
119 {
120  if (Comm::IsConnOpen(conn)) {
121  commUnsetConnTimeout(conn);
122  comm_remove_close_handler(conn->fd, closer);
123  }
124  clear();
125 }
126 
127 void
129 {
130  conn = nullptr;
131  closer = nullptr;
132 }
133 
134 /* Ftp::CtrlChannel */
135 
137  buf(nullptr),
138  size(0),
139  offset(0),
140  message(nullptr),
141  last_command(nullptr),
142  last_reply(nullptr),
143  replycode(0)
144 {
145  buf = static_cast<char*>(memAllocBuf(4096, &size));
146 }
147 
149 {
150  memFreeBuf(size, buf);
151  if (message)
152  wordlistDestroy(&message);
153  safe_free(last_command);
154  safe_free(last_reply);
155 }
156 
157 /* Ftp::DataChannel */
158 
160  readBuf(nullptr),
161  host(nullptr),
162  port(0),
163  read_pending(false)
164 {
165 }
166 
168 {
169  xfree(host);
170  delete readBuf;
171 }
172 
173 void
175 {
176  static char addrBuf[MAX_IPSTRLEN];
177  import.toStr(addrBuf, sizeof(addrBuf));
178  xfree(host);
179  host = xstrdup(addrBuf);
180  port = import.port();
181 }
182 
183 /* Ftp::Client */
184 
186  AsyncJob("Ftp::Client"),
187  ::Client(fwdState),
188  ctrl(),
189  data(),
190  state(BEGIN),
191  old_request(nullptr),
192  old_reply(nullptr),
193  shortenReadTimeout(false)
194 {
195  ++statCounter.server.all.requests;
196  ++statCounter.server.ftp.requests;
197 
198  ctrl.last_command = xstrdup("Connect to server");
199 
201  const AsyncCall::Pointer closer = JobCallback(9, 5, Dialer, this,
203  ctrl.opened(fwdState->serverConnection(), closer);
204 }
205 
207 {
208  data.close();
209 
210  safe_free(old_request);
211  safe_free(old_reply);
212  fwd = nullptr; // refcounted
213 }
214 
215 void
217 {
218  scheduleReadControlReply(0);
219 }
220 
221 void
223 {
224  if (data.readBuf == nullptr) {
225  data.readBuf = new MemBuf;
226  data.readBuf->init(4096, SQUID_TCP_SO_RCVBUF);
227  }
228 }
229 
233 void
235 {
236  if (Comm::IsConnOpen(ctrl.conn)) {
237  debugs(9, 3, "closing FTP server FD " << ctrl.conn->fd << ", this " << this);
238  fwd->unregister(ctrl.conn);
239  ctrl.close();
240  }
241 
242  if (Comm::IsConnOpen(data.conn)) {
243  debugs(9, 3, "closing FTP data FD " << data.conn->fd << ", this " << this);
244  data.close();
245  }
246 
247  debugs(9, 3, "FTP ctrl and data connections closed. this " << this);
248 }
249 
256 bool
258 {
259  return !Comm::IsConnOpen(ctrl.conn) && !Comm::IsConnOpen(data.conn);
260 }
261 
262 void
264 {
265  debugs(9, 3, "entry-null=" << (entry?entry->isEmpty():0) << ", entry=" << entry);
266 
267  const char *command, *reply;
268  ErrorState *ftperr;
269 
270  if (err) {
271  debugs(9, 6, "error=" << err->type << ", code=" << xerrno <<
272  ", status=" << err->httpStatus);
273  error = err->type;
274  ftperr = err;
275  } else {
276  Http::StatusCode httpStatus = failedHttpStatus(error);
277  ftperr = new ErrorState(error, httpStatus, fwd->request, fwd->al);
278  }
279 
280  ftperr->xerrno = xerrno;
281 
282  ftperr->ftp.server_msg = ctrl.message;
283  ctrl.message = nullptr;
284 
285  if (old_request)
286  command = old_request;
287  else
288  command = ctrl.last_command;
289 
290  if (command && strncmp(command, "PASS", 4) == 0)
291  command = "PASS <yourpassword>";
292 
293  if (old_reply)
294  reply = old_reply;
295  else
296  reply = ctrl.last_reply;
297 
298  if (command)
299  ftperr->ftp.request = xstrdup(command);
300 
301  if (reply)
302  ftperr->ftp.reply = xstrdup(reply);
303 
304  if (!err) {
305  fwd->request->detailError(error, SysErrorDetail::NewIfAny(xerrno));
306  fwd->fail(ftperr);
307  closeServer(); // we failed, so no serverComplete()
308  }
309 }
310 
313 {
314  if (error == ERR_NONE)
318 }
319 
325 void
327 {
328  debugs(9, 3, ctrl.conn);
329 
330  if (buffered_ok && ctrl.offset > 0) {
331  /* We've already read some reply data */
332  handleControlReply();
333  } else {
334 
335  if (!Comm::IsConnOpen(ctrl.conn)) {
336  debugs(9, 3, "cannot read without ctrl " << ctrl.conn);
337  return;
338  }
339  /*
340  * Cancel the timeout on the Data socket (if any) and
341  * establish one on the control socket.
342  */
343  if (Comm::IsConnOpen(data.conn)) {
344  commUnsetConnTimeout(data.conn);
345  }
346 
347  const time_t tout = shortenReadTimeout ?
350  shortenReadTimeout = false; // we only need to do this once, after PASV
351 
352  typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
353  AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this, Ftp::Client::timeout);
354  commSetConnTimeout(ctrl.conn, tout, timeoutCall);
355 
357  AsyncCall::Pointer reader = JobCallback(9, 5, Dialer, this, Ftp::Client::readControlReply);
358  comm_read(ctrl.conn, ctrl.buf + ctrl.offset, ctrl.size - ctrl.offset, reader);
359  }
360 }
361 
362 void
364 {
365  debugs(9, 3, "FD " << io.fd << ", Read " << io.size << " bytes");
366 
367  if (io.size > 0) {
368  statCounter.server.all.kbytes_in += io.size;
369  statCounter.server.ftp.kbytes_in += io.size;
370  }
371 
372  if (io.flag == Comm::ERR_CLOSING)
373  return;
374 
375  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
376  if (abortOnData("entry aborted during control reply read"))
377  return;
378  }
379 
380  assert(ctrl.offset < ctrl.size);
381 
382  if (io.flag == Comm::OK && io.size > 0) {
384  }
385 
386  if (io.flag != Comm::OK) {
387  debugs(50, ignoreErrno(io.xerrno) ? 3 : DBG_IMPORTANT,
388  "ERROR: FTP control reply read failure: " << xstrerr(io.xerrno));
389 
390  if (ignoreErrno(io.xerrno)) {
391  scheduleReadControlReply(0);
392  } else {
393  failed(ERR_READ_ERROR, io.xerrno);
394  /* failed closes ctrl.conn and frees ftpState */
395  }
396  return;
397  }
398 
399  if (io.size == 0) {
400  if (entry->store_status == STORE_PENDING) {
401  failed(ERR_FTP_FAILURE, 0);
402  /* failed closes ctrl.conn and frees ftpState */
403  return;
404  }
405 
406  /* XXX this may end up having to be serverComplete() .. */
407  abortAll("zero control reply read");
408  return;
409  }
410 
411  unsigned int len =io.size + ctrl.offset;
412  ctrl.offset = len;
413  assert(len <= ctrl.size);
414  if (Comm::IsConnOpen(ctrl.conn))
415  commUnsetConnTimeout(ctrl.conn); // we are done waiting for ctrl reply
416  handleControlReply();
417 }
418 
419 void
421 {
422  debugs(9, 3, status());
423 
424  size_t bytes_used = 0;
425  wordlistDestroy(&ctrl.message);
426 
427  if (!parseControlReply(bytes_used)) {
428  /* didn't get complete reply yet */
429 
430  if (ctrl.offset == ctrl.size) {
431  ctrl.buf = static_cast<char*>(memReallocBuf(ctrl.buf, ctrl.size << 1, &ctrl.size));
432  }
433 
434  scheduleReadControlReply(0);
435  return;
436  }
437 
438  assert(ctrl.message); // the entire FTP server response, line by line
439  assert(ctrl.replycode >= 0); // FTP status code (from the last line)
440  assert(ctrl.last_reply); // FTP reason (from the last line)
441 
442  if (ctrl.offset == bytes_used) {
443  /* used it all up */
444  ctrl.offset = 0;
445  } else {
446  /* Got some data past the complete reply */
447  assert(bytes_used < ctrl.offset);
448  ctrl.offset -= bytes_used;
449  memmove(ctrl.buf, ctrl.buf + bytes_used, ctrl.offset);
450  }
451 
452  debugs(9, 3, "state=" << state << ", code=" << ctrl.replycode);
453 }
454 
455 bool
457 {
458  int code = ctrl.replycode;
459  char *buf;
460  debugs(9, 3, status());
461 
462  if (!Comm::IsConnOpen(ctrl.conn)) {
463  debugs(9, 5, "The control connection to the remote end is closed");
464  return false;
465  }
466 
467  if (code != 227) {
468  debugs(9, 2, "PASV not supported by remote end");
469  return false;
470  }
471 
472  /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
473  /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
474  debugs(9, 5, "scanning: " << ctrl.last_reply);
475 
476  buf = ctrl.last_reply + strcspn(ctrl.last_reply, "0123456789");
477 
478  const char *forceIp = Config.Ftp.sanitycheck ?
479  fd_table[ctrl.conn->fd].ipaddr : nullptr;
480  if (!Ftp::ParseIpPort(buf, forceIp, srvAddr)) {
481  debugs(9, DBG_IMPORTANT, "Unsafe PASV reply from " <<
482  ctrl.conn->remote << ": " << ctrl.last_reply);
483  return false;
484  }
485 
486  data.addr(srvAddr);
487 
488  return true;
489 }
490 
491 bool
493 {
494  int code = ctrl.replycode;
495  char *buf;
496  debugs(9, 3, status());
497 
498  if (!Comm::IsConnOpen(ctrl.conn)) {
499  debugs(9, 5, "The control connection to the remote end is closed");
500  return false;
501  }
502 
503  if (code != 229 && code != 522) {
504  if (code == 200) {
505  /* handle broken servers (RFC 2428 says OK code for EPSV MUST be 229 not 200) */
506  /* vsftpd for one send '200 EPSV ALL ok.' without even port info.
507  * Its okay to re-send EPSV 1/2 but nothing else. */
508  debugs(9, DBG_IMPORTANT, "ERROR: Broken FTP Server at " << ctrl.conn->remote << ". Wrong accept code for EPSV");
509  } else {
510  debugs(9, 2, "EPSV not supported by remote end");
511  }
512  return sendPassive();
513  }
514 
515  if (code == 522) {
516  /* Peer responded with a list of supported methods:
517  * 522 Network protocol not supported, use (1)
518  * 522 Network protocol not supported, use (1,2)
519  * 522 Network protocol not supported, use (2)
520  * TODO: Handle the (1,2) case which may happen after EPSV ALL. Close
521  * data + control without self-destructing and re-open from scratch.
522  */
523  debugs(9, 5, "scanning: " << ctrl.last_reply);
524  buf = ctrl.last_reply;
525  while (buf != nullptr && *buf != '\0' && *buf != '\n' && *buf != '(')
526  ++buf;
527  if (buf != nullptr && *buf == '\n')
528  ++buf;
529 
530  if (buf == nullptr || *buf == '\0') {
531  /* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
532  debugs(9, DBG_IMPORTANT, "ERROR: Broken FTP Server at " << ctrl.conn->remote << ". 522 error missing protocol negotiation hints");
533  return sendPassive();
534  } else if (strcmp(buf, "(1)") == 0) {
535  state = SENT_EPSV_2; /* simulate having sent and failed EPSV 2 */
536  return sendPassive();
537  } else if (strcmp(buf, "(2)") == 0) {
538  if (Ip::EnableIpv6) {
539  /* If server only supports EPSV 2 and we have already tried that. Go straight to EPRT */
540  if (state == SENT_EPSV_2) {
541  return sendEprt();
542  } else {
543  /* or try the next Passive mode down the chain. */
544  return sendPassive();
545  }
546  } else {
547  /* Server only accept EPSV in IPv6 traffic. */
548  state = SENT_EPSV_1; /* simulate having sent and failed EPSV 1 */
549  return sendPassive();
550  }
551  } else {
552  /* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
553  debugs(9, DBG_IMPORTANT, "WARNING: Server at " << ctrl.conn->remote << " sent unknown protocol negotiation hint: " << buf);
554  return sendPassive();
555  }
556  /* coverity[unreachable] */
557  /* safeguard against possible future bugs in above conditions */
558  failed(ERR_FTP_FAILURE, 0);
559  return false;
560  }
561 
562  /* 229 Entering Extended Passive Mode (|||port|) */
563  /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
564  debugs(9, 5, "scanning: " << ctrl.last_reply);
565 
566  buf = ctrl.last_reply + strcspn(ctrl.last_reply, "(");
567 
568  char h1, h2, h3, h4;
569  unsigned short port;
570  int n = sscanf(buf, "(%c%c%c%hu%c)", &h1, &h2, &h3, &port, &h4);
571 
572  if (n < 4 || h1 != h2 || h1 != h3 || h1 != h4) {
573  debugs(9, DBG_IMPORTANT, "ERROR: Invalid EPSV reply from " <<
574  ctrl.conn->remote << ": " <<
575  ctrl.last_reply);
576 
577  return sendPassive();
578  }
579 
580  if (0 == port) {
581  debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
582  ctrl.conn->remote << ": " <<
583  ctrl.last_reply);
584 
585  return sendPassive();
586  }
587 
588  if (Config.Ftp.sanitycheck) {
589  if (port < 1024) {
590  debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
591  ctrl.conn->remote << ": " <<
592  ctrl.last_reply);
593 
594  return sendPassive();
595  }
596  }
597 
598  remoteAddr = ctrl.conn->remote;
599  remoteAddr.port(port);
600  data.addr(remoteAddr);
601  return true;
602 }
603 
604 // FTP clients do not support EPRT and PORT commands yet.
605 // The Ftp::Client::sendEprt() will fail because of the unimplemented
606 // openListenSocket() or sendPort() methods
607 bool
609 {
610  if (!Config.Ftp.eprt) {
611  /* Disabled. Switch immediately to attempting old PORT command. */
612  debugs(9, 3, "EPRT disabled by local administrator");
613  return sendPort();
614  }
615 
616  debugs(9, 3, status());
617 
618  if (!openListenSocket()) {
619  failed(ERR_FTP_FAILURE, 0);
620  return false;
621  }
622 
623  debugs(9, 3, "Listening for FTP data connection with FD " << data.conn);
624  if (!Comm::IsConnOpen(data.conn)) {
625  // TODO: Set error message.
626  failed(ERR_FTP_FAILURE, 0);
627  return false;
628  }
629 
630  static MemBuf mb;
631  mb.reset();
632  char buf[MAX_IPSTRLEN];
633  /* RFC 2428 defines EPRT as IPv6 equivalent to IPv4 PORT command. */
634  /* Which can be used by EITHER protocol. */
635  debugs(9, 3, "Listening for FTP data connection on port" << comm_local_port(data.conn->fd) << " or port?" << data.conn->local.port());
636  mb.appendf("EPRT |%d|%s|%d|%s",
637  ( data.conn->local.isIPv6() ? 2 : 1 ),
638  data.conn->local.toStr(buf,MAX_IPSTRLEN),
639  comm_local_port(data.conn->fd), Ftp::crlf );
640 
641  state = SENT_EPRT;
642  writeCommand(mb.content());
643  return true;
644 }
645 
646 bool
648 {
649  failed(ERR_FTP_FAILURE, 0);
650  return false;
651 }
652 
653 bool
655 {
656  debugs(9, 3, status());
657 
663  if (Config.Ftp.epsv_all && state == SENT_EPSV_1 ) {
664  // We are here because the last "EPSV 1" failed, but because of epsv_all
665  // no other method allowed.
666  debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
667  failed(ERR_FTP_FAILURE, 0);
668  return false;
669  }
670 
672  data.close();
673 
677  if (!Config.Ftp.passive || state == SENT_PASV) {
678  sendEprt();
679  return true;
680  }
681 
682  static MemBuf mb;
683  mb.reset();
692  switch (state) {
693  case SENT_EPSV_ALL: /* EPSV ALL resulted in a bad response. Try ther EPSV methods. */
694  if (ctrl.conn->local.isIPv6()) {
695  debugs(9, 5, "FTP Channel is IPv6 (" << ctrl.conn->remote << ") attempting EPSV 2 after EPSV ALL has failed.");
696  mb.appendf("EPSV 2%s", Ftp::crlf);
697  state = SENT_EPSV_2;
698  break;
699  }
700  [[fallthrough]]; // to skip EPSV 2
701 
702  case SENT_EPSV_2: /* EPSV IPv6 failed. Try EPSV IPv4 */
703  if (ctrl.conn->local.isIPv4()) {
704  debugs(9, 5, "FTP Channel is IPv4 (" << ctrl.conn->remote << ") attempting EPSV 1 after EPSV ALL has failed.");
705  mb.appendf("EPSV 1%s", Ftp::crlf);
706  state = SENT_EPSV_1;
707  break;
708  } else if (Config.Ftp.epsv_all) {
709  debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
710  failed(ERR_FTP_FAILURE, 0);
711  return false;
712  }
713  [[fallthrough]]; // to skip EPSV 1
714 
715  case SENT_EPSV_1: /* EPSV options exhausted. Try PASV now. */
716  debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << ") rejects EPSV connection attempts. Trying PASV instead.");
717  mb.appendf("PASV%s", Ftp::crlf);
718  state = SENT_PASV;
719  break;
720 
721  default: {
722  bool doEpsv = true;
723  if (Config.accessList.ftp_epsv) {
724  ACLFilledChecklist checklist(Config.accessList.ftp_epsv, fwd->request);
725  doEpsv = checklist.fastCheck().allowed();
726  }
727  if (!doEpsv) {
728  debugs(9, 5, "EPSV support manually disabled. Sending PASV for FTP Channel (" << ctrl.conn->remote <<")");
729  mb.appendf("PASV%s", Ftp::crlf);
730  state = SENT_PASV;
731  } else if (Config.Ftp.epsv_all) {
732  debugs(9, 5, "EPSV ALL manually enabled. Attempting with FTP Channel (" << ctrl.conn->remote <<")");
733  mb.appendf("EPSV ALL%s", Ftp::crlf);
734  state = SENT_EPSV_ALL;
735  } else {
736  if (ctrl.conn->local.isIPv6()) {
737  debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << "). Sending default EPSV 2");
738  mb.appendf("EPSV 2%s", Ftp::crlf);
739  state = SENT_EPSV_2;
740  }
741  if (ctrl.conn->local.isIPv4()) {
742  debugs(9, 5, "Channel (" << ctrl.conn->remote <<"). Sending default EPSV 1");
743  mb.appendf("EPSV 1%s", Ftp::crlf);
744  state = SENT_EPSV_1;
745  }
746  }
747  break;
748  }
749  }
750 
751  if (ctrl.message)
752  wordlistDestroy(&ctrl.message);
753  ctrl.message = nullptr; //No message to return to client.
754  ctrl.offset = 0; //reset readed response, to make room read the next response
755 
756  writeCommand(mb.content());
757 
758  shortenReadTimeout = true;
759  return true;
760 }
761 
762 void
764 {
765  if (!Comm::IsConnOpen(ctrl.conn)) {
766  debugs(9, 5, "The control connection to the remote end is closed");
767  return;
768  }
769 
770  safe_free(ctrl.last_command);
771 
772  safe_free(ctrl.last_reply);
773 
774  ctrl.last_command = xstrdup("Connect to server data port");
775 
776  // Generate a new data channel descriptor to be opened.
778  conn->setAddrs(ctrl.conn->local, data.host);
779  conn->local.port(0);
780  conn->remote.port(data.port);
781  conn->tos = ctrl.conn->tos;
782  conn->nfmark = ctrl.conn->nfmark;
783  // Using non-local addresses in TPROXY mode requires appropriate socket option.
784  conn->flags |= ctrl.conn->flags & COMM_TRANSPARENT;
785 
786  debugs(9, 3, "connecting to " << conn->remote);
787 
789  AsyncCall::Pointer callback = JobCallback(9, 3, Dialer, this, Ftp::Client::dataChannelConnected);
790  const auto cs = new Comm::ConnOpener(conn, callback, Config.Timeout.connect);
791  cs->setHost(data.host);
792  dataConnWait.start(cs, callback);
793 }
794 
795 bool
797 {
798  return false;
799 }
800 
804 {
806  return JobCallback(9, 5, Dialer, this, Ftp::Client::dataClosed);
807 }
808 
810 void
812 {
813  debugs(9, 4, status());
814  if (data.conn)
815  data.conn->noteClosure();
816  if (data.listenConn != nullptr) {
817  data.listenConn->close();
818  data.listenConn = nullptr;
819  }
820  data.clear();
821 }
822 
823 void
825 {
826  char *ebuf;
827  /* trace FTP protocol communications at level 2 */
828  debugs(9, 2, "ftp<< " << buf);
829 
830  if (Config.Ftp.telnet)
831  ebuf = escapeIAC(buf);
832  else
833  ebuf = xstrdup(buf);
834 
835  safe_free(ctrl.last_command);
836 
837  safe_free(ctrl.last_reply);
838 
839  ctrl.last_command = ebuf;
840 
841  if (!Comm::IsConnOpen(ctrl.conn)) {
842  debugs(9, 2, "cannot send to closing ctrl " << ctrl.conn);
843  // TODO: assert(ctrl.closer != NULL);
844  return;
845  }
846 
848  AsyncCall::Pointer call = JobCallback(9, 5, Dialer, this,
850  Comm::Write(ctrl.conn, ctrl.last_command, strlen(ctrl.last_command), call, nullptr);
851 
852  scheduleReadControlReply(0);
853 }
854 
855 void
857 {
858 
859  debugs(9, 5, "wrote " << io.size << " bytes");
860 
861  if (io.size > 0) {
863  statCounter.server.all.kbytes_out += io.size;
864  statCounter.server.ftp.kbytes_out += io.size;
865  }
866 
867  if (io.flag == Comm::ERR_CLOSING)
868  return;
869 
870  if (io.flag) {
871  debugs(9, DBG_IMPORTANT, "ERROR: FTP command write failure: " << io.conn << ": " << xstrerr(io.xerrno));
872  failed(ERR_WRITE_ERROR, io.xerrno);
873  /* failed closes ctrl.conn and frees ftpState */
874  return;
875  }
876 }
877 
879 void
881 {
882  debugs(9, 4, status());
883  if (ctrl.conn)
884  ctrl.conn->noteClosure();
885  ctrl.clear();
886  doneWithFwd = "ctrlClosed()"; // assume FwdState is monitoring too
887  mustStop("Ftp::Client::ctrlClosed");
888 }
889 
890 void
892 {
893  debugs(9, 4, io.conn << ": '" << entry->url() << "'" );
894 
895  if (abortOnBadEntry("entry went bad while waiting for a timeout"))
896  return;
897 
898  failed(ERR_READ_TIMEOUT, 0);
899  /* failed() closes ctrl.conn and frees ftpState */
900 }
901 
904 {
905  return data.conn;
906 }
907 
908 void
910 {
911  // TODO: Merge with HttpStateData::noteDelayAwareReadChance()
912  waitingForDelayAwareReadChance = false;
913  data.read_pending = false;
914  maybeReadVirginBody();
915 }
916 
917 void
919 {
920  // too late to read
921  if (!Comm::IsConnOpen(data.conn) || fd_table[data.conn->fd].closing())
922  return;
923 
924  if (data.read_pending)
925  return;
926 
927  initReadBuf();
928 
929  // XXX: We only use this call to decide whether to read; we never increase data.readBuf space.
930  // TODO: Upgrade data.readBuf to SBuf and merge this with similar HttpStateData::readReply() code.
931  const auto read_sz = calcBufferSpaceToReserve(data.readBuf->spaceSize(), data.readBuf->spaceSize());
932 
933  debugs(9, 9, "FTP may read up to " << read_sz << " bytes");
934 
935  if (!read_sz)
936  return;
937 
938  data.read_pending = true;
939 
940  typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
941  AsyncCall::Pointer timeoutCall = JobCallback(9, 5,
942  TimeoutDialer, this, Ftp::Client::timeout);
943  commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
944 
945  debugs(9,5,"queueing read on FD " << data.conn->fd);
946 
947  const auto amountToRead = entry->bytesWanted(Range<size_t>(0, read_sz));
948 
949  if (amountToRead <= 0) {
950  delayRead();
951  return;
952  }
953 
954  using ReadDialer = CommCbMemFunT<Client, CommIoCbParams>;
955  AsyncCall::Pointer readCallback = JobCallback(9, 5, ReadDialer, this, Client::dataRead);
956  comm_read(data.conn, data.readBuf->space(), amountToRead, readCallback);
957 }
958 
959 void
961 {
962  int j;
963  int bin;
964 
965  data.read_pending = false;
966 
967  debugs(9, 3, "FD " << io.fd << " Read " << io.size << " bytes");
968 
969  if (io.size > 0) {
970  statCounter.server.all.kbytes_in += io.size;
971  statCounter.server.ftp.kbytes_in += io.size;
972  }
973 
974  if (io.flag == Comm::ERR_CLOSING)
975  return;
976 
977  assert(io.fd == data.conn->fd);
978 
979  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
980  abortOnData("entry aborted during dataRead");
981  return;
982  }
983 
984  if (io.flag == Comm::OK && io.size > 0) {
985  debugs(9, 5, "appended " << io.size << " bytes to readBuf");
986  data.readBuf->appended(io.size);
987 #if USE_DELAY_POOLS
988  DelayId delayId = entry->mem_obj->mostBytesAllowed();
989  delayId.bytesIn(io.size);
990 #endif
991  ++ IOStats.Ftp.reads;
992 
993  for (j = io.size - 1, bin = 0; j; ++bin)
994  j >>= 1;
995 
996  ++ IOStats.Ftp.read_hist[bin];
997  }
998 
999  if (io.flag != Comm::OK) {
1000  debugs(50, ignoreErrno(io.xerrno) ? 3 : DBG_IMPORTANT,
1001  "ERROR: FTP data read failure: " << xstrerr(io.xerrno));
1002 
1003  if (ignoreErrno(io.xerrno)) {
1004  maybeReadVirginBody();
1005  } else {
1006  failed(ERR_READ_ERROR, 0);
1007  /* failed closes ctrl.conn and frees ftpState */
1008  return;
1009  }
1010  } else if (io.size == 0) {
1011  debugs(9, 3, "Calling dataComplete() because io.size == 0");
1012  /*
1013  * DPW 2007-04-23
1014  * Dangerous curves ahead. This call to dataComplete was
1015  * calling scheduleReadControlReply, handleControlReply,
1016  * and then ftpReadTransferDone. If ftpReadTransferDone
1017  * gets unexpected status code, it closes down the control
1018  * socket and our FtpStateData object gets destroyed. As
1019  * a workaround we no longer set the 'buffered_ok' flag in
1020  * the scheduleReadControlReply call.
1021  */
1022  dataComplete();
1023  }
1024 
1025  processReplyBody();
1026 }
1027 
1028 void
1030 {
1031  debugs(9, 3,status());
1032 
1033  /* Connection closed; transfer done. */
1034 
1036  data.close();
1037 
1038  /* expect the "transfer complete" message on the control socket */
1039  /*
1040  * DPW 2007-04-23
1041  * Previously, this was the only place where we set the
1042  * 'buffered_ok' flag when calling scheduleReadControlReply().
1043  * It caused some problems if the FTP server returns an unexpected
1044  * status code after the data command. FtpStateData was being
1045  * deleted in the middle of dataRead().
1046  */
1047  /* AYJ: 2011-01-13: Bug 2581.
1048  * 226 status is possibly waiting in the ctrl buffer.
1049  * The connection will hang if we DONT send buffered_ok.
1050  * This happens on all transfers which can be completely sent by the
1051  * server before the 150 started status message is read in by Squid.
1052  * ie all transfers of about one packet hang.
1053  */
1054  scheduleReadControlReply(1);
1055 }
1056 
1057 void
1058 Ftp::Client::abortAll(const char *reason)
1059 {
1060  debugs(9, 3, "aborting transaction for " << reason <<
1061  "; FD " << (ctrl.conn!=nullptr?ctrl.conn->fd:-1) << ", Data FD " << (data.conn!=nullptr?data.conn->fd:-1) << ", this " << this);
1062  mustStop(reason);
1063 }
1064 
1069 void
1071 {
1072  commUnsetConnTimeout(ctrl.conn);
1073 
1074  typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
1075  AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this,
1077  commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
1078 }
1079 
1080 void
1082 {
1083  if (io.size > 0)
1084  statCounter.server.ftp.kbytes_out += io.size;
1086 }
1087 
1091 void
1093 {
1095  debugs(9, 3, status());
1096  dataComplete();
1097  /* NP: RFC 959 3.3. DATA CONNECTION MANAGEMENT
1098  * if transfer type is 'stream' call dataComplete()
1099  * otherwise leave open. (reschedule control channel read?)
1100  */
1101 }
1102 
1105 bool
1107 {
1108  char *s;
1109  char *sbuf;
1110  char *end;
1111  int usable;
1112  int complete = 0;
1113  wordlist *head = nullptr;
1114  wordlist *list;
1115  wordlist **tail = &head;
1116  size_t linelen;
1117  debugs(9, 3, status());
1118  /*
1119  * We need a NULL-terminated buffer for scanning, ick
1120  */
1121  const size_t len = ctrl.offset;
1122  sbuf = (char *)xmalloc(len + 1);
1123  xstrncpy(sbuf, ctrl.buf, len + 1);
1124  end = sbuf + len - 1;
1125 
1126  while (*end != '\r' && *end != '\n' && end > sbuf)
1127  --end;
1128 
1129  usable = end - sbuf;
1130 
1131  debugs(9, 3, "usable = " << usable);
1132 
1133  if (usable == 0) {
1134  debugs(9, 3, "didn't find end of line");
1135  safe_free(sbuf);
1136  return false;
1137  }
1138 
1139  debugs(9, 3, len << " bytes to play with");
1140  ++end;
1141  s = sbuf;
1142  s += strspn(s, crlf);
1143 
1144  for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
1145  if (complete)
1146  break;
1147 
1148  debugs(9, 5, "s = {" << s << "}");
1149 
1150  linelen = strcspn(s, crlf) + 1;
1151 
1152  if (linelen < 2)
1153  break;
1154 
1155  if (linelen > 3)
1156  complete = (*s >= '0' && *s <= '9' && *(s + 3) == ' ');
1157 
1158  list = new wordlist();
1159 
1160  list->key = (char *)xmalloc(linelen);
1161 
1162  xstrncpy(list->key, s, linelen);
1163 
1164  /* trace the FTP communication chat at level 2 */
1165  debugs(9, 2, "ftp>> " << list->key);
1166 
1167  if (complete) {
1168  // use list->key for last_reply because s contains the new line
1169  safe_free(ctrl.last_reply);
1170  ctrl.last_reply = xstrdup(list->key + 4);
1171  ctrl.replycode = atoi(list->key);
1172  }
1173 
1174  *tail = list;
1175 
1176  tail = &list->next;
1177  }
1178 
1179  bytesUsed = static_cast<size_t>(s - sbuf);
1180  safe_free(sbuf);
1181 
1182  if (!complete) {
1184  return false;
1185  }
1186 
1187  ctrl.message = head;
1188  assert(ctrl.replycode >= 0);
1189  assert(ctrl.last_reply);
1190  assert(ctrl.message);
1191  return true;
1192 }
1193 
1194 }; // namespace Ftp
1195 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
SBuf verbose(const HttpRequestPointer &) const override
Definition: FtpClient.cc:81
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:942
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
bool sendEprt()
Definition: FtpClient.cc:608
void commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
Definition: comm.cc:618
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
struct StatCounters::@105 server
@ ERR_READ_ERROR
Definition: forward.h:28
#define xmalloc
void fd_bytes(const int fd, const int len, const IoDirection direction)
Definition: fd.cc:221
bool sendPort()
Definition: FtpClient.cc:647
void opened(const Comm::ConnectionPointer &conn, const AsyncCall::Pointer &aCloser)
called after the socket is opened, sets up close handler
Definition: FtpClient.cc:90
time_t connect
Definition: SquidConfig.h:115
int reads
Definition: IoStats.h:19
SBuf brief() const override
Definition: FtpClient.cc:75
void setAddrs(const Ip::Address &aLocal, const Ip::Address &aRemote)
Definition: Connection.h:106
void maybeReadVirginBody() override
read response data from the network
Definition: FtpClient.cc:918
char * reply
Definition: errorpage.h:191
void connectDataChannel()
Definition: FtpClient.cc:763
const char *const crlf
Definition: FtpClient.cc:40
void writeCommand(const char *buf)
Definition: FtpClient.cc:824
void sentRequestBody(const CommIoCbParams &io) override
Definition: FtpClient.cc:1081
void * memAllocBuf(size_t net_size, size_t *gross_size)
Definition: minimal.cc:46
@ ENTRY_ABORTED
Definition: enums.h:110
void error(char *format,...)
struct IoStats::@59 Ftp
~Client() override
Definition: FtpClient.cc:206
struct ErrorState::@47 ftp
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
Definition: SBuf.h:93
bool sendPassive()
Definition: FtpClient.cc:654
#define xstrdup
void abortAll(const char *reason) override
abnormal transaction termination; reason is for debugging only
Definition: FtpClient.cc:1058
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:85
void initReadBuf()
Definition: FtpClient.cc:222
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
IoStats IOStats
@ OK
Definition: Flag.h:16
struct SquidConfig::@77 Timeout
virtual void failed(err_type error=ERR_NONE, int xerrno=0, ErrorState *ftperr=nullptr)
handle a fatal transaction error, closing the control connection
Definition: FtpClient.cc:263
@ ERR_NONE
Definition: forward.h:15
StatusCode
Definition: StatusCode.h:20
err_type
Definition: forward.h:14
@ ERR_CLOSING
Definition: Flag.h:24
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:83
static int port
Definition: ldap_backend.cc:70
FTP client functionality shared among FTP Gateway and Relay clients.
Definition: FtpClient.h:110
Definition: forward.h:23
virtual void dataChannelConnected(const CommConnectCbParams &io)=0
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
static char * escapeIAC(const char *buf)
Definition: FtpClient.cc:43
Definition: Range.h:18
@ scGatewayTimeout
Definition: StatusCode.h:77
bool openListenSocket()
Definition: FtpClient.cc:796
bool handlePasvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:456
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer &callback)
Definition: Read.h:59
#define COMM_TRANSPARENT
Definition: Connection.h:50
void bytesIn(int qty)
Definition: DelayId.cc:135
wordlist * server_msg
Definition: errorpage.h:189
int size
Definition: ModDevPoll.cc:70
void leaveOrphanage()
resume relying on owner(s) to initiate an explicit connection closure
Definition: Connection.h:92
void start() override
called by AsyncStart; do not call directly
Definition: FtpClient.cc:216
void noteDelayAwareReadChance() override
Definition: FtpClient.cc:909
virtual void doneSendingRequestBody()=0
Definition: Client.cc:344
time_t read
Definition: SquidConfig.h:112
virtual void handleControlReply()
Definition: FtpClient.cc:420
char * last_command
Definition: FtpClient.h:83
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
Definition: FtpClient.cc:811
static ErrorDetail::Pointer NewIfAny(const int errorNo)
const Comm::ConnectionPointer & serverConnection() const
Definition: FwdState.h:138
err_type type
Definition: errorpage.h:170
HttpRequestPointer request
Definition: errorpage.h:177
const Acl::Answer & fastCheck()
Definition: Checklist.cc:298
void dataComplete()
Definition: FtpClient.cc:1029
unsigned short port() const
Definition: Address.cc:790
@ scBadGateway
Definition: StatusCode.h:75
Definition: MemBuf.h:23
struct StatCounters::@105::@115 all
Ip::Address local
Definition: Connection.h:149
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
bool handleEpsvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:492
unsigned short comm_local_port(int fd)
Definition: comm.cc:167
AsyncCall::Pointer dataCloser()
creates a data channel Comm close callback
Definition: FtpClient.cc:803
void scheduleReadControlReply(int buffered_ok)
Definition: FtpClient.cc:326
Comm::ConnectionPointer conn
Definition: CommCalls.h:80
bool doneWithServer() const override
Definition: FtpClient.cc:257
#define safe_free(x)
Definition: xalloc.h:73
Ip::Address remote
Definition: Connection.h:152
void close()
planned close: removes the close handler and calls comm_close
Definition: FtpClient.cc:107
#define assert(EX)
Definition: assert.h:17
SSL Connection
Definition: Session.h:49
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:82
void doneSendingRequestBody() override
Definition: FtpClient.cc:1092
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:70
void closeServer() override
Definition: FtpClient.cc:234
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:33
#define xfree
int xerrno
Definition: errorpage.h:179
wordlist * next
Definition: wordlist.h:60
Client(FwdState *fwdState)
Definition: FtpClient.cc:185
void dataRead(const CommIoCbParams &io)
Definition: FtpClient.cc:960
int ignoreErrno(int ierrno)
Definition: comm.cc:1407
#define fd_table
Definition: fde.h:189
void clear()
remove the close handler, leave connection open
Definition: FtpClient.cc:128
@ ERR_READ_TIMEOUT
Definition: forward.h:26
virtual Http::StatusCode failedHttpStatus(err_type &error)
Definition: FtpClient.cc:312
bool parseControlReply(size_t &bytesUsed)
Definition: FtpClient.cc:1106
void commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:594
void addr(const Ip::Address &addr)
import host and port
Definition: FtpClient.cc:174
bool ParseIpPort(const char *buf, const char *forceIp, Ip::Address &addr)
parses and validates "A1,A2,A3,A4,P1,P2" IP,port sequence
Definition: Parsing.cc:18
char * key
Definition: wordlist.h:59
bool allowed() const
Definition: Acl.h:82
void * memReallocBuf(void *buf, size_t net_size, size_t *gross_size)
Definition: minimal.cc:54
const Comm::ConnectionPointer & dataConnection() const override
Definition: FtpClient.cc:903
squidaio_request_t * head
Definition: aiops.cc:129
virtual void sentRequestBody(const CommIoCbParams &io)=0
Definition: Client.cc:368
char * content()
start of the added data
Definition: MemBuf.h:41
@ ERR_WRITE_ERROR
Definition: forward.h:29
struct SquidConfig::@91 accessList
void ctrlClosed(const CommCloseCbParams &io)
handler called by Comm when FTP control channel is closed unexpectedly
Definition: FtpClient.cc:880
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
acl_access * ftp_epsv
Definition: SquidConfig.h:402
struct SquidConfig::@92 Ftp
#define DBG_IMPORTANT
Definition: Stream.h:38
void readControlReply(const CommIoCbParams &io)
Definition: FtpClient.cc:363
void reset()
Definition: MemBuf.cc:129
CtrlChannel ctrl
FTP control channel state.
Definition: FtpClient.h:142
void forget()
Definition: FtpClient.cc:118
@ ERR_FTP_FAILURE
Definition: forward.h:53
void writeCommandCallback(const CommIoCbParams &io)
Definition: FtpClient.cc:856
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
Definition: FtpClient.cc:891
int read_hist[histSize]
Definition: IoStats.h:21
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
const A & min(A const &lhs, A const &rhs)
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:971
void switchTimeoutToDataChannel()
Definition: FtpClient.cc:1070
nfmark_t nfmark
Definition: Connection.h:166
Http::StatusCode httpStatus
Definition: errorpage.h:173
struct StatCounters::@105::@115 ftp
class SquidConfig Config
Definition: SquidConfig.cc:12
void memFreeBuf(size_t size, void *)
Definition: minimal.cc:67
StatCounters statCounter
Definition: StatCounters.cc:12
@ STORE_PENDING
Definition: enums.h:46

 

Introduction

Documentation

Support

Miscellaneous