40const char *
const crlf =
"\r\n";
47 unsigned const char *p;
50 for (p = (
unsigned const char *)buf, n = 1; *p; ++n, ++p)
56 for (p = (
unsigned const char *)buf, r=(
unsigned char *)ret; *p; ++p) {
68 assert((r - (
unsigned char *)ret) == n );
77 return ToSBuf(
"FTP_REPLY_CODE=", completionCode);
83 return ToSBuf(
"FTP reply with completion code ", completionCode);
97 assert(aCloser !=
nullptr);
141 last_command(nullptr),
177 import.toStr(addrBuf,
sizeof(addrBuf));
180 port =
import.port();
191 old_request(nullptr),
193 shortenReadTimeout(false)
218 scheduleReadControlReply(0);
224 if (data.readBuf ==
nullptr) {
225 data.readBuf =
new MemBuf;
237 debugs(9, 3,
"closing FTP server FD " << ctrl.conn->fd <<
", this " <<
this);
238 fwd->unregister(ctrl.conn);
243 debugs(9, 3,
"closing FTP data FD " << data.conn->fd <<
", this " <<
this);
247 debugs(9, 3,
"FTP ctrl and data connections closed. this " <<
this);
265 debugs(9, 3,
"entry-null=" << (entry?entry->isEmpty():0) <<
", entry=" << entry);
267 const char *command, *reply;
271 debugs(9, 6,
"error=" << err->
type <<
", code=" << xerrno <<
283 ctrl.message =
nullptr;
286 command = old_request;
288 command = ctrl.last_command;
290 if (command && strncmp(command,
"PASS", 4) == 0)
291 command =
"PASS <yourpassword>";
296 reply = ctrl.last_reply;
330 if (buffered_ok && ctrl.offset > 0) {
332 handleControlReply();
336 debugs(9, 3,
"cannot read without ctrl " << ctrl.conn);
347 const time_t tout = shortenReadTimeout ?
350 shortenReadTimeout =
false;
358 comm_read(ctrl.conn, ctrl.buf + ctrl.offset, ctrl.size - ctrl.offset, reader);
365 debugs(9, 3,
"FD " << io.
fd <<
", Read " << io.
size <<
" bytes");
376 if (abortOnData(
"entry aborted during control reply read"))
380 assert(ctrl.offset < ctrl.size);
388 "ERROR: FTP control reply read failure: " <<
xstrerr(io.
xerrno));
391 scheduleReadControlReply(0);
407 abortAll(
"zero control reply read");
411 unsigned int len =io.
size + ctrl.offset;
416 handleControlReply();
424 size_t bytes_used = 0;
427 if (!parseControlReply(bytes_used)) {
430 if (ctrl.offset == ctrl.size) {
431 ctrl.buf =
static_cast<char*
>(
memReallocBuf(ctrl.buf, ctrl.size << 1, &ctrl.size));
434 scheduleReadControlReply(0);
439 assert(ctrl.replycode >= 0);
442 if (ctrl.offset == bytes_used) {
447 assert(bytes_used < ctrl.offset);
448 ctrl.offset -= bytes_used;
449 memmove(ctrl.buf, ctrl.buf + bytes_used, ctrl.offset);
452 debugs(9, 3,
"state=" << state <<
", code=" << ctrl.replycode);
458 int code = ctrl.replycode;
463 debugs(9, 5,
"The control connection to the remote end is closed");
468 debugs(9, 2,
"PASV not supported by remote end");
474 debugs(9, 5,
"scanning: " << ctrl.last_reply);
476 buf = ctrl.last_reply + strcspn(ctrl.last_reply,
"0123456789");
479 fd_table[ctrl.conn->fd].ipaddr :
nullptr;
482 ctrl.conn->remote <<
": " << ctrl.last_reply);
494 int code = ctrl.replycode;
499 debugs(9, 5,
"The control connection to the remote end is closed");
503 if (code != 229 && code != 522) {
508 debugs(9,
DBG_IMPORTANT,
"ERROR: Broken FTP Server at " << ctrl.conn->remote <<
". Wrong accept code for EPSV");
510 debugs(9, 2,
"EPSV not supported by remote end");
512 return sendPassive();
523 debugs(9, 5,
"scanning: " << ctrl.last_reply);
524 buf = ctrl.last_reply;
525 while (buf !=
nullptr && *buf !=
'\0' && *buf !=
'\n' && *buf !=
'(')
527 if (buf !=
nullptr && *buf ==
'\n')
530 if (buf ==
nullptr || *buf ==
'\0') {
532 debugs(9,
DBG_IMPORTANT,
"ERROR: Broken FTP Server at " << ctrl.conn->remote <<
". 522 error missing protocol negotiation hints");
533 return sendPassive();
534 }
else if (strcmp(buf,
"(1)") == 0) {
536 return sendPassive();
537 }
else if (strcmp(buf,
"(2)") == 0) {
538 if (Ip::EnableIpv6) {
540 if (state == SENT_EPSV_2) {
544 return sendPassive();
549 return sendPassive();
553 debugs(9,
DBG_IMPORTANT,
"WARNING: Server at " << ctrl.conn->remote <<
" sent unknown protocol negotiation hint: " << buf);
554 return sendPassive();
564 debugs(9, 5,
"scanning: " << ctrl.last_reply);
566 buf = ctrl.last_reply + strcspn(ctrl.last_reply,
"(");
570 int n = sscanf(buf,
"(%c%c%c%hu%c)", &h1, &h2, &h3, &
port, &h4);
572 if (n < 4 || h1 != h2 || h1 != h3 || h1 != h4) {
574 ctrl.conn->remote <<
": " <<
577 return sendPassive();
582 ctrl.conn->remote <<
": " <<
585 return sendPassive();
591 ctrl.conn->remote <<
": " <<
594 return sendPassive();
598 remoteAddr = ctrl.conn->remote;
600 data.addr(remoteAddr);
612 debugs(9, 3,
"EPRT disabled by local administrator");
618 if (!openListenSocket()) {
623 debugs(9, 3,
"Listening for FTP data connection with FD " << data.conn);
635 debugs(9, 3,
"Listening for FTP data connection on port" <<
comm_local_port(data.conn->fd) <<
" or port?" << data.conn->local.port());
636 mb.
appendf(
"EPRT |%d|%s|%d|%s",
637 ( data.conn->local.isIPv6() ? 2 : 1 ),
666 debugs(9,
DBG_IMPORTANT,
"FTP does not allow PASV method after 'EPSV ALL' has been sent.");
694 if (ctrl.conn->local.isIPv6()) {
695 debugs(9, 5,
"FTP Channel is IPv6 (" << ctrl.conn->remote <<
") attempting EPSV 2 after EPSV ALL has failed.");
703 if (ctrl.conn->local.isIPv4()) {
704 debugs(9, 5,
"FTP Channel is IPv4 (" << ctrl.conn->remote <<
") attempting EPSV 1 after EPSV ALL has failed.");
709 debugs(9,
DBG_IMPORTANT,
"FTP does not allow PASV method after 'EPSV ALL' has been sent.");
716 debugs(9, 5,
"FTP Channel (" << ctrl.conn->remote <<
") rejects EPSV connection attempts. Trying PASV instead.");
728 debugs(9, 5,
"EPSV support manually disabled. Sending PASV for FTP Channel (" << ctrl.conn->remote <<
")");
732 debugs(9, 5,
"EPSV ALL manually enabled. Attempting with FTP Channel (" << ctrl.conn->remote <<
")");
734 state = SENT_EPSV_ALL;
736 if (ctrl.conn->local.isIPv6()) {
737 debugs(9, 5,
"FTP Channel (" << ctrl.conn->remote <<
"). Sending default EPSV 2");
741 if (ctrl.conn->local.isIPv4()) {
742 debugs(9, 5,
"Channel (" << ctrl.conn->remote <<
"). Sending default EPSV 1");
753 ctrl.message =
nullptr;
758 shortenReadTimeout =
true;
766 debugs(9, 5,
"The control connection to the remote end is closed");
774 ctrl.last_command =
xstrdup(
"Connect to server data port");
778 conn->
setAddrs(ctrl.conn->local, data.host);
781 conn->
tos = ctrl.conn->tos;
782 conn->
nfmark = ctrl.conn->nfmark;
791 cs->setHost(data.host);
792 dataConnWait.start(cs, callback);
815 data.conn->noteClosure();
816 if (data.listenConn !=
nullptr) {
817 data.listenConn->close();
818 data.listenConn =
nullptr;
828 debugs(9, 2,
"ftp<< " << buf);
839 ctrl.last_command = ebuf;
842 debugs(9, 2,
"cannot send to closing ctrl " << ctrl.conn);
850 Comm::Write(ctrl.conn, ctrl.last_command, strlen(ctrl.last_command), call,
nullptr);
852 scheduleReadControlReply(0);
859 debugs(9, 5,
"wrote " << io.
size <<
" bytes");
884 ctrl.conn->noteClosure();
886 doneWithFwd =
"ctrlClosed()";
887 mustStop(
"Ftp::Client::ctrlClosed");
893 debugs(9, 4, io.
conn <<
": '" << entry->url() <<
"'" );
895 if (abortOnBadEntry(
"entry went bad while waiting for a timeout"))
912 waitingForDelayAwareReadChance =
false;
913 data.read_pending =
false;
914 maybeReadVirginBody();
924 if (data.read_pending)
931 const auto read_sz = calcBufferSpaceToReserve(data.readBuf->spaceSize(), data.readBuf->spaceSize());
933 debugs(9, 9,
"FTP may read up to " << read_sz <<
" bytes");
938 data.read_pending =
true;
945 debugs(9,5,
"queueing read on FD " << data.conn->fd);
947 const auto amountToRead = entry->bytesWanted(
Range<size_t>(0, read_sz));
949 if (amountToRead <= 0) {
956 comm_read(data.conn, data.readBuf->space(), amountToRead, readCallback);
965 data.read_pending =
false;
967 debugs(9, 3,
"FD " << io.
fd <<
" Read " << io.
size <<
" bytes");
980 abortOnData(
"entry aborted during dataRead");
985 debugs(9, 5,
"appended " << io.
size <<
" bytes to readBuf");
986 data.readBuf->appended(io.
size);
988 DelayId delayId = entry->mem_obj->mostBytesAllowed();
993 for (j = io.
size - 1, bin = 0; j; ++bin)
1004 maybeReadVirginBody();
1010 }
else if (io.
size == 0) {
1011 debugs(9, 3,
"Calling dataComplete() because io.size == 0");
1054 scheduleReadControlReply(1);
1060 debugs(9, 3,
"aborting transaction for " << reason <<
1061 "; FD " << (ctrl.conn!=
nullptr?ctrl.conn->fd:-1) <<
", Data FD " << (data.conn!=
nullptr?data.conn->fd:-1) <<
", this " <<
this);
1121 const size_t len = ctrl.offset;
1122 sbuf = (
char *)
xmalloc(len + 1);
1124 end = sbuf + len - 1;
1126 while (*end !=
'\r' && *end !=
'\n' && end > sbuf)
1129 usable = end - sbuf;
1131 debugs(9, 3,
"usable = " << usable);
1134 debugs(9, 3,
"didn't find end of line");
1139 debugs(9, 3, len <<
" bytes to play with");
1142 s += strspn(s,
crlf);
1144 for (; s < end; s += strcspn(s,
crlf), s += strspn(s,
crlf)) {
1148 debugs(9, 5,
"s = {" << s <<
"}");
1150 linelen = strcspn(s,
crlf) + 1;
1156 complete = (*s >=
'0' && *s <=
'9' && *(s + 3) ==
' ');
1171 ctrl.replycode = atoi(list->
key);
1179 bytesUsed =
static_cast<size_t>(s - sbuf);
1187 ctrl.message =
head;
1188 assert(ctrl.replycode >= 0);
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer &callback)
void error(char *format,...)
squidaio_request_t * head
#define SQUID_TCP_SO_RCVBUF
Acl::Answer const & fastCheck()
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
int fd
FD which the call was about. Set by the async call creator.
Comm::Flag flag
comm layer result status.
Comm::ConnectionPointer conn
void setAddrs(const Ip::Address &aLocal, const Ip::Address &aRemote)
void leaveOrphanage()
resume relying on owner(s) to initiate an explicit connection closure
HttpRequestPointer request
struct ErrorState::@47 ftp
Http::StatusCode httpStatus
void close()
planned close: removes the close handler and calls comm_close
void opened(const Comm::ConnectionPointer &conn, const AsyncCall::Pointer &aCloser)
called after the socket is opened, sets up close handler
void clear()
remove the close handler, leave connection open
FTP client functionality shared among FTP Gateway and Relay clients.
virtual Http::StatusCode failedHttpStatus(err_type &error)
void start() override
called by AsyncStart; do not call directly
bool handleEpsvReply(Ip::Address &remoteAddr)
virtual void handleControlReply()
void dataRead(const CommIoCbParams &io)
void closeServer() override
void scheduleReadControlReply(int buffered_ok)
void sentRequestBody(const CommIoCbParams &io) override
void writeCommand(const char *buf)
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
bool handlePasvReply(Ip::Address &remoteAddr)
void switchTimeoutToDataChannel()
void connectDataChannel()
bool doneWithServer() const override
void writeCommandCallback(const CommIoCbParams &io)
Client(FwdState *fwdState)
void readControlReply(const CommIoCbParams &io)
void doneSendingRequestBody() override
void maybeReadVirginBody() override
read response data from the network
const Comm::ConnectionPointer & dataConnection() const override
virtual void failed(err_type error=ERR_NONE, int xerrno=0, ErrorState *ftperr=nullptr)
handle a fatal transaction error, closing the control connection
CtrlChannel ctrl
FTP control channel state.
bool parseControlReply(size_t &bytesUsed)
AsyncCall::Pointer dataCloser()
creates a data channel Comm close callback
void ctrlClosed(const CommCloseCbParams &io)
handler called by Comm when FTP control channel is closed unexpectedly
virtual void dataChannelConnected(const CommConnectCbParams &io)=0
void abortAll(const char *reason) override
abnormal transaction termination; reason is for debugging only
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
void noteDelayAwareReadChance() override
void addr(const Ip::Address &addr)
import host and port
SBuf verbose(const HttpRequestPointer &) const override
SBuf brief() const override
Comm::ConnectionPointer const & serverConnection() const
unsigned short port() const
void init(mb_size_t szInit, mb_size_t szMax)
char * content()
start of the added data
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
struct SquidConfig::@77 Timeout
struct SquidConfig::@92 Ftp
struct SquidConfig::@91 accessList
struct StatCounters::@105::@115 ftp
struct StatCounters::@105 server
struct StatCounters::@105::@115 all
static ErrorDetail::Pointer NewIfAny(const int errorNo)
void commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
unsigned short comm_local_port(int fd)
void commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
int ignoreErrno(int ierrno)
A const & min(A const &lhs, A const &rhs)
#define debugs(SECTION, LEVEL, CONTENT)
#define EBIT_TEST(flag, bit)
void fd_bytes(const int fd, const int len, const IoDirection direction)
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
void memFreeBuf(size_t size, void *)
void * memAllocBuf(size_t net_size, size_t *gross_size)
void * memReallocBuf(void *buf, size_t net_size, size_t *gross_size)
bool IsConnOpen(const Comm::ConnectionPointer &conn)
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
static char * escapeIAC(const char *buf)
bool ParseIpPort(const char *buf, const char *forceIp, Ip::Address &addr)
parses and validates "A1,A2,A3,A4,P1,P2" IP,port sequence
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
void wordlistDestroy(wordlist **list)
destroy a wordlist
const char * xstrerr(int error)
char * xstrncpy(char *dst, const char *src, size_t n)