]> git.ipfire.org Git - thirdparty/squid.git/blame - src/clients/FtpRelay.cc
Source Format Enforcement (#963)
[thirdparty/squid.git] / src / clients / FtpRelay.cc
CommitLineData
434a79b0 1/*
bf95c10a 2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
434a79b0 3 *
bbc27441
AJ
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.
434a79b0
DK
7 */
8
bbc27441
AJ
9/* DEBUG: section 09 File Transfer Protocol (FTP) */
10
434a79b0 11#include "squid.h"
54a6c0cd 12#include "anyp/PortCfg.h"
7e9f330d 13#include "base/AsyncCbdataCalls.h"
92ae4c86 14#include "client_side.h"
5517260a 15#include "clients/forward.h"
92ae4c86 16#include "clients/FtpClient.h"
83b053a0 17#include "error/SysErrorDetail.h"
43446566 18#include "ftp/Elements.h"
92ae4c86 19#include "ftp/Parsing.h"
d3dddfb5 20#include "http/Stream.h"
434a79b0
DK
21#include "HttpHdrCc.h"
22#include "HttpRequest.h"
65e41a45 23#include "sbuf/SBuf.h"
27c841f6 24#include "servers/FtpServer.h"
434a79b0
DK
25#include "SquidTime.h"
26#include "Store.h"
434a79b0
DK
27#include "wordlist.h"
28
27c841f6
AR
29namespace Ftp
30{
434a79b0 31
e7ce227f 32/// An FTP client receiving native FTP commands from our FTP server
5517260a
AR
33/// (Ftp::Server), forwarding them to the next FTP hop,
34/// and then relaying FTP replies back to our FTP server.
35class Relay: public Ftp::Client
434a79b0 36{
5c2f68b7
AJ
37 CBDATA_CLASS(Relay);
38
434a79b0 39public:
5517260a
AR
40 explicit Relay(FwdState *const fwdState);
41 virtual ~Relay();
434a79b0
DK
42
43protected:
92ae4c86
AR
44 const Ftp::MasterState &master() const;
45 Ftp::MasterState &updateMaster();
e7ce227f
AR
46 Ftp::ServerState serverState() const { return master().serverState; }
47 void serverState(const Ftp::ServerState newState);
92ae4c86 48
5517260a 49 /* Ftp::Client API */
777e8376 50 virtual void failed(err_type error = ERR_NONE, int xerrno = 0, ErrorState *ftperr = nullptr);
43446566 51 virtual void dataChannelConnected(const CommConnectCbParams &io);
5517260a 52
fccd4a86 53 /* Client API */
5517260a 54 virtual void serverComplete();
434a79b0 55 virtual void handleControlReply();
5517260a 56 virtual void processReplyBody();
434a79b0 57 virtual void handleRequestBodyProducerAborted();
719c885c
AR
58 virtual bool mayReadVirginReplyBody() const;
59 virtual void completeForwarding();
92cfc72f 60 virtual bool abortOnData(const char *reason);
5517260a
AR
61
62 /* AsyncJob API */
63 virtual void start();
3238b9b6 64 virtual void swanSong();
5517260a 65
434a79b0
DK
66 void forwardReply();
67 void forwardError(err_type error = ERR_NONE, int xerrno = 0);
719c885c 68 void failedErrorMessage(err_type error, int xerrno);
43446566 69 HttpReply *createHttpReply(const Http::StatusCode httpStatus, const int64_t clen = 0);
434a79b0 70 void handleDataRequest();
a5d444a5
DK
71 void startDataDownload();
72 void startDataUpload();
54a6c0cd
CT
73 bool startDirTracking();
74 void stopDirTracking();
75 bool weAreTrackingDir() const {return savedReply.message != NULL;}
434a79b0 76
5517260a 77 typedef void (Relay::*PreliminaryCb)();
434a79b0
DK
78 void forwardPreliminaryReply(const PreliminaryCb cb);
79 void proceedAfterPreliminaryReply();
80 PreliminaryCb thePreliminaryCb;
81
5517260a 82 typedef void (Relay::*SM_FUNC)();
434a79b0 83 static const SM_FUNC SM_FUNCS[];
125894ba 84 void readGreeting();
434a79b0
DK
85 void sendCommand();
86 void readReply();
5f3898e2 87 void readFeatReply();
434a79b0
DK
88 void readPasvReply();
89 void readDataReply();
90 void readTransferDoneReply();
000e664b 91 void readEpsvReply();
54a6c0cd
CT
92 void readCwdOrCdupReply();
93 void readUserOrPassReply();
434a79b0 94
434a79b0 95 void scheduleReadControlReply();
3238b9b6
CT
96
97 /// Inform Ftp::Server that we are done if originWaitInProgress
98 void stopOriginWait(int code);
7e9f330d
EB
99 /// called by Store if the entry is no longer usable
100 static void HandleStoreAbort(Relay *);
434a79b0 101
719c885c
AR
102 bool forwardingCompleted; ///< completeForwarding() has been called
103
3238b9b6
CT
104 /// whether we are between Ftp::Server::startWaitingForOrigin() and
105 /// Ftp::Server::stopWaitingForOrigin() calls
106 bool originWaitInProgress;
107
54a6c0cd
CT
108 struct {
109 wordlist *message; ///< reply message, one wordlist entry per message line
110 char *lastCommand; ///< the command caused the reply
111 char *lastReply; ///< last line of reply: reply status plus message
112 int replyCode; ///< the reply status
113 } savedReply; ///< set and delayed while we are tracking using PWD
434a79b0
DK
114};
115
5517260a 116} // namespace Ftp
434a79b0 117
5517260a
AR
118CBDATA_NAMESPACED_CLASS_INIT(Ftp, Relay);
119
120const Ftp::Relay::SM_FUNC Ftp::Relay::SM_FUNCS[] = {
121 &Ftp::Relay::readGreeting, // BEGIN
122 &Ftp::Relay::readUserOrPassReply, // SENT_USER
123 &Ftp::Relay::readUserOrPassReply, // SENT_PASS
e7ce227f
AR
124 NULL,/* &Ftp::Relay::readReply */ // SENT_TYPE
125 NULL,/* &Ftp::Relay::readReply */ // SENT_MDTM
126 NULL,/* &Ftp::Relay::readReply */ // SENT_SIZE
000e664b
AR
127 NULL, // SENT_EPRT
128 NULL, // SENT_PORT
5517260a
AR
129 &Ftp::Relay::readEpsvReply, // SENT_EPSV_ALL
130 &Ftp::Relay::readEpsvReply, // SENT_EPSV_1
131 &Ftp::Relay::readEpsvReply, // SENT_EPSV_2
132 &Ftp::Relay::readPasvReply, // SENT_PASV
133 &Ftp::Relay::readCwdOrCdupReply, // SENT_CWD
e7ce227f
AR
134 NULL,/* &Ftp::Relay::readDataReply, */ // SENT_LIST
135 NULL,/* &Ftp::Relay::readDataReply, */ // SENT_NLST
136 NULL,/* &Ftp::Relay::readReply */ // SENT_REST
137 NULL,/* &Ftp::Relay::readDataReply */ // SENT_RETR
138 NULL,/* &Ftp::Relay::readReply */ // SENT_STOR
139 NULL,/* &Ftp::Relay::readReply */ // SENT_QUIT
5517260a
AR
140 &Ftp::Relay::readTransferDoneReply, // READING_DATA
141 &Ftp::Relay::readReply, // WRITING_DATA
e7ce227f 142 NULL,/* &Ftp::Relay::readReply */ // SENT_MKDIR
5517260a 143 &Ftp::Relay::readFeatReply, // SENT_FEAT
e7ce227f 144 NULL,/* &Ftp::Relay::readPwdReply */ // SENT_PWD
5517260a
AR
145 &Ftp::Relay::readCwdOrCdupReply, // SENT_CDUP
146 &Ftp::Relay::readDataReply,// SENT_DATA_REQUEST
147 &Ftp::Relay::readReply, // SENT_COMMAND
000e664b 148 NULL
434a79b0
DK
149};
150
5517260a 151Ftp::Relay::Relay(FwdState *const fwdState):
f53969cc
SM
152 AsyncJob("Ftp::Relay"),
153 Ftp::Client(fwdState),
154 thePreliminaryCb(NULL),
3238b9b6
CT
155 forwardingCompleted(false),
156 originWaitInProgress(false)
434a79b0 157{
a5e978fd 158 savedReply.message = NULL;
54a6c0cd
CT
159 savedReply.lastCommand = NULL;
160 savedReply.lastReply = NULL;
161 savedReply.replyCode = 0;
162
8a07d123
AR
163 // Prevent the future response from becoming public and being shared/cached
164 // because FTP does not support response cachability and freshness checks.
a8f874af 165 entry->releaseRequest();
7e9f330d
EB
166 AsyncCall::Pointer call = asyncCall(9, 4, "Ftp::Relay::Abort", cbdataDialer(&Relay::HandleStoreAbort, this));
167 entry->registerAbortCallback(call);
434a79b0
DK
168}
169
5517260a 170Ftp::Relay::~Relay()
434a79b0 171{
7e9f330d
EB
172 entry->unregisterAbortCallback("Ftp::Relay object destructed");
173 // Client, our parent, calls entry->unlock().
174 // Client does not currently un/registerAbortCallback() because
175 // FwdState does that for other Client kids; \see FwdState::start().
176
fccd4a86 177 closeServer(); // TODO: move to clients/Client.cc?
54a6c0cd
CT
178 if (savedReply.message)
179 wordlistDestroy(&savedReply.message);
180
181 xfree(savedReply.lastCommand);
182 xfree(savedReply.lastReply);
434a79b0
DK
183}
184
185void
5517260a 186Ftp::Relay::start()
434a79b0 187{
92ae4c86 188 if (!master().clientReadGreeting)
5517260a 189 Ftp::Client::start();
27c841f6
AR
190 else if (serverState() == fssHandleDataRequest ||
191 serverState() == fssHandleUploadRequest)
434a79b0
DK
192 handleDataRequest();
193 else
194 sendCommand();
195}
196
3238b9b6
CT
197void
198Ftp::Relay::swanSong()
199{
200 stopOriginWait(0);
201 Ftp::Client::swanSong();
202}
203
29f23abf
AR
204/// Keep control connection for future requests, after we are done with it.
205/// Similar to COMPLETE_PERSISTENT_MSG handling in http.cc.
206void
5517260a 207Ftp::Relay::serverComplete()
29f23abf 208{
3238b9b6
CT
209 stopOriginWait(ctrl.replycode);
210
89b1d7a2
AR
211 CbcPointer<ConnStateData> &mgr = fwd->request->clientConnectionManager;
212 if (mgr.valid()) {
213 if (Comm::IsConnOpen(ctrl.conn)) {
214 debugs(9, 7, "completing FTP server " << ctrl.conn <<
215 " after " << ctrl.replycode);
216 fwd->unregister(ctrl.conn);
217 if (ctrl.replycode == 221) { // Server sends FTP 221 before closing
218 mgr->unpinConnection(false);
219 ctrl.close();
220 } else {
801cfc26
CT
221 CallJobHere1(9, 4, mgr,
222 ConnStateData,
223 notePinnedConnectionBecameIdle,
224 ConnStateData::PinnedIdleContext(ctrl.conn, fwd->request));
89b1d7a2
AR
225 ctrl.forget();
226 }
227 }
29f23abf 228 }
5517260a 229 Ftp::Client::serverComplete();
29f23abf
AR
230}
231
e7ce227f
AR
232/// Safely returns the master state,
233/// with safety checks in case the Ftp::Server side of the master xact is gone.
92ae4c86 234Ftp::MasterState &
5517260a 235Ftp::Relay::updateMaster()
92ae4c86
AR
236{
237 CbcPointer<ConnStateData> &mgr = fwd->request->clientConnectionManager;
238 if (mgr.valid()) {
239 if (Ftp::Server *srv = dynamic_cast<Ftp::Server*>(mgr.get()))
aea65fec 240 return *srv->master;
92ae4c86
AR
241 }
242 // this code will not be necessary once the master is inside MasterXaction
243 debugs(9, 3, "our server side is gone: " << mgr);
244 static Ftp::MasterState Master;
245 Master = Ftp::MasterState();
246 return Master;
247}
248
e7ce227f 249/// A const variant of updateMaster().
92ae4c86 250const Ftp::MasterState &
5517260a 251Ftp::Relay::master() const
92ae4c86 252{
e7ce227f 253 return const_cast<Ftp::Relay*>(this)->updateMaster(); // avoid code dupe
434a79b0
DK
254}
255
e7ce227f 256/// Changes server state and debugs about that important event.
434a79b0 257void
e7ce227f 258Ftp::Relay::serverState(const Ftp::ServerState newState)
434a79b0 259{
92ae4c86 260 Ftp::ServerState &cltState = updateMaster().serverState;
719c885c
AR
261 debugs(9, 3, "client state was " << cltState << " now: " << newState);
262 cltState = newState;
263}
264
265/**
266 * Ensure we do not double-complete on the forward entry.
e7ce227f 267 * We complete forwarding when the response adaptation is over
719c885c
AR
268 * (but we may still be waiting for 226 from the FTP server) and
269 * also when we get that 226 from the server (and adaptation is done).
270 *
9837567d 271 * TODO: Rewrite FwdState to ignore double completion?
719c885c
AR
272 */
273void
5517260a 274Ftp::Relay::completeForwarding()
719c885c
AR
275{
276 debugs(9, 5, forwardingCompleted);
277 if (forwardingCompleted)
278 return;
279 forwardingCompleted = true;
5517260a 280 Ftp::Client::completeForwarding();
434a79b0
DK
281}
282
283void
777e8376 284Ftp::Relay::failed(err_type error, int xerrno, ErrorState *ftpErr)
434a79b0
DK
285{
286 if (!doneWithServer())
e7ce227f 287 serverState(fssError);
434a79b0 288
719c885c
AR
289 // TODO: we need to customize ErrorState instead
290 if (entry->isEmpty())
291 failedErrorMessage(error, xerrno); // as a reply
292
777e8376 293 Ftp::Client::failed(error, xerrno, ftpErr);
434a79b0
DK
294}
295
296void
5517260a 297Ftp::Relay::failedErrorMessage(err_type error, int xerrno)
434a79b0
DK
298{
299 const Http::StatusCode httpStatus = failedHttpStatus(error);
300 HttpReply *const reply = createHttpReply(httpStatus);
301 entry->replaceHttpReply(reply);
83b053a0 302 fwd->request->detailError(error, SysErrorDetail::NewIfAny(xerrno));
434a79b0
DK
303}
304
305void
5517260a 306Ftp::Relay::processReplyBody()
434a79b0 307{
e7ce227f 308 debugs(9, 3, status());
434a79b0
DK
309
310 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
311 /*
312 * probably was aborted because content length exceeds one
313 * of the maximum size limits.
314 */
92cfc72f
CT
315 abortOnData("entry aborted after calling appendSuccessHeader()");
316 return;
317 }
318
319 if (master().userDataDone) {
320 // Squid-to-client data transfer done. Abort data transfer on our
321 // side to allow new commands from ftp client
322 abortOnData("Squid-to-client data connection is closed");
434a79b0
DK
323 return;
324 }
325
326#if USE_ADAPTATION
327
328 if (adaptationAccessCheckPending) {
e7ce227f 329 debugs(9, 3, "returning due to adaptationAccessCheckPending");
434a79b0
DK
330 return;
331 }
332
333#endif
334
adaff124
DK
335 if (data.readBuf != NULL && data.readBuf->hasContent()) {
336 const mb_size_t csize = data.readBuf->contentSize();
e7ce227f 337 debugs(9, 5, "writing " << csize << " bytes to the reply");
434a79b0
DK
338 addVirginReplyBody(data.readBuf->content(), csize);
339 data.readBuf->consume(csize);
340 }
341
342 entry->flush();
343
344 maybeReadVirginBody();
345}
346
347void
5517260a 348Ftp::Relay::handleControlReply()
434a79b0 349{
54a6c0cd
CT
350 if (!request->clientConnectionManager.valid()) {
351 debugs(9, 5, "client connection gone");
352 closeServer();
353 return;
354 }
355
5517260a 356 Ftp::Client::handleControlReply();
434a79b0
DK
357 if (ctrl.message == NULL)
358 return; // didn't get complete reply yet
359
29f23abf 360 assert(state < END);
000e664b 361 assert(this->SM_FUNCS[state] != NULL);
434a79b0
DK
362 (this->*SM_FUNCS[state])();
363}
364
365void
5517260a 366Ftp::Relay::handleRequestBodyProducerAborted()
434a79b0 367{
fccd4a86 368 ::Client::handleRequestBodyProducerAborted();
434a79b0 369
a5d444a5 370 failed(ERR_READ_ERROR);
434a79b0
DK
371}
372
719c885c 373bool
5517260a 374Ftp::Relay::mayReadVirginReplyBody() const
719c885c
AR
375{
376 // TODO: move this method to the regular FTP server?
377 return Comm::IsConnOpen(data.conn);
378}
379
434a79b0 380void
5517260a 381Ftp::Relay::forwardReply()
434a79b0
DK
382{
383 assert(entry->isEmpty());
434a79b0 384
15c82b83 385 HttpReply *const reply = createHttpReply(Http::scNoContent);
63df1d28 386 reply->sources |= Http::Message::srcFtp;
434a79b0
DK
387
388 setVirginReply(reply);
ba3fe8d9 389 markParsedVirginReplyAsWhole("Ftp::Relay::handleControlReply() does not forward partial replies");
434a79b0
DK
390 adaptOrFinalizeReply();
391
434a79b0
DK
392 serverComplete();
393}
394
395void
5517260a 396Ftp::Relay::forwardPreliminaryReply(const PreliminaryCb cb)
434a79b0 397{
e7ce227f 398 debugs(9, 5, "forwarding preliminary reply to client");
434a79b0 399
92ae4c86
AR
400 // we must prevent concurrent ConnStateData::sendControlMsg() calls
401 Must(thePreliminaryCb == NULL);
434a79b0
DK
402 thePreliminaryCb = cb;
403
404 const HttpReply::Pointer reply = createHttpReply(Http::scContinue);
405
406 // the Sink will use this to call us back after writing 1xx to the client
5517260a 407 typedef NullaryMemFunT<Relay> CbDialer;
434a79b0 408 const AsyncCall::Pointer call = JobCallback(11, 3, CbDialer, this,
27c841f6 409 Ftp::Relay::proceedAfterPreliminaryReply);
434a79b0
DK
410
411 CallJobHere1(9, 4, request->clientConnectionManager, ConnStateData,
412 ConnStateData::sendControlMsg, HttpControlMsg(reply, call));
413}
414
415void
5517260a 416Ftp::Relay::proceedAfterPreliminaryReply()
434a79b0 417{
e7ce227f 418 debugs(9, 5, "proceeding after preliminary reply to client");
434a79b0 419
92ae4c86 420 Must(thePreliminaryCb != NULL);
434a79b0
DK
421 const PreliminaryCb cb = thePreliminaryCb;
422 thePreliminaryCb = NULL;
423 (this->*cb)();
424}
425
426void
5517260a 427Ftp::Relay::forwardError(err_type error, int xerrno)
434a79b0 428{
434a79b0
DK
429 failed(error, xerrno);
430}
431
432HttpReply *
43446566
AR
433Ftp::Relay::createHttpReply(const Http::StatusCode httpStatus, const int64_t clen)
434{
435 HttpReply *const reply = Ftp::HttpReplyWrapper(ctrl.replycode, ctrl.last_reply, httpStatus, clen);
a2c7f09a
AR
436 if (ctrl.message) {
437 for (wordlist *W = ctrl.message; W && W->next; W = W->next)
789217a2
FC
438 reply->header.putStr(Http::HdrType::FTP_PRE, httpHeaderQuoteString(W->key).c_str());
439 // no hdrCacheInit() is needed for after Http::HdrType::FTP_PRE addition
a2c7f09a 440 }
434a79b0
DK
441 return reply;
442}
443
444void
5517260a 445Ftp::Relay::handleDataRequest()
434a79b0 446{
92ae4c86 447 data.addr(master().clientDataAddr);
434a79b0
DK
448 connectDataChannel();
449}
450
451void
5517260a 452Ftp::Relay::startDataDownload()
434a79b0
DK
453{
454 assert(Comm::IsConnOpen(data.conn));
455
e7ce227f 456 debugs(9, 3, "begin data transfer from " << data.conn->remote <<
434a79b0
DK
457 " (" << data.conn->local << ")");
458
459 HttpReply *const reply = createHttpReply(Http::scOkay, -1);
63df1d28 460 reply->sources |= Http::Message::srcFtp;
88df846b 461
434a79b0
DK
462 setVirginReply(reply);
463 adaptOrFinalizeReply();
464
434a79b0
DK
465 maybeReadVirginBody();
466 state = READING_DATA;
467}
468
a5d444a5 469void
5517260a 470Ftp::Relay::startDataUpload()
a5d444a5
DK
471{
472 assert(Comm::IsConnOpen(data.conn));
473
e7ce227f 474 debugs(9, 3, "begin data transfer to " << data.conn->remote <<
a5d444a5
DK
475 " (" << data.conn->local << ")");
476
477 if (!startRequestBodyFlow()) { // register to receive body data
478 failed();
479 return;
480 }
481
000e664b 482 state = WRITING_DATA;
a5d444a5
DK
483}
484
434a79b0 485void
5517260a 486Ftp::Relay::readGreeting()
434a79b0 487{
92ae4c86 488 assert(!master().clientReadGreeting);
434a79b0
DK
489
490 switch (ctrl.replycode) {
491 case 220:
92ae4c86 492 updateMaster().clientReadGreeting = true;
e7ce227f
AR
493 if (serverState() == fssBegin)
494 serverState(fssConnected);
29268829 495
43446566
AR
496 // Do not forward server greeting to the user because our FTP Server
497 // has greeted the user already. Also, an original origin greeting may
498 // confuse a user that has changed the origin mid-air.
29268829
DK
499
500 start();
434a79b0
DK
501 break;
502 case 120:
503 if (NULL != ctrl.message)
504 debugs(9, DBG_IMPORTANT, "FTP server is busy: " << ctrl.message->key);
5517260a 505 forwardPreliminaryReply(&Ftp::Relay::scheduleReadControlReply);
434a79b0
DK
506 break;
507 default:
508 failed();
509 break;
510 }
511}
512
513void
5517260a 514Ftp::Relay::sendCommand()
434a79b0 515{
789217a2 516 if (!fwd->request->header.has(Http::HdrType::FTP_COMMAND)) {
92cfc72f 517 abortAll("Internal error: FTP relay request with no command");
434a79b0
DK
518 return;
519 }
520
c8059ea9 521 HttpHeader &header = fwd->request->header;
789217a2
FC
522 assert(header.has(Http::HdrType::FTP_COMMAND));
523 const String &cmd = header.findEntry(Http::HdrType::FTP_COMMAND)->value;
524 assert(header.has(Http::HdrType::FTP_ARGUMENTS));
525 const String &params = header.findEntry(Http::HdrType::FTP_ARGUMENTS)->value;
c8059ea9
DK
526
527 if (params.size() > 0)
e7ce227f 528 debugs(9, 5, "command: " << cmd << ", parameters: " << params);
434a79b0 529 else
e7ce227f 530 debugs(9, 5, "command: " << cmd << ", no parameters");
434a79b0 531
e7ce227f 532 if (serverState() == fssHandlePasv ||
27c841f6
AR
533 serverState() == fssHandleEpsv ||
534 serverState() == fssHandleEprt ||
535 serverState() == fssHandlePort) {
000e664b
AR
536 sendPassive();
537 return;
538 }
539
1ab04517 540 SBuf buf;
c8059ea9 541 if (params.size() > 0)
1ab04517 542 buf.Printf("%s %s%s", cmd.termedBuf(), params.termedBuf(), Ftp::crlf);
434a79b0 543 else
1ab04517 544 buf.Printf("%s%s", cmd.termedBuf(), Ftp::crlf);
434a79b0 545
1ab04517 546 writeCommand(buf.c_str());
434a79b0 547
cff221ee 548 state =
e7ce227f
AR
549 serverState() == fssHandleCdup ? SENT_CDUP :
550 serverState() == fssHandleCwd ? SENT_CWD :
551 serverState() == fssHandleFeat ? SENT_FEAT :
552 serverState() == fssHandleDataRequest ? SENT_DATA_REQUEST :
553 serverState() == fssHandleUploadRequest ? SENT_DATA_REQUEST :
554 serverState() == fssConnected ? SENT_USER :
555 serverState() == fssHandlePass ? SENT_PASS :
434a79b0 556 SENT_COMMAND;
3238b9b6
CT
557
558 if (state == SENT_DATA_REQUEST) {
559 CbcPointer<ConnStateData> &mgr = fwd->request->clientConnectionManager;
560 if (mgr.valid()) {
561 if (Ftp::Server *srv = dynamic_cast<Ftp::Server*>(mgr.get())) {
562 typedef NullaryMemFunT<Ftp::Server> CbDialer;
563 AsyncCall::Pointer call = JobCallback(11, 3, CbDialer, srv,
564 Ftp::Server::startWaitingForOrigin);
565 ScheduleCallHere(call);
566 originWaitInProgress = true;
567 }
568 }
569 }
434a79b0
DK
570}
571
572void
5517260a 573Ftp::Relay::readReply()
434a79b0 574{
e7ce227f
AR
575 assert(serverState() == fssConnected ||
576 serverState() == fssHandleUploadRequest);
434a79b0 577
4f1c93a7 578 if (Is1xx(ctrl.replycode))
5517260a 579 forwardPreliminaryReply(&Ftp::Relay::scheduleReadControlReply);
434a79b0
DK
580 else
581 forwardReply();
582}
583
5f3898e2 584void
5517260a 585Ftp::Relay::readFeatReply()
5f3898e2 586{
e7ce227f 587 assert(serverState() == fssHandleFeat);
5f3898e2 588
4f1c93a7 589 if (Is1xx(ctrl.replycode))
5f3898e2
AR
590 return; // ignore preliminary replies
591
592 forwardReply();
593}
594
434a79b0 595void
5517260a 596Ftp::Relay::readPasvReply()
434a79b0 597{
e7ce227f 598 assert(serverState() == fssHandlePasv || serverState() == fssHandleEpsv || serverState() == fssHandlePort || serverState() == fssHandleEprt);
434a79b0 599
4f1c93a7 600 if (Is1xx(ctrl.replycode))
434a79b0
DK
601 return; // ignore preliminary replies
602
92ae4c86 603 if (handlePasvReply(updateMaster().clientDataAddr))
434a79b0 604 forwardReply();
73950ceb 605 else
434a79b0
DK
606 forwardError();
607}
608
cff221ee 609void
5517260a 610Ftp::Relay::readEpsvReply()
cff221ee 611{
4f1c93a7 612 if (Is1xx(ctrl.replycode))
cff221ee
AR
613 return; // ignore preliminary replies
614
92ae4c86 615 if (handleEpsvReply(updateMaster().clientDataAddr)) {
000e664b
AR
616 if (ctrl.message == NULL)
617 return; // didn't get complete reply yet
618
cff221ee 619 forwardReply();
000e664b 620 } else
cff221ee
AR
621 forwardError();
622}
623
434a79b0 624void
5517260a 625Ftp::Relay::readDataReply()
434a79b0 626{
e7ce227f
AR
627 assert(serverState() == fssHandleDataRequest ||
628 serverState() == fssHandleUploadRequest);
a5d444a5 629
6af7d28d 630 if (ctrl.replycode == 125 || ctrl.replycode == 150) {
e7ce227f 631 if (serverState() == fssHandleDataRequest)
5517260a 632 forwardPreliminaryReply(&Ftp::Relay::startDataDownload);
ec69bdb2
CT
633 else if (fwd->request->forcedBodyContinuation /*&& serverState() == fssHandleUploadRequest*/)
634 startDataUpload();
f53969cc 635 else // serverState() == fssHandleUploadRequest
5517260a 636 forwardPreliminaryReply(&Ftp::Relay::startDataUpload);
a5d444a5 637 } else
434a79b0 638 forwardReply();
54a6c0cd
CT
639}
640
641bool
5517260a 642Ftp::Relay::startDirTracking()
54a6c0cd
CT
643{
644 if (!fwd->request->clientConnectionManager->port->ftp_track_dirs)
645 return false;
646
e7ce227f 647 debugs(9, 5, "start directory tracking");
54a6c0cd
CT
648 savedReply.message = ctrl.message;
649 savedReply.lastCommand = ctrl.last_command;
650 savedReply.lastReply = ctrl.last_reply;
651 savedReply.replyCode = ctrl.replycode;
652
653 ctrl.last_command = NULL;
654 ctrl.last_reply = NULL;
655 ctrl.message = NULL;
656 ctrl.offset = 0;
657 writeCommand("PWD\r\n");
658 return true;
659}
660
661void
5517260a 662Ftp::Relay::stopDirTracking()
54a6c0cd 663{
e7ce227f 664 debugs(9, 5, "got code from pwd: " << ctrl.replycode << ", msg: " << ctrl.last_reply);
54a6c0cd 665
92ae4c86
AR
666 if (ctrl.replycode == 257)
667 updateMaster().workingDir = Ftp::UnescapeDoubleQuoted(ctrl.last_reply);
54a6c0cd
CT
668
669 wordlistDestroy(&ctrl.message);
670 safe_free(ctrl.last_command);
671 safe_free(ctrl.last_reply);
672
673 ctrl.message = savedReply.message;
674 ctrl.last_command = savedReply.lastCommand;
675 ctrl.last_reply = savedReply.lastReply;
676 ctrl.replycode = savedReply.replyCode;
677
678 savedReply.message = NULL;
679 savedReply.lastReply = NULL;
680 savedReply.lastCommand = NULL;
681}
682
683void
5517260a 684Ftp::Relay::readCwdOrCdupReply()
54a6c0cd 685{
e7ce227f
AR
686 assert(serverState() == fssHandleCwd ||
687 serverState() == fssHandleCdup);
54a6c0cd 688
e7ce227f 689 debugs(9, 5, "got code " << ctrl.replycode << ", msg: " << ctrl.last_reply);
54a6c0cd 690
4f1c93a7 691 if (Is1xx(ctrl.replycode))
54a6c0cd
CT
692 return;
693
694 if (weAreTrackingDir()) { // we are tracking
695 stopDirTracking(); // and forward the delayed response below
696 } else if (startDirTracking())
697 return;
698
699 forwardReply();
700}
701
702void
5517260a 703Ftp::Relay::readUserOrPassReply()
54a6c0cd 704{
4f1c93a7 705 if (Is1xx(ctrl.replycode))
54a6c0cd
CT
706 return; //Just ignore
707
708 if (weAreTrackingDir()) { // we are tracking
709 stopDirTracking(); // and forward the delayed response below
710 } else if (ctrl.replycode == 230) { // successful login
711 if (startDirTracking())
712 return;
713 }
714
715 forwardReply();
434a79b0
DK
716}
717
718void
5517260a 719Ftp::Relay::readTransferDoneReply()
434a79b0 720{
e7ce227f 721 debugs(9, 3, status());
434a79b0 722
ba3fe8d9
EB
723 // RFC 959 says that code 226 may indicate a successful response to a file
724 // transfer and file abort commands, but since we do not send abort
725 // commands, let's assume it was a successful file transfer.
726 if (ctrl.replycode == 226 || ctrl.replycode == 250) {
727 markParsedVirginReplyAsWhole("Ftp::Relay::readTransferDoneReply() code 226 or 250");
728 } else {
e7ce227f
AR
729 debugs(9, DBG_IMPORTANT, "got FTP code " << ctrl.replycode <<
730 " after reading response data");
434a79b0
DK
731 }
732
3238b9b6
CT
733 debugs(9, 2, "Complete data downloading");
734
735 serverComplete();
434a79b0
DK
736}
737
738void
43446566 739Ftp::Relay::dataChannelConnected(const CommConnectCbParams &io)
434a79b0 740{
e7ce227f 741 debugs(9, 3, status());
2b6b1bcb 742 dataConnWait.finish();
434a79b0 743
43446566 744 if (io.flag != Comm::OK) {
e7ce227f 745 debugs(9, 2, "failed to connect FTP server data channel");
43446566 746 forwardError(ERR_CONNECT_FAIL, io.xerrno);
434a79b0
DK
747 return;
748 }
749
43446566 750 debugs(9, 2, "connected FTP server data channel: " << io.conn);
434a79b0 751
43446566 752 data.opened(io.conn, dataCloser());
434a79b0
DK
753
754 sendCommand();
755}
756
757void
5517260a 758Ftp::Relay::scheduleReadControlReply()
434a79b0 759{
5517260a 760 Ftp::Client::scheduleReadControlReply(0);
434a79b0
DK
761}
762
92cfc72f
CT
763bool
764Ftp::Relay::abortOnData(const char *reason)
765{
766 debugs(9, 3, "aborting transaction for " << reason <<
767 "; FD " << (ctrl.conn != NULL ? ctrl.conn->fd : -1) << ", Data FD " << (data.conn != NULL ? data.conn->fd : -1) << ", this " << this);
768 // this method is only called to handle data connection problems
769 // the control connection should keep going
770
771#if USE_ADAPTATION
772 if (adaptedBodySource != NULL)
773 stopConsumingFrom(adaptedBodySource);
774#endif
775
776 if (Comm::IsConnOpen(data.conn))
777 dataComplete();
778
779 return !Comm::IsConnOpen(ctrl.conn);
780}
781
3238b9b6
CT
782void
783Ftp::Relay::stopOriginWait(int code)
784{
785 if (originWaitInProgress) {
786 CbcPointer<ConnStateData> &mgr = fwd->request->clientConnectionManager;
787 if (mgr.valid()) {
788 if (Ftp::Server *srv = dynamic_cast<Ftp::Server*>(mgr.get())) {
789 typedef UnaryMemFunT<Ftp::Server, int> CbDialer;
790 AsyncCall::Pointer call = asyncCall(11, 3, "Ftp::Server::stopWaitingForOrigin",
791 CbDialer(srv, &Ftp::Server::stopWaitingForOrigin, code));
792 ScheduleCallHere(call);
793 }
794 }
795 originWaitInProgress = false;
796 }
797}
798
92cfc72f 799void
7e9f330d 800Ftp::Relay::HandleStoreAbort(Relay *ftpClient)
92cfc72f 801{
92cfc72f 802 debugs(9, 2, "Client Data connection closed!");
92cfc72f
CT
803 if (Comm::IsConnOpen(ftpClient->data.conn))
804 ftpClient->dataComplete();
805}
806
2b6b1bcb 807void
5517260a 808Ftp::StartRelay(FwdState *const fwdState)
434a79b0 809{
2b6b1bcb 810 AsyncJob::Start(new Ftp::Relay(fwdState));
434a79b0 811}
f53969cc 812