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"
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
46namespace Ftp
47{
48static void PrintReply(MemBuf &mb, const HttpReply *reply, const char *const prefix = "");
49static bool SupportedCommand(const SBuf &name);
50static 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
79int
81{
82 return 0; // no support for concurrent FTP requests
83}
84
85time_t
87{
89}
90
91void
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
108void
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
127void
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
155void
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
167void
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
201void
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
221void
223{
224 if (!isOpen()) // if we are closing, nothing to do
225 return;
226
227 shovelUploadData();
228}
229
230void
232{
233 if (!isOpen()) // if we are closing, nothing to do
234 return;
235
237 closeDataConnection();
238}
239
241void
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
264void
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
286void
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
301void
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
315void
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
335void
336Ftp::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
344void
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
369unsigned 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
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
400void
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
444void
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
475void
476Ftp::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
487 Comm::Write(clientConnection, &mb, call);
488
489 flags.readMore = false;
490
491 // TODO: Create master transaction. Log it in wroteEarlyReply().
492}
493
494void
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
506void
507Ftp::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
528void
529Ftp::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
543static 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
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
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
777void
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
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
811void
813{
814 if (pipeline.front()->http->request->error) {
815 writeCustomReply(502, "Server does not support FEAT", reply);
816 return;
817 }
818
819 Must(reply);
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
880void
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
920void
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
933void
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
942void
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
979 Comm::Write(dataConn, &mb, call);
980
981 pipeline.front()->noteSentBodyBytes(data.length);
982}
983
985void
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
1003void
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
1028void
1030{
1031 writeForwardedReply(reply);
1032 // note that the client data connection may already be closed by now
1033}
1034
1035void
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
1058void
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
1071void
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
1094void
1095Ftp::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
1142void
1144{
1145 changeState(fssConnected, "foreign reply");
1146 closeDataConnection();
1147 // 451: We intend to keep the control connection open.
1148 writeErrorReply(reply, 451);
1149}
1150
1151bool
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
1160void
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
1212static void
1213Ftp::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
1233void
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
1255void
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
1298bool
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
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
1405bool
1407{
1408 changeState(fssHandleFeat, "handleFeatRequest");
1409 return true;
1410}
1411
1412bool
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
1431bool
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
1490bool
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
1519bool
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
1533bool
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, nullptr);
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
1566bool
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
1595bool
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
1620bool
1622{
1623 changeState(fssHandleCwd, "handleCwdRequest");
1624 return true;
1625}
1626
1627bool
1629{
1630 changeState(fssHandlePass, "handlePassRequest");
1631 return true;
1632}
1633
1634bool
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.
1643void
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
1660bool
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
1691bool
1693{
1694 if (!Comm::IsConnOpen(dataConn)) {
1695 debugs(33, 3, "missing client data conn: " << dataConn);
1696 return false;
1697 }
1698 return true;
1699}
1700
1702void
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
1728void
1729Ftp::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
1749void
1750Ftp::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
1760void
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
1770void
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
1841static 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
#define Assure(condition)
Definition: Assure.h:35
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:58
CommCbFunPtrCallT< Dialer > * commCbCall(int debugSection, int debugLevel, const char *callName, const Dialer &dialer)
Definition: CommCalls.h:312
#define COMM_TRANSPARENT
Definition: Connection.h:50
#define COMM_INTERCEPTION
Definition: Connection.h:51
#define COMM_DOBIND
Definition: Connection.h:49
#define COMM_NONBLOCKING
Definition: Connection.h:46
CBDATA_NAMESPACED_CLASS_INIT(Ftp, Server)
ssize_t HttpHeaderPos
Definition: HttpHeader.h:45
int httpHeaderParseQuotedString(const char *start, const int len, String *val)
#define HttpHeaderInitPos
Definition: HttpHeader.h:48
int NHttpSockets
Definition: PortCfg.cc:25
AnyP::PortCfgPointer FtpPortList
list of Squid ftp_port configured
Definition: PortCfg.cc:23
#define MAXTCPLISTENPORTS
Definition: PortCfg.h:87
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer &callback)
Definition: Read.h:59
#define SQUIDSBUFPH
Definition: SBuf.h:31
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
class SquidConfig Config
Definition: SquidConfig.cc:12
StatCounters statCounter
Definition: StatCounters.cc:12
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:63
#define Must(condition)
Definition: TextException.h:75
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:17
static char server[MAXLINE]
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
void syncAle(HttpRequest *adaptedRequest, const char *logUri) const override
assigns uninitialized adapted_request and url ALE components
bool allowed() const
Definition: Acl.h:156
HttpHeader allMeta
All REQMOD and RESPMOD meta headers merged. Last field wins conflicts.
Definition: History.h:63
static void Start(const Pointer &job)
Definition: AsyncJob.cc:37
virtual void callException(const std::exception &e)
called when the job throws during an async call
Definition: AsyncJob.cc:143
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:18
CharacterSet complement(const char *complementLabel=nullptr) const
Definition: CharacterSet.cc:74
static const CharacterSet SP
Definition: CharacterSet.h:94
static const CharacterSet LF
Definition: CharacterSet.h:92
HttpRequest *const request
void initRequest(HttpRequest *)
size_t req_sz
raw request size on input, not current request size
StoreEntry * storeEntry() const
const AccessLogEntry::Pointer al
access.log entry
static const Pointer & Current()
Definition: CodeContext.cc:33
static void Reset()
forgets the current context, setting it to nil/unknown
Definition: CodeContext.cc:77
AnyP::PortCfgPointer port
the configuration listening port this call relates to (may be nil)
Definition: CommCalls.h:100
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:83
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:82
Comm::ConnectionPointer conn
Definition: CommCalls.h:80
Ip::Address remote
Definition: Connection.h:149
void leaveOrphanage()
resume relying on owner(s) to initiate an explicit connection closure
Definition: Connection.h:92
virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io)
Our close handler called by Comm when the pinned connection is closed.
void start() override
called by AsyncStart; do not call directly
struct ConnStateData::@36 flags
void noteBodyConsumerAborted(BodyPipe::Pointer) override=0
static bool Enabled(const int section, const int level)
whether debugging the given section and the given level produces output
Definition: Stream.h:75
ErrorDetails details
Definition: Error.h:60
err_type category
primary error classification (or ERR_NONE)
Definition: Error.h:55
Transaction information shared among our FTP client and server jobs.
Definition: FtpServer.h:43
Manages a control connection from an FTP client.
Definition: FtpServer.h:59
void writeCustomReply(const int code, const char *msg, const HttpReply *reply=nullptr)
Definition: FtpServer.cc:507
void handleDataReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:943
void calcUri(const SBuf *file)
computes uri member from host and, if tracked, working dir with file name
Definition: FtpServer.cc:345
bool handlePasvRequest(String &cmd, String &params)
Definition: FtpServer.cc:1413
bool handleDataRequest(String &cmd, String &params)
Definition: FtpServer.cc:1520
void setDataCommand()
Definition: FtpServer.cc:1644
bool handleUploadRequest(String &cmd, String &params)
Definition: FtpServer.cc:1534
void writeForwardedReply(const HttpReply *reply)
Definition: FtpServer.cc:1036
void userDataCompletionCheckpoint(int finalStatusCode)
Definition: FtpServer.cc:1809
void connectedForData(const CommConnectCbParams &params)
Done establishing a data connection to the user.
Definition: FtpServer.cc:1703
bool handleCdupRequest(String &cmd, String &params)
Definition: FtpServer.cc:1635
void writeForwardedForeign(const HttpReply *reply)
Definition: FtpServer.cc:1143
static void AcceptCtrlConnection(const CommAcceptCbParams &params)
accept a new FTP control connection and hand it to a dedicated Server
Definition: FtpServer.cc:242
void maybeReadUploadData()
schedules another data connection read if needed
Definition: FtpServer.cc:109
void handleEprtReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:1059
void changeState(const Ftp::ServerState newState, const char *reason)
Definition: FtpServer.cc:529
void wroteReply(const CommIoCbParams &io)
Definition: FtpServer.cc:1256
void handlePasvReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:881
Http::Stream * earlyError(const EarlyErrorKind eek)
creates a context filled with an error message for a given early error
Definition: FtpServer.cc:570
void callException(const std::exception &e) override
called when the job throws during an async call
Definition: FtpServer.cc:1750
void completeDataDownload()
Definition: FtpServer.cc:1834
void handleFeatReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:812
bool handlePortRequest(String &cmd, String &params)
Definition: FtpServer.cc:1491
void acceptDataConnection(const CommAcceptCbParams &params)
Definition: FtpServer.cc:401
bool handleFeatRequest(String &cmd, String &params)
Definition: FtpServer.cc:1406
void writeErrorReply(const HttpReply *reply, const int status)
writes FTP error response with given status and reply-derived error details
Definition: FtpServer.cc:1095
void startWaitingForOrigin()
Definition: FtpServer.cc:1761
bool checkDataConnPre()
Definition: FtpServer.cc:1661
void writeReply(MemBuf &mb)
Definition: FtpServer.cc:495
void resetLogin(const char *reason)
clear client and server login-related state after the old login is gone
Definition: FtpServer.cc:336
void processParsedRequest(Http::StreamPointer &context) override
start processing a freshly parsed request
Definition: FtpServer.cc:156
void noteBodyConsumerAborted(BodyPipe::Pointer ptr) override
Definition: FtpServer.cc:231
void wroteReplyData(const CommIoCbParams &io)
called when we are done writing a chunk of the response data
Definition: FtpServer.cc:986
time_t idleTimeout() const override
timeout to use when waiting for the next request
Definition: FtpServer.cc:86
void clientPinnedConnectionClosed(const CommCloseCbParams &io) override
Our close handler called by Comm when the pinned connection is closed.
Definition: FtpServer.cc:316
Server(const MasterXaction::Pointer &xact)
Definition: FtpServer.cc:53
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
void start() override
called by AsyncStart; do not call directly
Definition: FtpServer.cc:92
void handlePortReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:921
void handleErrorReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:934
void notePeerConnection(Comm::ConnectionPointer conn) override
called just before a FwdState-dispatched job starts using connection
Definition: FtpServer.cc:302
Http::Stream * parseOneRequest() override
Definition: FtpServer.cc:640
char uploadBuf[CLIENT_REQ_BUF_SZ]
data connection input buffer
Definition: FtpServer.h:190
void handleReply(HttpReply *header, StoreIOBuffer receivedData) override
Definition: FtpServer.cc:778
bool createDataConnection(Ip::Address cltAddr)
[Re]initializes dataConn for active data transfers. Does not connect.
Definition: FtpServer.cc:1432
bool handleEpsvRequest(String &cmd, String &params)
Definition: FtpServer.cc:1596
void setReply(const int code, const char *msg)
Definition: FtpServer.cc:1729
void replyDataWritingCheckpoint()
ClientStream checks after (actual or skipped) reply data writing.
Definition: FtpServer.cc:1004
bool handlePassRequest(String &cmd, String &params)
Definition: FtpServer.cc:1628
void handleEpsvReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:1072
void wroteEarlyReply(const CommIoCbParams &io)
Definition: FtpServer.cc:1234
Http::Stream * handleUserRequest(const SBuf &cmd, SBuf &params)
Definition: FtpServer.cc:1358
void handleUploadReply(const HttpReply *header, StoreIOBuffer receivedData)
Definition: FtpServer.cc:1029
bool handleRequest(HttpRequest *)
Definition: FtpServer.cc:1299
void noteMoreBodySpaceAvailable(BodyPipe::Pointer) override
Definition: FtpServer.cc:222
void stopWaitingForOrigin(int status)
Definition: FtpServer.cc:1771
void shovelUploadData()
shovel upload data from the internal buffer to the body pipe if possible
Definition: FtpServer.cc:202
unsigned int listenForDataConnection()
Definition: FtpServer.cc:370
void writeForwardedReplyAndCall(const HttpReply *reply, AsyncCall::Pointer &call)
Definition: FtpServer.cc:1161
int pipelinePrefetchMax() const override
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
Definition: FtpServer.cc:80
bool checkDataConnPost() const
Check that client data connection is ready for immediate I/O.
Definition: FtpServer.cc:1692
void doProcessRequest()
react to the freshly parsed request
Definition: FtpServer.cc:128
void readUploadData(const CommIoCbParams &io)
imports more upload data from the data connection
Definition: FtpServer.cc:168
bool handleCwdRequest(String &cmd, String &params)
Definition: FtpServer.cc:1621
bool handleEprtRequest(String &cmd, String &params)
Definition: FtpServer.cc:1567
void writeEarlyReply(const int code, const char *msg)
Definition: FtpServer.cc:476
~Server() override
Definition: FtpServer.cc:74
void closeDataConnection()
Definition: FtpServer.cc:445
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:996
void refreshMask()
Definition: HttpHeader.cc:723
HttpHeaderEntry * getEntry(HttpHeaderPos *pos) const
Definition: HttpHeader.cc:584
const char * getStr(Http::HdrType id) const
Definition: HttpHeader.cc:1164
void addEntry(HttpHeaderEntry *e)
Definition: HttpHeader.cc:737
String getByName(const SBuf &name) const
Definition: HttpHeader.cc:849
int has(Http::HdrType id) const
Definition: HttpHeader.cc:938
int getInt(Http::HdrType id) const
Definition: HttpHeader.cc:1121
HttpHeaderEntry * findEntry(Http::HdrType id) const
Definition: HttpHeader.cc:603
void updateOrAddStr(Http::HdrType, const SBuf &)
Definition: HttpHeader.cc:1084
Http::StatusLine sline
Definition: HttpReply.h:56
Adaptation::History::Pointer adaptHistory(bool createIfNone=false) const
Returns possibly nil history, creating it if requested.
Definition: HttpRequest.cc:404
void pack(Packable *p) const
Definition: HttpRequest.cc:344
HttpRequestMethod method
Definition: HttpRequest.h:114
bool forcedBodyContinuation
whether we have responded with HTTP 100 or FTP 150 already
Definition: HttpRequest.h:194
static HttpRequest * FromUrl(const SBuf &url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:517
Error error
the first transaction problem encountered (or falsy)
Definition: HttpRequest.h:161
HttpHeader header
Definition: Message.h:74
const char * reason() const
retrieve the reason string for this status line
Definition: StatusLine.cc:44
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
struct Http::Stream::@74 flags
unsigned parsed_ok
Was this parsed correctly?
Definition: Stream.h:140
char reqbuf[HTTP_REQBUF_SZ]
Definition: Stream.h:137
clientStreamNode * getClientReplyContext() const
Definition: Stream.cc:510
unsigned int toHostStr(char *buf, const unsigned int len) const
Definition: Address.cc:842
bool isIPv4() const
Definition: Address.cc:158
bool isAnyAddr() const
Definition: Address.cc:170
static Pointer MakePortful(const AnyP::PortCfgPointer &aPort)
Definition: MasterXaction.h:54
Definition: MemBuf.h:24
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
mb_size_t size
Definition: MemBuf.h:135
char * buf
Definition: MemBuf.h:134
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
Definition: Raw.h:21
C * getRaw() const
Definition: RefCount.h:89
void disableCacheUse(const char *reason)
Definition: RequestFlags.cc:30
Definition: SBuf.h:94
void toUpper()
converts all characters to upper case;
Definition: SBuf.cc:824
int caseCmp(const SBuf &S, const size_type n) const
shorthand version for case-insensitive compare()
Definition: SBuf.h:283
static const size_type npos
Definition: SBuf.h:99
const char * c_str()
Definition: SBuf.cc:516
SBuf & chop(size_type pos, size_type n=npos)
Definition: SBuf.cc:530
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
size_type rfind(char c, size_type endPos=npos) const
Definition: SBuf.cc:692
SBuf & trim(const SBuf &toRemove, bool atBeginning=true, bool atEnd=true)
Definition: SBuf.cc:551
bool isEmpty() const
Definition: SBuf.h:431
void clear()
Definition: SBuf.cc:175
SBuf substr(size_type pos, size_type n=npos) const
Definition: SBuf.cc:576
MemBlob::size_type size_type
Definition: SBuf.h:96
Definition: Server.h:32
struct SquidConfig::@107 accessList
time_t ftpClientIdle
Definition: SquidConfig.h:121
size_t maxRequestHeaderSize
Definition: SquidConfig.h:134
time_t connect
Definition: SquidConfig.h:115
struct SquidConfig::@93 Timeout
acl_access * forceRequestBodyContinuation
Definition: SquidConfig.h:404
struct StatCounters::@122 client_http
ByteCounter kbytes_in
Definition: StatCounters.h:45
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1691
int cmp(char const *) const
Definition: String.cc:236
char const * termedBuf() const
Definition: SquidString.h:92
int caseCmp(char const *) const
Definition: String.cc:266
size_type size() const
Definition: SquidString.h:73
void setReplyToReply(HttpReply *reply)
creates a store entry for the reply and appends error reply to it
void createStoreEntry(const HttpRequestMethod &m, RequestFlags flags)
void clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
void clientSetKeepaliveFlag(ClientHttpRequest *http)
decide whether to expect multiple requests on the corresponding connection
Definition: client_side.cc:698
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
CSD clientSocketDetach
Definition: client_side.h:533
SQUIDCEXTERN CSR clientGetMoreData
Definition: client_side.h:529
CSCB clientSocketRecipient
Definition: client_side.h:532
SQUIDCEXTERN CSS clientReplyStatus
Definition: client_side.h:530
SQUIDCEXTERN CSD clientReplyDetach
Definition: client_side.h:531
unsigned short comm_local_port(int fd)
Definition: comm.cc:164
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note)
Definition: comm.cc:256
bool isOpen(const int fd)
Definition: comm.cc:88
A const & min(A const &lhs, A const &rhs)
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define DBG_CRITICAL
Definition: Stream.h:37
static int port
Definition: ldap_backend.cc:70
clientStream_status_t
Definition: enums.h:125
@ STREAM_COMPLETE
Definition: enums.h:127
@ STREAM_UNPLANNED_COMPLETE
Definition: enums.h:132
@ STREAM_NONE
Definition: enums.h:126
@ STREAM_FAILED
Definition: enums.h:137
void fatal(const char *message)
Definition: fatal.cc:28
void fd_note(int fd, const char *s)
Definition: fd.cc:216
void clientStreamInit(dlink_list *list, CSR *func, CSD *rdetach, CSS *readstatus, ClientStreamData readdata, CSCB *callback, CSD *cdetach, ClientStreamData callbackdata, StoreIOBuffer tailBuffer)
const char * errorPageName(int pageId)
error ID to string
Definition: errorpage.cc:662
#define HTTP_REQBUF_SZ
Definition: forward.h:14
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
void ReadCancel(int fd, AsyncCall::Pointer &callback)
Cancel the read pending on FD. No action if none pending.
Definition: Read.cc:219
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:33
@ OK
Definition: Flag.h:16
@ ERR_CLOSING
Definition: Flag.h:24
Definition: forward.h:24
static bool SupportedCommand(const SBuf &name)
Whether Squid FTP Relay supports a named feature (e.g., a command).
Definition: FtpServer.cc:1842
const SBuf & cmdAppe()
Definition: Elements.cc:56
AnyP::ProtocolVersion ProtocolVersion()
Protocol version to use in Http::Message structures wrapping FTP messages.
Definition: Elements.cc:24
bool ParseProtoIpPort(const char *buf, Ip::Address &addr)
Definition: Parsing.cc:52
const SBuf & cmdDele()
Definition: Elements.cc:77
const SBuf & cmdEpsv()
Definition: Elements.cc:91
const SBuf & cmdSmnt()
Definition: Elements.cc:161
const SBuf & cmdStou()
Definition: Elements.cc:182
ServerState
Definition: FtpServer.h:23
@ fssError
Definition: FtpServer.h:36
@ fssHandlePort
Definition: FtpServer.h:28
@ fssHandleCdup
Definition: FtpServer.h:35
@ fssHandleFeat
Definition: FtpServer.h:26
@ fssHandlePasv
Definition: FtpServer.h:27
@ fssConnected
Definition: FtpServer.h:25
@ fssHandleEprt
Definition: FtpServer.h:31
@ fssHandleCwd
Definition: FtpServer.h:33
@ fssHandlePass
Definition: FtpServer.h:34
@ fssHandleUploadRequest
Definition: FtpServer.h:30
@ fssBegin
Definition: FtpServer.h:24
@ fssHandleEpsv
Definition: FtpServer.h:32
@ fssHandleDataRequest
Definition: FtpServer.h:29
const SBuf & cmdStor()
Definition: Elements.cc:175
const SBuf & cmdMlsd()
Definition: Elements.cc:112
const SBuf & cmdList()
Definition: Elements.cc:98
const SBuf & cmdRetr()
Definition: Elements.cc:133
const SBuf & cmdNlst()
Definition: Elements.cc:126
const SBuf & cmdMlst()
Definition: Elements.cc:119
const SBuf & cmdStat()
Definition: Elements.cc:168
const SBuf & cmdAuth()
Definition: Elements.cc:63
static bool CommandHasPathParameter(const SBuf &cmd)
whether the given FTP command has a pathname parameter
Definition: FtpServer.cc:544
const SBuf & cmdCwd()
Definition: Elements.cc:70
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 SBuf & cmdEprt()
Definition: Elements.cc:84
const SBuf & cmdRnto()
Definition: Elements.cc:154
const SBuf & cmdMkd()
Definition: Elements.cc:105
const SBuf & cmdUser()
Definition: Elements.cc:189
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 & cmdRnfr()
Definition: Elements.cc:147
void StopListening()
reject new connections to any configured ftp_port
Definition: FtpServer.cc:287
void StartListening()
accept connections on all configured ftp_ports
Definition: FtpServer.cc:265
const SBuf & cmdRmd()
Definition: Elements.cc:140
static void PrintReply(MemBuf &mb, const HttpReply *reply, const char *const prefix="")
Definition: FtpServer.cc:1213
@ scBadRequest
Definition: StatusCode.h:44
@ scOkay
Definition: StatusCode.h:26
@ scNoContent
Definition: StatusCode.h:30
@ METHOD_PUT
Definition: MethodType.h:27
@ METHOD_GET
Definition: MethodType.h:25
@ TRANSFER_ENCODING
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
@ fdnFtpSocket
Definition: FdNotes.h:20
SSL Connection
Definition: Session.h:45
#define xstrdup
static void handler(int signo)
Definition: purge.cc:858
int code
Definition: smb-errors.c:145
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
Definition: parse.c:104
Definition: parse.c:160
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors