FtpClient.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 {
94  assert(closer == NULL);
95 
96  assert(Comm::IsConnOpen(newConn));
97  assert(aCloser != NULL);
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)) {
122  comm_remove_close_handler(conn->fd, closer);
123  }
124  clear();
125 }
126 
127 void
129 {
130  conn = NULL;
131  closer = NULL;
132 }
133 
134 /* Ftp::CtrlChannel */
135 
137  buf(NULL),
138  size(0),
139  offset(0),
140  message(NULL),
141  last_command(NULL),
142  last_reply(NULL),
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(NULL),
161  host(NULL),
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(NULL),
191  old_reply(NULL),
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 = NULL; // refcounted
212 }
213 
214 void
216 {
217  scheduleReadControlReply(0);
218 }
219 
220 void
222 {
223  if (data.readBuf == NULL) {
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 = NULL;
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) {
382  fd_bytes(io.fd, io.size, FD_READ);
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 : NULL;
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 != NULL && *buf != '\0' && *buf != '\n' && *buf != '(')
525  ++buf;
526  if (buf != NULL && *buf == '\n')
527  ++buf;
528 
529  if (buf == NULL || *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, NULL);
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 = NULL; //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 != NULL) {
816  data.listenConn->close();
817  data.listenConn = NULL;
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, NULL);
850 
851  scheduleReadControlReply(0);
852 }
853 
854 void
856 {
857 
858  debugs(9, 5, "wrote " << io.size << " bytes");
859 
860  if (io.size > 0) {
861  fd_bytes(io.fd, io.size, FD_WRITE);
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  data.read_pending = false;
911  maybeReadVirginBody();
912 }
913 
914 void
916 {
917  // too late to read
918  if (!Comm::IsConnOpen(data.conn) || fd_table[data.conn->fd].closing())
919  return;
920 
921  if (data.read_pending)
922  return;
923 
924  initReadBuf();
925 
926  const int read_sz = replyBodySpace(*data.readBuf, 0);
927 
928  debugs(9, 9, "FTP may read up to " << read_sz << " bytes");
929 
930  if (read_sz < 2) // see http.cc
931  return;
932 
933  data.read_pending = true;
934 
935  typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
936  AsyncCall::Pointer timeoutCall = JobCallback(9, 5,
937  TimeoutDialer, this, Ftp::Client::timeout);
938  commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
939 
940  debugs(9,5,"queueing read on FD " << data.conn->fd);
941 
942  const auto amountToRead = entry->bytesWanted(Range<size_t>(0, read_sz));
943 
944  if (amountToRead <= 0) {
945  delayRead();
946  return;
947  }
948 
949  using ReadDialer = CommCbMemFunT<Client, CommIoCbParams>;
950  AsyncCall::Pointer readCallback = JobCallback(9, 5, ReadDialer, this, Client::dataRead);
951  comm_read(data.conn, data.readBuf->space(), amountToRead, readCallback);
952 }
953 
954 void
956 {
957  int j;
958  int bin;
959 
960  data.read_pending = false;
961 
962  debugs(9, 3, "FD " << io.fd << " Read " << io.size << " bytes");
963 
964  if (io.size > 0) {
965  statCounter.server.all.kbytes_in += io.size;
966  statCounter.server.ftp.kbytes_in += io.size;
967  }
968 
969  if (io.flag == Comm::ERR_CLOSING)
970  return;
971 
972  assert(io.fd == data.conn->fd);
973 
974  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
975  abortOnData("entry aborted during dataRead");
976  return;
977  }
978 
979  if (io.flag == Comm::OK && io.size > 0) {
980  debugs(9, 5, "appended " << io.size << " bytes to readBuf");
981  data.readBuf->appended(io.size);
982 #if USE_DELAY_POOLS
983  DelayId delayId = entry->mem_obj->mostBytesAllowed();
984  delayId.bytesIn(io.size);
985 #endif
986  ++ IOStats.Ftp.reads;
987 
988  for (j = io.size - 1, bin = 0; j; ++bin)
989  j >>= 1;
990 
991  ++ IOStats.Ftp.read_hist[bin];
992  }
993 
994  if (io.flag != Comm::OK) {
995  debugs(50, ignoreErrno(io.xerrno) ? 3 : DBG_IMPORTANT,
996  "ERROR: FTP data read failure: " << xstrerr(io.xerrno));
997 
998  if (ignoreErrno(io.xerrno)) {
999  maybeReadVirginBody();
1000  } else {
1001  failed(ERR_READ_ERROR, 0);
1002  /* failed closes ctrl.conn and frees ftpState */
1003  return;
1004  }
1005  } else if (io.size == 0) {
1006  debugs(9, 3, "Calling dataComplete() because io.size == 0");
1007  /*
1008  * DPW 2007-04-23
1009  * Dangerous curves ahead. This call to dataComplete was
1010  * calling scheduleReadControlReply, handleControlReply,
1011  * and then ftpReadTransferDone. If ftpReadTransferDone
1012  * gets unexpected status code, it closes down the control
1013  * socket and our FtpStateData object gets destroyed. As
1014  * a workaround we no longer set the 'buffered_ok' flag in
1015  * the scheduleReadControlReply call.
1016  */
1017  dataComplete();
1018  }
1019 
1020  processReplyBody();
1021 }
1022 
1023 void
1025 {
1026  debugs(9, 3,status());
1027 
1028  /* Connection closed; transfer done. */
1029 
1031  data.close();
1032 
1033  /* expect the "transfer complete" message on the control socket */
1034  /*
1035  * DPW 2007-04-23
1036  * Previously, this was the only place where we set the
1037  * 'buffered_ok' flag when calling scheduleReadControlReply().
1038  * It caused some problems if the FTP server returns an unexpected
1039  * status code after the data command. FtpStateData was being
1040  * deleted in the middle of dataRead().
1041  */
1042  /* AYJ: 2011-01-13: Bug 2581.
1043  * 226 status is possibly waiting in the ctrl buffer.
1044  * The connection will hang if we DONT send buffered_ok.
1045  * This happens on all transfers which can be completely sent by the
1046  * server before the 150 started status message is read in by Squid.
1047  * ie all transfers of about one packet hang.
1048  */
1049  scheduleReadControlReply(1);
1050 }
1051 
1052 void
1053 Ftp::Client::abortAll(const char *reason)
1054 {
1055  debugs(9, 3, "aborting transaction for " << reason <<
1056  "; FD " << (ctrl.conn!=NULL?ctrl.conn->fd:-1) << ", Data FD " << (data.conn!=NULL?data.conn->fd:-1) << ", this " << this);
1057  mustStop(reason);
1058 }
1059 
1064 void
1066 {
1067  commUnsetConnTimeout(ctrl.conn);
1068 
1069  typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
1070  AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this,
1072  commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
1073 }
1074 
1075 void
1077 {
1078  if (io.size > 0)
1079  statCounter.server.ftp.kbytes_out += io.size;
1081 }
1082 
1086 void
1088 {
1090  debugs(9, 3, status());
1091  dataComplete();
1092  /* NP: RFC 959 3.3. DATA CONNECTION MANAGEMENT
1093  * if transfer type is 'stream' call dataComplete()
1094  * otherwise leave open. (reschedule control channel read?)
1095  */
1096 }
1097 
1100 bool
1102 {
1103  char *s;
1104  char *sbuf;
1105  char *end;
1106  int usable;
1107  int complete = 0;
1108  wordlist *head = NULL;
1109  wordlist *list;
1110  wordlist **tail = &head;
1111  size_t linelen;
1112  debugs(9, 3, status());
1113  /*
1114  * We need a NULL-terminated buffer for scanning, ick
1115  */
1116  const size_t len = ctrl.offset;
1117  sbuf = (char *)xmalloc(len + 1);
1118  xstrncpy(sbuf, ctrl.buf, len + 1);
1119  end = sbuf + len - 1;
1120 
1121  while (*end != '\r' && *end != '\n' && end > sbuf)
1122  --end;
1123 
1124  usable = end - sbuf;
1125 
1126  debugs(9, 3, "usable = " << usable);
1127 
1128  if (usable == 0) {
1129  debugs(9, 3, "didn't find end of line");
1130  safe_free(sbuf);
1131  return false;
1132  }
1133 
1134  debugs(9, 3, len << " bytes to play with");
1135  ++end;
1136  s = sbuf;
1137  s += strspn(s, crlf);
1138 
1139  for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
1140  if (complete)
1141  break;
1142 
1143  debugs(9, 5, "s = {" << s << "}");
1144 
1145  linelen = strcspn(s, crlf) + 1;
1146 
1147  if (linelen < 2)
1148  break;
1149 
1150  if (linelen > 3)
1151  complete = (*s >= '0' && *s <= '9' && *(s + 3) == ' ');
1152 
1153  list = new wordlist();
1154 
1155  list->key = (char *)xmalloc(linelen);
1156 
1157  xstrncpy(list->key, s, linelen);
1158 
1159  /* trace the FTP communication chat at level 2 */
1160  debugs(9, 2, "ftp>> " << list->key);
1161 
1162  if (complete) {
1163  // use list->key for last_reply because s contains the new line
1164  ctrl.last_reply = xstrdup(list->key + 4);
1165  ctrl.replycode = atoi(list->key);
1166  }
1167 
1168  *tail = list;
1169 
1170  tail = &list->next;
1171  }
1172 
1173  bytesUsed = static_cast<size_t>(s - sbuf);
1174  safe_free(sbuf);
1175 
1176  if (!complete) {
1178  return false;
1179  }
1180 
1181  ctrl.message = head;
1182  assert(ctrl.replycode >= 0);
1183  assert(ctrl.last_reply);
1184  assert(ctrl.message);
1185  return true;
1186 }
1187 
1188 }; // namespace Ftp
1189 
struct SquidConfig::@96 Timeout
const char * xstrerr(int error)
Definition: xstrerror.cc:83
virtual SBuf verbose(const HttpRequestPointer &) const override
Definition: FtpClient.cc:81
virtual void abortAll(const char *reason)
abnormal transaction termination; reason is for debugging only
Definition: FtpClient.cc:1053
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:921
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
bool sendEprt()
Definition: FtpClient.cc:607
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
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
time_t connect
Definition: SquidConfig.h:113
struct StatCounters::@128::@138 ftp
int reads
Definition: IoStats.h:19
virtual SBuf brief() const override
Definition: FtpClient.cc:75
char * reply
Definition: errorpage.h:190
void connectDataChannel()
Definition: FtpClient.cc:762
const char *const crlf
Definition: FtpClient.cc:40
void writeCommand(const char *buf)
Definition: FtpClient.cc:823
virtual void closeServer()
Definition: FtpClient.cc:233
void * memAllocBuf(size_t net_size, size_t *gross_size)
Definition: minimal.cc:46
void error(char *format,...)
struct ErrorState::@59 ftp
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
Definition: SBuf.h:94
bool sendPassive()
Definition: FtpClient.cc:653
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:563
virtual ~Client()
Definition: FtpClient.cc:205
#define xstrdup
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
void initReadBuf()
Definition: FtpClient.cc:221
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
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:25
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
static int port
Definition: ldap_backend.cc:70
struct SquidConfig::@112 Ftp
FTP client functionality shared among FTP Gateway and Relay clients.
Definition: FtpClient.h:111
struct StatCounters::@128 server
Definition: forward.h:28
virtual void maybeReadVirginBody()
read response data from the network
Definition: FtpClient.cc:915
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
@ ENTRY_ABORTED
Definition: enums.h:115
static char * escapeIAC(const char *buf)
Definition: FtpClient.cc:43
Definition: Range.h:19
@ scGatewayTimeout
Definition: StatusCode.h:75
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
virtual void start()
called by AsyncStart; do not call directly
Definition: FtpClient.cc:215
void bytesIn(int qty)
Definition: DelayId.cc:152
wordlist * server_msg
Definition: errorpage.h:188
int size
Definition: ModDevPoll.cc:75
#define NULL
Definition: types.h:166
virtual void doneSendingRequestBody()=0
Definition: Client.cc:340
time_t read
Definition: SquidConfig.h:110
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)
err_type type
Definition: errorpage.h:168
HttpRequestPointer request
Definition: errorpage.h:175
void dataComplete()
Definition: FtpClient.cc:1024
unsigned short port() const
Definition: Address.cc:778
@ scBadGateway
Definition: StatusCode.h:73
Definition: MemBuf.h:24
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
bool handleEpsvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:491
unsigned char code
Definition: html_quote.c:20
unsigned short comm_local_port(int fd)
Definition: comm.cc:161
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:85
#define safe_free(x)
Definition: xalloc.h:73
void close()
planned close: removes the close handler and calls comm_close
Definition: FtpClient.cc:107
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:19
SSL Connection
Definition: Session.h:45
virtual bool doneWithServer() const
Definition: FtpClient.cc:256
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:33
int commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
Definition: comm.cc:589
virtual void doneSendingRequestBody()
Definition: FtpClient.cc:1087
virtual void noteDelayAwareReadChance()
Definition: FtpClient.cc:908
#define xfree
int xerrno
Definition: errorpage.h:177
void fd_bytes(int fd, int len, unsigned int type)
Definition: fd.cc:226
wordlist * next
Definition: wordlist.h:33
Client(FwdState *fwdState)
Definition: FtpClient.cc:184
void dataRead(const CommIoCbParams &io)
Definition: FtpClient.cc:955
int ignoreErrno(int ierrno)
Definition: comm.cc:1412
#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
struct IoStats::@78 Ftp
struct SquidConfig::@111 accessList
virtual Http::StatusCode failedHttpStatus(err_type &error)
Definition: FtpClient.cc:311
bool parseControlReply(size_t &bytesUsed)
Definition: FtpClient.cc:1101
@ FD_READ
Definition: enums.h:23
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:32
bool allowed() const
Definition: Acl.h:149
void * memReallocBuf(void *buf, size_t net_size, size_t *gross_size)
Definition: minimal.cc:53
virtual void sentRequestBody(const CommIoCbParams &io)
Definition: FtpClient.cc:1076
squidaio_request_t * head
Definition: aiops.cc:126
virtual void sentRequestBody(const CommIoCbParams &io)=0
Definition: Client.cc:364
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
struct StatCounters::@128::@138 all
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:408
#define DBG_IMPORTANT
Definition: Stream.h:41
void readControlReply(const CommIoCbParams &io)
Definition: FtpClient.cc:362
void reset()
Definition: MemBuf.cc:129
virtual const Comm::ConnectionPointer & dataConnection() const
Definition: FtpClient.cc:902
CtrlChannel ctrl
FTP control channel state.
Definition: FtpClient.h:142
void forget()
Definition: FtpClient.cc:118
@ FD_WRITE
Definition: enums.h:24
@ 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
#define false
Definition: GnuRegex.c:233
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
Definition: FtpClient.cc:890
A const & min(A const &lhs, A const &rhs)
int read_hist[histSize]
Definition: IoStats.h:21
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:950
void switchTimeoutToDataChannel()
Definition: FtpClient.cc:1065
Comm::ConnectionPointer const & serverConnection() const
Definition: FwdState.h:106
Http::StatusCode httpStatus
Definition: errorpage.h:171
class SquidConfig Config
Definition: SquidConfig.cc:12
void memFreeBuf(size_t size, void *)
Definition: minimal.cc:84
StatCounters statCounter
Definition: StatCounters.cc:12
@ STORE_PENDING
Definition: enums.h:51

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors