62 void start()
override;
153 thePreliminaryCb(nullptr),
154 forwardingCompleted(false),
155 originWaitInProgress(false)
171 entry->unregisterAbortCallback(
"Ftp::Relay object destructed");
177 if (savedReply.message)
180 xfree(savedReply.lastCommand);
181 xfree(savedReply.lastReply);
187 if (!master().clientReadGreeting)
208 stopOriginWait(ctrl.replycode);
213 debugs(9, 7,
"completing FTP server " << ctrl.conn <<
214 " after " << ctrl.replycode);
215 fwd->unregister(ctrl.conn);
216 if (ctrl.replycode == 221) {
217 mgr->unpinConnection(
false);
222 notePinnedConnectionBecameIdle,
242 debugs(9, 3,
"our server side is gone: " << mgr);
252 return const_cast<Ftp::Relay*
>(
this)->updateMaster();
260 debugs(9, 3,
"client state was " << cltState <<
" now: " << newState);
275 debugs(9, 5, forwardingCompleted);
276 if (forwardingCompleted)
278 forwardingCompleted =
true;
285 if (!doneWithServer())
289 if (entry->isEmpty())
290 failedErrorMessage(
error, xerrno);
299 HttpReply *
const reply = createHttpReply(httpStatus);
300 entry->replaceHttpReply(reply);
314 abortOnData(
"entry aborted after calling appendSuccessHeader()");
318 if (master().userDataDone) {
321 abortOnData(
"Squid-to-client data connection is closed");
327 if (adaptationAccessCheckPending) {
328 debugs(9, 3,
"returning due to adaptationAccessCheckPending");
334 if (data.readBuf !=
nullptr && data.readBuf->hasContent()) {
335 const mb_size_t csize = data.readBuf->contentSize();
336 debugs(9, 5,
"writing " << csize <<
" bytes to the reply");
337 addVirginReplyBody(data.readBuf->content(), csize);
338 data.readBuf->consume(csize);
343 maybeReadVirginBody();
349 if (!request->clientConnectionManager.valid()) {
350 debugs(9, 5,
"client connection gone");
356 if (ctrl.message ==
nullptr)
360 assert(this->SM_FUNCS[state] !=
nullptr);
361 (this->*SM_FUNCS[state])();
387 setVirginReply(reply);
388 markParsedVirginReplyAsWhole(
"Ftp::Relay::handleControlReply() does not forward partial replies");
389 adaptOrFinalizeReply();
397 debugs(9, 5,
"forwarding preliminary reply to client");
400 Must(thePreliminaryCb ==
nullptr);
401 thePreliminaryCb = cb;
417 debugs(9, 5,
"proceeding after preliminary reply to client");
419 Must(thePreliminaryCb !=
nullptr);
420 const PreliminaryCb cb = thePreliminaryCb;
421 thePreliminaryCb =
nullptr;
428 failed(
error, xerrno);
436 for (
wordlist *W = ctrl.message; W && W->next; W = W->next)
446 data.addr(master().clientDataAddr);
447 connectDataChannel();
455 debugs(9, 3,
"begin data transfer from " << data.conn->remote <<
456 " (" << data.conn->local <<
")");
461 setVirginReply(reply);
462 adaptOrFinalizeReply();
464 maybeReadVirginBody();
465 state = READING_DATA;
473 debugs(9, 3,
"begin data transfer to " << data.conn->remote <<
474 " (" << data.conn->local <<
")");
476 if (!startRequestBodyFlow()) {
481 state = WRITING_DATA;
487 assert(!master().clientReadGreeting);
489 switch (ctrl.replycode) {
491 updateMaster().clientReadGreeting =
true;
502 if (
nullptr != ctrl.message)
516 abortAll(
"Internal error: FTP relay request with no command");
526 if (params.
size() > 0)
527 debugs(9, 5,
"command: " << cmd <<
", parameters: " << params);
529 debugs(9, 5,
"command: " << cmd <<
", no parameters");
540 if (params.
size() > 0)
545 writeCommand(buf.
c_str());
557 if (state == SENT_DATA_REQUEST) {
565 originWaitInProgress =
true;
577 if (
Is1xx(ctrl.replycode))
588 if (
Is1xx(ctrl.replycode))
599 if (
Is1xx(ctrl.replycode))
602 if (handlePasvReply(updateMaster().clientDataAddr))
611 if (
Is1xx(ctrl.replycode))
614 if (handleEpsvReply(updateMaster().clientDataAddr)) {
615 if (ctrl.message ==
nullptr)
629 if (ctrl.replycode == 125 || ctrl.replycode == 150) {
632 else if (fwd->request->forcedBodyContinuation )
643 if (!fwd->request->clientConnectionManager->port->ftp_track_dirs)
646 debugs(9, 5,
"start directory tracking");
647 savedReply.message = ctrl.message;
648 savedReply.lastCommand = ctrl.last_command;
649 savedReply.lastReply = ctrl.last_reply;
650 savedReply.replyCode = ctrl.replycode;
652 ctrl.last_command =
nullptr;
653 ctrl.last_reply =
nullptr;
654 ctrl.message =
nullptr;
656 writeCommand(
"PWD\r\n");
663 debugs(9, 5,
"got code from pwd: " << ctrl.replycode <<
", msg: " << ctrl.last_reply);
665 if (ctrl.replycode == 257)
672 ctrl.message = savedReply.message;
673 ctrl.last_command = savedReply.lastCommand;
674 ctrl.last_reply = savedReply.lastReply;
675 ctrl.replycode = savedReply.replyCode;
677 savedReply.message =
nullptr;
678 savedReply.lastReply =
nullptr;
679 savedReply.lastCommand =
nullptr;
688 debugs(9, 5,
"got code " << ctrl.replycode <<
", msg: " << ctrl.last_reply);
690 if (
Is1xx(ctrl.replycode))
693 if (weAreTrackingDir()) {
695 }
else if (startDirTracking())
704 if (
Is1xx(ctrl.replycode))
707 if (weAreTrackingDir()) {
709 }
else if (ctrl.replycode == 230) {
710 if (startDirTracking())
725 if (ctrl.replycode == 226 || ctrl.replycode == 250) {
726 markParsedVirginReplyAsWhole(
"Ftp::Relay::readTransferDoneReply() code 226 or 250");
729 " after reading response data");
732 debugs(9, 2,
"Complete data downloading");
741 dataConnWait.finish();
744 debugs(9, 2,
"failed to connect FTP server data channel");
749 debugs(9, 2,
"connected FTP server data channel: " << io.
conn);
751 data.opened(io.
conn, dataCloser());
765 debugs(9, 3,
"aborting transaction for " << reason <<
766 "; FD " << (ctrl.conn !=
nullptr ? ctrl.conn->fd : -1) <<
", Data FD " << (data.conn !=
nullptr ? data.conn->fd : -1) <<
", this " <<
this);
771 if (adaptedBodySource !=
nullptr)
772 stopConsumingFrom(adaptedBodySource);
784 if (originWaitInProgress) {
794 originWaitInProgress =
false;
801 debugs(9, 2,
"Client Data connection closed!");
#define ScheduleCallHere(call)
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
UnaryCbdataDialer< Argument1 > cbdataDialer(typename UnaryCbdataDialer< Argument1 >::Handler *handler, Argument1 *arg1)
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
#define CallJobHere1(debugSection, debugLevel, job, Class, method, arg1)
void error(char *format,...)
#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type)
static void Start(const Pointer &job)
Cbc * valid() const
was set and is valid
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
virtual void completeForwarding()
virtual void handleRequestBodyProducerAborted()=0
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Comm::Flag flag
comm layer result status.
Comm::ConnectionPointer conn
parameters for the async notePinnedConnectionBecameIdle() call
void sendControlMsg(HttpControlMsg) override
called to send the 1xx message and notify the Source
Comm::ConnectionPointer conn
channel descriptor
FTP client functionality shared among FTP Gateway and Relay clients.
void start() override
called by AsyncStart; do not call directly
virtual void handleControlReply()
void scheduleReadControlReply(int buffered_ok)
DataChannel data
FTP data channel state.
virtual void failed(err_type error=ERR_NONE, int xerrno=0, ErrorState *ftperr=nullptr)
handle a fatal transaction error, closing the control connection
Transaction information shared among our FTP client and server jobs.
ServerState serverState
what our FTP server is doing
bool originWaitInProgress
void completeForwarding() override
virtual void serverComplete()
void readTransferDoneReply()
Ftp::MasterState & updateMaster()
void failedErrorMessage(err_type error, int xerrno)
void proceedAfterPreliminaryReply()
Ftp::ServerState serverState() const
void start() override
called by AsyncStart; do not call directly
void failed(err_type error=ERR_NONE, int xerrno=0, ErrorState *ftperr=nullptr) override
handle a fatal transaction error, closing the control connection
void(Relay::* PreliminaryCb)()
const Ftp::MasterState & master() const
A const variant of updateMaster().
int replyCode
the reply status
char * lastCommand
the command caused the reply
HttpReply * createHttpReply(const Http::StatusCode httpStatus, const int64_t clen=0)
struct Ftp::Relay::@31 savedReply
set and delayed while we are tracking using PWD
bool weAreTrackingDir() const
bool abortOnData(const char *reason) override
void forwardError(err_type error=ERR_NONE, int xerrno=0)
Relay(FwdState *const fwdState)
void stopOriginWait(int code)
Inform Ftp::Server that we are done if originWaitInProgress.
bool forwardingCompleted
completeForwarding() has been called
bool mayReadVirginReplyBody() const override
whether we may receive more virgin response body bytes
void processReplyBody() override
void dataChannelConnected(const CommConnectCbParams &io) override
char * lastReply
last line of reply: reply status plus message
static void HandleStoreAbort(Relay *)
called by Store if the entry is no longer usable
PreliminaryCb thePreliminaryCb
void readUserOrPassReply()
static const SM_FUNC SM_FUNCS[]
void handleControlReply() override
void readCwdOrCdupReply()
void scheduleReadControlReply()
void forwardPreliminaryReply(const PreliminaryCb cb)
wordlist * message
reply message, one wordlist entry per message line
void handleRequestBodyProducerAborted() override
Manages a control connection from an FTP client.
void startWaitingForOrigin()
void stopWaitingForOrigin(int status)
bundles HTTP 1xx reply and the "successfully forwarded" callback
@ srcFtp
ftp_port or FTP server
uint32_t sources
The message sources.
SBuf & Printf(const char *fmt,...) PRINTF_FORMAT_ARG2
void registerAbortCallback(const AsyncCall::Pointer &)
notify the StoreEntry writer of a 3rd-party initiated StoreEntry abort
void releaseRequest(const bool shareable=false)
char const * termedBuf() const
static ErrorDetail::Pointer NewIfAny(const int errorNo)
#define debugs(SECTION, LEVEL, CONTENT)
#define EBIT_TEST(flag, bit)
bool IsConnOpen(const Comm::ConnectionPointer &conn)
bool Is1xx(const int sc)
whether this is an informational 1xx response status code
const char * UnescapeDoubleQuoted(const char *quotedPath)
parses an FTP-quoted quote-escaped path
void StartRelay(FwdState *const fwdState)
A new FTP Relay job.
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.
void wordlistDestroy(wordlist **list)
destroy a wordlist