windows-nt/Source/XPSP1/NT/net/homenet/alg/alg_ftp/ftpcontrol.cpp

1621 lines
38 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//
// Copyright (C) 2001 Microsoft Corp
//
// FtpControl.cpp : Implementation
//
// JPDup
// Sanjiv
//
#include "precomp.h"
#include "MyAlg.h"
//
// Default constructor
//
CFtpControlConnection::CFtpControlConnection()
{
MYTRACE_ENTER_NOSHOWEXIT("CFtpControlConnection::CFtpControlConnection()");
m_ClientConnectedSocket = INVALID_SOCKET;
m_AlgConnectedSocket = INVALID_SOCKET;
m_ControlState.m_nAddressNew = 0;
m_ControlState.m_nPortNew = 0;
m_nSourcePortReplacement = 0;
m_RefCount = 0;
m_pPendingProxy = NULL;
}
//
// Destructor
//
CFtpControlConnection::~CFtpControlConnection()
{
MYTRACE_ENTER_NOSHOWEXIT("CFtpControlConnection::~CFtpControlConnection()");
}
//
// Find a unique source port for the public client address given
//
USHORT
PickNewSourcePort(
ULONG nPublicSourceAddress,
USHORT nPublicSourcePort
)
{
MYTRACE_ENTER("CFtpControlConnection::PickNewSourcePort()");
USHORT nNewSourcePort = 45000-nPublicSourcePort; // example 45000 - 3000
bool bPortAvailable;
do
{
nNewSourcePort--;
bPortAvailable = g_ControlObjectList.IsSourcePortAvailable(nPublicSourceAddress, nNewSourcePort);
MYTRACE("Port %d is %s", nNewSourcePort, bPortAvailable ? "Available" : "Inuse" );
} while ( (false == bPortAvailable) && (nNewSourcePort > 6001) );
return nNewSourcePort;
}
//
// Initialize
//
HRESULT
CFtpControlConnection::Init(
SOCKET AcceptedSocket,
ULONG nToAddr,
USHORT nToPort,
CONNECTION_TYPE ConnType
)
{
MYTRACE_ENTER("CFtpControlConnection::Init");
//
// Figure what address to use
//
ULONG BestAddress;
HRESULT hr = g_pIAlgServicesAlgFTP->GetBestSourceAddressForDestinationAddress(
nToAddr,
TRUE,
&BestAddress
);
if ( FAILED(hr) )
{
MYTRACE_ERROR("Could not get best source address", hr);
return hr;
}
ULONG Err = 0;
m_ClientConnectedSocket = AcceptedSocket;
m_ConnectionType = ConnType;
IncReference();
m_AlgConnectedSocket = INVALID_SOCKET;
Err = MyHelperCreateStreamSocket(BestAddress,0,&m_AlgConnectedSocket);
if ( Err == 0 )
{
if ( m_ConnectionType == OUTGOING )
{
MYTRACE("OUTGOING FTP");
ULONG icsAddr;
USHORT icsPort;
Err = MyHelperQueryLocalEndpointSocket(m_AlgConnectedSocket,&icsAddr,&icsPort);
MYTRACE("AlgConnectedSocket Local %s:%d",MYTRACE_IP(icsAddr), ntohs(icsPort) );
if ( Err == 0 )
{
hr = g_pIAlgServicesAlgFTP->PrepareProxyConnection(
eALG_TCP,
icsAddr,
icsPort,
nToAddr,
nToPort,
FALSE,
&m_pPendingProxy
);
}
}
else if (m_ConnectionType == INCOMING)
{
MYTRACE("INCOMING FTP");
ULONG icsAddr,pubAddr;
USHORT icsPort,pubPort;
Err = MyHelperQueryLocalEndpointSocket(m_AlgConnectedSocket,&icsAddr,&icsPort);
MYTRACE("AlgConnectedSocket Local %s:%d",MYTRACE_IP(icsAddr), ntohs(icsPort) );
if (Err == 0)
{
Err = MyHelperQueryRemoteEndpointSocket(m_ClientConnectedSocket,&pubAddr,&pubPort);
if ( Err == 0 )
{
if ( icsAddr == nToAddr )
{
//
// Special case it the FTP server is hosted on the EDGE box
// we would create a loop the incoming public client address/port
// this new modified connection would look exacly like
// the original one example:
//
// 1.1.1.2:3000 connects to 1.1.1.1:21
// we accept this connection
// and in return we connect to the FTP server destination 1.1.1.1:21
// asking the NAT to source mofify and replace the source with 1.1.1.2:3000
// that does not work
// in order to go arround this we pick another source port example 45000
//
// Cache this info in order to pick a unique one next time
m_nSourcePortReplacement = PickNewSourcePort(pubAddr, pubPort);
pubPort = m_nSourcePortReplacement; // This is the new bogus port to use now
}
hr = g_pIAlgServicesAlgFTP->PrepareSourceModifiedProxyConnection(
eALG_TCP,
icsAddr,
icsPort,
nToAddr,
nToPort,
pubAddr,
pubPort,
&m_pPendingProxy
);
if ( FAILED(hr) )
{
MYTRACE_ERROR("PrepareSourceModifiedProxyConnection",hr);
}
}
else
{
MYTRACE_ERROR("MyHelperQueryRemoteEndpointSocket",Err);
}
}
else
{
MYTRACE_ERROR("LocalEndpointSocket", Err);
}
}
}
else
{
MYTRACE_ERROR("MyHelperCreateStreamSocket",Err);
}
if ( SUCCEEDED(hr) && Err == 0 )
{
Err = MyHelperConnectStreamSocket(
NULL,
m_AlgConnectedSocket,
nToAddr,
nToPort,
NULL,
MyConnectCompletion,
(void *)this,
NULL
);
if ( Err != 0 )
{
MYTRACE_ERROR("From MyHelperConnectStreamSocket", Err);
m_pPendingProxy->Cancel();
}
}
if ( FAILED(hr) || Err )
{
MYTRACE_ERROR("We can't init this Connection", hr);
ULONG ref;
ref = DecReference();
_ASSERT(ref == 0);
if ( SUCCEEDED(hr) )
hr = HRESULT_FROM_WIN32(Err);
}
return hr;
}
#define MAKE_ADDRESS(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
#define MAKE_PORT(a,b) ((a) | ((b) << 8))
//
//
//
ULONG
GetNumFromString(UCHAR *String,ULONG *pNum)
{
ULONG retval = 0;
int i = 0;
while (String[i] != ',')
{
retval = retval*10 + (String[i]-'0');
i++;
}
*pNum = i;
return retval;
}
//
// Needs to return in Network address order
//
USHORT
GetUSHORTFromString(UCHAR *String,ULONG *pNum)
{
MYTRACE_ENTER("GetUSHORTFromString");
ULONG Num;
UCHAR Numbers[2];
*pNum = 0;
Numbers[0] = (UCHAR)GetNumFromString(String,&Num);
*pNum += Num+1;
Numbers[1] = (UCHAR)GetNumFromString(String+*pNum,&Num);
*pNum += Num;
USHORT retval = (USHORT)MAKE_PORT((USHORT)Numbers[0], (USHORT)Numbers[1]);
return retval;
}
//
// return the String IP Address as 192,168,0,0, in a ULONG in HOST format
//
ULONG
GetULONGFromString(
UCHAR* String,
ULONG* pNum
)
{
UCHAR Numbers[4];
ULONG retval = 0;
ULONG Num;
*pNum = 0;
Numbers[0] = (UCHAR)GetNumFromString(String,&Num);
*pNum += Num+1;
Numbers[1] = (UCHAR)GetNumFromString(String+*pNum,&Num);
*pNum += Num+1;
Numbers[2] = (UCHAR)GetNumFromString(String+*pNum,&Num);
*pNum += Num+1;
Numbers[3] = (UCHAR)GetNumFromString(String+*pNum,&Num);
*pNum += Num;
retval = MAKE_ADDRESS(Numbers[0], Numbers[1], Numbers[2], Numbers[3]);
return retval;
}
//
//
//
void
CFtpControlConnection::ConnectCompletionRoutine(
ULONG ErrCode,
ULONG BytesTransferred
)
{
MYTRACE_ENTER("CFtpControlConnection::ConnectCompletionRoutine");
ULONG Err;
if ( ErrCode )
{
MYTRACE_ERROR("ConnectCompletionRoutine", ErrCode);
if ( m_pPendingProxy )
{
MYTRACE("PendingProxy still active CANCEL");
m_pPendingProxy->Cancel();
}
ULONG ref;
ref = DecReference();
_ASSERT(ref == 0);
return;
}
Err = MyHelperReadStreamSocket(
NULL,
m_ClientConnectedSocket,
NULL,
FTP_MAX_MSG_SIZE,
0,
MyReadCompletion,
(void *)this,
(void *)CLIENT_READ
);
if ( Err )
{
MYTRACE_ERROR("From MyHelperReadStreamSocket CLIENT_READ",Err);
ULONG ref;
ref = DecReference();
_ASSERT(ref == 0);
return;
}
IncReference();
Err = MyHelperReadStreamSocket(
NULL,
m_AlgConnectedSocket,
NULL,
FTP_MAX_MSG_SIZE,0,
MyReadCompletion,
(void *)this,
(void *)SERVER_READ
);
if ( Err )
{
MYTRACE("MyHelperReadStreamSocket SERVER_READ",Err);
ULONG ref;
ref = DecReference();
_ASSERT(ref == 1);
if (ref)
Shutdown();
return;
}
return;
}
//
//
//
ULONG
CFtpControlConnection::IncReference(void)
{
MYTRACE_ENTER("CFtpControlConnection::IncReference()");
ULONG nRef = InterlockedIncrement((LPLONG)&m_RefCount);
MYTRACE("REFCOUNT for 0x%X is now %d", this, nRef);
return nRef;
}
//
//
//
ULONG
CFtpControlConnection::DecReference(void)
{
MYTRACE_ENTER("CFtpControlConnection::DecReference()");
ULONG tmp = InterlockedDecrement((LPLONG)&m_RefCount);
MYTRACE("REFCOUNT for 0x%X is now %d", this, tmp);
if ( tmp > 0 )
return tmp;
MYTRACE("HIT ZERO refcount cleanup the CFtpControlConnection");
if ( m_AlgConnectedSocket == INVALID_SOCKET )
{
MYTRACE("SOCKET SERVER ALREADY CLOSED!");
}
else
{
MYTRACE("CLOSING SOCKET ALGCONNECTED!");
shutdown(m_AlgConnectedSocket, SD_BOTH);
closesocket(m_AlgConnectedSocket);
m_AlgConnectedSocket = INVALID_SOCKET;
}
if ( m_ClientConnectedSocket == INVALID_SOCKET )
{
MYTRACE("SOCKET CLIENT ALREADY CLOSED!");
}
else
{
MYTRACE("CLOSING SOCKET CLIENT CONNECTED!");
shutdown(m_ClientConnectedSocket, SD_BOTH);
closesocket(m_ClientConnectedSocket);
m_ClientConnectedSocket = INVALID_SOCKET;
}
if ( m_pPendingProxy )
{
//
// At this point NAT already cancel this redirect, so no need to call cancel
// m_pPendingProxy->Cancel();
// this was causing a ERROR on a multi-client scenario
//
m_pPendingProxy->Release();
m_pPendingProxy = NULL;
}
if ( m_ControlState.m_nPortNew )
{
MYTRACE("ReleaseReservedPort-A %d", ntohs(m_ControlState.m_nPortNew));
g_pIAlgServicesAlgFTP->ReleaseReservedPort(m_ControlState.m_nPortNew,1);
m_ControlState.m_nPortNew = 0;
}
//
// CleanUp the collection of DataChannel
//
IDataChannel* pData;
USHORT Port;
HANDLE CreationHandle,DeletionHandle;
MYTRACE("Empty CDataChannelList");
while ( m_DataChannelList.Remove(&pData,&Port,&CreationHandle,&DeletionHandle) )
{
//
// Creation and Deletion events are not used for now
// NhUnRegisterEvent(CreationHandle); // Hopefully nothing bad will happen ! May have been called before
// NhUnRegisterEvent(DeletionHandle); // if delete has been called it would mean that Remove has been called.
//
pData->Cancel();
pData->Release();
MYTRACE("ReleaseReservedPort-B %d", ntohs(Port));
g_pIAlgServicesAlgFTP->ReleaseReservedPort(Port,1);
}
if ( g_ControlObjectList.Remove(this) )
{
// happens when this was called from within ChannelDeletion or some DecReferece after that.
}
else
{
// would happen if this was called from shutdown. not otherwise.
}
delete this;
return 0;
}
//
// The last one to call DecReference would take it off control list.
// The first one to call DecReference because of fatal error would call Shutdown to start off
// the DecReference for all the connected stuff.
//
void
CFtpControlConnection::Shutdown()
{
MYTRACE_ENTER("CFtpControlConnection::Shutdown()");
if ( m_AlgConnectedSocket != INVALID_SOCKET )
{
MYTRACE("CLOSING SOCKET ALG CONNECTED! %d", m_AlgConnectedSocket);
shutdown(m_AlgConnectedSocket, SD_BOTH);
closesocket(m_AlgConnectedSocket);
m_AlgConnectedSocket = INVALID_SOCKET;
}
if ( m_ClientConnectedSocket != INVALID_SOCKET )
{
MYTRACE("CLOSING SOCKET CLIENT CONNECTED! %d", m_ClientConnectedSocket);
shutdown(m_ClientConnectedSocket, SD_BOTH);
closesocket(m_ClientConnectedSocket);
m_ClientConnectedSocket = INVALID_SOCKET;
}
return;
}
//
//
//
void
CFtpControlConnection::ReadCompletionRoutine(
ULONG ErrCode,
ULONG BytesTransferred,
PNH_BUFFER Bufferp
)
{
MYTRACE_ENTER( "CFtpControlConnection::ReadCompletionRoutine" );
if ( ErrCode || BytesTransferred == 0 )
{
if ( ErrCode )
{
MYTRACE("Shutdown because of read ERROR 0x%x", ErrCode);
}
else
{
MYTRACE("Shutdown because of read 0 bytes");
}
MyHelperReleaseBuffer(Bufferp);
if (DecReference())
Shutdown();
return;
}
ULONG_PTR ReadType = (ULONG_PTR)Bufferp->Context2;
ULONG_PTR WriteType;
SOCKET ReadSocket;
SOCKET WriteSocket;
ULONG Err;
if ( ReadType == CLIENT_READ )
{
WriteType = SERVER_READ;
ReadSocket = m_ClientConnectedSocket;
WriteSocket = m_AlgConnectedSocket;
}
else
{
WriteType = CLIENT_READ;
ReadSocket = m_AlgConnectedSocket;
WriteSocket = m_ClientConnectedSocket;
}
#if defined(DBG) || defined(_DEBUG)
ULONG TraceAddr = 0;
USHORT TracePort = 0;
if ( ReadSocket != INVALID_SOCKET )
Err = MyHelperQueryRemoteEndpointSocket(ReadSocket ,&TraceAddr,&TracePort);
MYTRACE("from %s (%s:%d)",
ReadType == CLIENT_READ ? "CLIENT":"SERVER",
MYTRACE_IP(TraceAddr),
ntohs(TracePort)
);
MYTRACE("EC(0x%x) Buffer size(%d)='%s'", ErrCode, BytesTransferred, MYTRACE_BUFFER2STR((char*)Bufferp->Buffer, BytesTransferred));
#endif
if ( (ReadType == CLIENT_READ && m_ConnectionType == OUTGOING) || (ReadType == SERVER_READ && m_ConnectionType == INCOMING) )
{
// the number of bytes transferred can change.
// because the ProcessFtpMessage may have to
// buffer the Address,Port string from PORT or PASV response command.
ProcessFtpMessage(Bufferp->Buffer,&BytesTransferred);
}
if ( BytesTransferred != 0 && WriteSocket != INVALID_SOCKET )
{
IncReference();
MYTRACE(
"Write to %s size(%d)='%s'",
WriteType == SERVER_READ ? "SERVER" : "CLIENT",
BytesTransferred,
MYTRACE_BUFFER2STR((char*)Bufferp->Buffer, BytesTransferred)
);
Err = MyHelperWriteStreamSocket(
NULL,
WriteSocket,
Bufferp,BytesTransferred,
0,
MyWriteCompletion,
(void *)this,(PVOID)WriteType
);
if (Err)
{
MYTRACE_ERROR("from MyHelperWriteStreamSocket", Err);
DecReference();
if (DecReference())
Shutdown(); // I am not going to call the Read again so one more DecReference is needed.
MyHelperReleaseBuffer(Bufferp);
return;
}
}
if ( INVALID_SOCKET == ReadSocket )
{
if (DecReference())
Shutdown();
}
else
{
Err = MyHelperReadStreamSocket(
NULL,
ReadSocket,
NULL,
FTP_MAX_MSG_SIZE,
0,
MyReadCompletion,
(void *)this,
(void *)ReadType
);
if (Err)
{
MYTRACE_ERROR("from MyHelperReadStreamSocket",Err);
if (DecReference())
Shutdown();
}
}
return;
}
//
//
//
void
CFtpControlConnection::WriteCompletionRoutine(
ULONG ErrCode,
ULONG BytesTransferred,
PNH_BUFFER Bufferp
)
{
MYTRACE_ENTER("CFtpControlConnection::WriteCompletionRoutine");
if (BytesTransferred == 0)
ErrCode = ERROR_IO_CANCELLED;
if (ErrCode)
{
if (MyHelperIsFatalSocketError(ErrCode) || ErrCode == ERROR_IO_CANCELLED)
{
MYTRACE_ERROR("FATAL ERROR", ErrCode);
MyHelperReleaseBuffer(Bufferp);
if (DecReference())
Shutdown();
}
else
{
MYTRACE_ERROR("ANOTHER MyHelperWriteStreamSocket", ErrCode);
ULONG_PTR Type = (ULONG_PTR)Bufferp->Context2;
ULONG Err = MyHelperWriteStreamSocket(
NULL,
Bufferp->Socket,
Bufferp,Bufferp->BytesToTransfer,
0,
MyWriteCompletion,
(void *)this,
(PVOID)Type
);
if (Err)
{
MYTRACE_ERROR("From MyHelperWriteStreamSocket", Err);
MyHelperReleaseBuffer(Bufferp);
if (DecReference())
Shutdown();
}
}
}
else
{
ULONG_PTR Type = (ULONG_PTR)Bufferp->Context2;
MYTRACE(Type == CLIENT_READ ? "to CLIENT" : "to SERVER" );
MYTRACE("EC(0x%x) Buffer size(%d)='%s'", ErrCode, BytesTransferred, MYTRACE_BUFFER2STR((char*)Bufferp->Buffer, BytesTransferred));
MYTRACE("Write Succeeded now cleanup");
MyHelperReleaseBuffer(Bufferp);
DecReference();
}
return;
}
bool
FtpExtractOctet(
UCHAR** Buffer,
UCHAR* BufferEnd,
UCHAR* Octet
)
/*++
Routine Description:
This routine is called to extract an octet from a string.
Arguments:
Buffer - points to a pointer to a string where conversion starts; on
return it points to the pointer to the string where conversion ends
BufferEnd - points to the end of the string
Octet - points to a caller-suplied storage to store converted octet
Return Value:
BOOLEAN - TRUE if successfuly converted, FALSE otherwise.
--*/
{
bool bSuccess;
ULONG nDigitFound = 0;
ULONG Value = 0;
while (
*Buffer <= BufferEnd
&& nDigitFound < 3
&& **Buffer >= '0'
&& **Buffer <= '9'
)
{
Value *= 10;
Value += **Buffer - '0';
(*Buffer)++;
nDigitFound++;
}
bSuccess = nDigitFound > 0 && Value < 256;
if ( bSuccess )
{
*Octet = (UCHAR)Value;
}
return bSuccess;
}
//
// Extract host and port numbers.
// example 192,168,0,2,100,200
//
bool
ExtractAddressAndPortCommandValue(
UCHAR* pCommandBuffer,
UCHAR* pEndOfBuffer,
UCHAR* Numbers,
ULONG* nTotalLen
)
{
UCHAR* pStartingPosition = pCommandBuffer;
bool bSuccess = FtpExtractOctet(
&pCommandBuffer,
pEndOfBuffer,
&Numbers[0]
);
int i = 1;
while ( i < 6 && bSuccess && *pCommandBuffer == ',' )
{
pCommandBuffer++;
bSuccess = FtpExtractOctet(
&pCommandBuffer,
pEndOfBuffer,
&Numbers[i]
);
i++;
}
if ( bSuccess && i == 6 )
{
*nTotalLen = (ULONG)(pCommandBuffer - pStartingPosition);
return true;
}
return false;
}
#define TOUPPER(c) ((c) > 'z' ? (c) : ((c) < 'a' ? (c) : (c) ^ 0x20))
//
// Look for the "PORT" or "227" command and remap the private address associated with these command
// to a public address
//
void
CFtpControlConnection::ProcessFtpMessage(
UCHAR* Buffer,
ULONG* pBytes
)
{
MYTRACE_ENTER("CFtpControlConnection::ProcessFtpMessage");
MYTRACE("Buffer size(%d)='%s'", *pBytes, MYTRACE_BUFFER2STR((char*)Buffer, *pBytes));
ULONG Bytes = *pBytes;
UCHAR* pCommandBuffer = reinterpret_cast<UCHAR*>(Buffer);
UCHAR* EndOfBufferp = reinterpret_cast<UCHAR*>(Buffer + *pBytes);
HRESULT hr;
char *String;
UCHAR* pBeginAddressAndPortOld=NULL;
UCHAR* pEndAddressAndPortOld=NULL;
ULONG nOldAddressLen=0;
CONST CHAR *pCommandToFind;
// for now lets keep the OUTGOING and INCOMING seperate.
// can be put together since most of the code is the same.
// differences in the first few bytes to scan for.
if ( m_ConnectionType == OUTGOING )
{
MYTRACE("OUTGOING - Look for 'PORT ' command");
pCommandToFind = (PCHAR)"PORT ";
}
else
{
MYTRACE("INCOMING - Look for '227 ' command ");
pCommandToFind = (PCHAR)"227 ";
}
while ( *pCommandToFind != '\0' && *pCommandToFind == TOUPPER(*pCommandBuffer))
{
pCommandToFind++;
pCommandBuffer++;
}
if ( *pCommandToFind == '\0' )
{
MYTRACE("COMMAND found");
//
// Skip non digit char
//
if ( m_ConnectionType == OUTGOING )
{
//
// Skip white space. example -> PORT 10,12,13,14,1,2
//
while (*pCommandBuffer == ' ')
pCommandBuffer++;
}
else
{
//
// Skip non digit char example 227 Entering passive mode (10,12,13,14,1,2)
//
while ( pCommandBuffer < EndOfBufferp && !isdigit(*pCommandBuffer) )
pCommandBuffer++;
}
//
// so next stuff should be the addr,port combination.
//
UCHAR Numbers[6];
if ( ExtractAddressAndPortCommandValue(pCommandBuffer, EndOfBufferp, Numbers, &nOldAddressLen) )
{
pBeginAddressAndPortOld = pCommandBuffer;
pEndAddressAndPortOld = pCommandBuffer + nOldAddressLen;
m_ControlState.m_nAddressOld = MAKE_ADDRESS(Numbers[0], Numbers[1], Numbers[2], Numbers[3]);
m_ControlState.m_nPortOld = MAKE_PORT(Numbers[4], Numbers[5]);
MYTRACE("***** PRIVATE PORT is %d %d", m_ControlState.m_nPortOld, ntohs(m_ControlState.m_nPortOld));
if ( ntohs(m_ControlState.m_nPortOld) <= 1025 )
{
//
// For security reason we will disallow any redirection to ports lower then 1025
// this port range is reserver for standard port Like 139/Netbios
// if this port range was requested it probably is the source of hacker attacking this FTP proxy
//
MYTRACE("***** Port to redirect is lower then 1025 so rejected");
m_ControlState.m_nAddressNew = htonl(0);
m_ControlState.m_nPortNew = htons(0);
m_ControlState.m_nAddressLenNew = 11;
strcpy((char*)m_ControlState.m_szAddressPortNew, "0,0,0,0,0,0");
// pretend that a Redirection got created
// This way we send out a PORT command with the Public addapter address and a new reserver PORT
// but when the public hacker comes back it wil not be redirect but simply droped
}
else
{
//
// Get best public address to use and reserver a port
// This will be the Address/Port expose on the public side.
//
hr = CreateNewAddress();
if ( FAILED(hr) )
{
MYTRACE_ERROR("CreateNewAddress failed",hr);
// We screwed up. cant make redirects now. so for now lets just act
// as if nothing happened and carry on with the stuff.
}
}
}
else
{
MYTRACE_ERROR("NOT a valid PORT command syntax", E_INVALIDARG);
}
}
//
// Rebuild the string command with the new address port
//
if ( pBeginAddressAndPortOld )
{
if ( ntohs(m_ControlState.m_nPortOld) <= 1025 )
{
// No need to setup a redirection
hr = S_OK;
}
else
{
hr = SetupDataRedirect();
}
if ( FAILED(hr) )
{
// we got screwed badly here. we wont set up redirect and act as if nothing happened.
MYTRACE_ERROR("Could not setup a redirect", hr);
}
else
{
//
// Move trailing buffer
// Left if new address is smaller then old address
// Right if new address is bigger then old address
//
// This is the right side reminder of the buffer just after the last digit of the ascii port value
int nReminerSize = (int)(Bytes - (pEndAddressAndPortOld - Buffer));
if ( *pBytes + nReminerSize < FTP_MAX_MSG_SIZE )
{
int nOffset = m_ControlState.m_nAddressLenNew - nOldAddressLen; // What is the delta size between the old and new address
MoveMemory(
pEndAddressAndPortOld + nOffset, // Destination
pEndAddressAndPortOld, // Source
nReminerSize // Size
);
//
// Insert the new address and port
//
memcpy(
pBeginAddressAndPortOld, // Destination
m_ControlState.m_szAddressPortNew, // Source
m_ControlState.m_nAddressLenNew // Size
);
MYTRACE("OLD Address size(%d) %s:%d", nOldAddressLen, MYTRACE_IP(m_ControlState.m_nAddressOld), ntohs(m_ControlState.m_nPortOld));
MYTRACE("New Address size(%d) %s:%d", m_ControlState.m_nAddressLenNew, MYTRACE_IP(m_ControlState.m_nAddressNew), ntohs(m_ControlState.m_nPortNew));
*pBytes = Bytes - nOldAddressLen + m_ControlState.m_nAddressLenNew;
MYTRACE("Edited COMMAND is '%s' size(%d)", MYTRACE_BUFFER2STR((char*)Buffer, *pBytes), *pBytes);
// Now we are sure to have a DataChannel created and in the list of DataChanel
// on the last DecRefer the ResertPort was deleted twice
// now by setting m_nPortNew to zero only the DataChannel code will release the port
//
m_ControlState.m_nPortNew = 0;
}
else
{
MYTRACE_ERROR("Could not alter the command the new address size does not fit in the the current buffer ", E_ABORT);
}
}
}
return;
}
//
//
//
int
CreateStringFromNumber(UCHAR *String,ULONG Num)
{
int retval = 0;
UCHAR ch1,ch2,ch3;
ch3 = (UCHAR)(Num%10) + '0';
Num = Num/10;
ch2 = (UCHAR)(Num%10) + '0';
Num = Num/10;
ch1 = (UCHAR)(Num%10) + '0';
_ASSERT(Num == 0);
if (ch1 != '0') {
String[retval++] = ch1;
String[retval++] = ch2;
String[retval++] = ch3;
}
else if (ch2 != '0') {
String[retval++] = ch2;
String[retval++] = ch3;
}
else {
String[retval++] = ch3;
}
return retval;
}
//
//
//
int
CreateULONGString(UCHAR *String,ULONG Num)
{
int retval = 0;
retval += CreateStringFromNumber(String,Num&0xff);
String[retval++] = ',';
retval += CreateStringFromNumber(String+retval,(Num>>8)&0xff);
String[retval++] = ',';
retval += CreateStringFromNumber(String+retval,(Num>>16)&0xff);
String[retval++] = ',';
retval += CreateStringFromNumber(String+retval,(Num>>24)&0xff);
return retval;
}
//
//
//
int
CreateUSHORTString(UCHAR *String,USHORT Num)
{
int retval = 0;
retval += CreateStringFromNumber(String,Num&0xff);
String[retval++] = ',';
retval += CreateStringFromNumber(String+retval,(Num>>8)&0xff);
return retval;
}
//
//
//
HRESULT
CFtpControlConnection::CreateNewAddress(void)
{
MYTRACE_ENTER("CFtpControlConnection::CreateNewAddress");
SOCKET sd;
HRESULT hr = S_OK;
ULONG Err = 0;
sd = (m_ConnectionType == OUTGOING ? m_AlgConnectedSocket : m_ClientConnectedSocket);
ULONG OtherAddr,PublicAddr;
USHORT OtherPort,PublicPort;
Err = MyHelperQueryRemoteEndpointSocket(sd,&OtherAddr,&OtherPort);
if (Err == 0)
{
hr = g_pIAlgServicesAlgFTP->GetBestSourceAddressForDestinationAddress(OtherAddr,FALSE,&PublicAddr);
if ( SUCCEEDED(hr) )
{
hr = g_pIAlgServicesAlgFTP->ReservePort(1,&PublicPort);
}
else
{
MYTRACE_ERROR("Could not GetBestSourceAddressForDestinationAddress", hr);
PublicAddr = 0; // Try with this
}
MYTRACE("ICS Reserved Address %s:%d", MYTRACE_IP(PublicAddr), ntohs(PublicPort));
m_ControlState.m_nAddressNew = PublicAddr;
m_ControlState.m_nPortNew = PublicPort;
ULONG StrLen = CreateULONGString(m_ControlState.m_szAddressPortNew,PublicAddr);
m_ControlState.m_szAddressPortNew[StrLen++] = ',';
StrLen += CreateUSHORTString(m_ControlState.m_szAddressPortNew+StrLen,PublicPort);
m_ControlState.m_nAddressLenNew = StrLen;
MYTRACE("NEW AddressPort String %s Len(%d)", MYTRACE_BUFFER2STR((char*)m_ControlState.m_szAddressPortNew, StrLen), StrLen);
}
return hr;
}
//
//
//
HRESULT
CFtpControlConnection::SetupDataRedirect(void)
{
MYTRACE_ENTER("CFtpControlConnection::SetupDataRedirect");
ULONG pubAddr,prvAddr,icsAddr;
USHORT pubPort,prvPort,icsPort;
ULONG Err = 0;
switch ( m_ConnectionType )
{
case OUTGOING:
MYTRACE("OUTGOING");
Err = MyHelperQueryRemoteEndpointSocket(m_AlgConnectedSocket,&pubAddr,&pubPort);
pubPort = 0;
icsAddr = m_ControlState.m_nAddressNew;
icsPort = m_ControlState.m_nPortNew;
prvAddr = m_ControlState.m_nAddressOld;
prvPort = m_ControlState.m_nPortOld;
break;
case INCOMING:
MYTRACE("INCOMING");
Err = MyHelperQueryRemoteEndpointSocket(m_ClientConnectedSocket,&pubAddr,&pubPort);
pubPort = 0;
pubAddr = 0;
icsAddr = m_ControlState.m_nAddressNew;
icsPort = m_ControlState.m_nPortNew;
prvAddr = m_ControlState.m_nAddressOld;
prvPort = m_ControlState.m_nPortOld;
break;
default:
// m_ConnectionType is corrupt
_ASSERT( FALSE );
break;
}
if ( Err != 0 )
{
MYTRACE_ERROR("MyHelperQueryRemoteEndpointSocket", Err);
return E_FAIL;
}
HRESULT hr = S_OK;
IDataChannel* pDataChannel = NULL;
hr = g_pIAlgServicesAlgFTP->CreateDataChannel(
eALG_TCP,
prvAddr,
prvPort,
icsAddr,
icsPort,
pubAddr,
pubPort,
eALG_INBOUND, //| eALG_OUTBOUND, not needed i suppose since we
// are not bothered if client tries to open connection.
(ALG_NOTIFICATION)0,// (eALG_SESSION_CREATION | eALG_SESSION_DELETION),
FALSE,
&pDataChannel
);
if ( FAILED(hr) )
{
MYTRACE_ERROR("g_pIAlgServicesAlgFTP->CreateDataChannel", hr);
return hr;
}
m_DataChannelList.Insert(
pDataChannel,
icsPort,
0,
0
);
return S_OK;
//
// Don't use creation and deletion events for now
//
#if 0
HANDLE HandleDataChannelCreation = NULL;
HANDLE HandleDataChannelDeletion = NULL;
HANDLE MyHandleRegisteredCreation = NULL;
HANDLE MyHandleRegisteredDeletion = NULL;
//
// Get the CREATION handle
//
hr = pDataChannel->GetSessionCreationEventHandle((HANDLE_PTR *)&HandleDataChannelCreation);
if ( SUCCEEDED(hr) )
{
MYTRACE("Creation Handle is %d", HandleDataChannelCreation);
MyHandleRegisteredCreation = NhRegisterEvent(
HandleDataChannelCreation,
DataChannelCreationCallback,
(PVOID)this,
(PVOID)pDataChannel,
DATA_CREATION_TIMEO
);
if ( MyHandleRegisteredCreation )
{
//
// Get the DELETION handle
//
hr = pDataChannel->GetSessionDeletionEventHandle((HANDLE_PTR *)&HandleDataChannelDeletion);
if ( SUCCEEDED(hr) )
{
MYTRACE("Deletion Handle is %d", HandleDataChannelDeletion);
MyHandleRegisteredDeletion = NhRegisterEvent(
HandleDataChannelDeletion,
DataChannelDeletionCallback,
(PVOID)this,
(PVOID)pDataChannel,
INFINITE
);
if ( MyHandleRegisteredDeletion )
{
//
// We have a valid DataChannel
//
MYTRACE ("Inserting into DataChannelList");
m_DataChannelList.Insert(
pDataChannel,
icsPort,
MyHandleRegisteredCreation,
MyHandleRegisteredDeletion
);
return S_OK;
}
else
{
MYTRACE_ERROR("NhRegisterEven(HandleDataChannelDeletion)", 0);
}
}
else
{
MYTRACE_ERROR("GetSessionDeletionEventHandle",hr);
}
}
else
{
MYTRACE_ERROR("NhRegisterEvent(HandleDataChannelCreation)", 0);
}
}
else
{
MYTRACE_ERROR("GetSessionCreationEventHandle",hr);
}
//
// ERROR if we got here, rollback
//
pDataChannel->Cancel();
pDataChannel->Release();
if ( MyHandleRegisteredCreation )
NhUnRegisterEvent(MyHandleRegisteredCreation);
if ( MyHandleRegisteredDeletion )
NhUnRegisterEvent(MyHandleRegisteredDeletion);
return hr; // return the last error
#endif
}
//
//
//
void
CFtpControlConnection::DataChannelDeletion(
BOOLEAN TimerOrWait,
PVOID Context
)
{
MYTRACE_ENTER("CFtpControlConnection::DataChannelDeletion");
USHORT port;
IDataChannel *pDataChannel = (IDataChannel *)Context;
/*
if (m_DataChannelList.Remove(pDataChannel,&port))
{
MYTRACE("Releasing Port");
pDataChannel->Release();
g_pIAlgServicesAlgFTP->ReleaseReservedPort(port,1);
ULONG ref;
ref = DecReference();
}
*/
return;
}
//
//
//
void
CFtpControlConnection::DataChannelCreation(
BOOLEAN TimerOrWait,
PVOID Context
)
{
MYTRACE_ENTER("CFtpControlConnection::DataChannelCreation");
MYTRACE("TimerOrWait: %d", TimerOrWait);
USHORT port;
if (TimerOrWait==0)
{
/*
IDataChannel *pDataChannel = (IDataChannel *)Context;
HANDLE DeletionHandle;
if ( m_DataChannelList.Remove(pDataChannel,&port,&DeletionHandle))
{
MYTRACE("Cancelling DataChannel");
pDataChannel->Cancel();
pDataChannel->Release();
MYTRACE("Releasing Port");
g_pIAlgServicesAlgFTP->ReleaseReservedPort(port,1);
NhUnRegisterEvent(DeletionHandle);
DecReference();
}
*/
}
return;
}
CComAutoCriticalSection m_AutoCS_FtpIO;
//
//
//
void
DataChannelCreationCallback(
BOOLEAN TimerOrWait,
PVOID Context,
PVOID Context2
)
{
MYTRACE_ENTER("DataChannelCreationCallback");
CFtpControlConnection *pFtpControl = (CFtpControlConnection *)Context;
pFtpControl->DataChannelCreation(TimerOrWait,Context2);
}
//
//
//
void
DataChannelDeletionCallback(
BOOLEAN TimerOrWait,
PVOID Context,
PVOID Context2
)
{
MYTRACE_ENTER("DataChannelDeletionCallback");
CFtpControlConnection *pFtpControl = (CFtpControlConnection *)Context;
pFtpControl->DataChannelDeletion(TimerOrWait,Context2);
}
//
//
//
void
MyAcceptCompletion(
ULONG ErrCode,
ULONG BytesTransferred,
PNH_BUFFER Bufferp
)
{
m_AutoCS_FtpIO.Lock();
MYTRACE_ENTER("MyAcceptCompletion");
CAlgFTP* pMainObj = (CAlgFTP*)Bufferp->Context;
if ( pMainObj )
pMainObj->AcceptCompletionRoutine(ErrCode,BytesTransferred,Bufferp);
m_AutoCS_FtpIO.Unlock();
}
//
//
//
void
MyConnectCompletion(
ULONG ErrCode,
ULONG BytesTransferred,
PNH_BUFFER pContext
)
{
m_AutoCS_FtpIO.Lock();
MYTRACE_ENTER("MyConnectCompletion");
CFtpControlConnection* pControl = (CFtpControlConnection *)pContext; // Special case here see socket.cpp MyHelperpConnectOrCloseCallbackRoutine
if ( pControl )
pControl->ConnectCompletionRoutine(ErrCode,BytesTransferred);
m_AutoCS_FtpIO.Unlock();
}
//
//
//
void
MyReadCompletion(
ULONG ErrCode,
ULONG BytesTransferred,
PNH_BUFFER Bufferp
)
{
m_AutoCS_FtpIO.Lock();
MYTRACE_ENTER("");
CFtpControlConnection *pControl = (CFtpControlConnection *)Bufferp->Context;
if ( pControl )
pControl->ReadCompletionRoutine(ErrCode,BytesTransferred,Bufferp);
else
{
MYTRACE_ENTER("ERROR ERROR ERROR MyReadCompletion");
}
m_AutoCS_FtpIO.Unlock();
}
//
//
//
void
MyWriteCompletion(
ULONG ErrCode,
ULONG BytesTransferred,
PNH_BUFFER Bufferp
)
{
m_AutoCS_FtpIO.Lock();
MYTRACE_ENTER("");
CFtpControlConnection *pControl = (CFtpControlConnection *)Bufferp->Context;
if ( pControl )
pControl->WriteCompletionRoutine(ErrCode,BytesTransferred,Bufferp);
else
{
MYTRACE_ENTER("ERROR ERROR ERROR MyWriteCompletion");
}
m_AutoCS_FtpIO.Unlock();
}