/*
- * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "errorpage.h"
#include "fd.h"
#include "ftp/Parsing.h"
+#include "http/Stream.h"
#include "ip/tools.h"
#include "SquidConfig.h"
#include "SquidString.h"
}
void
-Ftp::Client::failed(err_type error, int xerrno)
+Ftp::Client::failed(err_type error, int xerrno, ErrorState *err)
{
debugs(9, 3, "entry-null=" << (entry?entry->isEmpty():0) << ", entry=" << entry);
const char *command, *reply;
- const Http::StatusCode httpStatus = failedHttpStatus(error);
- ErrorState *const ftperr = new ErrorState(error, httpStatus, fwd->request);
+ ErrorState *ftperr;
+
+ if (err) {
+ debugs(9, 6, "error=" << err->type << ", code=" << xerrno <<
+ ", status=" << err->httpStatus);
+ error = err->type;
+ ftperr = err;
+ } else {
+ Http::StatusCode httpStatus = failedHttpStatus(error);
+ ftperr = new ErrorState(error, httpStatus, fwd->request);
+ }
+
ftperr->xerrno = xerrno;
ftperr->ftp.server_msg = ctrl.message;
if (reply)
ftperr->ftp.reply = xstrdup(reply);
- fwd->request->detailError(error, xerrno);
- fwd->fail(ftperr);
-
- closeServer(); // we failed, so no serverComplete()
+ if (!err) {
+ fwd->request->detailError(error, xerrno);
+ fwd->fail(ftperr);
+ closeServer(); // we failed, so no serverComplete()
+ }
}
Http::StatusCode
/* We've already read some reply data */
handleControlReply();
} else {
+
+ if (!Comm::IsConnOpen(ctrl.conn)) {
+ debugs(9, 3, "cannot read without ctrl " << ctrl.conn);
+ return;
+ }
/*
* Cancel the timeout on the Data socket (if any) and
* establish one on the control socket.
debugs(9, 3, "FD " << io.fd << ", Read " << io.size << " bytes");
if (io.size > 0) {
- kb_incr(&(statCounter.server.all.kbytes_in), io.size);
- kb_incr(&(statCounter.server.ftp.kbytes_in), io.size);
+ statCounter.server.all.kbytes_in += io.size;
+ statCounter.server.ftp.kbytes_in += io.size;
}
if (io.flag == Comm::ERR_CLOSING)
return;
if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
- abortTransaction("entry aborted during control reply read");
- return;
+ if (abortOnData("entry aborted during control reply read"))
+ return;
}
assert(ctrl.offset < ctrl.size);
}
/* XXX this may end up having to be serverComplete() .. */
- abortTransaction("zero control reply read");
+ abortAll("zero control reply read");
return;
}
char *buf;
debugs(9, 3, status());
+ if (!Comm::IsConnOpen(ctrl.conn)) {
+ debugs(9, 5, "The control connection to the remote end is closed");
+ return false;
+ }
+
if (code != 227) {
debugs(9, 2, "PASV not supported by remote end");
return false;
char *buf;
debugs(9, 3, status());
+ if (!Comm::IsConnOpen(ctrl.conn)) {
+ debugs(9, 5, "The control connection to the remote end is closed");
+ return false;
+ }
+
if (code != 229 && code != 522) {
if (code == 200) {
/* handle broken servers (RFC 2428 says OK code for EPSV MUST be 229 not 200) */
debugs(9, DBG_IMPORTANT, "WARNING: Server at " << ctrl.conn->remote << " sent unknown protocol negotiation hint: " << buf);
return sendPassive();
}
+ /* coverity[unreachable] */
+ /* safeguard against possible future bugs in above conditions */
failed(ERR_FTP_FAILURE, 0);
return false;
}
/* RFC 2428 defines EPRT as IPv6 equivalent to IPv4 PORT command. */
/* Which can be used by EITHER protocol. */
debugs(9, 3, "Listening for FTP data connection on port" << comm_local_port(data.conn->fd) << " or port?" << data.conn->local.port());
- mb.Printf("EPRT |%d|%s|%d|%s",
- ( data.conn->local.isIPv6() ? 2 : 1 ),
- data.conn->local.toStr(buf,MAX_IPSTRLEN),
- comm_local_port(data.conn->fd), Ftp::crlf );
+ mb.appendf("EPRT |%d|%s|%d|%s",
+ ( data.conn->local.isIPv6() ? 2 : 1 ),
+ data.conn->local.toStr(buf,MAX_IPSTRLEN),
+ comm_local_port(data.conn->fd), Ftp::crlf );
state = SENT_EPRT;
writeCommand(mb.content());
case SENT_EPSV_ALL: /* EPSV ALL resulted in a bad response. Try ther EPSV methods. */
if (ctrl.conn->local.isIPv6()) {
debugs(9, 5, "FTP Channel is IPv6 (" << ctrl.conn->remote << ") attempting EPSV 2 after EPSV ALL has failed.");
- mb.Printf("EPSV 2%s", Ftp::crlf);
+ mb.appendf("EPSV 2%s", Ftp::crlf);
state = SENT_EPSV_2;
break;
}
case SENT_EPSV_2: /* EPSV IPv6 failed. Try EPSV IPv4 */
if (ctrl.conn->local.isIPv4()) {
debugs(9, 5, "FTP Channel is IPv4 (" << ctrl.conn->remote << ") attempting EPSV 1 after EPSV ALL has failed.");
- mb.Printf("EPSV 1%s", Ftp::crlf);
+ mb.appendf("EPSV 1%s", Ftp::crlf);
state = SENT_EPSV_1;
break;
} else if (Config.Ftp.epsv_all) {
case SENT_EPSV_1: /* EPSV options exhausted. Try PASV now. */
debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << ") rejects EPSV connection attempts. Trying PASV instead.");
- mb.Printf("PASV%s", Ftp::crlf);
+ mb.appendf("PASV%s", Ftp::crlf);
state = SENT_PASV;
break;
}
if (!doEpsv) {
debugs(9, 5, "EPSV support manually disabled. Sending PASV for FTP Channel (" << ctrl.conn->remote <<")");
- mb.Printf("PASV%s", Ftp::crlf);
+ mb.appendf("PASV%s", Ftp::crlf);
state = SENT_PASV;
} else if (Config.Ftp.epsv_all) {
debugs(9, 5, "EPSV ALL manually enabled. Attempting with FTP Channel (" << ctrl.conn->remote <<")");
- mb.Printf("EPSV ALL%s", Ftp::crlf);
+ mb.appendf("EPSV ALL%s", Ftp::crlf);
state = SENT_EPSV_ALL;
} else {
if (ctrl.conn->local.isIPv6()) {
debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << "). Sending default EPSV 2");
- mb.Printf("EPSV 2%s", Ftp::crlf);
+ mb.appendf("EPSV 2%s", Ftp::crlf);
state = SENT_EPSV_2;
}
if (ctrl.conn->local.isIPv4()) {
debugs(9, 5, "Channel (" << ctrl.conn->remote <<"). Sending default EPSV 1");
- mb.Printf("EPSV 1%s", Ftp::crlf);
+ mb.appendf("EPSV 1%s", Ftp::crlf);
state = SENT_EPSV_1;
}
}
void
Ftp::Client::connectDataChannel()
{
+ if (!Comm::IsConnOpen(ctrl.conn)) {
+ debugs(9, 5, "The control connection to the remote end is closed");
+ return;
+ }
+
safe_free(ctrl.last_command);
safe_free(ctrl.last_reply);
if (io.size > 0) {
fd_bytes(io.fd, io.size, FD_WRITE);
- kb_incr(&(statCounter.server.all.kbytes_out), io.size);
- kb_incr(&(statCounter.server.ftp.kbytes_out), io.size);
+ statCounter.server.all.kbytes_out += io.size;
+ statCounter.server.ftp.kbytes_out += io.size;
}
if (io.flag == Comm::ERR_CLOSING)
{
debugs(9, 4, status());
ctrl.clear();
+ doneWithFwd = "ctrlClosed()"; // assume FwdState is monitoring too
mustStop("Ftp::Client::ctrlClosed");
}
debugs(9, 3, "FD " << io.fd << " Read " << io.size << " bytes");
if (io.size > 0) {
- kb_incr(&(statCounter.server.all.kbytes_in), io.size);
- kb_incr(&(statCounter.server.ftp.kbytes_in), io.size);
+ statCounter.server.all.kbytes_in += io.size;
+ statCounter.server.ftp.kbytes_in += io.size;
}
if (io.flag == Comm::ERR_CLOSING)
assert(io.fd == data.conn->fd);
if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
- abortTransaction("entry aborted during dataRead");
+ abortOnData("entry aborted during dataRead");
return;
}
scheduleReadControlReply(1);
}
-/**
- * Quickly abort the transaction
- *
- \todo destruction should be sufficient as the destructor should cleanup,
- * including canceling close handlers
- */
void
-Ftp::Client::abortTransaction(const char *reason)
+Ftp::Client::abortAll(const char *reason)
{
debugs(9, 3, "aborting transaction for " << reason <<
"; FD " << (ctrl.conn!=NULL?ctrl.conn->fd:-1) << ", Data FD " << (data.conn!=NULL?data.conn->fd:-1) << ", this " << this);
- if (Comm::IsConnOpen(ctrl.conn)) {
- ctrl.conn->close();
- return;
- }
-
- fwd->handleUnregisteredServerEnd();
- mustStop("Ftp::Client::abortTransaction");
+ mustStop(reason);
}
/**
Ftp::Client::sentRequestBody(const CommIoCbParams &io)
{
if (io.size > 0)
- kb_incr(&(statCounter.server.ftp.kbytes_out), io.size);
+ statCounter.server.ftp.kbytes_out += io.size;
::Client::sentRequestBody(io);
}