FtpServer.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 33 Transfer protocol servers */
10 
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "base/CharacterSet.h"
14 #include "base/Raw.h"
15 #include "base/RefCount.h"
16 #include "base/Subscription.h"
17 #include "client_side_reply.h"
18 #include "client_side_request.h"
19 #include "clientStream.h"
20 #include "comm/ConnOpener.h"
21 #include "comm/Read.h"
22 #include "comm/TcpAcceptor.h"
23 #include "comm/Write.h"
24 #include "errorpage.h"
25 #include "fd.h"
26 #include "ftp/Elements.h"
27 #include "ftp/Parsing.h"
28 #include "globals.h"
29 #include "http/one/RequestParser.h"
30 #include "http/Stream.h"
31 #include "HttpHdrCc.h"
32 #include "ip/tools.h"
33 #include "ipc/FdNotes.h"
34 #include "parser/Tokenizer.h"
35 #include "servers/forward.h"
36 #include "servers/FtpServer.h"
37 #include "SquidConfig.h"
38 #include "StatCounters.h"
39 #include "tools.h"
40 
41 #include <set>
42 #include <map>
43 
45 
46 namespace Ftp
47 {
48 static void PrintReply(MemBuf &mb, const HttpReply *reply, const char *const prefix = "");
49 static bool SupportedCommand(const SBuf &name);
50 static bool CommandHasPathParameter(const SBuf &cmd);
51 };
52 
54  AsyncJob("Ftp::Server"),
55  ConnStateData(xact),
56  master(new MasterState),
57  uri(),
58  host(),
59  gotEpsvAll(false),
60  onDataAcceptCall(),
61  dataListenConn(),
62  dataConn(),
63  uploadAvailSize(0),
64  listener(),
65  dataConnWait(),
66  reader(),
67  waitingForOrigin(false),
68  originDataDownloadAbortedOnError(false)
69 {
70  flags.readMore = false; // we need to announce ourselves first
71  *uploadBuf = 0;
72 }
73 
75 {
76  closeDataConnection();
77 }
78 
79 int
81 {
82  return 0; // no support for concurrent FTP requests
83 }
84 
85 time_t
87 {
89 }
90 
91 void
93 {
95 
96  if (transparent()) {
97  char buf[MAX_IPSTRLEN];
98  clientConnection->local.toUrl(buf, MAX_IPSTRLEN);
99  host = buf;
100  calcUri(NULL);
101  debugs(33, 5, "FTP transparent URL: " << uri);
102  }
103 
104  writeEarlyReply(220, "Service ready");
105 }
106 
108 void
110 {
111  if (reader != NULL)
112  return;
113 
114  const size_t availSpace = sizeof(uploadBuf) - uploadAvailSize;
115  if (availSpace <= 0)
116  return;
117 
118  debugs(33, 4, dataConn << ": reading FTP data...");
119 
121  reader = JobCallback(33, 5, Dialer, this, Ftp::Server::readUploadData);
122  comm_read(dataConn, uploadBuf + uploadAvailSize, availSpace,
123  reader);
124 }
125 
127 void
129 {
130  // zero pipelinePrefetchMax() ensures that there is only parsed request
131  Must(pipeline.count() == 1);
132  Http::StreamPointer context = pipeline.front();
133  Must(context != nullptr);
134 
135  ClientHttpRequest *const http = context->http;
136  assert(http != NULL);
137 
138  HttpRequest *const request = http->request;
139  Must(http->storeEntry() || request);
140  const bool mayForward = !http->storeEntry() && handleRequest(request);
141 
142  if (http->storeEntry() != NULL) {
143  debugs(33, 4, "got an immediate response");
145  context->pullData();
146  } else if (mayForward) {
147  debugs(33, 4, "forwarding request to server side");
148  assert(http->storeEntry() == NULL);
150  } else {
151  debugs(33, 4, "will resume processing later");
152  }
153 }
154 
155 void
157 {
158  Must(pipeline.count() == 1);
159 
160  // Process FTP request asynchronously to make sure FTP
161  // data connection accept callback is fired first.
162  CallJobHere(33, 4, CbcPointer<Server>(this),
163  Ftp::Server, doProcessRequest);
164 }
165 
167 void
169 {
170  debugs(33, 5, io.conn << " size " << io.size);
171  Must(reader != NULL);
172  reader = NULL;
173 
174  assert(Comm::IsConnOpen(dataConn));
175  assert(io.conn->fd == dataConn->fd);
176 
177  if (io.flag == Comm::OK && bodyPipe != NULL) {
178  if (io.size > 0) {
180 
181  char *const current_buf = uploadBuf + uploadAvailSize;
182  if (io.buf != current_buf)
183  memmove(current_buf, io.buf, io.size);
184  uploadAvailSize += io.size;
185  shovelUploadData();
186  } else if (io.size == 0) {
187  debugs(33, 5, io.conn << " closed");
188  closeDataConnection();
189  if (uploadAvailSize <= 0)
190  finishDechunkingRequest(true);
191  }
192  } else { // not Comm::Flags::OK or unexpected read
193  debugs(33, 5, io.conn << " closed");
194  closeDataConnection();
195  finishDechunkingRequest(false);
196  }
197 
198 }
199 
201 void
203 {
204  assert(bodyPipe != NULL);
205 
206  debugs(33, 5, "handling FTP request data for " << clientConnection);
207  const size_t putSize = bodyPipe->putMoreData(uploadBuf,
208  uploadAvailSize);
209  if (putSize > 0) {
210  uploadAvailSize -= putSize;
211  if (uploadAvailSize > 0)
212  memmove(uploadBuf, uploadBuf + putSize, uploadAvailSize);
213  }
214 
215  if (Comm::IsConnOpen(dataConn))
216  maybeReadUploadData();
217  else if (uploadAvailSize <= 0)
218  finishDechunkingRequest(true);
219 }
220 
221 void
223 {
224  if (!isOpen()) // if we are closing, nothing to do
225  return;
226 
227  shovelUploadData();
228 }
229 
230 void
232 {
233  if (!isOpen()) // if we are closing, nothing to do
234  return;
235 
237  closeDataConnection();
238 }
239 
241 void
243 {
244  Assure(params.port);
245 
246  // NP: it is possible the port was reconfigured when the call or accept() was queued.
247 
248  if (params.flag != Comm::OK) {
249  // Its possible the call was still queued when the client disconnected
250  debugs(33, 2, params.port->listenConn << ": FTP accept failure: " << xstrerr(params.xerrno));
251  return;
252  }
253 
254  debugs(33, 4, params.conn << ": accepted");
255  fd_note(params.conn->fd, "client ftp connect");
256 
257  const auto xact = MasterXaction::MakePortful(params.port);
258  xact->tcpClient = params.conn;
259 
260  AsyncJob::Start(new Server(xact));
261  // XXX: do not abandon the MasterXaction object
262 }
263 
264 void
266 {
267  for (AnyP::PortCfgPointer s = FtpPortList; s != NULL; s = s->next) {
269  debugs(1, DBG_IMPORTANT, "Ignoring ftp_port lines exceeding the" <<
270  " limit of " << MAXTCPLISTENPORTS << " ports.");
271  break;
272  }
273 
274  // direct new connections accepted by listenConn to Accept()
275  typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
276  RefCount<AcceptCall> subCall = commCbCall(5, 5, "Ftp::Server::AcceptCtrlConnection",
280  }
281 }
282 
283 void
285 {
286  for (AnyP::PortCfgPointer s = FtpPortList; s != NULL; s = s->next) {
287  if (s->listenConn != NULL) {
288  debugs(1, DBG_IMPORTANT, "Closing FTP port " << s->listenConn->local);
289  s->listenConn->close();
290  s->listenConn = NULL;
291  }
292  }
293 }
294 
295 void
297 {
298  // find request
299  Http::StreamPointer context = pipeline.front();
300  Must(context != nullptr);
301  ClientHttpRequest *const http = context->http;
302  Must(http != NULL);
303  HttpRequest *const request = http->request;
304  Must(request != NULL);
305  // make FTP peer connection exclusive to our request
306  pinBusyConnection(conn, request);
307 }
308 
309 void
311 {
313 
314  // TODO: Keep the control connection open after fixing the reset
315  // problem below
316  if (Comm::IsConnOpen(clientConnection))
317  clientConnection->close();
318 
319  // TODO: If the server control connection is gone, reset state to login
320  // again. Resetting login alone is not enough: FtpRelay::sendCommand() will
321  // not re-login because FtpRelay::serverState() is not going to be
322  // fssConnected. Calling resetLogin() alone is also harmful because
323  // it does not reset correctly the client-to-squid control connection (eg
324  // respond if required with an error code, in all cases)
325  // resetLogin("control connection closure");
326 }
327 
329 void
330 Ftp::Server::resetLogin(const char *reason)
331 {
332  debugs(33, 5, "will need to re-login due to " << reason);
333  master->clientReadGreeting = false;
334  changeState(fssBegin, reason);
335 }
336 
338 void
340 {
341  // TODO: fill a class AnyP::Uri instead of string
342  uri = "ftp://";
343  uri.append(host);
344  if (port->ftp_track_dirs && master->workingDir.length()) {
345  if (master->workingDir[0] != '/')
346  uri.append("/", 1);
347  uri.append(master->workingDir);
348  }
349 
350  if (uri[uri.length() - 1] != '/')
351  uri.append("/", 1);
352 
353  if (port->ftp_track_dirs && file) {
354  static const CharacterSet Slash("/", "/");
355  Parser::Tokenizer tok(*file);
356  tok.skipAll(Slash);
357  uri.append(tok.remaining());
358  }
359 }
360 
363 unsigned int
365 {
366  closeDataConnection();
367 
369  conn->flags = COMM_NONBLOCKING;
370  conn->local = transparent() ? port->s : clientConnection->local;
371  conn->local.port(0);
372  const char *const note = uri.c_str();
373  comm_open_listener(SOCK_STREAM, IPPROTO_TCP, conn, note);
374  if (!Comm::IsConnOpen(conn)) {
375  debugs(5, DBG_CRITICAL, "ERROR: comm_open_listener failed for FTP data: " <<
376  conn->local << " error: " << errno);
377  writeCustomReply(451, "Internal error");
378  return 0;
379  }
380 
381  typedef CommCbMemFunT<Server, CommAcceptCbParams> AcceptDialer;
382  typedef AsyncCallT<AcceptDialer> AcceptCall;
383  RefCount<AcceptCall> call = static_cast<AcceptCall*>(JobCallback(5, 5, AcceptDialer, this, Ftp::Server::acceptDataConnection));
385  listener = call.getRaw();
386  dataListenConn = conn;
387  AsyncJob::Start(new Comm::TcpAcceptor(conn, note, sub));
388 
389  const unsigned int listeningPort = comm_local_port(conn->fd);
390  conn->local.port(listeningPort);
391  return listeningPort;
392 }
393 
394 void
396 {
397  if (params.flag != Comm::OK) {
398  // Its possible the call was still queued when the client disconnected
399  debugs(33, 2, dataListenConn << ": accept "
400  "failure: " << xstrerr(params.xerrno));
401  return;
402  }
403 
404  debugs(33, 4, "accepted " << params.conn);
405  fd_note(params.conn->fd, "passive client ftp data");
406 
407  if (!clientConnection) {
408  debugs(33, 5, "late data connection?");
409  closeDataConnection(); // in case we are still listening
410  params.conn->close();
411  } else if (params.conn->remote != clientConnection->remote) {
412  debugs(33, 2, "rogue data conn? ctrl: " << clientConnection->remote);
413  params.conn->close();
414  // Some FTP servers close control connection here, but it may make
415  // things worse from DoS p.o.v. and no better from data stealing p.o.v.
416  } else {
417  closeDataConnection();
418  dataConn = params.conn;
419  dataConn->leaveOrphanage();
420  uploadAvailSize = 0;
421  debugs(33, 7, "ready for data");
422  if (onDataAcceptCall != NULL) {
423  AsyncCall::Pointer call = onDataAcceptCall;
424  onDataAcceptCall = NULL;
425  // If we got an upload request, start reading data from the client.
426  if (master->serverState == fssHandleUploadRequest)
427  maybeReadUploadData();
428  else
429  Must(master->serverState == fssHandleDataRequest);
430  MemBuf mb;
431  mb.init();
432  mb.appendf("150 Data connection opened.\r\n");
433  Comm::Write(clientConnection, &mb, call);
434  }
435  }
436 }
437 
438 void
440 {
441  if (listener != NULL) {
442  listener->cancel("no longer needed");
443  listener = NULL;
444  }
445 
446  if (Comm::IsConnOpen(dataListenConn)) {
447  debugs(33, 5, "FTP closing client data listen socket: " <<
448  *dataListenConn);
449  dataListenConn->close();
450  }
451  dataListenConn = NULL;
452 
453  if (reader != NULL) {
454  // Comm::ReadCancel can deal with negative FDs
455  Comm::ReadCancel(dataConn->fd, reader);
456  reader = NULL;
457  }
458 
459  if (Comm::IsConnOpen(dataConn)) {
460  debugs(33, 5, "FTP closing client data connection: " <<
461  *dataConn);
462  dataConn->close();
463  }
464  dataConn = NULL;
465 }
466 
469 void
470 Ftp::Server::writeEarlyReply(const int code, const char *msg)
471 {
472  debugs(33, 7, code << ' ' << msg);
473  assert(99 < code && code < 1000);
474 
475  MemBuf mb;
476  mb.init();
477  mb.appendf("%i %s\r\n", code, msg);
478 
480  AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, Ftp::Server::wroteEarlyReply);
481  Comm::Write(clientConnection, &mb, call);
482 
483  flags.readMore = false;
484 
485  // TODO: Create master transaction. Log it in wroteEarlyReply().
486 }
487 
488 void
490 {
491  debugs(9, 2, "FTP Client " << clientConnection);
492  debugs(9, 2, "FTP Client REPLY:\n---------\n" << mb.buf <<
493  "\n----------");
494 
496  AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, Ftp::Server::wroteReply);
497  Comm::Write(clientConnection, &mb, call);
498 }
499 
500 void
501 Ftp::Server::writeCustomReply(const int code, const char *msg, const HttpReply *reply)
502 {
503  debugs(33, 7, code << ' ' << msg);
504  assert(99 < code && code < 1000);
505 
506  const bool sendDetails = reply != NULL &&
508 
509  MemBuf mb;
510  mb.init();
511  if (sendDetails) {
512  mb.appendf("%i-%s\r\n", code, msg);
513  mb.appendf(" Server reply:\r\n");
514  Ftp::PrintReply(mb, reply, " ");
515  mb.appendf("%i \r\n", code);
516  } else
517  mb.appendf("%i %s\r\n", code, msg);
518 
519  writeReply(mb);
520 }
521 
522 void
523 Ftp::Server::changeState(const ServerState newState, const char *reason)
524 {
525  if (master->serverState == newState) {
526  debugs(33, 3, "client state unchanged at " << master->serverState <<
527  " because " << reason);
528  master->serverState = newState;
529  } else {
530  debugs(33, 3, "client state was " << master->serverState <<
531  ", now " << newState << " because " << reason);
532  master->serverState = newState;
533  }
534 }
535 
537 static bool
539 {
540  static std::set<SBuf> PathedCommands;
541  if (!PathedCommands.size()) {
542  PathedCommands.insert(cmdMlst());
543  PathedCommands.insert(cmdMlsd());
544  PathedCommands.insert(cmdStat());
545  PathedCommands.insert(cmdNlst());
546  PathedCommands.insert(cmdList());
547  PathedCommands.insert(cmdMkd());
548  PathedCommands.insert(cmdRmd());
549  PathedCommands.insert(cmdDele());
550  PathedCommands.insert(cmdRnto());
551  PathedCommands.insert(cmdRnfr());
552  PathedCommands.insert(cmdAppe());
553  PathedCommands.insert(cmdStor());
554  PathedCommands.insert(cmdRetr());
555  PathedCommands.insert(cmdSmnt());
556  PathedCommands.insert(cmdCwd());
557  }
558 
559  return PathedCommands.find(cmd) != PathedCommands.end();
560 }
561 
563 Http::Stream *
565 {
566  /* Default values, to be updated by the switch statement below */
567  int scode = 421;
568  const char *reason = "Internal error";
569  const char *errUri = "error:ftp-internal-early-error";
570 
571  switch (eek) {
572  case EarlyErrorKind::HugeRequest:
573  scode = 421;
574  reason = "Huge request";
575  errUri = "error:ftp-huge-request";
576  break;
577 
578  case EarlyErrorKind::MissingLogin:
579  scode = 530;
580  reason = "Must login first";
581  errUri = "error:ftp-must-login-first";
582  break;
583 
584  case EarlyErrorKind::MissingUsername:
585  scode = 501;
586  reason = "Missing username";
587  errUri = "error:ftp-missing-username";
588  break;
589 
590  case EarlyErrorKind::MissingHost:
591  scode = 501;
592  reason = "Missing host";
593  errUri = "error:ftp-missing-host";
594  break;
595 
596  case EarlyErrorKind::UnsupportedCommand:
597  scode = 502;
598  reason = "Unknown or unsupported command";
599  errUri = "error:ftp-unsupported-command";
600  break;
601 
602  case EarlyErrorKind::InvalidUri:
603  scode = 501;
604  reason = "Invalid URI";
605  errUri = "error:ftp-invalid-uri";
606  break;
607 
608  case EarlyErrorKind::MalformedCommand:
609  scode = 421;
610  reason = "Malformed command";
611  errUri = "error:ftp-malformed-command";
612  break;
613 
614  // no default so that a compiler can check that we have covered all cases
615  }
616 
617  Http::Stream *context = abortRequestParsing(errUri);
619  Must(node);
620  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
621  Must(repContext);
622 
623  // We cannot relay FTP scode/reason via HTTP-specific ErrorState.
624  // TODO: When/if ErrorState can handle native FTP errors, use it instead.
625  HttpReply *reply = Ftp::HttpReplyWrapper(scode, reason, Http::scBadRequest, -1);
626  repContext->setReplyToReply(reply);
627  return context;
628 }
629 
633 Http::Stream *
635 {
636  flags.readMore = false; // common for all but one case below
637 
638  // OWS <command> [ RWS <parameter> ] OWS LF
639 
640  // InlineSpaceChars are isspace(3) or RFC 959 Section 3.1.1.5.2, except
641  // for the LF character that we must exclude here (but see FullWhiteSpace).
642  static const char * const InlineSpaceChars = " \f\r\t\v";
643  static const CharacterSet InlineSpace = CharacterSet("Ftp::Inline", InlineSpaceChars);
644  static const CharacterSet FullWhiteSpace = (InlineSpace + CharacterSet::LF).rename("Ftp::FWS");
645  static const CharacterSet CommandChars = FullWhiteSpace.complement("Ftp::Command");
646  static const CharacterSet TailChars = CharacterSet::LF.complement("Ftp::Tail");
647 
648  // This set is used to ignore empty commands without allowing an attacker
649  // to keep us endlessly busy by feeding us whitespace or empty commands.
650  static const CharacterSet &LeadingSpace = FullWhiteSpace;
651 
652  SBuf cmd;
653  SBuf params;
654 
655  Parser::Tokenizer tok(inBuf);
656 
657  (void)tok.skipAll(LeadingSpace); // leading OWS and empty commands
658  const bool parsed = tok.prefix(cmd, CommandChars); // required command
659 
660  // note that the condition below will eat either RWS or trailing OWS
661  if (parsed && tok.skipAll(InlineSpace) && tok.prefix(params, TailChars)) {
662  // now params may include trailing OWS
663  // TODO: Support right-trimming using CharacterSet in Tokenizer instead
664  static const SBuf bufWhiteSpace(InlineSpaceChars);
665  params.trim(bufWhiteSpace, false, true);
666  }
667 
668  // Why limit command line and parameters size? Did not we just parse them?
669  // XXX: Our good old String cannot handle very long strings.
670  const SBuf::size_type tokenMax = min(
671  static_cast<SBuf::size_type>(32*1024), // conservative
673  if (cmd.length() > tokenMax || params.length() > tokenMax) {
674  changeState(fssError, "huge req token");
675  quitAfterError(NULL);
676  return earlyError(EarlyErrorKind::HugeRequest);
677  }
678 
679  // technically, we may skip multiple NLs below, but that is OK
680  if (!parsed || !tok.skipAll(CharacterSet::LF)) { // did not find terminating LF yet
681  // we need more data, but can we buffer more?
682  if (inBuf.length() >= Config.maxRequestHeaderSize) {
683  changeState(fssError, "huge req");
684  quitAfterError(NULL);
685  return earlyError(EarlyErrorKind::HugeRequest);
686  } else {
687  flags.readMore = true;
688  debugs(33, 5, "Waiting for more, up to " <<
689  (Config.maxRequestHeaderSize - inBuf.length()));
690  return NULL;
691  }
692  }
693 
694  Must(parsed && cmd.length());
695  consumeInput(tok.parsedSize()); // TODO: Would delaying optimize copying?
696 
697  debugs(33, 2, ">>ftp " << cmd << (params.isEmpty() ? "" : " ") << params);
698 
699  cmd.toUpper(); // this should speed up and simplify future comparisons
700 
701  // interception cases do not need USER to calculate the uri
702  if (!transparent()) {
703  if (!master->clientReadGreeting) {
704  // the first command must be USER
705  if (!pinning.pinned && cmd != cmdUser())
706  return earlyError(EarlyErrorKind::MissingLogin);
707  }
708 
709  // process USER request now because it sets FTP peer host name
710  if (cmd == cmdUser()) {
711  if (Http::Stream *errCtx = handleUserRequest(cmd, params))
712  return errCtx;
713  }
714  }
715 
716  if (!Ftp::SupportedCommand(cmd))
717  return earlyError(EarlyErrorKind::UnsupportedCommand);
718 
719  const HttpRequestMethod method =
720  cmd == cmdAppe() || cmd == cmdStor() || cmd == cmdStou() ?
722 
723  const SBuf *path = (params.length() && CommandHasPathParameter(cmd)) ?
724  &params : NULL;
725  calcUri(path);
726  const auto mx = MasterXaction::MakePortful(port);
727  mx->tcpClient = clientConnection;
728  auto * const request = HttpRequest::FromUrl(uri, mx, method);
729  if (!request) {
730  debugs(33, 5, "Invalid FTP URL: " << uri);
731  uri.clear();
732  return earlyError(EarlyErrorKind::InvalidUri);
733  }
734  char *newUri = xstrdup(uri.c_str());
735 
736  request->flags.ftpNative = true;
738 
739  // Our fake Request-URIs are not distinctive enough for caching to work
740  request->flags.cachable = false; // XXX: reset later by maybeCacheable()
741  request->flags.noCache = true;
742 
743  request->header.putStr(Http::HdrType::FTP_COMMAND, cmd.c_str());
744  request->header.putStr(Http::HdrType::FTP_ARGUMENTS, params.c_str()); // may be ""
745  if (method == Http::METHOD_PUT) {
746  request->header.putStr(Http::HdrType::EXPECT, "100-continue");
747  request->header.putStr(Http::HdrType::TRANSFER_ENCODING, "chunked");
748  }
749 
750  ClientHttpRequest *const http = new ClientHttpRequest(this);
751  http->req_sz = tok.parsedSize();
752  http->uri = newUri;
753  http->initRequest(request);
754 
755  Http::Stream *const result =
756  new Http::Stream(clientConnection, http);
757 
758  StoreIOBuffer tempBuffer;
759  tempBuffer.data = result->reqbuf;
760  tempBuffer.length = HTTP_REQBUF_SZ;
761 
762  ClientStreamData newServer = new clientReplyContext(http);
763  ClientStreamData newClient = result;
766  clientSocketDetach, newClient, tempBuffer);
767 
768  result->flags.parsed_ok = 1;
769  return result;
770 }
771 
772 void
774 {
775  // the caller guarantees that we are dealing with the current context only
776  Http::StreamPointer context = pipeline.front();
777  assert(context != nullptr);
778 
779  static ReplyHandler handlers[] = {
780  NULL, // fssBegin
781  NULL, // fssConnected
782  &Ftp::Server::handleFeatReply, // fssHandleFeat
783  &Ftp::Server::handlePasvReply, // fssHandlePasv
784  &Ftp::Server::handlePortReply, // fssHandlePort
785  &Ftp::Server::handleDataReply, // fssHandleDataRequest
786  &Ftp::Server::handleUploadReply, // fssHandleUploadRequest
787  &Ftp::Server::handleEprtReply,// fssHandleEprt
788  &Ftp::Server::handleEpsvReply,// fssHandleEpsv
789  NULL, // fssHandleCwd
790  NULL, // fssHandlePass
791  NULL, // fssHandleCdup
792  &Ftp::Server::handleErrorReply // fssError
793  };
794  try {
795  const Server &server = dynamic_cast<const Ftp::Server&>(*context->getConn());
796  if (const ReplyHandler handler = handlers[server.master->serverState])
797  (this->*handler)(reply, data);
798  else
799  writeForwardedReply(reply);
800  } catch (const std::exception &e) {
801  callException(e);
802  throw TexcHere(e.what());
803  }
804 }
805 
806 void
808 {
809  if (pipeline.front()->http->request->error) {
810  writeCustomReply(502, "Server does not support FEAT", reply);
811  return;
812  }
813 
814  Must(reply);
815  HttpReply::Pointer featReply = Ftp::HttpReplyWrapper(211, "End", Http::scNoContent, 0);
816  HttpHeader const &serverReplyHeader = reply->header;
817 
819  bool hasEPRT = false;
820  bool hasEPSV = false;
821  int prependSpaces = 1;
822 
823  featReply->header.putStr(Http::HdrType::FTP_PRE, "\"211-Features:\"");
824  const int scode = serverReplyHeader.getInt(Http::HdrType::FTP_STATUS);
825  if (scode == 211) {
826  while (const HttpHeaderEntry *e = serverReplyHeader.getEntry(&pos)) {
827  if (e->id == Http::HdrType::FTP_PRE) {
828  // assume RFC 2389 FEAT response format, quoted by Squid:
829  // <"> SP NAME [SP PARAMS] <">
830  // but accommodate MS servers sending four SPs before NAME
831 
832  // command name ends with (SP parameter) or quote
833  static const CharacterSet AfterFeatNameChars("AfterFeatName", " \"");
834  static const CharacterSet FeatNameChars = AfterFeatNameChars.complement("FeatName");
835 
836  Parser::Tokenizer tok(SBuf(e->value.termedBuf()));
837  if (!tok.skip('"') || !tok.skip(' '))
838  continue;
839 
840  // optional spaces; remember their number to accommodate MS servers
841  prependSpaces = 1 + tok.skipAll(CharacterSet::SP);
842 
843  SBuf cmd;
844  if (!tok.prefix(cmd, FeatNameChars))
845  continue;
846  cmd.toUpper();
847 
848  if (Ftp::SupportedCommand(cmd)) {
849  featReply->header.addEntry(e->clone());
850  }
851 
852  if (cmd == cmdEprt())
853  hasEPRT = true;
854  else if (cmd == cmdEpsv())
855  hasEPSV = true;
856  }
857  }
858  } // else we got a FEAT error and will only report Squid-supported features
859 
860  char buf[256];
861  if (!hasEPRT) {
862  snprintf(buf, sizeof(buf), "\"%*s\"", prependSpaces + 4, "EPRT");
863  featReply->header.putStr(Http::HdrType::FTP_PRE, buf);
864  }
865  if (!hasEPSV) {
866  snprintf(buf, sizeof(buf), "\"%*s\"", prependSpaces + 4, "EPSV");
867  featReply->header.putStr(Http::HdrType::FTP_PRE, buf);
868  }
869 
870  featReply->header.refreshMask();
871 
872  writeForwardedReply(featReply.getRaw());
873 }
874 
875 void
877 {
878  const Http::StreamPointer context(pipeline.front());
879  assert(context != nullptr);
880 
881  if (context->http->request->error) {
882  writeCustomReply(502, "Server does not support PASV", reply);
883  return;
884  }
885 
886  const unsigned short localPort = listenForDataConnection();
887  if (!localPort)
888  return;
889 
890  char addr[MAX_IPSTRLEN];
891  // remote server in interception setups and local address otherwise
892  const Ip::Address &server = transparent() ?
893  clientConnection->local : dataListenConn->local;
894  server.toStr(addr, MAX_IPSTRLEN, AF_INET);
895  addr[MAX_IPSTRLEN - 1] = '\0';
896  for (char *c = addr; *c != '\0'; ++c) {
897  if (*c == '.')
898  *c = ',';
899  }
900 
901  // In interception setups, we combine remote server address with a
902  // local port number and hope that traffic will be redirected to us.
903  // Do not use "227 =a,b,c,d,p1,p2" format or omit parens: some nf_ct_ftp
904  // versions block responses that use those alternative syntax rules!
905  MemBuf mb;
906  mb.init();
907  mb.appendf("227 Entering Passive Mode (%s,%i,%i).\r\n",
908  addr,
909  static_cast<int>(localPort / 256),
910  static_cast<int>(localPort % 256));
911  debugs(9, 3, Raw("writing", mb.buf, mb.size));
912  writeReply(mb);
913 }
914 
915 void
917 {
918  if (pipeline.front()->http->request->error) {
919  writeCustomReply(502, "Server does not support PASV (converted from PORT)", reply);
920  return;
921  }
922 
923  writeCustomReply(200, "PORT successfully converted to PASV.");
924 
925  // and wait for RETR
926 }
927 
928 void
930 {
931  if (!pinning.pinned) // we failed to connect to server
932  uri.clear();
933  // 421: we will close due to fssError
934  writeErrorReply(reply, 421);
935 }
936 
937 void
939 {
940  if (reply != NULL && reply->sline.status() != Http::scOkay) {
941  writeForwardedReply(reply);
942  if (Comm::IsConnOpen(dataConn)) {
943  debugs(33, 3, "closing " << dataConn << " on KO reply");
944  closeDataConnection();
945  }
946  return;
947  }
948 
949  if (!dataConn) {
950  // We got STREAM_COMPLETE (or error) and closed the client data conn.
951  debugs(33, 3, "ignoring FTP srv data response after clt data closure");
952  return;
953  }
954 
955  if (!checkDataConnPost()) {
956  writeCustomReply(425, "Data connection is not established.");
957  closeDataConnection();
958  return;
959  }
960 
961  debugs(33, 7, data.length);
962 
963  if (data.length <= 0) {
964  replyDataWritingCheckpoint(); // skip the actual write call
965  return;
966  }
967 
968  MemBuf mb;
969  mb.init(data.length + 1, data.length + 1);
970  mb.append(data.data, data.length);
971 
973  AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, Ftp::Server::wroteReplyData);
974  Comm::Write(dataConn, &mb, call);
975 
976  pipeline.front()->noteSentBodyBytes(data.length);
977 }
978 
980 void
982 {
983  if (io.flag == Comm::ERR_CLOSING)
984  return;
985 
986  if (io.flag != Comm::OK) {
987  debugs(33, 3, "FTP reply data writing failed: " << xstrerr(io.xerrno));
988  userDataCompletionCheckpoint(426);
989  return;
990  }
991 
992  assert(pipeline.front()->http);
993  pipeline.front()->http->out.size += io.size;
994  replyDataWritingCheckpoint();
995 }
996 
998 void
1000 {
1001  switch (pipeline.front()->socketState()) {
1002  case STREAM_NONE:
1003  debugs(33, 3, "Keep going");
1004  pipeline.front()->pullData();
1005  return;
1006  case STREAM_COMPLETE:
1007  debugs(33, 3, "FTP reply data transfer successfully complete");
1008  userDataCompletionCheckpoint(226);
1009  break;
1011  debugs(33, 3, "FTP reply data transfer failed: STREAM_UNPLANNED_COMPLETE");
1012  userDataCompletionCheckpoint(451);
1013  break;
1014  case STREAM_FAILED:
1015  userDataCompletionCheckpoint(451);
1016  debugs(33, 3, "FTP reply data transfer failed: STREAM_FAILED");
1017  break;
1018  default:
1019  fatal("unreachable code");
1020  }
1021 }
1022 
1023 void
1025 {
1026  writeForwardedReply(reply);
1027  // note that the client data connection may already be closed by now
1028 }
1029 
1030 void
1032 {
1033  Must(reply);
1034 
1035  if (waitingForOrigin) {
1036  Must(delayedReply == NULL);
1037  delayedReply = reply;
1038  return;
1039  }
1040 
1041  const HttpHeader &header = reply->header;
1042  // adaptation and forwarding errors lack Http::HdrType::FTP_STATUS
1043  if (!header.has(Http::HdrType::FTP_STATUS)) {
1044  writeForwardedForeign(reply); // will get to Ftp::Server::wroteReply
1045  return;
1046  }
1047 
1049  AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, Ftp::Server::wroteReply);
1050  writeForwardedReplyAndCall(reply, call);
1051 }
1052 
1053 void
1055 {
1056  if (pipeline.front()->http->request->error) {
1057  writeCustomReply(502, "Server does not support PASV (converted from EPRT)", reply);
1058  return;
1059  }
1060 
1061  writeCustomReply(200, "EPRT successfully converted to PASV.");
1062 
1063  // and wait for RETR
1064 }
1065 
1066 void
1068 {
1069  if (pipeline.front()->http->request->error) {
1070  writeCustomReply(502, "Cannot connect to server", reply);
1071  return;
1072  }
1073 
1074  const unsigned short localPort = listenForDataConnection();
1075  if (!localPort)
1076  return;
1077 
1078  // In interception setups, we use a local port number and hope that data
1079  // traffic will be redirected to us.
1080  MemBuf mb;
1081  mb.init();
1082  mb.appendf("229 Entering Extended Passive Mode (|||%u|)\r\n", localPort);
1083 
1084  debugs(9, 3, Raw("writing", mb.buf, mb.size));
1085  writeReply(mb);
1086 }
1087 
1089 void
1090 Ftp::Server::writeErrorReply(const HttpReply *reply, const int scode)
1091 {
1092  const HttpRequest *request = pipeline.front()->http->request;
1093  assert(request);
1094 
1095  MemBuf mb;
1096  mb.init();
1097 
1098  if (request->error)
1099  mb.appendf("%i-%s\r\n", scode, errorPageName(request->error.category));
1100 
1101  if (const auto &detail = request->error.detail) {
1102  mb.appendf("%i-Error-Detail-Brief: " SQUIDSBUFPH "\r\n", scode, SQUIDSBUFPRINT(detail->brief()));
1103  mb.appendf("%i-Error-Detail-Verbose: " SQUIDSBUFPH "\r\n", scode, SQUIDSBUFPRINT(detail->verbose(request)));
1104  }
1105 
1106 #if USE_ADAPTATION
1107  // XXX: Remove hard coded names. Use an error page template instead.
1108  const Adaptation::History::Pointer ah = request->adaptHistory();
1109  if (ah != NULL) { // XXX: add adapt::<all_h but use lastMeta here
1110  const String info = ah->allMeta.getByName("X-Response-Info");
1111  const String desc = ah->allMeta.getByName("X-Response-Desc");
1112  if (info.size())
1113  mb.appendf("%i-Information: %s\r\n", scode, info.termedBuf());
1114  if (desc.size())
1115  mb.appendf("%i-Description: %s\r\n", scode, desc.termedBuf());
1116  }
1117 #endif
1118 
1119  const char *reason = "Lost Error";
1120  if (reply) {
1121  reason = reply->header.has(Http::HdrType::FTP_REASON) ?
1123  reply->sline.reason();
1124  }
1125 
1126  mb.appendf("%i %s\r\n", scode, reason); // error terminating line
1127 
1128  // TODO: errorpage.cc should detect FTP client and use
1129  // configurable FTP-friendly error templates which we should
1130  // write to the client "as is" instead of hiding most of the info
1131 
1132  writeReply(mb);
1133 }
1134 
1137 void
1139 {
1140  changeState(fssConnected, "foreign reply");
1141  closeDataConnection();
1142  // 451: We intend to keep the control connection open.
1143  writeErrorReply(reply, 451);
1144 }
1145 
1146 bool
1148 {
1149  // the caller guarantees that we are dealing with the current context only
1150  // the caller should also make sure reply->header.has(Http::HdrType::FTP_STATUS)
1151  writeForwardedReplyAndCall(reply, call);
1152  return true;
1153 }
1154 
1155 void
1157 {
1158  assert(reply != NULL);
1159  const HttpHeader &header = reply->header;
1160 
1161  // without status, the caller must use the writeForwardedForeign() path
1164  const int scode = header.getInt(Http::HdrType::FTP_STATUS);
1165  debugs(33, 7, "scode: " << scode);
1166 
1167  // Status 125 or 150 implies upload or data request, but we still check
1168  // the state in case the server is buggy.
1169  if ((scode == 125 || scode == 150) &&
1170  (master->serverState == fssHandleUploadRequest ||
1171  master->serverState == fssHandleDataRequest)) {
1172  if (checkDataConnPost()) {
1173  // If the data connection is ready, start reading data (here)
1174  // and forward the response to client (further below).
1175  debugs(33, 7, "data connection established, start data transfer");
1176  if (master->serverState == fssHandleUploadRequest)
1177  maybeReadUploadData();
1178  } else {
1179  // If we are waiting to accept the data connection, keep waiting.
1180  if (Comm::IsConnOpen(dataListenConn)) {
1181  debugs(33, 7, "wait for the client to establish a data connection");
1182  onDataAcceptCall = call;
1183  // TODO: Add connect timeout for passive connections listener?
1184  // TODO: Remember server response so that we can forward it?
1185  } else {
1186  // Either the connection was establised and closed after the
1187  // data was transferred OR we failed to establish an active
1188  // data connection and already sent the error to the client.
1189  // In either case, there is nothing more to do.
1190  debugs(33, 7, "done with data OR active connection failed");
1191  }
1192  return;
1193  }
1194  }
1195 
1196  MemBuf mb;
1197  mb.init();
1198  Ftp::PrintReply(mb, reply);
1199 
1200  debugs(9, 2, "FTP Client " << clientConnection);
1201  debugs(9, 2, "FTP Client REPLY:\n---------\n" << mb.buf <<
1202  "\n----------");
1203 
1204  Comm::Write(clientConnection, &mb, call);
1205 }
1206 
1207 static void
1208 Ftp::PrintReply(MemBuf &mb, const HttpReply *reply, const char *const)
1209 {
1210  const HttpHeader &header = reply->header;
1211 
1213  while (const HttpHeaderEntry *e = header.getEntry(&pos)) {
1214  if (e->id == Http::HdrType::FTP_PRE) {
1215  String raw;
1216  if (httpHeaderParseQuotedString(e->value.rawBuf(), e->value.size(), &raw))
1217  mb.appendf("%s\r\n", raw.termedBuf());
1218  }
1219  }
1220 
1221  if (header.has(Http::HdrType::FTP_STATUS)) {
1222  const char *reason = header.getStr(Http::HdrType::FTP_REASON);
1223  mb.appendf("%i %s\r\n", header.getInt(Http::HdrType::FTP_STATUS),
1224  (reason ? reason : 0));
1225  }
1226 }
1227 
1228 void
1230 {
1231  if (io.flag == Comm::ERR_CLOSING)
1232  return;
1233 
1234  if (io.flag != Comm::OK) {
1235  debugs(33, 3, "FTP reply writing failed: " << xstrerr(io.xerrno));
1236  io.conn->close();
1237  return;
1238  }
1239 
1240  Http::StreamPointer context = pipeline.front();
1241  if (context != nullptr && context->http) {
1242  context->http->out.size += io.size;
1243  context->http->out.headers_sz += io.size;
1244  }
1245 
1246  flags.readMore = true;
1247  readSomeData();
1248 }
1249 
1250 void
1252 {
1253  if (io.flag == Comm::ERR_CLOSING)
1254  return;
1255 
1256  if (io.flag != Comm::OK) {
1257  debugs(33, 3, "FTP reply writing failed: " << xstrerr(io.xerrno));
1258  io.conn->close();
1259  return;
1260  }
1261 
1262  Http::StreamPointer context = pipeline.front();
1263  assert(context->http);
1264  context->http->out.size += io.size;
1265  context->http->out.headers_sz += io.size;
1266 
1267  if (master->serverState == fssError) {
1268  debugs(33, 5, "closing on FTP server error");
1269  io.conn->close();
1270  return;
1271  }
1272 
1273  const clientStream_status_t socketState = context->socketState();
1274  debugs(33, 5, "FTP client stream state " << socketState);
1275  switch (socketState) {
1277  case STREAM_FAILED:
1278  io.conn->close();
1279  return;
1280 
1281  case STREAM_NONE:
1282  case STREAM_COMPLETE:
1283  flags.readMore = true;
1284  changeState(fssConnected, "Ftp::Server::wroteReply");
1285  if (bodyParser)
1286  finishDechunkingRequest(false);
1287  context->finished();
1288  kick();
1289  return;
1290  }
1291 }
1292 
1293 bool
1295 {
1296  debugs(33, 9, request);
1297  Must(request);
1298 
1299  HttpHeader &header = request->header;
1304 
1305  if (Debug::Enabled(9, 2)) {
1306  MemBuf mb;
1307  mb.init();
1308  request->pack(&mb);
1309 
1310  debugs(9, 2, "FTP Client " << clientConnection);
1311  debugs(9, 2, "FTP Client REQUEST:\n---------\n" << mb.buf <<
1312  "\n----------");
1313  }
1314 
1315  // TODO: When HttpHeader uses SBuf, change keys to SBuf
1316  typedef std::map<const std::string, RequestHandler> RequestHandlers;
1317  static RequestHandlers handlers;
1318  if (!handlers.size()) {
1319  handlers["LIST"] = &Ftp::Server::handleDataRequest;
1320  handlers["NLST"] = &Ftp::Server::handleDataRequest;
1321  handlers["MLSD"] = &Ftp::Server::handleDataRequest;
1322  handlers["FEAT"] = &Ftp::Server::handleFeatRequest;
1323  handlers["PASV"] = &Ftp::Server::handlePasvRequest;
1324  handlers["PORT"] = &Ftp::Server::handlePortRequest;
1325  handlers["RETR"] = &Ftp::Server::handleDataRequest;
1326  handlers["EPRT"] = &Ftp::Server::handleEprtRequest;
1327  handlers["EPSV"] = &Ftp::Server::handleEpsvRequest;
1328  handlers["CWD"] = &Ftp::Server::handleCwdRequest;
1329  handlers["PASS"] = &Ftp::Server::handlePassRequest;
1330  handlers["CDUP"] = &Ftp::Server::handleCdupRequest;
1331  }
1332 
1333  RequestHandler handler = NULL;
1336  else {
1337  const RequestHandlers::const_iterator hi = handlers.find(cmd.termedBuf());
1338  if (hi != handlers.end())
1339  handler = hi->second;
1340  }
1341 
1342  if (!handler) {
1343  debugs(9, 7, "forwarding " << cmd << " as is, no post-processing");
1344  return true;
1345  }
1346 
1347  return (this->*handler)(cmd, params);
1348 }
1349 
1352 Http::Stream *
1354 {
1355  if (params.isEmpty())
1356  return earlyError(EarlyErrorKind::MissingUsername);
1357 
1358  // find the [end of] user name
1359  const SBuf::size_type eou = params.rfind('@');
1360  if (eou == SBuf::npos || eou + 1 >= params.length())
1361  return earlyError(EarlyErrorKind::MissingHost);
1362 
1363  // Determine the intended destination.
1364  host = params.substr(eou + 1, params.length());
1365  // If we can parse it as raw IPv6 address, then surround with "[]".
1366  // Otherwise (domain, IPv4, [bracketed] IPv6, garbage, etc), use as is.
1367  if (host.find(':') != SBuf::npos) {
1368  const Ip::Address ipa(host.c_str());
1369  if (!ipa.isAnyAddr()) {
1370  char ipBuf[MAX_IPSTRLEN];
1371  ipa.toHostStr(ipBuf, MAX_IPSTRLEN);
1372  host = ipBuf;
1373  }
1374  }
1375 
1376  // const SBuf login = params.substr(0, eou);
1377  params.chop(0, eou); // leave just the login part for the peer
1378 
1379  SBuf oldUri;
1380  if (master->clientReadGreeting)
1381  oldUri = uri;
1382 
1383  master->workingDir.clear();
1384  calcUri(NULL);
1385 
1386  if (!master->clientReadGreeting) {
1387  debugs(9, 3, "set URI to " << uri);
1388  } else if (oldUri.caseCmp(uri) == 0) {
1389  debugs(9, 5, "kept URI as " << oldUri);
1390  } else {
1391  debugs(9, 3, "reset URI from " << oldUri << " to " << uri);
1392  closeDataConnection();
1393  unpinConnection(true); // close control connection to peer
1394  resetLogin("URI reset");
1395  }
1396 
1397  return NULL; // no early errors
1398 }
1399 
1400 bool
1402 {
1403  changeState(fssHandleFeat, "handleFeatRequest");
1404  return true;
1405 }
1406 
1407 bool
1409 {
1410  if (gotEpsvAll) {
1411  setReply(500, "Bad PASV command");
1412  return false;
1413  }
1414 
1415  if (params.size() > 0) {
1416  setReply(501, "Unexpected parameter");
1417  return false;
1418  }
1419 
1420  changeState(fssHandlePasv, "handlePasvRequest");
1421  // no need to fake PASV request via setDataCommand() in true PASV case
1422  return true;
1423 }
1424 
1426 bool
1428 {
1429  assert(clientConnection != NULL);
1430  assert(!clientConnection->remote.isAnyAddr());
1431 
1432  if (cltAddr != clientConnection->remote) {
1433  debugs(33, 2, "rogue PORT " << cltAddr << " request? ctrl: " << clientConnection->remote);
1434  // Closing the control connection would not help with attacks because
1435  // the client is evidently able to connect to us. Besides, closing
1436  // makes retrials easier for the client and more damaging to us.
1437  setReply(501, "Prohibited parameter value");
1438  return false;
1439  }
1440 
1441  closeDataConnection();
1442 
1444  conn->flags |= COMM_DOBIND;
1445 
1446  if (clientConnection->flags & COMM_INTERCEPTION) {
1447  // In the case of NAT interception conn->local value is not set
1448  // because the TCP stack will automatically pick correct source
1449  // address for the data connection. We must only ensure that IP
1450  // version matches client's address.
1451  conn->local.setAnyAddr();
1452 
1453  if (cltAddr.isIPv4())
1454  conn->local.setIPv4();
1455 
1456  conn->remote = cltAddr;
1457  } else {
1458  // In the case of explicit-proxy the local IP of the control connection
1459  // is the Squid IP the client is knowingly talking to.
1460  //
1461  // In the case of TPROXY the IP address of the control connection is
1462  // server IP the client is connecting to, it can be spoofed by Squid.
1463  //
1464  // In both cases some clients may refuse to accept data connections if
1465  // these control connectin local-IP's are not used.
1466  conn->setAddrs(clientConnection->local, cltAddr);
1467 
1468  // Using non-local addresses in TPROXY mode requires appropriate socket option.
1469  if (clientConnection->flags & COMM_TRANSPARENT)
1470  conn->flags |= COMM_TRANSPARENT;
1471  }
1472 
1473  // RFC 959 requires active FTP connections to originate from port 20
1474  // but that would preclude us from supporting concurrent transfers! (XXX?)
1475  conn->local.port(0);
1476 
1477  debugs(9, 3, "will actively connect from " << conn->local << " to " <<
1478  conn->remote);
1479 
1480  dataConn = conn;
1481  uploadAvailSize = 0;
1482  return true;
1483 }
1484 
1485 bool
1487 {
1488  // TODO: Should PORT errors trigger closeDataConnection() cleanup?
1489 
1490  if (gotEpsvAll) {
1491  setReply(500, "Rejecting PORT after EPSV ALL");
1492  return false;
1493  }
1494 
1495  if (!params.size()) {
1496  setReply(501, "Missing parameter");
1497  return false;
1498  }
1499 
1500  Ip::Address cltAddr;
1501  if (!Ftp::ParseIpPort(params.termedBuf(), NULL, cltAddr)) {
1502  setReply(501, "Invalid parameter");
1503  return false;
1504  }
1505 
1506  if (!createDataConnection(cltAddr))
1507  return false;
1508 
1509  changeState(fssHandlePort, "handlePortRequest");
1510  setDataCommand();
1511  return true; // forward our fake PASV request
1512 }
1513 
1514 bool
1516 {
1517  if (!checkDataConnPre())
1518  return false;
1519 
1520  master->userDataDone = 0;
1521  originDataDownloadAbortedOnError = false;
1522 
1523  changeState(fssHandleDataRequest, "handleDataRequest");
1524 
1525  return true;
1526 }
1527 
1528 bool
1530 {
1531  if (!checkDataConnPre())
1532  return false;
1533 
1535  ClientHttpRequest *http = pipeline.front()->http;
1536  HttpRequest *request = http->request;
1538  bodyContinuationCheck.al = http->al;
1539  bodyContinuationCheck.syncAle(request, http->log_uri);
1540  if (bodyContinuationCheck.fastCheck().allowed()) {
1541  request->forcedBodyContinuation = true;
1542  if (checkDataConnPost()) {
1543  // Write control Msg
1544  writeEarlyReply(150, "Data connection opened");
1545  maybeReadUploadData();
1546  } else {
1547  // wait for acceptDataConnection but tell it to call wroteEarlyReply
1548  // after writing "150 Data connection opened"
1550  AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, Ftp::Server::wroteEarlyReply);
1551  onDataAcceptCall = call;
1552  }
1553  }
1554  }
1555 
1556  changeState(fssHandleUploadRequest, "handleDataRequest");
1557 
1558  return true;
1559 }
1560 
1561 bool
1563 {
1564  debugs(9, 3, "Process an EPRT " << params);
1565 
1566  if (gotEpsvAll) {
1567  setReply(500, "Rejecting EPRT after EPSV ALL");
1568  return false;
1569  }
1570 
1571  if (!params.size()) {
1572  setReply(501, "Missing parameter");
1573  return false;
1574  }
1575 
1576  Ip::Address cltAddr;
1577  if (!Ftp::ParseProtoIpPort(params.termedBuf(), cltAddr)) {
1578  setReply(501, "Invalid parameter");
1579  return false;
1580  }
1581 
1582  if (!createDataConnection(cltAddr))
1583  return false;
1584 
1585  changeState(fssHandleEprt, "handleEprtRequest");
1586  setDataCommand();
1587  return true; // forward our fake PASV request
1588 }
1589 
1590 bool
1592 {
1593  debugs(9, 3, "Process an EPSV command with params: " << params);
1594  if (params.size() <= 0) {
1595  // treat parameterless EPSV as "use the protocol of the ctrl conn"
1596  } else if (params.caseCmp("ALL") == 0) {
1597  setReply(200, "EPSV ALL ok");
1598  gotEpsvAll = true;
1599  return false;
1600  } else if (params.cmp("2") == 0) {
1601  if (!Ip::EnableIpv6) {
1602  setReply(522, "Network protocol not supported, use (1)");
1603  return false;
1604  }
1605  } else if (params.cmp("1") != 0) {
1606  setReply(501, "Unsupported EPSV parameter");
1607  return false;
1608  }
1609 
1610  changeState(fssHandleEpsv, "handleEpsvRequest");
1611  setDataCommand();
1612  return true; // forward our fake PASV request
1613 }
1614 
1615 bool
1617 {
1618  changeState(fssHandleCwd, "handleCwdRequest");
1619  return true;
1620 }
1621 
1622 bool
1624 {
1625  changeState(fssHandlePass, "handlePassRequest");
1626  return true;
1627 }
1628 
1629 bool
1631 {
1632  changeState(fssHandleCdup, "handleCdupRequest");
1633  return true;
1634 }
1635 
1636 // Convert user PORT, EPRT, PASV, or EPSV data command to Squid PASV command.
1637 // Squid FTP client decides what data command to use with peers.
1638 void
1640 {
1641  ClientHttpRequest *const http = pipeline.front()->http;
1642  assert(http != NULL);
1643  HttpRequest *const request = http->request;
1644  assert(request != NULL);
1645  HttpHeader &header = request->header;
1647  header.putStr(Http::HdrType::FTP_COMMAND, "PASV");
1650  debugs(9, 5, "client data command converted to fake PASV");
1651 }
1652 
1655 bool
1657 {
1658  if (Comm::IsConnOpen(dataConn))
1659  return true;
1660 
1661  if (Comm::IsConnOpen(dataListenConn)) {
1662  // We are still waiting for a client to connect to us after PASV.
1663  // Perhaps client's data conn handshake has not reached us yet.
1664  // After we talk to the server, checkDataConnPost() will recheck.
1665  debugs(33, 3, "expecting clt data conn " << dataListenConn);
1666  return true;
1667  }
1668 
1669  if (!dataConn || dataConn->remote.isAnyAddr()) {
1670  debugs(33, 5, "missing " << dataConn);
1671  // TODO: use client address and default port instead.
1672  setReply(425, "Use PORT or PASV first");
1673  return false;
1674  }
1675 
1676  // active transfer: open a data connection from Squid to client
1678  AsyncCall::Pointer callback = JobCallback(17, 3, Dialer, this, Ftp::Server::connectedForData);
1679  const auto cs = new Comm::ConnOpener(dataConn->cloneProfile(), callback,
1681  dataConnWait.start(cs, callback);
1682  return false;
1683 }
1684 
1686 bool
1688 {
1689  if (!Comm::IsConnOpen(dataConn)) {
1690  debugs(33, 3, "missing client data conn: " << dataConn);
1691  return false;
1692  }
1693  return true;
1694 }
1695 
1697 void
1699 {
1700  dataConnWait.finish();
1701 
1702  if (params.flag != Comm::OK) {
1703  setReply(425, "Cannot open data connection.");
1704  Http::StreamPointer context = pipeline.front();
1705  Must(context->http);
1706  Must(context->http->storeEntry() != NULL);
1707  // TODO: call closeDataConnection() to reset data conn processing?
1708  } else {
1709  // Finalize the details and start owning the supplied connection.
1710  assert(params.conn);
1711  assert(dataConn);
1712  assert(!dataConn->isOpen());
1713  dataConn = params.conn;
1714  // XXX: Missing comm_add_close_handler() to track external closures.
1715 
1716  Must(Comm::IsConnOpen(params.conn));
1717  fd_note(params.conn->fd, "active client ftp data");
1718  }
1719 
1720  doProcessRequest();
1721 }
1722 
1723 void
1724 Ftp::Server::setReply(const int code, const char *msg)
1725 {
1726  Http::StreamPointer context = pipeline.front();
1727  ClientHttpRequest *const http = context->http;
1728  assert(http != NULL);
1729  assert(http->storeEntry() == NULL);
1730 
1731  HttpReply *const reply = Ftp::HttpReplyWrapper(code, msg, Http::scNoContent, 0);
1732 
1733  clientStreamNode *const node = context->getClientReplyContext();
1734  clientReplyContext *const repContext =
1735  dynamic_cast<clientReplyContext *>(node->data.getRaw());
1736  assert(repContext != NULL);
1737 
1738  RequestFlags reqFlags;
1739  reqFlags.cachable = false; // force releaseRequest() in storeCreateEntry()
1740  reqFlags.noCache = true;
1741  repContext->createStoreEntry(http->request->method, reqFlags);
1742  http->storeEntry()->replaceHttpReply(reply);
1743 }
1744 
1745 void
1746 Ftp::Server::callException(const std::exception &e)
1747 {
1748  debugs(33, 2, "FTP::Server job caught: " << e.what());
1749  closeDataConnection();
1750  unpinConnection(true);
1751  if (Comm::IsConnOpen(clientConnection))
1752  clientConnection->close();
1754 }
1755 
1756 void
1758 {
1759  if (!isOpen()) // if we are closing, nothing to do
1760  return;
1761 
1762  debugs(33, 5, "waiting for Ftp::Client data transfer to end");
1763  waitingForOrigin = true;
1764 }
1765 
1766 void
1768 {
1769  Must(waitingForOrigin);
1770  waitingForOrigin = false;
1771 
1772  if (!isOpen()) // if we are closing, nothing to do
1773  return;
1774 
1775  // if we have already decided how to respond, respond now
1776  if (delayedReply) {
1777  HttpReply::Pointer reply = delayedReply;
1778  delayedReply = nullptr;
1779  writeForwardedReply(reply.getRaw());
1780  return; // do not completeDataDownload() after an earlier response
1781  }
1782 
1783  if (master->serverState != fssHandleDataRequest)
1784  return;
1785 
1786  // completeDataDownload() could be waitingForOrigin in fssHandleDataRequest
1787  // Depending on which side has finished downloading first, either trust
1788  // master->userDataDone status or set originDataDownloadAbortedOnError:
1789  if (master->userDataDone) {
1790  // We finished downloading before Ftp::Client. Most likely, the
1791  // adaptation shortened the origin response or we hit an error.
1792  // Our status (stored in master->userDataDone) is more informative.
1793  // Use master->userDataDone; avoid originDataDownloadAbortedOnError.
1794  completeDataDownload();
1795  } else {
1796  debugs(33, 5, "too early to write the response");
1797  // Ftp::Client naturally finished downloading before us. Set
1798  // originDataDownloadAbortedOnError to overwrite future
1799  // master->userDataDone and relay Ftp::Client error, if there was
1800  // any, to the user.
1801  originDataDownloadAbortedOnError = (originStatus >= 400);
1802  }
1803 }
1804 
1806 {
1807  Must(!master->userDataDone);
1808  master->userDataDone = finalStatusCode;
1809 
1810  if (bodyParser)
1811  finishDechunkingRequest(false);
1812 
1813  if (waitingForOrigin) {
1814  // The completeDataDownload() is not called here unconditionally
1815  // because we want to signal the FTP user that we are not fully
1816  // done processing its data stream, even though all data bytes
1817  // have been sent or received already.
1818  debugs(33, 5, "Transferring from FTP server is not complete");
1819  return;
1820  }
1821 
1822  // Adjust our reply if the server aborted with an error before we are done.
1823  if (master->userDataDone == 226 && originDataDownloadAbortedOnError) {
1824  debugs(33, 5, "Transferring from FTP server terminated with an error, adjust status code");
1825  master->userDataDone = 451;
1826  }
1827  completeDataDownload();
1828 }
1829 
1831 {
1832  writeCustomReply(master->userDataDone, master->userDataDone == 226 ? "Transfer complete" : "Server error; transfer aborted");
1833  closeDataConnection();
1834 }
1835 
1837 static bool
1839 {
1840  static std::set<SBuf> BlockList;
1841  if (BlockList.empty()) {
1842  /* Add FTP commands that Squid cannot relay correctly. */
1843 
1844  // We probably do not support AUTH TLS.* and AUTH SSL,
1845  // but let's disclaim all AUTH support to KISS, for now.
1846  BlockList.insert(cmdAuth());
1847  }
1848 
1849  // we claim support for all commands that we do not know about
1850  return BlockList.find(name) == BlockList.end();
1851 }
1852 
bool handleEprtRequest(String &cmd, String &params)
Definition: FtpServer.cc:1562
int caseCmp(char const *) const
Definition: String.cc:285
struct SquidConfig::@96 Timeout
void fatal(const char *message)
Definition: fatal.cc:28
const char * xstrerr(int error)
Definition: xstrerror.cc:83
Definition: parse.c:104
virtual void noteBodyConsumerAborted(BodyPipe::Pointer)=0
char method[16]
Definition: tcp-banger2.c:115
void refreshMask()
Definition: HttpHeader.cc:751
@ fssHandleCdup
Definition: FtpServer.h:34
void wroteEarlyReply(const CommIoCbParams &io)
Definition: FtpServer.cc:1229
char * buf
Definition: MemBuf.h:134
virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io)
Our close handler called by Comm when the pinned connection is closed.
Http::Stream * handleUserRequest(const SBuf &cmd, SBuf &params)
Definition: FtpServer.cc:1353
SQUIDCEXTERN CSR clientGetMoreData
Definition: client_side.h:528
static HttpRequest * FromUrl(const SBuf &url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:518
void completeDataDownload()
Definition: FtpServer.cc:1830
const SBuf & cmdEpsv()
Definition: Elements.cc:91
CSCB clientSocketRecipient
Definition: client_side.h:531
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
#define DBG_CRITICAL
Definition: Stream.h:40
SQUIDCEXTERN CSS clientReplyStatus
Definition: client_side.h:529
HttpHeader allMeta
All REQMOD and RESPMOD meta headers merged. Last field wins conflicts.
Definition: History.h:63
time_t connect
Definition: SquidConfig.h:113
@ scBadRequest
Definition: StatusCode.h:44
AnyP::ProtocolVersion ProtocolVersion()
Protocol version to use in Http::Message structures wrapping FTP messages.
Definition: Elements.cc:24
const char * getStr(Http::HdrType id) const
Definition: HttpHeader.cc:1192
void handleDataReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:938
mb_size_t size
Definition: MemBuf.h:135
void fd_note(int fd, const char *s)
Definition: fd.cc:216
HttpHeader header
Definition: Message.h:75
bool ParseProtoIpPort(const char *buf, Ip::Address &addr)
Definition: Parsing.cc:52
bool isEmpty() const
Definition: SBuf.h:431
void initRequest(HttpRequest *)
Transaction information shared among our FTP client and server jobs.
Definition: FtpServer.h:42
static bool SupportedCommand(const SBuf &name)
Whether Squid FTP Relay supports a named feature (e.g., a command).
Definition: FtpServer.cc:1838
ssize_t HttpHeaderPos
Definition: HttpHeader.h:45
AnyP::PortCfgPointer port
the configuration listening port this call relates to (may be nil)
Definition: CommCalls.h:105
@ fssHandleUploadRequest
Definition: FtpServer.h:29
static void handler(int signo)
Definition: purge.cc:854
void createStoreEntry(const HttpRequestMethod &m, RequestFlags flags)
static void PrintReply(MemBuf &mb, const HttpReply *reply, const char *const prefix="")
Definition: FtpServer.cc:1208
@ STREAM_NONE
Definition: enums.h:126
static void AcceptCtrlConnection(const CommAcceptCbParams &params)
accept a new FTP control connection and hand it to a dedicated Server
Definition: FtpServer.cc:242
bool isAnyAddr() const
Definition: Address.cc:170
@ fssBegin
Definition: FtpServer.h:23
#define HttpHeaderInitPos
Definition: HttpHeader.h:48
void clientStartListeningOn(AnyP::PortCfgPointer &port, const RefCount< CommCbFunPtrCallT< CommAcceptCbPtrFun > > &subCall, const Ipc::FdNoteId fdNote)
accept requests to a given port and inform subCall about them
const SBuf & cmdMlst()
Definition: Elements.cc:119
char reqbuf[HTTP_REQBUF_SZ]
Definition: Stream.h:134
const SBuf & cmdAppe()
Definition: Elements.cc:56
virtual int pipelinePrefetchMax() const override
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
Definition: FtpServer.cc:80
@ fssHandleCwd
Definition: FtpServer.h:32
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
CBDATA_NAMESPACED_CLASS_INIT(Ftp, Server)
Definition: SBuf.h:94
bool handlePortRequest(String &cmd, String &params)
Definition: FtpServer.cc:1486
void closeDataConnection()
Definition: FtpServer.cc:439
void writeForwardedForeign(const HttpReply *reply)
Definition: FtpServer.cc:1138
const SBuf & cmdRmd()
Definition: Elements.cc:140
HttpReply * HttpReplyWrapper(const int ftpStatus, const char *ftpReason, const Http::StatusCode httpStatus, const int64_t clen)
Create an internal HttpReply structure to house FTP control response info.
Definition: Elements.cc:30
bool handleCwdRequest(String &cmd, String &params)
Definition: FtpServer.cc:1616
CharacterSet complement(const char *complementLabel=nullptr) const
Definition: CharacterSet.cc:74
#define xstrdup
const char * errorPageName(int pageId)
error ID to string
Definition: errorpage.cc:663
Definition: Server.h:30
const SBuf & cmdStat()
Definition: Elements.cc:168
bool handleFeatRequest(String &cmd, String &params)
Definition: FtpServer.cc:1401
CSD clientSocketDetach
Definition: client_side.h:532
int cmp(char const *) const
Definition: String.cc:255
void wroteReplyData(const CommIoCbParams &io)
called when we are done writing a chunk of the response data
Definition: FtpServer.cc:981
C * getRaw() const
Definition: RefCount.h:80
virtual void append(const char *c, int sz)
Definition: MemBuf.cc:209
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
void doProcessRequest()
react to the freshly parsed request
Definition: FtpServer.cc:128
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
@ OK
Definition: Flag.h:16
void handleFeatReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:807
static const CharacterSet LF
Definition: CharacterSet.h:92
struct ConnStateData::@38 flags
Http::StatusLine sline
Definition: HttpReply.h:56
void StopListening()
reject new connections to any configured ftp_port
Definition: FtpServer.cc:284
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1683
@ STREAM_COMPLETE
Definition: enums.h:127
bool isIPv4() const
Definition: Address.cc:158
void writeForwardedReply(const HttpReply *reply)
Definition: FtpServer.cc:1031
char uploadBuf[CLIENT_REQ_BUF_SZ]
data connection input buffer
Definition: FtpServer.h:189
ServerState
Definition: FtpServer.h:22
void StartListening()
accept connections on all configured ftp_ports
Definition: FtpServer.cc:265
@ ERR_CLOSING
Definition: Flag.h:25
bool handlePasvRequest(String &cmd, String &params)
Definition: FtpServer.cc:1408
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
void handleEprtReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:1054
SBuf substr(size_type pos, size_type n=npos) const
Definition: SBuf.cc:576
void wroteReply(const CommIoCbParams &io)
Definition: FtpServer.cc:1251
void clear()
Definition: SBuf.cc:175
#define COMM_NONBLOCKING
Definition: Connection.h:46
void shovelUploadData()
shovel upload data from the internal buffer to the body pipe if possible
Definition: FtpServer.cc:202
struct Http::Stream::@77 flags
@ STREAM_FAILED
Definition: enums.h:137
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:59
static bool Enabled(const int section, const int level)
whether debugging the given section and the given level produces output
Definition: Stream.h:79
SBuf & chop(size_type pos, size_type n=npos)
Definition: SBuf.cc:530
Definition: forward.h:28
virtual void start() override
called by AsyncStart; do not call directly
Definition: FtpServer.cc:92
bool createDataConnection(Ip::Address cltAddr)
[Re]initializes dataConn for active data transfers. Does not connect.
Definition: FtpServer.cc:1427
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
#define COMM_INTERCEPTION
Definition: Connection.h:51
Server(const MasterXaction::Pointer &xact)
Definition: FtpServer.cc:53
void ReadCancel(int fd, AsyncCall::Pointer &callback)
Cancel the read pending on FD. No action if none pending.
Definition: Read.cc:219
bool checkDataConnPre()
Definition: FtpServer.cc:1656
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note)
Definition: comm.cc:233
virtual time_t idleTimeout() const override
timeout to use when waiting for the next request
Definition: FtpServer.cc:86
Definition: Raw.h:21
size_type rfind(char c, size_type endPos=npos) const
Definition: SBuf.cc:692
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer &callback)
Definition: Read.h:59
void toUpper()
converts all characters to upper case;
Definition: SBuf.cc:824
#define COMM_TRANSPARENT
Definition: Connection.h:50
unsigned int toHostStr(char *buf, const unsigned int len) const
Definition: Address.cc:842
void setReply(const int code, const char *msg)
Definition: FtpServer.cc:1724
bool handleDataRequest(String &cmd, String &params)
Definition: FtpServer.cc:1515
void replyDataWritingCheckpoint()
ClientStream checks after (actual or skipped) reply data writing.
Definition: FtpServer.cc:999
ByteCounter kbytes_in
Definition: StatCounters.h:45
void writeErrorReply(const HttpReply *reply, const int status)
writes FTP error response with given status and reply-derived error details
Definition: FtpServer.cc:1090
@ fssConnected
Definition: FtpServer.h:24
void leaveOrphanage()
resume relying on owner(s) to initiate an explicit connection closure
Definition: Connection.h:90
void setDataCommand()
Definition: FtpServer.cc:1639
#define NULL
Definition: types.h:166
const SBuf & cmdRnfr()
Definition: Elements.cc:147
static bool CommandHasPathParameter(const SBuf &cmd)
whether the given FTP command has a pathname parameter
Definition: FtpServer.cc:538
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
struct StatCounters::@127 client_http
void calcUri(const SBuf *file)
computes uri member from host and, if tracked, working dir with file name
Definition: FtpServer.cc:339
const SBuf & cmdList()
Definition: Elements.cc:98
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
MemBlob::size_type size_type
Definition: SBuf.h:96
void writeReply(MemBuf &mb)
Definition: FtpServer.cc:489
virtual void notePeerConnection(Comm::ConnectionPointer conn) override
called just before a FwdState-dispatched job starts using connection
Definition: FtpServer.cc:296
int httpHeaderParseQuotedString(const char *start, const int len, String *val)
virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData) override
Definition: FtpServer.cc:773
void setReplyToReply(HttpReply *reply)
creates a store entry for the reply and appends error reply to it
Definition: MemBuf.h:24
@ STREAM_UNPLANNED_COMPLETE
Definition: enums.h:132
void acceptDataConnection(const CommAcceptCbParams &params)
Definition: FtpServer.cc:395
clientStreamNode * getClientReplyContext() const
Definition: Stream.cc:513
bool handleCdupRequest(String &cmd, String &params)
Definition: FtpServer.cc:1630
unsigned char code
Definition: html_quote.c:20
@ fssHandlePort
Definition: FtpServer.h:27
void maybeReadUploadData()
schedules another data connection read if needed
Definition: FtpServer.cc:109
const SBuf & cmdUser()
Definition: Elements.cc:189
int delById(Http::HdrType id)
Definition: HttpHeader.cc:695
unsigned short comm_local_port(int fd)
Definition: comm.cc:161
const SBuf & cmdRnto()
Definition: Elements.cc:154
void handleUploadReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:1024
Http::Stream * earlyError(const EarlyErrorKind eek)
creates a context filled with an error message for a given early error
Definition: FtpServer.cc:564
void writeCustomReply(const int code, const char *msg, const HttpReply *reply=NULL)
Definition: FtpServer.cc:501
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:58
virtual void noteBodyConsumerAborted(BodyPipe::Pointer ptr) override
Definition: FtpServer.cc:231
const SBuf & cmdCwd()
Definition: Elements.cc:70
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
void addEntry(HttpHeaderEntry *e)
Definition: HttpHeader.cc:765
void stopWaitingForOrigin(int status)
Definition: FtpServer.cc:1767
Ip::Address remote
Definition: Connection.h:147
AnyP::PortCfgPointer FtpPortList
list of Squid ftp_port configured
Definition: PortCfg.cc:23
CommCbFunPtrCallT< Dialer > * commCbCall(int debugSection, int debugLevel, const char *callName, const Dialer &dialer)
Definition: CommCalls.h:342
void userDataCompletionCheckpoint(int finalStatusCode)
Definition: FtpServer.cc:1805
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:19
char const * termedBuf() const
Definition: SquidString.h:92
SSL Connection
Definition: Session.h:45
@ fdnFtpSocket
Definition: FdNotes.h:20
virtual void callException(const std::exception &e) override
called when the job throws during an async call
Definition: FtpServer.cc:1746
int getInt(Http::HdrType id) const
Definition: HttpHeader.cc:1149
virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const
assigns uninitialized adapted_request and url ALE components
@ METHOD_PUT
Definition: MethodType.h:27
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
@ TRANSFER_ENCODING
#define Assure(condition)
Definition: Assure.h:35
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
@ fssHandlePasv
Definition: FtpServer.h:26
SBuf & trim(const SBuf &toRemove, bool atBeginning=true, bool atEnd=true)
Definition: SBuf.cc:551
const char * c_str()
Definition: SBuf.cc:516
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:33
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
size_t maxRequestHeaderSize
Definition: SquidConfig.h:132
Manages a control connection from an FTP client.
Definition: FtpServer.h:58
virtual ~Server() override
Definition: FtpServer.cc:74
static const size_type npos
Definition: SBuf.h:99
virtual void start()
called by AsyncStart; do not call directly
@ fssHandleEpsv
Definition: FtpServer.h:31
void clientSetKeepaliveFlag(ClientHttpRequest *http)
decide whether to expect multiple requests on the corresponding connection
Definition: client_side.cc:687
@ fssHandlePass
Definition: FtpServer.h:33
const SBuf & cmdRetr()
Definition: Elements.cc:133
const SBuf & cmdStou()
Definition: Elements.cc:182
unsigned int listenForDataConnection()
Definition: FtpServer.cc:364
#define COMM_DOBIND
Definition: Connection.h:49
void clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
String getByName(const SBuf &name) const
Definition: HttpHeader.cc:902
@ fssError
Definition: FtpServer.h:35
const SBuf & cmdNlst()
Definition: Elements.cc:126
unsigned parsed_ok
Was this parsed correctly?
Definition: Stream.h:137
HttpRequestMethod method
Definition: HttpRequest.h:114
acl_access * forceRequestBodyContinuation
Definition: SquidConfig.h:410
struct SquidConfig::@111 accessList
void handlePasvReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:876
HttpHeaderEntry * findEntry(Http::HdrType id) const
Definition: HttpHeader.cc:631
const char * reason() const
retrieve the reason string for this status line
Definition: StatusLine.cc:44
const SBuf & cmdAuth()
Definition: Elements.cc:63
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
bool allowed() const
Definition: Acl.h:149
bool handlePassRequest(String &cmd, String &params)
Definition: FtpServer.cc:1623
void readUploadData(const CommIoCbParams &io)
imports more upload data from the data connection
Definition: FtpServer.cc:168
Definition: parse.c:160
@ scNoContent
Definition: StatusCode.h:30
const SBuf & cmdMlsd()
Definition: Elements.cc:112
int has(Http::HdrType id) const
Definition: HttpHeader.cc:991
static char server[MAXLINE]
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1052
size_type size() const
Definition: SquidString.h:73
const SBuf & cmdSmnt()
Definition: Elements.cc:161
@ fssHandleEprt
Definition: FtpServer.h:30
clientStream_status_t
Definition: enums.h:125
#define Must(condition)
Definition: TextException.h:71
bool checkDataConnPost() const
Check that client data connection is ready for immediate I/O.
Definition: FtpServer.cc:1687
void writeEarlyReply(const int code, const char *msg)
Definition: FtpServer.cc:470
size_t req_sz
raw request size on input, not current request size
virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) override
handle a control message received by context from a peer and call back
Definition: FtpServer.cc:1147
#define DBG_IMPORTANT
Definition: Stream.h:41
virtual void processParsedRequest(Http::StreamPointer &context) override
start processing a freshly parsed request
Definition: FtpServer.cc:156
const AccessLogEntry::Pointer al
access.log entry
SQUIDCEXTERN CSD clientReplyDetach
Definition: client_side.h:530
void clientStreamInit(dlink_list *list, CSR *func, CSD *rdetach, CSS *readstatus, ClientStreamData readdata, CSCB *callback, CSD *cdetach, ClientStreamData callbackdata, StoreIOBuffer tailBuffer)
void writeForwardedReplyAndCall(const HttpReply *reply, AsyncCall::Pointer &call)
Definition: FtpServer.cc:1156
void handleErrorReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:929
void handleEpsvReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:1067
#define HTTP_REQBUF_SZ
Definition: forward.h:14
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:18
@ fssHandleDataRequest
Definition: FtpServer.h:28
int caseCmp(const SBuf &S, const size_type n) const
shorthand version for case-insensitive compare()
Definition: SBuf.h:283
int NHttpSockets
Definition: PortCfg.cc:25
StoreEntry * storeEntry() const
virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io) override
Our close handler called by Comm when the pinned connection is closed.
Definition: FtpServer.cc:310
bool handleUploadRequest(String &cmd, String &params)
Definition: FtpServer.cc:1529
void changeState(const Ftp::ServerState newState, const char *reason)
Definition: FtpServer.cc:523
@ scOkay
Definition: StatusCode.h:26
HttpHeaderEntry * getEntry(HttpHeaderPos *pos) const
Definition: HttpHeader.cc:612
bool isOpen(const int fd)
Definition: comm.cc:85
virtual Http::Stream * parseOneRequest() override
Definition: FtpServer.cc:634
virtual void callException(const std::exception &e)
called when the job throws during an async call
Definition: AsyncJob.cc:128
@ METHOD_GET
Definition: MethodType.h:25
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
#define false
Definition: GnuRegex.c:233
void connectedForData(const CommConnectCbParams &params)
Done establishing a data connection to the user.
Definition: FtpServer.cc:1698
A const & min(A const &lhs, A const &rhs)
virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer) override
Definition: FtpServer.cc:222
static const CharacterSet SP
Definition: CharacterSet.h:94
#define MAXTCPLISTENPORTS
Definition: PortCfg.h:87
const SBuf & cmdStor()
Definition: Elements.cc:175
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void startWaitingForOrigin()
Definition: FtpServer.cc:1757
void resetLogin(const char *reason)
clear client and server login-related state after the old login is gone
Definition: FtpServer.cc:330
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
const SBuf & cmdDele()
Definition: Elements.cc:77
bool handleEpsvRequest(String &cmd, String &params)
Definition: FtpServer.cc:1591
#define SQUIDSBUFPH
Definition: SBuf.h:31
const SBuf & cmdMkd()
Definition: Elements.cc:105
const SBuf & cmdEprt()
Definition: Elements.cc:84
class SquidConfig Config
Definition: SquidConfig.cc:12
@ fssHandleFeat
Definition: FtpServer.h:25
HttpRequest *const request
void handlePortReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:916
StatCounters statCounter
Definition: StatCounters.cc:12
bool handleRequest(HttpRequest *)
Definition: FtpServer.cc:1294
static void Start(const Pointer &job)
Definition: AsyncJob.cc:24
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
time_t ftpClientIdle
Definition: SquidConfig.h:119
static Pointer MakePortful(const AnyP::PortCfgPointer &aPort)
Definition: MasterXaction.h:54

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors