|
|
//
// 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();
}
|