static FtpRequestHandler FtpHandleDataRequest;
static FtpRequestHandler FtpHandleUploadRequest;
-static bool FtpCheckDataConnection(ClientSocketContext *context);
+static bool FtpCheckDataConnPre(ClientSocketContext *context);
+static bool FtpCheckDataConnPost(ClientSocketContext *context);
static void FtpSetReply(ClientSocketContext *context, const int code, const char *msg);
static bool FtpSupportedCommand(const String &name);
FtpHandlePasvReply, // FTP_HANDLE_PASV
FtpHandlePortReply, // FTP_HANDLE_PORT
FtpHandleDataReply, // FTP_HANDLE_DATA_REQUEST
- FtpHandleUploadReply, // FTP_HANDLE_DATA_REQUEST
+ FtpHandleUploadReply, // FTP_HANDLE_UPLOAD_REQUEST
FtpHandleErrorReply // FTP_ERROR
};
const ConnStateData::FtpState state = context->getConn()->ftp.state;
return;
}
+ if (!conn->ftp.dataConn) {
+ // We got STREAM_COMPLETE (or error) and closed the client data conn.
+ debugs(33, 3, "ignoring FTP srv data response after clt data closure");
+ return;
+ }
+
+ if (!FtpCheckDataConnPost(context))
+ return;
+
debugs(33, 7, HERE << data.length);
if (data.length <= 0) {
return;
}
- if (!Comm::IsConnOpen(conn->ftp.dataConn)) {
- debugs(33, 3, HERE << "got FTP reply data when client data connection "
- "is closed, ignoring");
- return;
- }
-
MemBuf mb;
mb.init(data.length + 1, data.length + 1);
mb.append(data.data, data.length);
static void
FtpHandleUploadReply(ClientSocketContext *context, const HttpReply *reply, StoreIOBuffer data)
{
+ if (!FtpCheckDataConnPost(context))
+ return;
+
FtpWriteForwardedReply(context, reply);
}
bool
FtpHandleDataRequest(ClientSocketContext *context, String &cmd, String ¶ms)
{
- if (!FtpCheckDataConnection(context))
+ if (!FtpCheckDataConnPre(context))
return false;
FtpChangeState(context->getConn(), ConnStateData::FTP_HANDLE_DATA_REQUEST, "FtpHandleDataRequest");
bool
FtpHandleUploadRequest(ClientSocketContext *context, String &cmd, String ¶ms)
{
- if (!FtpCheckDataConnection(context))
+ if (!FtpCheckDataConnPre(context))
return false;
FtpChangeState(context->getConn(), ConnStateData::FTP_HANDLE_UPLOAD_REQUEST, "FtpHandleDataRequest");
return true;
}
+/// check that client data connection is ready for future I/O or at least
+/// has a chance of becoming ready soon.
bool
-FtpCheckDataConnection(ClientSocketContext *context)
+FtpCheckDataConnPre(ClientSocketContext *context)
{
ConnStateData *const connState = context->getConn();
if (Comm::IsConnOpen(connState->ftp.dataConn))
return true;
if (Comm::IsConnOpen(connState->ftp.dataListenConn)) {
- FtpSetReply(context, 425, "Data connection is not established");
- return false;
+ // We are still waiting for a client to connect to us after PASV.
+ // Perhaps client's data conn handshake has not reached us yet.
+ // After we talk to the server, FtpCheckDataConnPost() will recheck.
+ debugs(33, 3, "expecting clt data conn " << connState->ftp.dataListenConn);
+ return true;
}
if (!connState->ftp.dataConn || connState->ftp.dataConn->remote.isAnyAddr()) {
- // XXX: use client address and default port instead.
+ debugs(33, 5, "missing " << connState->ftp.dataConn);
+ // TODO: use client address and default port instead.
FtpSetReply(context, 425, "Use PORT or PASV first");
return false;
}
return false; // ConnStateData::processFtpRequest waits FtpHandleConnectDone
}
+/// Check that client data connection is ready for immediate I/O.
+static bool
+FtpCheckDataConnPost(ClientSocketContext *context)
+{
+ const ConnStateData *connState = context->getConn();
+ assert(connState);
+ const Comm::ConnectionPointer &dataConn = connState->ftp.dataConn;
+ if (dataConn != NULL && !Comm::IsConnOpen(dataConn)) {
+ // This check is deliberately missing from FtpCheckDataConnPre()
+ debugs(33, 3, "missing client data conn: " << dataConn);
+ FtpWriteCustomReply(context, 425, "Data connection is not established");
+ dataConn->close();
+ return false;
+ }
+ return true;
+}
+
void
FtpHandleConnectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
{