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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors