]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/clients/FtpClient.cc
fe737832469327478a786715d5a84276b8dffd3e
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 09 File Transfer Protocol (FTP) */
12 #include "acl/FilledChecklist.h"
13 #include "base/AsyncJobCalls.h"
14 #include "base/Range.h"
15 #include "client_side.h"
16 #include "clients/FtpClient.h"
17 #include "comm/ConnOpener.h"
18 #include "comm/Read.h"
19 #include "comm/TcpAcceptor.h"
20 #include "comm/Write.h"
21 #include "error/SysErrorDetail.h"
22 #include "errorpage.h"
24 #include "ftp/Parsing.h"
25 #include "http/Stream.h"
27 #include "sbuf/SBuf.h"
28 #include "sbuf/Stream.h"
29 #include "SquidConfig.h"
30 #include "SquidString.h"
31 #include "StatCounters.h"
40 const char *const crlf
= "\r\n";
43 escapeIAC(const char *buf
)
47 unsigned const char *p
;
50 for (p
= (unsigned const char *)buf
, n
= 1; *p
; ++n
, ++p
)
54 ret
= (char *)xmalloc(n
);
56 for (p
= (unsigned const char *)buf
, r
=(unsigned char *)ret
; *p
; ++p
) {
68 assert((r
- (unsigned char *)ret
) == n
);
72 /* Ftp::ErrorDetail */
75 Ftp::ErrorDetail::brief() const
77 return ToSBuf("FTP_REPLY_CODE=", completionCode
);
81 Ftp::ErrorDetail::verbose(const HttpRequest::Pointer
&) const
83 return ToSBuf("FTP reply with completion code ", completionCode
);
88 /// configures the channel with a descriptor and registers a close handler
90 Ftp::Channel::opened(const Comm::ConnectionPointer
&newConn
,
91 const AsyncCall::Pointer
&aCloser
)
93 assert(!Comm::IsConnOpen(conn
));
94 assert(closer
== nullptr);
96 assert(Comm::IsConnOpen(newConn
));
97 assert(aCloser
!= nullptr);
100 conn
->leaveOrphanage();
102 comm_add_close_handler(conn
->fd
, closer
);
105 /// planned close: removes the close handler and calls comm_close
107 Ftp::Channel::close()
109 // channels with active listeners will be closed when the listener handler dies.
110 if (Comm::IsConnOpen(conn
)) {
111 comm_remove_close_handler(conn
->fd
, closer
);
112 conn
->close(); // we do not expect to be called back
118 Ftp::Channel::forget()
120 if (Comm::IsConnOpen(conn
)) {
121 commUnsetConnTimeout(conn
);
122 comm_remove_close_handler(conn
->fd
, closer
);
128 Ftp::Channel::clear()
134 /* Ftp::CtrlChannel */
136 Ftp::CtrlChannel::CtrlChannel():
141 last_command(nullptr),
145 buf
= static_cast<char*>(memAllocBuf(4096, &size
));
148 Ftp::CtrlChannel::~CtrlChannel()
150 memFreeBuf(size
, buf
);
152 wordlistDestroy(&message
);
153 safe_free(last_command
);
154 safe_free(last_reply
);
157 /* Ftp::DataChannel */
159 Ftp::DataChannel::DataChannel():
167 Ftp::DataChannel::~DataChannel()
173 Ftp::DataChannel::addr(const Ip::Address
&import
)
175 static char addrBuf
[MAX_IPSTRLEN
];
176 import
.toStr(addrBuf
, sizeof(addrBuf
));
178 host
= xstrdup(addrBuf
);
179 port
= import
.port();
184 Ftp::Client::Client(FwdState
*fwdState
):
185 AsyncJob("Ftp::Client"),
190 old_request(nullptr),
192 shortenReadTimeout(false)
194 ++statCounter
.server
.all
.requests
;
195 ++statCounter
.server
.ftp
.requests
;
197 ctrl
.last_command
= xstrdup("Connect to server");
199 typedef CommCbMemFunT
<Client
, CommCloseCbParams
> Dialer
;
200 const AsyncCall::Pointer closer
= JobCallback(9, 5, Dialer
, this,
201 Ftp::Client::ctrlClosed
);
202 ctrl
.opened(fwdState
->serverConnection(), closer
);
205 Ftp::Client::~Client()
209 safe_free(old_request
);
210 safe_free(old_reply
);
211 fwd
= nullptr; // refcounted
217 scheduleReadControlReply(0);
221 Ftp::Client::initReadBuf()
223 if (data
.readBuf
== nullptr) {
224 data
.readBuf
= new MemBuf
;
225 data
.readBuf
->init(4096, SQUID_TCP_SO_RCVBUF
);
230 * Close the FTP server connection(s). Used by serverComplete().
233 Ftp::Client::closeServer()
235 if (Comm::IsConnOpen(ctrl
.conn
)) {
236 debugs(9, 3, "closing FTP server FD " << ctrl
.conn
->fd
<< ", this " << this);
237 fwd
->unregister(ctrl
.conn
);
241 if (Comm::IsConnOpen(data
.conn
)) {
242 debugs(9, 3, "closing FTP data FD " << data
.conn
->fd
<< ", this " << this);
246 debugs(9, 3, "FTP ctrl and data connections closed. this " << this);
250 * Did we close all FTP server connection(s)?
252 \retval true Both server control and data channels are closed. And not waiting for a new data connection to open.
253 \retval false Either control channel or data is still active.
256 Ftp::Client::doneWithServer() const
258 return !Comm::IsConnOpen(ctrl
.conn
) && !Comm::IsConnOpen(data
.conn
);
262 Ftp::Client::failed(err_type error
, int xerrno
, ErrorState
*err
)
264 debugs(9, 3, "entry-null=" << (entry
?entry
->isEmpty():0) << ", entry=" << entry
);
266 const char *command
, *reply
;
270 debugs(9, 6, "error=" << err
->type
<< ", code=" << xerrno
<<
271 ", status=" << err
->httpStatus
);
275 Http::StatusCode httpStatus
= failedHttpStatus(error
);
276 ftperr
= new ErrorState(error
, httpStatus
, fwd
->request
, fwd
->al
);
279 ftperr
->xerrno
= xerrno
;
281 ftperr
->ftp
.server_msg
= ctrl
.message
;
282 ctrl
.message
= nullptr;
285 command
= old_request
;
287 command
= ctrl
.last_command
;
289 if (command
&& strncmp(command
, "PASS", 4) == 0)
290 command
= "PASS <yourpassword>";
295 reply
= ctrl
.last_reply
;
298 ftperr
->ftp
.request
= xstrdup(command
);
301 ftperr
->ftp
.reply
= xstrdup(reply
);
304 fwd
->request
->detailError(error
, SysErrorDetail::NewIfAny(xerrno
));
306 closeServer(); // we failed, so no serverComplete()
311 Ftp::Client::failedHttpStatus(err_type
&error
)
313 if (error
== ERR_NONE
)
314 error
= ERR_FTP_FAILURE
;
315 return error
== ERR_READ_TIMEOUT
? Http::scGatewayTimeout
:
321 * Looks like there are no longer anymore callers that set
322 * buffered_ok=1. Perhaps it can be removed at some point.
325 Ftp::Client::scheduleReadControlReply(int buffered_ok
)
327 debugs(9, 3, ctrl
.conn
);
329 if (buffered_ok
&& ctrl
.offset
> 0) {
330 /* We've already read some reply data */
331 handleControlReply();
334 if (!Comm::IsConnOpen(ctrl
.conn
)) {
335 debugs(9, 3, "cannot read without ctrl " << ctrl
.conn
);
339 * Cancel the timeout on the Data socket (if any) and
340 * establish one on the control socket.
342 if (Comm::IsConnOpen(data
.conn
)) {
343 commUnsetConnTimeout(data
.conn
);
346 const time_t tout
= shortenReadTimeout
?
347 min(Config
.Timeout
.connect
, Config
.Timeout
.read
):
349 shortenReadTimeout
= false; // we only need to do this once, after PASV
351 typedef CommCbMemFunT
<Client
, CommTimeoutCbParams
> TimeoutDialer
;
352 AsyncCall::Pointer timeoutCall
= JobCallback(9, 5, TimeoutDialer
, this, Ftp::Client::timeout
);
353 commSetConnTimeout(ctrl
.conn
, tout
, timeoutCall
);
355 typedef CommCbMemFunT
<Client
, CommIoCbParams
> Dialer
;
356 AsyncCall::Pointer reader
= JobCallback(9, 5, Dialer
, this, Ftp::Client::readControlReply
);
357 comm_read(ctrl
.conn
, ctrl
.buf
+ ctrl
.offset
, ctrl
.size
- ctrl
.offset
, reader
);
362 Ftp::Client::readControlReply(const CommIoCbParams
&io
)
364 debugs(9, 3, "FD " << io
.fd
<< ", Read " << io
.size
<< " bytes");
367 statCounter
.server
.all
.kbytes_in
+= io
.size
;
368 statCounter
.server
.ftp
.kbytes_in
+= io
.size
;
371 if (io
.flag
== Comm::ERR_CLOSING
)
374 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
375 if (abortOnData("entry aborted during control reply read"))
379 assert(ctrl
.offset
< ctrl
.size
);
381 if (io
.flag
== Comm::OK
&& io
.size
> 0) {
382 fd_bytes(io
.fd
, io
.size
, IoDirection::Read
);
385 if (io
.flag
!= Comm::OK
) {
386 debugs(50, ignoreErrno(io
.xerrno
) ? 3 : DBG_IMPORTANT
,
387 "ERROR: FTP control reply read failure: " << xstrerr(io
.xerrno
));
389 if (ignoreErrno(io
.xerrno
)) {
390 scheduleReadControlReply(0);
392 failed(ERR_READ_ERROR
, io
.xerrno
);
393 /* failed closes ctrl.conn and frees ftpState */
399 if (entry
->store_status
== STORE_PENDING
) {
400 failed(ERR_FTP_FAILURE
, 0);
401 /* failed closes ctrl.conn and frees ftpState */
405 /* XXX this may end up having to be serverComplete() .. */
406 abortAll("zero control reply read");
410 unsigned int len
=io
.size
+ ctrl
.offset
;
412 assert(len
<= ctrl
.size
);
413 if (Comm::IsConnOpen(ctrl
.conn
))
414 commUnsetConnTimeout(ctrl
.conn
); // we are done waiting for ctrl reply
415 handleControlReply();
419 Ftp::Client::handleControlReply()
421 debugs(9, 3, status());
423 size_t bytes_used
= 0;
424 wordlistDestroy(&ctrl
.message
);
426 if (!parseControlReply(bytes_used
)) {
427 /* didn't get complete reply yet */
429 if (ctrl
.offset
== ctrl
.size
) {
430 ctrl
.buf
= static_cast<char*>(memReallocBuf(ctrl
.buf
, ctrl
.size
<< 1, &ctrl
.size
));
433 scheduleReadControlReply(0);
437 assert(ctrl
.message
); // the entire FTP server response, line by line
438 assert(ctrl
.replycode
>= 0); // FTP status code (from the last line)
439 assert(ctrl
.last_reply
); // FTP reason (from the last line)
441 if (ctrl
.offset
== bytes_used
) {
445 /* Got some data past the complete reply */
446 assert(bytes_used
< ctrl
.offset
);
447 ctrl
.offset
-= bytes_used
;
448 memmove(ctrl
.buf
, ctrl
.buf
+ bytes_used
, ctrl
.offset
);
451 debugs(9, 3, "state=" << state
<< ", code=" << ctrl
.replycode
);
455 Ftp::Client::handlePasvReply(Ip::Address
&srvAddr
)
457 int code
= ctrl
.replycode
;
459 debugs(9, 3, status());
461 if (!Comm::IsConnOpen(ctrl
.conn
)) {
462 debugs(9, 5, "The control connection to the remote end is closed");
467 debugs(9, 2, "PASV not supported by remote end");
471 /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
472 /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
473 debugs(9, 5, "scanning: " << ctrl
.last_reply
);
475 buf
= ctrl
.last_reply
+ strcspn(ctrl
.last_reply
, "0123456789");
477 const char *forceIp
= Config
.Ftp
.sanitycheck
?
478 fd_table
[ctrl
.conn
->fd
].ipaddr
: nullptr;
479 if (!Ftp::ParseIpPort(buf
, forceIp
, srvAddr
)) {
480 debugs(9, DBG_IMPORTANT
, "Unsafe PASV reply from " <<
481 ctrl
.conn
->remote
<< ": " << ctrl
.last_reply
);
491 Ftp::Client::handleEpsvReply(Ip::Address
&remoteAddr
)
493 int code
= ctrl
.replycode
;
495 debugs(9, 3, status());
497 if (!Comm::IsConnOpen(ctrl
.conn
)) {
498 debugs(9, 5, "The control connection to the remote end is closed");
502 if (code
!= 229 && code
!= 522) {
504 /* handle broken servers (RFC 2428 says OK code for EPSV MUST be 229 not 200) */
505 /* vsftpd for one send '200 EPSV ALL ok.' without even port info.
506 * Its okay to re-send EPSV 1/2 but nothing else. */
507 debugs(9, DBG_IMPORTANT
, "ERROR: Broken FTP Server at " << ctrl
.conn
->remote
<< ". Wrong accept code for EPSV");
509 debugs(9, 2, "EPSV not supported by remote end");
511 return sendPassive();
515 /* Peer responded with a list of supported methods:
516 * 522 Network protocol not supported, use (1)
517 * 522 Network protocol not supported, use (1,2)
518 * 522 Network protocol not supported, use (2)
519 * TODO: Handle the (1,2) case which may happen after EPSV ALL. Close
520 * data + control without self-destructing and re-open from scratch.
522 debugs(9, 5, "scanning: " << ctrl
.last_reply
);
523 buf
= ctrl
.last_reply
;
524 while (buf
!= nullptr && *buf
!= '\0' && *buf
!= '\n' && *buf
!= '(')
526 if (buf
!= nullptr && *buf
== '\n')
529 if (buf
== nullptr || *buf
== '\0') {
530 /* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
531 debugs(9, DBG_IMPORTANT
, "ERROR: Broken FTP Server at " << ctrl
.conn
->remote
<< ". 522 error missing protocol negotiation hints");
532 return sendPassive();
533 } else if (strcmp(buf
, "(1)") == 0) {
534 state
= SENT_EPSV_2
; /* simulate having sent and failed EPSV 2 */
535 return sendPassive();
536 } else if (strcmp(buf
, "(2)") == 0) {
537 if (Ip::EnableIpv6
) {
538 /* If server only supports EPSV 2 and we have already tried that. Go straight to EPRT */
539 if (state
== SENT_EPSV_2
) {
542 /* or try the next Passive mode down the chain. */
543 return sendPassive();
546 /* Server only accept EPSV in IPv6 traffic. */
547 state
= SENT_EPSV_1
; /* simulate having sent and failed EPSV 1 */
548 return sendPassive();
551 /* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
552 debugs(9, DBG_IMPORTANT
, "WARNING: Server at " << ctrl
.conn
->remote
<< " sent unknown protocol negotiation hint: " << buf
);
553 return sendPassive();
555 /* coverity[unreachable] */
556 /* safeguard against possible future bugs in above conditions */
557 failed(ERR_FTP_FAILURE
, 0);
561 /* 229 Entering Extended Passive Mode (|||port|) */
562 /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
563 debugs(9, 5, "scanning: " << ctrl
.last_reply
);
565 buf
= ctrl
.last_reply
+ strcspn(ctrl
.last_reply
, "(");
569 int n
= sscanf(buf
, "(%c%c%c%hu%c)", &h1
, &h2
, &h3
, &port
, &h4
);
571 if (n
< 4 || h1
!= h2
|| h1
!= h3
|| h1
!= h4
) {
572 debugs(9, DBG_IMPORTANT
, "ERROR: Invalid EPSV reply from " <<
573 ctrl
.conn
->remote
<< ": " <<
576 return sendPassive();
580 debugs(9, DBG_IMPORTANT
, "Unsafe EPSV reply from " <<
581 ctrl
.conn
->remote
<< ": " <<
584 return sendPassive();
587 if (Config
.Ftp
.sanitycheck
) {
589 debugs(9, DBG_IMPORTANT
, "Unsafe EPSV reply from " <<
590 ctrl
.conn
->remote
<< ": " <<
593 return sendPassive();
597 remoteAddr
= ctrl
.conn
->remote
;
598 remoteAddr
.port(port
);
599 data
.addr(remoteAddr
);
603 // FTP clients do not support EPRT and PORT commands yet.
604 // The Ftp::Client::sendEprt() will fail because of the unimplemented
605 // openListenSocket() or sendPort() methods
607 Ftp::Client::sendEprt()
609 if (!Config
.Ftp
.eprt
) {
610 /* Disabled. Switch immediately to attempting old PORT command. */
611 debugs(9, 3, "EPRT disabled by local administrator");
615 debugs(9, 3, status());
617 if (!openListenSocket()) {
618 failed(ERR_FTP_FAILURE
, 0);
622 debugs(9, 3, "Listening for FTP data connection with FD " << data
.conn
);
623 if (!Comm::IsConnOpen(data
.conn
)) {
624 // TODO: Set error message.
625 failed(ERR_FTP_FAILURE
, 0);
631 char buf
[MAX_IPSTRLEN
];
632 /* RFC 2428 defines EPRT as IPv6 equivalent to IPv4 PORT command. */
633 /* Which can be used by EITHER protocol. */
634 debugs(9, 3, "Listening for FTP data connection on port" << comm_local_port(data
.conn
->fd
) << " or port?" << data
.conn
->local
.port());
635 mb
.appendf("EPRT |%d|%s|%d|%s",
636 ( data
.conn
->local
.isIPv6() ? 2 : 1 ),
637 data
.conn
->local
.toStr(buf
,MAX_IPSTRLEN
),
638 comm_local_port(data
.conn
->fd
), Ftp::crlf
);
641 writeCommand(mb
.content());
646 Ftp::Client::sendPort()
648 failed(ERR_FTP_FAILURE
, 0);
653 Ftp::Client::sendPassive()
655 debugs(9, 3, status());
658 * Checks for EPSV ALL special conditions:
659 * If enabled to be sent, squid MUST NOT request any other connect methods.
660 * If 'ALL' is sent and fails the entire FTP Session fails.
661 * NP: By my reading exact EPSV protocols maybe attempted, but only EPSV method. */
662 if (Config
.Ftp
.epsv_all
&& state
== SENT_EPSV_1
) {
663 // We are here because the last "EPSV 1" failed, but because of epsv_all
664 // no other method allowed.
665 debugs(9, DBG_IMPORTANT
, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
666 failed(ERR_FTP_FAILURE
, 0);
670 /// Closes any old FTP-Data connection which may exist. */
674 * Checks for previous EPSV/PASV failures on this server/session.
675 * Diverts to EPRT immediately if they are not working. */
676 if (!Config
.Ftp
.passive
|| state
== SENT_PASV
) {
684 * Send EPSV (ALL,2,1) or PASV on the control channel.
686 * - EPSV ALL is used if enabled.
687 * - EPSV 2 is used if ALL is disabled and IPv6 is available and ctrl channel is IPv6.
688 * - EPSV 1 is used if EPSV 2 (IPv6) fails or is not available or ctrl channel is IPv4.
689 * - PASV is used if EPSV 1 fails.
692 case SENT_EPSV_ALL
: /* EPSV ALL resulted in a bad response. Try ther EPSV methods. */
693 if (ctrl
.conn
->local
.isIPv6()) {
694 debugs(9, 5, "FTP Channel is IPv6 (" << ctrl
.conn
->remote
<< ") attempting EPSV 2 after EPSV ALL has failed.");
695 mb
.appendf("EPSV 2%s", Ftp::crlf
);
699 [[fallthrough
]]; // to skip EPSV 2
701 case SENT_EPSV_2
: /* EPSV IPv6 failed. Try EPSV IPv4 */
702 if (ctrl
.conn
->local
.isIPv4()) {
703 debugs(9, 5, "FTP Channel is IPv4 (" << ctrl
.conn
->remote
<< ") attempting EPSV 1 after EPSV ALL has failed.");
704 mb
.appendf("EPSV 1%s", Ftp::crlf
);
707 } else if (Config
.Ftp
.epsv_all
) {
708 debugs(9, DBG_IMPORTANT
, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
709 failed(ERR_FTP_FAILURE
, 0);
712 [[fallthrough
]]; // to skip EPSV 1
714 case SENT_EPSV_1
: /* EPSV options exhausted. Try PASV now. */
715 debugs(9, 5, "FTP Channel (" << ctrl
.conn
->remote
<< ") rejects EPSV connection attempts. Trying PASV instead.");
716 mb
.appendf("PASV%s", Ftp::crlf
);
722 if (Config
.accessList
.ftp_epsv
) {
723 ACLFilledChecklist
checklist(Config
.accessList
.ftp_epsv
, fwd
->request
, nullptr);
724 doEpsv
= checklist
.fastCheck().allowed();
727 debugs(9, 5, "EPSV support manually disabled. Sending PASV for FTP Channel (" << ctrl
.conn
->remote
<<")");
728 mb
.appendf("PASV%s", Ftp::crlf
);
730 } else if (Config
.Ftp
.epsv_all
) {
731 debugs(9, 5, "EPSV ALL manually enabled. Attempting with FTP Channel (" << ctrl
.conn
->remote
<<")");
732 mb
.appendf("EPSV ALL%s", Ftp::crlf
);
733 state
= SENT_EPSV_ALL
;
735 if (ctrl
.conn
->local
.isIPv6()) {
736 debugs(9, 5, "FTP Channel (" << ctrl
.conn
->remote
<< "). Sending default EPSV 2");
737 mb
.appendf("EPSV 2%s", Ftp::crlf
);
740 if (ctrl
.conn
->local
.isIPv4()) {
741 debugs(9, 5, "Channel (" << ctrl
.conn
->remote
<<"). Sending default EPSV 1");
742 mb
.appendf("EPSV 1%s", Ftp::crlf
);
751 wordlistDestroy(&ctrl
.message
);
752 ctrl
.message
= nullptr; //No message to return to client.
753 ctrl
.offset
= 0; //reset readed response, to make room read the next response
755 writeCommand(mb
.content());
757 shortenReadTimeout
= true;
762 Ftp::Client::connectDataChannel()
764 if (!Comm::IsConnOpen(ctrl
.conn
)) {
765 debugs(9, 5, "The control connection to the remote end is closed");
769 safe_free(ctrl
.last_command
);
771 safe_free(ctrl
.last_reply
);
773 ctrl
.last_command
= xstrdup("Connect to server data port");
775 // Generate a new data channel descriptor to be opened.
776 Comm::ConnectionPointer conn
= new Comm::Connection
;
777 conn
->setAddrs(ctrl
.conn
->local
, data
.host
);
779 conn
->remote
.port(data
.port
);
780 conn
->tos
= ctrl
.conn
->tos
;
781 conn
->nfmark
= ctrl
.conn
->nfmark
;
782 // Using non-local addresses in TPROXY mode requires appropriate socket option.
783 conn
->flags
|= ctrl
.conn
->flags
& COMM_TRANSPARENT
;
785 debugs(9, 3, "connecting to " << conn
->remote
);
787 typedef CommCbMemFunT
<Client
, CommConnectCbParams
> Dialer
;
788 AsyncCall::Pointer callback
= JobCallback(9, 3, Dialer
, this, Ftp::Client::dataChannelConnected
);
789 const auto cs
= new Comm::ConnOpener(conn
, callback
, Config
.Timeout
.connect
);
790 cs
->setHost(data
.host
);
791 dataConnWait
.start(cs
, callback
);
795 Ftp::Client::openListenSocket()
800 /// creates a data channel Comm close callback
802 Ftp::Client::dataCloser()
804 typedef CommCbMemFunT
<Client
, CommCloseCbParams
> Dialer
;
805 return JobCallback(9, 5, Dialer
, this, Ftp::Client::dataClosed
);
808 /// handler called by Comm when FTP data channel is closed unexpectedly
810 Ftp::Client::dataClosed(const CommCloseCbParams
&)
812 debugs(9, 4, status());
814 data
.conn
->noteClosure();
815 if (data
.listenConn
!= nullptr) {
816 data
.listenConn
->close();
817 data
.listenConn
= nullptr;
823 Ftp::Client::writeCommand(const char *buf
)
826 /* trace FTP protocol communications at level 2 */
827 debugs(9, 2, "ftp<< " << buf
);
829 if (Config
.Ftp
.telnet
)
830 ebuf
= escapeIAC(buf
);
834 safe_free(ctrl
.last_command
);
836 safe_free(ctrl
.last_reply
);
838 ctrl
.last_command
= ebuf
;
840 if (!Comm::IsConnOpen(ctrl
.conn
)) {
841 debugs(9, 2, "cannot send to closing ctrl " << ctrl
.conn
);
842 // TODO: assert(ctrl.closer != NULL);
846 typedef CommCbMemFunT
<Client
, CommIoCbParams
> Dialer
;
847 AsyncCall::Pointer call
= JobCallback(9, 5, Dialer
, this,
848 Ftp::Client::writeCommandCallback
);
849 Comm::Write(ctrl
.conn
, ctrl
.last_command
, strlen(ctrl
.last_command
), call
, nullptr);
851 scheduleReadControlReply(0);
855 Ftp::Client::writeCommandCallback(const CommIoCbParams
&io
)
858 debugs(9, 5, "wrote " << io
.size
<< " bytes");
861 fd_bytes(io
.fd
, io
.size
, IoDirection::Write
);
862 statCounter
.server
.all
.kbytes_out
+= io
.size
;
863 statCounter
.server
.ftp
.kbytes_out
+= io
.size
;
866 if (io
.flag
== Comm::ERR_CLOSING
)
870 debugs(9, DBG_IMPORTANT
, "ERROR: FTP command write failure: " << io
.conn
<< ": " << xstrerr(io
.xerrno
));
871 failed(ERR_WRITE_ERROR
, io
.xerrno
);
872 /* failed closes ctrl.conn and frees ftpState */
877 /// handler called by Comm when FTP control channel is closed unexpectedly
879 Ftp::Client::ctrlClosed(const CommCloseCbParams
&)
881 debugs(9, 4, status());
883 ctrl
.conn
->noteClosure();
885 doneWithFwd
= "ctrlClosed()"; // assume FwdState is monitoring too
886 mustStop("Ftp::Client::ctrlClosed");
890 Ftp::Client::timeout(const CommTimeoutCbParams
&io
)
892 debugs(9, 4, io
.conn
<< ": '" << entry
->url() << "'" );
894 if (abortOnBadEntry("entry went bad while waiting for a timeout"))
897 failed(ERR_READ_TIMEOUT
, 0);
898 /* failed() closes ctrl.conn and frees ftpState */
901 const Comm::ConnectionPointer
&
902 Ftp::Client::dataConnection() const
908 Ftp::Client::noteDelayAwareReadChance()
910 data
.read_pending
= false;
911 maybeReadVirginBody();
915 Ftp::Client::maybeReadVirginBody()
918 if (!Comm::IsConnOpen(data
.conn
) || fd_table
[data
.conn
->fd
].closing())
921 if (data
.read_pending
)
926 const int read_sz
= replyBodySpace(*data
.readBuf
, 0);
928 debugs(9, 9, "FTP may read up to " << read_sz
<< " bytes");
930 if (read_sz
< 2) // see http.cc
933 data
.read_pending
= true;
935 typedef CommCbMemFunT
<Client
, CommTimeoutCbParams
> TimeoutDialer
;
936 AsyncCall::Pointer timeoutCall
= JobCallback(9, 5,
937 TimeoutDialer
, this, Ftp::Client::timeout
);
938 commSetConnTimeout(data
.conn
, Config
.Timeout
.read
, timeoutCall
);
940 debugs(9,5,"queueing read on FD " << data
.conn
->fd
);
942 const auto amountToRead
= entry
->bytesWanted(Range
<size_t>(0, read_sz
));
944 if (amountToRead
<= 0) {
949 using ReadDialer
= CommCbMemFunT
<Client
, CommIoCbParams
>;
950 AsyncCall::Pointer readCallback
= JobCallback(9, 5, ReadDialer
, this, Client::dataRead
);
951 comm_read(data
.conn
, data
.readBuf
->space(), amountToRead
, readCallback
);
955 Ftp::Client::dataRead(const CommIoCbParams
&io
)
960 data
.read_pending
= false;
962 debugs(9, 3, "FD " << io
.fd
<< " Read " << io
.size
<< " bytes");
965 statCounter
.server
.all
.kbytes_in
+= io
.size
;
966 statCounter
.server
.ftp
.kbytes_in
+= io
.size
;
969 if (io
.flag
== Comm::ERR_CLOSING
)
972 assert(io
.fd
== data
.conn
->fd
);
974 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
975 abortOnData("entry aborted during dataRead");
979 if (io
.flag
== Comm::OK
&& io
.size
> 0) {
980 debugs(9, 5, "appended " << io
.size
<< " bytes to readBuf");
981 data
.readBuf
->appended(io
.size
);
983 DelayId delayId
= entry
->mem_obj
->mostBytesAllowed();
984 delayId
.bytesIn(io
.size
);
986 ++ IOStats
.Ftp
.reads
;
988 for (j
= io
.size
- 1, bin
= 0; j
; ++bin
)
991 ++ IOStats
.Ftp
.read_hist
[bin
];
994 if (io
.flag
!= Comm::OK
) {
995 debugs(50, ignoreErrno(io
.xerrno
) ? 3 : DBG_IMPORTANT
,
996 "ERROR: FTP data read failure: " << xstrerr(io
.xerrno
));
998 if (ignoreErrno(io
.xerrno
)) {
999 maybeReadVirginBody();
1001 failed(ERR_READ_ERROR
, 0);
1002 /* failed closes ctrl.conn and frees ftpState */
1005 } else if (io
.size
== 0) {
1006 debugs(9, 3, "Calling dataComplete() because io.size == 0");
1009 * Dangerous curves ahead. This call to dataComplete was
1010 * calling scheduleReadControlReply, handleControlReply,
1011 * and then ftpReadTransferDone. If ftpReadTransferDone
1012 * gets unexpected status code, it closes down the control
1013 * socket and our FtpStateData object gets destroyed. As
1014 * a workaround we no longer set the 'buffered_ok' flag in
1015 * the scheduleReadControlReply call.
1024 Ftp::Client::dataComplete()
1026 debugs(9, 3,status());
1028 /* Connection closed; transfer done. */
1030 /// Close data channel, if any, to conserve resources while we wait.
1033 /* expect the "transfer complete" message on the control socket */
1036 * Previously, this was the only place where we set the
1037 * 'buffered_ok' flag when calling scheduleReadControlReply().
1038 * It caused some problems if the FTP server returns an unexpected
1039 * status code after the data command. FtpStateData was being
1040 * deleted in the middle of dataRead().
1042 /* AYJ: 2011-01-13: Bug 2581.
1043 * 226 status is possibly waiting in the ctrl buffer.
1044 * The connection will hang if we DONT send buffered_ok.
1045 * This happens on all transfers which can be completely sent by the
1046 * server before the 150 started status message is read in by Squid.
1047 * ie all transfers of about one packet hang.
1049 scheduleReadControlReply(1);
1053 Ftp::Client::abortAll(const char *reason
)
1055 debugs(9, 3, "aborting transaction for " << reason
<<
1056 "; FD " << (ctrl
.conn
!=nullptr?ctrl
.conn
->fd
:-1) << ", Data FD " << (data
.conn
!=nullptr?data
.conn
->fd
:-1) << ", this " << this);
1061 * Cancel the timeout on the Control socket and establish one
1062 * on the data socket
1065 Ftp::Client::switchTimeoutToDataChannel()
1067 commUnsetConnTimeout(ctrl
.conn
);
1069 typedef CommCbMemFunT
<Client
, CommTimeoutCbParams
> TimeoutDialer
;
1070 AsyncCall::Pointer timeoutCall
= JobCallback(9, 5, TimeoutDialer
, this,
1071 Ftp::Client::timeout
);
1072 commSetConnTimeout(data
.conn
, Config
.Timeout
.read
, timeoutCall
);
1076 Ftp::Client::sentRequestBody(const CommIoCbParams
&io
)
1079 statCounter
.server
.ftp
.kbytes_out
+= io
.size
;
1080 ::Client::sentRequestBody(io
);
1084 * called after we wrote the last byte of the request body
1087 Ftp::Client::doneSendingRequestBody()
1089 ::Client::doneSendingRequestBody();
1090 debugs(9, 3, status());
1092 /* NP: RFC 959 3.3. DATA CONNECTION MANAGEMENT
1093 * if transfer type is 'stream' call dataComplete()
1094 * otherwise leave open. (reschedule control channel read?)
1098 /// Parses FTP server control response into ctrl structure fields,
1099 /// setting bytesUsed and returning true on success.
1101 Ftp::Client::parseControlReply(size_t &bytesUsed
)
1108 wordlist
*head
= nullptr;
1110 wordlist
**tail
= &head
;
1112 debugs(9, 3, status());
1114 * We need a NULL-terminated buffer for scanning, ick
1116 const size_t len
= ctrl
.offset
;
1117 sbuf
= (char *)xmalloc(len
+ 1);
1118 xstrncpy(sbuf
, ctrl
.buf
, len
+ 1);
1119 end
= sbuf
+ len
- 1;
1121 while (*end
!= '\r' && *end
!= '\n' && end
> sbuf
)
1124 usable
= end
- sbuf
;
1126 debugs(9, 3, "usable = " << usable
);
1129 debugs(9, 3, "didn't find end of line");
1134 debugs(9, 3, len
<< " bytes to play with");
1137 s
+= strspn(s
, crlf
);
1139 for (; s
< end
; s
+= strcspn(s
, crlf
), s
+= strspn(s
, crlf
)) {
1143 debugs(9, 5, "s = {" << s
<< "}");
1145 linelen
= strcspn(s
, crlf
) + 1;
1151 complete
= (*s
>= '0' && *s
<= '9' && *(s
+ 3) == ' ');
1153 list
= new wordlist();
1155 list
->key
= (char *)xmalloc(linelen
);
1157 xstrncpy(list
->key
, s
, linelen
);
1159 /* trace the FTP communication chat at level 2 */
1160 debugs(9, 2, "ftp>> " << list
->key
);
1163 // use list->key for last_reply because s contains the new line
1164 ctrl
.last_reply
= xstrdup(list
->key
+ 4);
1165 ctrl
.replycode
= atoi(list
->key
);
1173 bytesUsed
= static_cast<size_t>(s
- sbuf
);
1177 wordlistDestroy(&head
);
1181 ctrl
.message
= head
;
1182 assert(ctrl
.replycode
>= 0);
1183 assert(ctrl
.last_reply
);
1184 assert(ctrl
.message
);