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