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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors