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

 

Introduction

Documentation

Support

Miscellaneous