mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
993 lines
31 KiB
993 lines
31 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ftpio.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code for the FTP transparent proxy's network
|
|
I/O completion routines.
|
|
|
|
Author:
|
|
|
|
Qiang Wang (qiangw) 10-Apr-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "ftpmsg.h"
|
|
|
|
VOID
|
|
FtpAcceptCompletionRoutine(
|
|
ULONG ErrorCode,
|
|
ULONG BytesTransferred,
|
|
PNH_BUFFER Bufferp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked upon completion of an accept operation
|
|
on a FTP transparent proxy stream socket.
|
|
|
|
Arguments:
|
|
|
|
ErrorCode - Win32 status code for the I/O operation
|
|
|
|
BytesTransferred - number of bytes in 'Bufferp'
|
|
|
|
Bufferp - holds the local and remote IP address and port
|
|
for the connection.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Environment:
|
|
|
|
Runs in the context of a worker-thread which has just dequeued
|
|
an I/O completion packet from the common I/O completion port
|
|
with which our stream sockets are associated.
|
|
A reference to the component will have been made on our behalf
|
|
by 'NhAcceptStreamSocket'.
|
|
A reference to the interface will have been made on our behalf
|
|
by whoever issued the I/O request.
|
|
|
|
--*/
|
|
|
|
{
|
|
SOCKET AcceptedSocket;
|
|
PFTP_CONNECTION Connectionp;
|
|
ULONG Error;
|
|
PFTP_INTERFACE Interfacep;
|
|
SOCKET ListeningSocket;
|
|
PROFILE("FtpAcceptCompletionRoutine");
|
|
do {
|
|
AcceptedSocket = (SOCKET)Bufferp->Socket;
|
|
Interfacep = (PFTP_INTERFACE)Bufferp->Context;
|
|
ListeningSocket = (SOCKET)Bufferp->Context2;
|
|
|
|
//
|
|
// Acquire three additional references to the interface
|
|
// for the followup requests that we will issue below,
|
|
// and lock the interface.
|
|
//
|
|
|
|
EnterCriticalSection(&FtpInterfaceLock);
|
|
if (!FTP_REFERENCE_INTERFACE(Interfacep)) {
|
|
LeaveCriticalSection(&FtpInterfaceLock);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhDeleteStreamSocket(AcceptedSocket);
|
|
break;
|
|
}
|
|
FTP_REFERENCE_INTERFACE(Interfacep);
|
|
FTP_REFERENCE_INTERFACE(Interfacep);
|
|
LeaveCriticalSection(&FtpInterfaceLock);
|
|
ACQUIRE_LOCK(Interfacep);
|
|
|
|
//
|
|
// Process the accept-completion.
|
|
// First look for an error code. If an error occurred
|
|
// and the interface is no longer active, end the completion-handling.
|
|
// Otherwise, attempt to reissue the accept-request.
|
|
//
|
|
|
|
if (ErrorCode) {
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpAcceptCompletionRoutine: error %d for interface %d",
|
|
ErrorCode, Interfacep->Index
|
|
);
|
|
|
|
//
|
|
// See if the interface is still active and, if so, reissue
|
|
// the accept-request. Since we will not be creating an active
|
|
// endpoint, we won't need the second reference to the interface.
|
|
//
|
|
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
|
|
if (!FTP_INTERFACE_ACTIVE(Interfacep)) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhDeleteStreamSocket(AcceptedSocket);
|
|
} else {
|
|
|
|
//
|
|
// Reissue the accept-request. Note that the callee is now
|
|
// responsible for the reference we made to the interface.
|
|
//
|
|
|
|
Error =
|
|
FtpAcceptConnectionInterface(
|
|
Interfacep,
|
|
ListeningSocket,
|
|
AcceptedSocket,
|
|
Bufferp,
|
|
NULL
|
|
);
|
|
RELEASE_LOCK(Interfacep);
|
|
if (Error) {
|
|
NhReleaseBuffer(Bufferp);
|
|
NhDeleteStreamSocket(AcceptedSocket);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpAcceptCompletionRoutine: error %d reissuing accept",
|
|
Error
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now see if the interface is operational.
|
|
// If it isn't, we need to destroy the accepted socket
|
|
// and return control.
|
|
//
|
|
|
|
if (!FTP_INTERFACE_ACTIVE(Interfacep)) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhDeleteStreamSocket(AcceptedSocket);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpAcceptCompletionRoutine: interface %d inactive",
|
|
Interfacep->Index
|
|
);
|
|
InterlockedIncrement(
|
|
reinterpret_cast<LPLONG>(&FtpStatistics.ConnectionsDropped)
|
|
);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We now create a 'FTP_CONNECTION' for the new connection,
|
|
// in the process launching operations for the connection.
|
|
// The connection management module will handle the accepted socket
|
|
// from here onward, and is responsible for the references to the
|
|
// interface that were made above.
|
|
//
|
|
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpAcceptCompletionRoutine: socket %d accepting connection",
|
|
ListeningSocket
|
|
);
|
|
Error =
|
|
FtpCreateConnection(
|
|
Interfacep,
|
|
ListeningSocket,
|
|
AcceptedSocket,
|
|
Bufferp->Buffer,
|
|
&Connectionp
|
|
);
|
|
if (Error) {
|
|
InterlockedIncrement(
|
|
reinterpret_cast<LPLONG>(&FtpStatistics.ConnectionsDropped)
|
|
);
|
|
} else {
|
|
InterlockedIncrement(
|
|
reinterpret_cast<LPLONG>(&FtpStatistics.ConnectionsAccepted)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Finally, issue an accept operation for the next connection-request
|
|
// on the listening socket. Note that the callee is responsible
|
|
// for releasing the reference to the interface in case of a failure.
|
|
//
|
|
|
|
Error =
|
|
FtpAcceptConnectionInterface(
|
|
Interfacep,
|
|
ListeningSocket,
|
|
INVALID_SOCKET,
|
|
Bufferp,
|
|
NULL
|
|
);
|
|
RELEASE_LOCK(Interfacep);
|
|
if (Error) { NhReleaseBuffer(Bufferp); }
|
|
|
|
} while(FALSE);
|
|
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
DEREFERENCE_FTP();
|
|
} // FtpAcceptCompletionRoutine
|
|
|
|
|
|
VOID
|
|
FtpCloseEndpointNotificationRoutine(
|
|
ULONG ErrorCode,
|
|
ULONG BytesTransferred,
|
|
PNH_BUFFER Bufferp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked upon notification of a close operation
|
|
on a FTP transparent proxy stream socket.
|
|
|
|
Arguments:
|
|
|
|
ErrorCode - Win32 status code for the I/O operation
|
|
|
|
BytesTransferred - number of bytes in 'Bufferp'
|
|
|
|
Bufferp - holds context information for the closed socket.
|
|
Note that we are not allowed to release this buffer here.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Environment:
|
|
|
|
Runs in the context of a wait-thread.
|
|
A reference to the component will have been made on our behalf
|
|
by 'NhAcceptStreamSocket' or 'NhConnectStreamSocket'.
|
|
A reference to the interface will have been made on our behalf
|
|
by whoever issued the I/O request.
|
|
Both of these references are released here.
|
|
|
|
--*/
|
|
|
|
{
|
|
SOCKET ClosedSocket;
|
|
ULONG EndpointId;
|
|
PFTP_INTERFACE Interfacep;
|
|
PROFILE("FtpCloseEndpointNotificationRoutine");
|
|
do {
|
|
ClosedSocket = (SOCKET)Bufferp->Socket;
|
|
Interfacep = (PFTP_INTERFACE)Bufferp->Context;
|
|
EndpointId = PtrToUlong(Bufferp->Context2);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpCloseEndpointNotificationRoutine: endpoint %d socket %d "
|
|
"closed, error %d",
|
|
EndpointId, ClosedSocket, ErrorCode
|
|
);
|
|
|
|
#if 0
|
|
PFTP_ENDPOINT Endpointp;
|
|
|
|
//
|
|
// Lock the interface, and retrieve the endpoint whose socket has
|
|
// been closed.
|
|
//
|
|
|
|
ACQUIRE_LOCK(Interfacep);
|
|
Endpointp = FtpLookupInterfaceEndpoint(Interfacep, EndpointId, NULL);
|
|
if (Endpointp) {
|
|
FtpCloseActiveEndpoint(Endpointp, ClosedSocket);
|
|
}
|
|
RELEASE_LOCK(Interfacep);
|
|
#endif
|
|
} while(FALSE);
|
|
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
DEREFERENCE_FTP();
|
|
} // FtpCloseEndpointNotificationRoutine
|
|
|
|
|
|
VOID
|
|
FtpConnectEndpointCompletionRoutine(
|
|
ULONG ErrorCode,
|
|
ULONG BytesTransferred,
|
|
PNH_BUFFER Bufferp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked upon completion of a connect operation
|
|
on a FTP transparent proxy stream socket.
|
|
|
|
Arguments:
|
|
|
|
ErrorCode - Win32 status code for the I/O operation
|
|
|
|
BytesTransferred - number of bytes in 'Bufferp'
|
|
|
|
Bufferp - holds the context information for the endpoint.
|
|
Note that we are not allowed to release this buffer here.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Environment:
|
|
|
|
Runs in the context of a wait-thread.
|
|
A reference to the component will have been made on our behalf
|
|
by 'NhConnectStreamSocket'.
|
|
A reference to the interface will have been made on our behalf
|
|
by whoever issued the I/O request.
|
|
Neither of these references may be released here; they are both
|
|
released in the close-notification routine, which we are guaranteed
|
|
will be invoked. (Eventually.)
|
|
|
|
--*/
|
|
|
|
{
|
|
SOCKET ConnectedSocket;
|
|
ULONG EndpointId;
|
|
PFTP_ENDPOINT Endpointp;
|
|
ULONG Error;
|
|
PFTP_INTERFACE Interfacep;
|
|
PROFILE("FtpConnectEndpointCompletionRoutine");
|
|
do {
|
|
ConnectedSocket = (SOCKET)Bufferp->Socket;
|
|
Interfacep = (PFTP_INTERFACE)Bufferp->Context;
|
|
EndpointId = PtrToUlong(Bufferp->Context2);
|
|
|
|
//
|
|
// Acquire two additional references to the interface
|
|
// for the endpoint-activation that we will initiate below,
|
|
// lock the interface, and retrieve the endpoint.
|
|
//
|
|
|
|
EnterCriticalSection(&FtpInterfaceLock);
|
|
if (!FTP_REFERENCE_INTERFACE(Interfacep)) {
|
|
LeaveCriticalSection(&FtpInterfaceLock);
|
|
break;
|
|
}
|
|
FTP_REFERENCE_INTERFACE(Interfacep);
|
|
LeaveCriticalSection(&FtpInterfaceLock);
|
|
ACQUIRE_LOCK(Interfacep);
|
|
Endpointp = FtpLookupInterfaceEndpoint(Interfacep, EndpointId, NULL);
|
|
|
|
//
|
|
// First look for an error code.
|
|
// If an error occurred and the interface is still active,
|
|
// destroy the endpoint.
|
|
// If the interface is inactive, we're done, since the endpoint
|
|
// will have already been destroyed.
|
|
// If the interface is active but the endpoint has already
|
|
// been destroyed, end this connection-attempt.
|
|
//
|
|
|
|
if (ErrorCode) {
|
|
if (Endpointp) {
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpConnectEndpointCompletionRoutine: deleting endpoint %d "
|
|
"on error %d", EndpointId, ErrorCode
|
|
);
|
|
FtpDeleteActiveEndpoint(Endpointp);
|
|
}
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
break;
|
|
} else if (!FTP_INTERFACE_ACTIVE(Interfacep)) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpConnectEndpointCompletionRoutine: interface %d inactive",
|
|
Interfacep->Index
|
|
);
|
|
break;
|
|
} else if (!Endpointp) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpConnectEndpointCompletionRoutine: endpoint %d removed",
|
|
EndpointId
|
|
);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We now activate the endpoint, beginning data transfer.
|
|
// Note that it is the caller's responsibility to release
|
|
// the two new references to the interface if an error occurs.
|
|
//
|
|
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpConnectEndpointCompletionRoutine: endpoint %d socket %d "
|
|
"connected", EndpointId, ConnectedSocket
|
|
);
|
|
Error = FtpActivateActiveEndpoint(Interfacep, Endpointp);
|
|
RELEASE_LOCK(Interfacep);
|
|
|
|
} while(FALSE);
|
|
|
|
} // FtpConnectEndpointCompletionRoutine
|
|
|
|
|
|
VOID
|
|
FtpReadEndpointCompletionRoutine(
|
|
ULONG ErrorCode,
|
|
ULONG BytesTransferred,
|
|
PNH_BUFFER Bufferp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked upon completion of a read operation
|
|
on a FTP transparent proxy stream socket.
|
|
|
|
The contexts for all reads are the interface and endpoint-identifier
|
|
corresponding to the socket, stored in 'Context' and 'Context2',
|
|
respectively.
|
|
|
|
Arguments:
|
|
|
|
ErrorCode - Win32 status code for the I/O operation
|
|
|
|
BytesTransferred - number of bytes in 'Bufferp'
|
|
|
|
Bufferp - holds data read from the socket
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Environment:
|
|
|
|
Runs in the context of a worker-thread which has just dequeued an
|
|
I/O completion packet from the common I/O completion port with which
|
|
our stream sockets are associated.
|
|
A reference to the component will have been made on our behalf
|
|
by 'NhReadStreamSocket'.
|
|
A reference to the interface will have been made on our behalf
|
|
by whoever issued the I/O request.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG EndpointId;
|
|
PFTP_ENDPOINT Endpointp;
|
|
ULONG Error;
|
|
PFTP_INTERFACE Interfacep;
|
|
PROFILE("FtpReadEndpointCompletionRoutine");
|
|
do {
|
|
Interfacep = (PFTP_INTERFACE)Bufferp->Context;
|
|
EndpointId = PtrToUlong(Bufferp->Context2);
|
|
|
|
//
|
|
// Acquire two additional references to the interface
|
|
// for the followup requests that we will issue below,
|
|
// lock the interface, and retrieve the endpoint.
|
|
//
|
|
|
|
EnterCriticalSection(&FtpInterfaceLock);
|
|
if (!FTP_REFERENCE_INTERFACE(Interfacep)) {
|
|
LeaveCriticalSection(&FtpInterfaceLock);
|
|
NhReleaseBuffer(Bufferp);
|
|
break;
|
|
}
|
|
FTP_REFERENCE_INTERFACE(Interfacep);
|
|
LeaveCriticalSection(&FtpInterfaceLock);
|
|
ACQUIRE_LOCK(Interfacep);
|
|
Endpointp = FtpLookupInterfaceEndpoint(Interfacep, EndpointId, NULL);
|
|
|
|
//
|
|
// Process the read-completion. First we look for an error-code,
|
|
// and if we find one, we decide whether to re-issue the read-request.
|
|
// If the interface is still active, the error-code is non-fatal, and
|
|
// the endpoint still exists, we reissue the read.
|
|
//
|
|
|
|
if (ErrorCode) {
|
|
|
|
//
|
|
// We won't be needing the second reference to the interface,
|
|
// since we won't be calling 'FtpProcessMessage.
|
|
//
|
|
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpReadEndpointCompletionRoutine: error %d for endpoint %d",
|
|
ErrorCode, EndpointId
|
|
);
|
|
if (!FTP_INTERFACE_ACTIVE(Interfacep) || !Endpointp) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
} else if (NhIsFatalSocketError(ErrorCode)) {
|
|
FtpDeleteActiveEndpoint(Endpointp);
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpReadEndpointCompletionRoutine: deleting endpoint %d "
|
|
"on fatal read-error %d", EndpointId, ErrorCode
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// We need to repost the buffer for another read operation,
|
|
// so we now reissue a read for the same number of bytes as
|
|
// before.
|
|
//
|
|
|
|
Error =
|
|
NhReadStreamSocket(
|
|
&FtpComponentReference,
|
|
Bufferp->Socket,
|
|
Bufferp,
|
|
Bufferp->BytesToTransfer,
|
|
Bufferp->TransferOffset,
|
|
FtpReadEndpointCompletionRoutine,
|
|
Bufferp->Context,
|
|
Bufferp->Context2
|
|
);
|
|
if (Error) {
|
|
FtpDeleteActiveEndpoint(Endpointp);
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpReadEndpointCompletionRoutine: deleting endpoint "
|
|
"%d, NhReadStreamSocket=%d", EndpointId, Error
|
|
);
|
|
if (Error != ERROR_NETNAME_DELETED) {
|
|
NhWarningLog(
|
|
IP_FTP_LOG_RECEIVE_FAILED,
|
|
Error,
|
|
"%I",
|
|
NhQueryAddressSocket(Bufferp->Socket)
|
|
);
|
|
}
|
|
NhReleaseBuffer(Bufferp);
|
|
break;
|
|
}
|
|
|
|
RELEASE_LOCK(Interfacep);
|
|
}
|
|
|
|
break;
|
|
} else if (!BytesTransferred) {
|
|
|
|
//
|
|
// Zero bytes were read from the endpoint's socket.
|
|
// This indicates that the sender has closed the socket.
|
|
// We now propagate the closure to the alternate socket
|
|
// for the endpoint. When the 'other' sender is done,
|
|
// this endpoint will be removed altogether.
|
|
//
|
|
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpReadEndpointCompletionRoutine: endpoint %d socket %d "
|
|
"closed", EndpointId, Bufferp->Socket
|
|
);
|
|
if (Endpointp) {
|
|
FtpCloseActiveEndpoint(Endpointp, Bufferp->Socket);
|
|
}
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The original request completed successfully.
|
|
// Now see if the interface and endpoint are operational and,
|
|
// if not, return control.
|
|
//
|
|
|
|
if (!FTP_INTERFACE_ACTIVE(Interfacep)) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpReadEndpointCompletionRoutine: interface %d inactive",
|
|
Interfacep->Index
|
|
);
|
|
break;
|
|
} else if (!Endpointp) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpReadEndpointCompletionRoutine: endpoint %d not found",
|
|
EndpointId
|
|
);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Record the number of bytes read, and issue a read-request
|
|
// for the remainder if necessary. Otherwise, process the completed
|
|
// message.
|
|
//
|
|
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpReadEndpointCompletionRoutine: endpoint %d socket %d read %d "
|
|
"bytes", EndpointId, Bufferp->Socket, BytesTransferred
|
|
);
|
|
ASSERT(BytesTransferred <= Bufferp->BytesToTransfer);
|
|
Bufferp->BytesToTransfer -= BytesTransferred;
|
|
Bufferp->TransferOffset += BytesTransferred;
|
|
|
|
if (Bufferp->BytesToTransfer > 0 &&
|
|
FtpIsFullMessage(
|
|
reinterpret_cast<CHAR*>(Bufferp->Buffer),
|
|
Bufferp->TransferOffset
|
|
) == NULL) {
|
|
|
|
//
|
|
// Read the remainder of the message, after releasing
|
|
// the second reference to the interface, which is needed
|
|
// only when we call 'FtpProcessMessage'.
|
|
//
|
|
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
|
|
Error =
|
|
NhReadStreamSocket(
|
|
&FtpComponentReference,
|
|
Bufferp->Socket,
|
|
Bufferp,
|
|
Bufferp->BytesToTransfer,
|
|
Bufferp->TransferOffset,
|
|
FtpReadEndpointCompletionRoutine,
|
|
Bufferp->Context,
|
|
Bufferp->Context2
|
|
);
|
|
if (Error) {
|
|
FtpDeleteActiveEndpoint(Endpointp);
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpReadEndpointCompletionRoutine: deleting endpoint "
|
|
"%d, NhReadStreamSocket=%d", EndpointId, Error
|
|
);
|
|
if (Error != ERROR_NETNAME_DELETED) {
|
|
NhWarningLog(
|
|
IP_FTP_LOG_RECEIVE_FAILED,
|
|
Error,
|
|
"%I",
|
|
NhQueryAddressSocket(Bufferp->Socket)
|
|
);
|
|
}
|
|
NhReleaseBuffer(Bufferp);
|
|
break;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// We've finished reading something. Process it.
|
|
//
|
|
|
|
FtpProcessMessage(Interfacep, Endpointp, Bufferp);
|
|
}
|
|
|
|
RELEASE_LOCK(Interfacep);
|
|
|
|
} while(FALSE);
|
|
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
DEREFERENCE_FTP();
|
|
|
|
} // FtpReadEndpointCompletionRoutine
|
|
|
|
|
|
VOID
|
|
FtpWriteEndpointCompletionRoutine(
|
|
ULONG ErrorCode,
|
|
ULONG BytesTransferred,
|
|
PNH_BUFFER Bufferp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked upon completion of a write-operation
|
|
on a stream socket for a FTP control-channel connection.
|
|
|
|
The contexts for all writes are the interface and endpoint-identifier
|
|
corresponding to the socket, stored in 'Context' and 'Context2',
|
|
respectively.
|
|
|
|
Arguments:
|
|
|
|
ErrorCode - Win32 status code for the I/O operation
|
|
|
|
BytesTransferred - number of bytes in 'Bufferp'
|
|
|
|
Bufferp - holds data read from the stream socket
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Environment:
|
|
|
|
Runs in the context of a worker-thread which has just dequeued an
|
|
I/O completion packet from the common I/O completion port with which our
|
|
stream sockets are associated.
|
|
A reference to the component will have been made on our behalf
|
|
by 'NhWriteStreamSocket'.
|
|
A reference to the interface will have been made on our behalf
|
|
by whoever issued the I/O request.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Error;
|
|
ULONG EndpointId;
|
|
PFTP_ENDPOINT Endpointp;
|
|
PFTP_INTERFACE Interfacep;
|
|
PROFILE("FtpWriteEndpointCompletionRoutine");
|
|
do {
|
|
Interfacep = (PFTP_INTERFACE)Bufferp->Context;
|
|
EndpointId = PtrToUlong(Bufferp->Context2);
|
|
|
|
//
|
|
// Acquire an additional reference to the interface
|
|
// for the followup requests that we will issue below,
|
|
// lock the interface, and retrieve the endpoint.
|
|
//
|
|
|
|
EnterCriticalSection(&FtpInterfaceLock);
|
|
if (!FTP_REFERENCE_INTERFACE(Interfacep)) {
|
|
LeaveCriticalSection(&FtpInterfaceLock);
|
|
NhReleaseBuffer(Bufferp);
|
|
break;
|
|
}
|
|
LeaveCriticalSection(&FtpInterfaceLock);
|
|
ACQUIRE_LOCK(Interfacep);
|
|
Endpointp = FtpLookupInterfaceEndpoint(Interfacep, EndpointId, NULL);
|
|
|
|
//
|
|
// Process the write-completion. First we look for an error-code,
|
|
// and if we find one, we decide whether to re-issue the write-request.
|
|
// If the interface is still active, the error-code is non-fatal, and
|
|
// the endpoint still exists, we reissue the write.
|
|
//
|
|
|
|
if (ErrorCode) {
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpWriteEndpointCompletionRoutine: error %d for endpoint %d",
|
|
ErrorCode, EndpointId
|
|
);
|
|
if (!FTP_INTERFACE_ACTIVE(Interfacep) || !Endpointp) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
} else if (NhIsFatalSocketError(ErrorCode)) {
|
|
FtpDeleteActiveEndpoint(Endpointp);
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpWriteEndpointCompletionRoutine: deleting endpoint %d "
|
|
"on fatal write-error %d", EndpointId, ErrorCode
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// We need to repost the buffer for another write operation,
|
|
// so we now reissue a write for the same number of bytes
|
|
// as before.
|
|
//
|
|
|
|
Error =
|
|
NhWriteStreamSocket(
|
|
&FtpComponentReference,
|
|
Bufferp->Socket,
|
|
Bufferp,
|
|
Bufferp->BytesToTransfer,
|
|
Bufferp->TransferOffset,
|
|
FtpWriteEndpointCompletionRoutine,
|
|
Bufferp->Context,
|
|
Bufferp->Context2
|
|
);
|
|
if (Error) {
|
|
FtpDeleteActiveEndpoint(Endpointp);
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpWriteEndpointCompletionRoutine: deleting endpoint "
|
|
"%d, NhWriteStreamSocket=%d", EndpointId, Error
|
|
);
|
|
NhWarningLog(
|
|
IP_FTP_LOG_SEND_FAILED,
|
|
Error,
|
|
"%I",
|
|
NhQueryAddressSocket(Bufferp->Socket)
|
|
);
|
|
NhReleaseBuffer(Bufferp);
|
|
break;
|
|
}
|
|
|
|
RELEASE_LOCK(Interfacep);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The original request completed successfully.
|
|
// Now see if the interface and endpoint are operational and,
|
|
// if not, return control.
|
|
//
|
|
|
|
if (!FTP_INTERFACE_ACTIVE(Interfacep)) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpWriteEndpointCompletionRoutine: interface %d inactive",
|
|
Interfacep->Index
|
|
);
|
|
break;
|
|
} else if (!Endpointp) {
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhReleaseBuffer(Bufferp);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpWriteEndpointCompletionRoutine: endpoint %d not found",
|
|
EndpointId
|
|
);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Record the number of bytes written, and issue a write-request
|
|
// for the remainder if necessary. Otherwise, we are done,
|
|
// and we return to reading from the 'other' socket for the
|
|
// control-channel.
|
|
//
|
|
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpWriteEndpointCompletionRoutine: endpoint %d socket %d wrote %d "
|
|
"bytes", EndpointId, Bufferp->Socket, BytesTransferred
|
|
);
|
|
|
|
ASSERT(BytesTransferred <= Bufferp->BytesToTransfer);
|
|
Bufferp->BytesToTransfer -= BytesTransferred;
|
|
Bufferp->TransferOffset += BytesTransferred;
|
|
if (Bufferp->BytesToTransfer) {
|
|
|
|
//
|
|
// Write the remainder of the message
|
|
//
|
|
|
|
Error =
|
|
NhWriteStreamSocket(
|
|
&FtpComponentReference,
|
|
Bufferp->Socket,
|
|
Bufferp,
|
|
Bufferp->BytesToTransfer,
|
|
Bufferp->TransferOffset,
|
|
FtpWriteEndpointCompletionRoutine,
|
|
Bufferp->Context,
|
|
Bufferp->Context2
|
|
);
|
|
if (Error) {
|
|
FtpDeleteActiveEndpoint(Endpointp);
|
|
RELEASE_LOCK(Interfacep);
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpWriteEndpointCompletionRoutine: deleting endpoint %d, "
|
|
"NhWriteStreamSocket=%d", EndpointId, Error
|
|
);
|
|
NhWarningLog(
|
|
IP_FTP_LOG_SEND_FAILED,
|
|
Error,
|
|
"%I",
|
|
NhQueryAddressSocket(Bufferp->Socket)
|
|
);
|
|
NhReleaseBuffer(Bufferp);
|
|
break;
|
|
}
|
|
} else {
|
|
SOCKET Socket;
|
|
ULONG UserFlags;
|
|
|
|
//
|
|
// We now go back to reading from the other socket of the
|
|
// endpoint, by issuing the next read on the endpoint's other
|
|
// socket. Note that it is the responsibility of the callee
|
|
// to release the reference to the interface if a failure occurs.
|
|
//
|
|
|
|
UserFlags = Bufferp->UserFlags;
|
|
if (UserFlags & FTP_BUFFER_FLAG_FROM_ACTUAL_CLIENT) {
|
|
Socket = Endpointp->HostSocket;
|
|
UserFlags &= ~(ULONG)FTP_BUFFER_FLAG_CONTINUATION;
|
|
UserFlags |= FTP_BUFFER_FLAG_FROM_ACTUAL_CLIENT;
|
|
} else {
|
|
Socket = Endpointp->ClientSocket;
|
|
UserFlags &= ~(ULONG)FTP_BUFFER_FLAG_CONTINUATION;
|
|
UserFlags |= FTP_BUFFER_FLAG_FROM_ACTUAL_HOST;
|
|
}
|
|
NhReleaseBuffer(Bufferp);
|
|
Error =
|
|
FtpReadActiveEndpoint(
|
|
Interfacep,
|
|
Endpointp,
|
|
Socket,
|
|
UserFlags
|
|
);
|
|
if (Error) {
|
|
NhTrace(
|
|
TRACE_FLAG_IO,
|
|
"FtpWriteEndpointCompletionRoutine: deleting endpoint %d, "
|
|
"FtpReadActiveEndpoint=%d", EndpointId, Error
|
|
);
|
|
FtpDeleteActiveEndpoint(Endpointp);
|
|
RELEASE_LOCK(Interfacep);
|
|
NhWarningLog(
|
|
IP_FTP_LOG_RECEIVE_FAILED,
|
|
Error,
|
|
"%I",
|
|
NhQueryAddressSocket(Socket)
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_LOCK(Interfacep);
|
|
|
|
} while(FALSE);
|
|
|
|
FTP_DEREFERENCE_INTERFACE(Interfacep);
|
|
DEREFERENCE_FTP();
|
|
|
|
} // FtpWriteEndpointCompletionRoutine
|