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