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