Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2665 lines
98 KiB

#include "precomp.h"
DEBUG_FILEZONE(ZONE_T120_MSMCSTCP);
#include <datguids.h>
#include <nmqos.h>
#include <t120qos.h>
#include "tprtsec.h"
#include <tsecctrl.h>
#include <tprtntfy.h>
#include "cnpcoder.h"
#include "plgxprt.h"
// #undef TRACE_OUT
// #define TRACE_OUT WARNING_OUT
/* Tprtctrl.cpp
*
* Copyright (c) 1996 by Microsoft Corporation
*
* Abstract:
* This module maintains the TCP transport and all connections.
*
*/
/* External definitions */
extern HINSTANCE g_hDllInst;
extern PTransportInterface g_Transport;
extern SOCKET Listen_Socket;
extern SOCKET Listen_Socket_Secure;
extern CRITICAL_SECTION csQOS;
extern CPluggableTransport *g_pPluggableTransport;
extern PController g_pMCSController;
extern CCNPCoder *g_CNPCoder;
extern HWND TCP_Window_Handle;
BOOL FindSocketNumber(DWORD dwGCCID, SOCKET * socket_number);
/*
* The following array contains a template for the X.224 data header.
* The 5 of the 7 bytes that it initializes are actually sent to the
* wire. Bytes 3 and 4 will be set to contain the size of the PDU.
* The array is only used when we encode a data PDU.
*/
extern UChar g_X224Header[];
// The external MCS Controller object
extern PController g_pMCSController;
// plugable transport prototypes
int X224Recv(PSocket pSocket, LPBYTE buffer, int length, PLUGXPRT_RESULT *pnLastError);
int Q922Recv(PSocket pSocket, LPBYTE buffer, int length, PLUGXPRT_RESULT *pnLastError);
/*
* void QoSLock(Void)
*
* Functional Description:
* This function locks the QoS data
* All other reader or writer threads will be blocked.
*/
void QoSLock(Void)
{
EnterCriticalSection(&csQOS);
}
/*
* void QoSUnlock(Void)
*
* Functional Description:
* This function unlocks the QoS data
* Waiting reader or writer threads will be unblocked.
*/
void QoSUnlock(Void)
{
LeaveCriticalSection(&csQOS);
}
/*
* TransportError ConnectRequest ( TransportAddress transport_address,
* BOOL fSecure
* PTransportConnection pXprtConn)
*
* Functional Description:
* This function initiates a connection. It passes the transport address
* to the TCP transport. It will either deny the request or accept the
* request and call us back when the physical connection is established.
*
* We return the transport connection handle in the transport_connection
* address. Although we return this transport number to the user, it
* is not ready for data transfer until the user receives the
* TRANSPORT_CONNECT_INDICATION and responds with a ConnectResponse() call.
* At that point, the transport connection is up and running.
*/
TransportError ConnectRequest (TransportAddress transport_address,
BOOL fSecure,
/* out */ PTransportConnection pXprtConn)
{
TransportError rc = TRANSPORT_NO_ERROR;
PSocket pSocket;
PSecurityContext pSC = NULL;
ULong address;
SOCKADDR_IN sin;
CPluggableConnection *p = NULL;
// initialize transport connection
UINT nPluggableConnID = ::GetPluggableTransportConnID(transport_address);
if (nPluggableConnID)
{
p = ::GetPluggableConnection(nPluggableConnID);
if (NULL != p)
{
pXprtConn->eType = p->GetType();
pXprtConn->nLogicalHandle = nPluggableConnID;
ASSERT(IS_PLUGGABLE(*pXprtConn));
}
else
{
return TRANSPORT_NO_SUCH_CONNECTION;
}
}
else
{
pXprtConn->eType = TRANSPORT_TYPE_WINSOCK;
pXprtConn->nLogicalHandle = INVALID_SOCKET;
}
// we are connecting X224...
::OnProtocolControl(*pXprtConn, PLUGXPRT_CONNECTING);
// Try to prepare a security context object if we're told to do so.
if ( fSecure )
{
// If we're trying to connect securely but can't, fail
if ( NULL == g_Transport->pSecurityInterface )
{
WARNING_OUT(("Placing secure call failed: no valid security interface"));
return TRANSPORT_SECURITY_FAILED;
}
DBG_SAVE_FILE_LINE
if (NULL != (pSC = new SecurityContext(g_Transport->pSecurityInterface,
transport_address)))
{
if ( TPRTSEC_NOERROR != pSC->Initialize(NULL,0))
{
// If we can't init a security context, fail
delete pSC;
pSC = NULL;
WARNING_OUT(("Placing secure call failed: could not initialize security context"));
return TRANSPORT_SECURITY_FAILED;
}
}
}
/* Create and Initialize the Socket object */
pSocket = newSocket(*pXprtConn, pSC);
if( pSocket == NULL )
return (TRANSPORT_MEMORY_FAILURE);
pSocket->SecState = ( NULL == pSC ) ? SC_NONSECURE : SC_SECURE;
if (IS_SOCKET(*pXprtConn))
{
u_short uPort = TCP_PORT_NUMBER;
TCHAR szAddress[MAXIMUM_IP_ADDRESS_SIZE];
lstrcpyn(szAddress, transport_address, MAXIMUM_IP_ADDRESS_SIZE);
LPTSTR pszSeparator = (LPTSTR)_StrChr(szAddress, _T(':'));
if (NULL != pszSeparator)
{
uPort = (u_short)DecimalStringToUINT(CharNext(pszSeparator));
*pszSeparator = _T('\0');
}
/* Convert the ascii string into an Internet Address */
if ((address = inet_addr(szAddress)) == INADDR_NONE)
{
WARNING_OUT (("ConnectRequest: %s is an invalid host addr", szAddress));
rc = TRANSPORT_CONNECT_REQUEST_FAILED;
goto Bail;
}
lstrcpyn (pSocket->Remote_Address, transport_address, MAXIMUM_IP_ADDRESS_SIZE);
/*
* Load the socket control structure with the parameters necessary.
*
* - Internet socket
* - Let it assign any address to this socket
* - Assign our port number (depending on secure/nonsecure call!)
*/
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = address;
sin.sin_port = htons (uPort);
/* Attempt a connection to the remote site */
TRACE_OUT (("ConnectRequest: Issuing connect: address = %s", transport_address));
if (::connect(pSocket->XprtConn.nLogicalHandle, (const struct sockaddr *) &sin, sizeof(sin)) == 0)
{
TRACE_OUT (("ConnectRequest: State = SOCKET_CONNECTED..."));
/* Add socket to connection list */
// bugbug: we may fail to insert.
g_pSocketList->SafeAppend(pSocket);
::SendX224ConnectRequest(pSocket->XprtConn);
}
else
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
/* If the error message is WSAEWOULDBLOCK, we must wait for the FD_CONNECT. */
TRACE_OUT (("ConnectRequest: State = WAITING_FOR_CONNECTION..."));
pSocket -> State = WAITING_FOR_CONNECTION;
/* Add socket to connection list */
// bugbug: we may fail to insert.
g_pSocketList->SafeAppend(pSocket);
// SendStatusMessage(pSocket -> Remote_Address, TSTATE_CONNECT_PENDING, IDS_NULL_STRING);
}
else
{
WARNING_OUT (("ConnectRequest: Connect Failed error = %d",WSAGetLastError()));
/* The connect() call failed, close the socket and notify the owner */
// SendStatusMessage (pSocket -> Remote_Address, TSTATE_NOT_READY, IDS_NULL_STRING);
::ShutdownAndClose(pSocket->XprtConn, FALSE, 2);
rc = TRANSPORT_CONNECT_REQUEST_FAILED;
goto Bail;
}
}
else
{
ASSERT(IS_PLUGGABLE(*pXprtConn));
g_pSocketList->SafeAppend(pSocket);
if (IS_PLUGGABLE_X224(*pXprtConn))
{
::SendX224ConnectRequest(pSocket->XprtConn);
}
else
if (IS_PLUGGABLE_PSTN(*pXprtConn))
{
rc = p->TConnectRequest();
ASSERT(TRANSPORT_NO_ERROR == rc);
}
}
Bail:
ASSERT(NULL != pSocket);
if (TRANSPORT_NO_ERROR == rc)
{
*pXprtConn = pSocket->XprtConn;
}
else
{
::freeSocket(pSocket, *pXprtConn);
}
return rc;
}
/*
* BOOL ConnectResponse (TransportConnection XprtConn)
*
* Functional Description:
* This function is called by the user in response to a
* TRANSPORT_CONNECT_INDICATION callback from us. By making this call the
* user is accepting the call. If the user does not want to accept the
* call, he should call DisconnectRequest();
*/
BOOL ConnectResponse (TransportConnection XprtConn)
{
PSocket pSocket;
TRACE_OUT (("ConnectResponse(%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
/* If this is an invalid handle, return error */
if(NULL != (pSocket = g_pSocketList->FindByTransportConnection(XprtConn)))
{
BOOL fRet;
if (pSocket->State == SOCKET_CONNECTED)
{
/* We do not change this state in ANY other place BECAUSE it breaks the connect request*/
pSocket->State = X224_CONNECTED;
fRet = TRUE;
}
else
{
ERROR_OUT(("ConnectResponse: Illegal ConnectResponse packet"));
fRet = FALSE;
}
pSocket->Release();
return fRet;
}
return FALSE;
}
#ifdef TSTATUS_INDICATION
/*
* Void SendStatusMessage ( PChar RemoteAddress,
* TransportState State,
* UInt message_id)
*
* Functional Description:
* This function is called to send a status indication to the user. The
* specific text of the message is contained in a string resource.
*/
Void SendStatusMessage( PChar RemoteAddress,
TransportState state,
UInt message_id)
{
TransportStatus transport_status;
char sTransport[80] = "";
char message[80] = "";
if( message_id == IDS_NULL_STRING )
message[0] = '\000';
else
LoadString(
(HINSTANCE) g_hDllInst,
(UINT) message_id,
(LPSTR) message,
(int) sizeof(message) );
/*
** We issue a callback to the user to notify him of the message
*/
transport_status.device_identifier = "";
transport_status.remote_address = RemoteAddress;
transport_status.message = message;
transport_status.state = state;
g_pMCSController->HandleTransportStatusIndication(&transport_status);
}
#endif
/*
* Void SendX224ConnectRequest(TransportConnection XprtConn)
*
* Functional Description:
* This function is called upon receipt of the FD_CONNECT from Winsock.
* It indicates that the physical connection is established, and sends
* the X224 connection request packet.
*/
void SendX224ConnectRequest(TransportConnection XprtConn)
{
PSocket pSocket;
static X224_CR_FIXED cr_fixed =
{
{ 3, 0, 0, UNK },
UNK,
{ CONNECTION_REQUEST_PACKET, UNK, UNK, UNK, UNK, 0 } // common info
};
TRACE_OUT(("SendX224ConnectRequest"));
CNPPDU cnp_pdu;
ConnectRequestPDU_reliableSecurityProtocols_Element cnp_cr_rsp_element;
LPBYTE pbToSendBuf = NULL;
UINT cbToSendBuf = 0;
LPBYTE encoded_pdu;
UINT encoded_pdu_length;
TransportError error;
cnp_pdu.choice = connectRequest_chosen;
cnp_pdu.u.connectRequest.bit_mask = 0;
cnp_pdu.u.connectRequest.protocolIdentifier = t123AnnexBProtocolId;
cnp_pdu.u.connectRequest.reconnectRequested = FALSE;
// Sanity check field sizes... these need to conform to protocol
ASSERT (sizeof(RFC_HEADER) == 4);
ASSERT (sizeof(X224_DATA_PACKET) == 7);
ASSERT (sizeof(X224_CONNECT_COMMON) == 6);
ASSERT (sizeof(X224_TPDU_INFO) == 3);
/* If this is an invalid handle, return */
if (NULL == (pSocket = g_pSocketList->FindByTransportConnection(XprtConn)))
return;
if (IS_SOCKET(pSocket->XprtConn))
{
if (pSocket -> State != WAITING_FOR_CONNECTION)
{
ERROR_OUT (("SendX224ConnectRequest: Illegal Socket State"));
goto MyExit;
}
}
else
{
ASSERT(IS_PLUGGABLE(pSocket->XprtConn));
if (X224_CONNECTED == pSocket->State)
{
// after query remote, we need to reset the state back to socket connected
pSocket->State = SOCKET_CONNECTED;
}
if (SOCKET_CONNECTED != pSocket->State)
{
ERROR_OUT (("SendX224ConnectRequest: Illegal Socket State"));
goto MyExit;
}
}
// If there is a security context associated with this socket, we
// are settting up for a secure call and will indicate that in the CNP
// portion of the packet
if (NULL != pSocket->pSC)
{
TRACE_OUT(("SendX224ConnectRequest: requesting secure connection"));
cnp_pdu.u.connectRequest.bit_mask |= reliableSecurityProtocols_present;
cnp_cr_rsp_element.next = NULL;
cnp_cr_rsp_element.value.choice = gssApiX224_chosen;
cnp_pdu.u.connectRequest.reliableSecurityProtocols = &cnp_cr_rsp_element;
}
else
{
TRACE_OUT(("SendX224ConnectRequest: requesting NON-secure connection"));
}
if (! g_CNPCoder->Encode((LPVOID) &cnp_pdu,
CNPPDU_PDU,
PACKED_ENCODING_RULES,
&encoded_pdu,
&encoded_pdu_length))
{
ERROR_OUT(("SendX224ConnectRequest: Can't encode cnp pdu"));
goto MyExit;
}
pSocket -> State = SOCKET_CONNECTED;
/* X224 header */
cr_fixed.conn.msbSrc = (UChar) (XprtConn.nLogicalHandle >> 8);
cr_fixed.conn.lsbSrc = (UChar) XprtConn.nLogicalHandle;
cbToSendBuf = sizeof(X224_CR_FIXED)+sizeof(X224_TPDU_INFO)+sizeof(X224_VARIABLE_INFO)+encoded_pdu_length;
cr_fixed.rfc.lsbPacketSize = (UChar)cbToSendBuf;
cr_fixed.HeaderSize = (UChar)(sizeof(X224_CONNECT_COMMON)+sizeof(X224_TPDU_INFO)+sizeof(X224_VARIABLE_INFO)+encoded_pdu_length);
ASSERT ( cbToSendBuf <= 128);
DBG_SAVE_FILE_LINE
pbToSendBuf = new BYTE[cbToSendBuf];
if (NULL == pbToSendBuf)
{
ERROR_OUT(("SendX224ConnectRequest: failed to allocate memory"));
goto MyExit;
}
{
LPBYTE pbTemp = pbToSendBuf;
memcpy(pbTemp, (LPBYTE) &cr_fixed, sizeof(cr_fixed));
pbTemp += sizeof(cr_fixed);
{
X224_TPDU_INFO x224_tpdu_info = { TPDU_SIZE, 1, DEFAULT_TPDU_SIZE };
memcpy(pbTemp, (LPBYTE) &x224_tpdu_info, sizeof(x224_tpdu_info));
pbTemp += sizeof(x224_tpdu_info);
}
{
X224_VARIABLE_INFO x224_var_info = { T_SELECTOR, (UChar)encoded_pdu_length };
memcpy(pbTemp, (LPBYTE) &x224_var_info, sizeof(x224_var_info)); // bug: error handling
pbTemp += sizeof(x224_var_info);
memcpy(pbTemp, encoded_pdu, encoded_pdu_length);
}
}
g_CNPCoder->FreeEncoded(encoded_pdu);
/* Attempt to send data out the socket */
error = FlushSendBuffer(pSocket, pbToSendBuf, cbToSendBuf);
ASSERT (TRANSPORT_NO_ERROR == error);
delete [] pbToSendBuf;
MyExit:
pSocket->Release();
}
/*
* Void SendX224ConnectConfirm (PSocket pSocket, unsigned int remote)
*
* Functional Description:
* This function is called upon receipt of the X224 connection request
* packet. It indicates that the remote side wants to establish a
* logical connection, and sends the X224 connection response packet.
*
* Return value:
* TRUE, if everything went ok.
* FALSE, otherwise (this implies a Disconnect will be issued for the socket).
*/
// LONCHANC: "remote" is from the X.224 ConnectRequest
BOOL SendX224ConnectConfirm (PSocket pSocket, unsigned int remote)
{
//PUChar ptr;
LPBYTE pbToSendBuf = NULL;
UINT cbToSendBuf = 0;
LPBYTE encoded_pdu = NULL;
UINT encoded_pdu_length = 0;
CNPPDU cnp_pdu;
BOOL fAcceptSecure = FALSE;
BOOL fRequireSecure = FALSE;
TRACE_OUT(("SendX224ConnectConfirm"));
{
RegEntry re(POLICIES_KEY, HKEY_CURRENT_USER);
switch (re.GetNumber(REGVAL_POL_SECURITY, DEFAULT_POL_SECURITY))
{
case DISABLED_POL_SECURITY:
break;
case REQUIRED_POL_SECURITY:
fAcceptSecure = TRUE;
fRequireSecure = TRUE;
break;
default:
fAcceptSecure = TRUE;
break;
}
}
static X224_CC_FIXED cc_fixed =
{
{ 3, 0, 0, UNK }, // RFC1006 header
UNK,
{ CONNECTION_CONFIRM_PACKET, UNK, UNK, UNK, UNK, 0 } // common info
};
// Sanity check field sizes... these need to conform to protocol
ASSERT (sizeof(RFC_HEADER) == 4);
ASSERT (sizeof(X224_DATA_PACKET) == 7);
ASSERT (sizeof(X224_CONNECT_COMMON) == 6);
ASSERT (sizeof(X224_TPDU_INFO) == 3);
/* X224 header */
cc_fixed.conn.msbDest = (UChar) (remote >> 8);
cc_fixed.conn.lsbDest = (UChar) remote;
cc_fixed.conn.msbSrc = (UChar) (pSocket->XprtConn.nLogicalHandle >> 8);
cc_fixed.conn.lsbSrc = (UChar) pSocket->XprtConn.nLogicalHandle;
cnp_pdu.choice = connectConfirm_chosen;
cnp_pdu.u.connectConfirm.bit_mask = 0;
cnp_pdu.u.connectConfirm.protocolIdentifier = t123AnnexBProtocolId;
if ( pSocket->fExtendedX224 )
{
TRACE_OUT(("SendX224ConnectConfirm reply using extended X224"));
if ( pSocket->fIncomingSecure )
{
TRACE_OUT(("SendX224ConnectConfirm: reply to secure call request"));
// Security not even initialized?
if ( NULL == g_Transport->pSecurityInterface )
{
WARNING_OUT(("Can't accept secure call: no sec interface"));
}
// Registry indicates no secure calls? If we're in the service
// then security is always 'on'.
else if ( !g_Transport->pSecurityInterface->IsInServiceContext() &&
!fAcceptSecure)
{
WARNING_OUT(("Can't accept secure call: security disabled"));
}
else // OK to take secure call
{
TRACE_OUT(("Creating security context for incoming call on socket (%d, %d).", pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle ));
if ( NULL != (pSocket->pSC =
new SecurityContext(g_Transport->pSecurityInterface, "")))
{
// Indicate we're ready for a secure call in the CC packet
cnp_pdu.u.connectConfirm.bit_mask |=
ConnectConfirmPDU_reliableSecurityProtocol_present;
cnp_pdu.u.connectConfirm.reliableSecurityProtocol.choice =
gssApiX224_chosen;
pSocket->SecState = SC_SECURE;
}
else
{
ERROR_OUT(("Error creating sec context on received call"));
// We will report no-support for security in our CC
pSocket->SecState = SC_NONSECURE;
}
}
}
else if ( // Incoming call is not secure, but not downlevel
// Running as a service?
g_Transport->bInServiceContext ||
fRequireSecure)
{
WARNING_OUT(("Can't accept non-secure call in RDS, or if security required"));
return FALSE;
}
else
{
pSocket->SecState = SC_NONSECURE;
}
if (! g_CNPCoder->Encode((LPVOID) &cnp_pdu,
CNPPDU_PDU,
PACKED_ENCODING_RULES,
&encoded_pdu,
&encoded_pdu_length))
{
ERROR_OUT(("SendX224ConnectRequest: Can't encode cnp pdu"));
return FALSE;
}
cbToSendBuf = sizeof(X224_CC_FIXED)+sizeof(X224_VARIABLE_INFO)+encoded_pdu_length;
cc_fixed.rfc.lsbPacketSize = (UChar)cbToSendBuf;
cc_fixed.HeaderSize = (UChar)(sizeof(X224_CONNECT_COMMON) + sizeof(X224_VARIABLE_INFO) + encoded_pdu_length);
ASSERT( cbToSendBuf <= 128 );
pbToSendBuf = new BYTE[cbToSendBuf];
if (NULL == pbToSendBuf)
{
ERROR_OUT(("SendX224ConnectConfirm: failed to allocate memory"));
return FALSE;
}
PBYTE pbTemp = pbToSendBuf;
memcpy(pbTemp, (LPBYTE) &cc_fixed, sizeof(cc_fixed));
pbTemp += sizeof(cc_fixed);
X224_VARIABLE_INFO x224_var_info = { T_SELECTOR_2 /*0xc2*/, (UChar)encoded_pdu_length };
memcpy(pbTemp, (LPBYTE) &x224_var_info, sizeof(x224_var_info));
pbTemp += sizeof(x224_var_info);
memcpy(pbTemp, encoded_pdu, encoded_pdu_length);
g_CNPCoder->FreeEncoded(encoded_pdu);
}
else // Incoming call is downlevel
{
if ( g_Transport->bInServiceContext || fRequireSecure)
{
WARNING_OUT(("Can't accept downlevel call in RDS or if security required"));
return FALSE;
}
pSocket->SecState = SC_NONSECURE;
// Downlevel: send packet w/out TSELECTOR variable portion
cc_fixed.rfc.lsbPacketSize = sizeof(X224_CC_FIXED);
cc_fixed.HeaderSize = sizeof(X224_CONNECT_COMMON);
cbToSendBuf = sizeof(X224_CC_FIXED);
pbToSendBuf = new BYTE[cbToSendBuf];
memcpy(pbToSendBuf, (LPBYTE) &cc_fixed, sizeof(cc_fixed));
}
/* Attempt to send data out the socket */
#ifdef DEBUG
TransportError error =
#endif // DEBUG
FlushSendBuffer(pSocket, pbToSendBuf, cbToSendBuf);
#ifdef DEBUG
ASSERT (TRANSPORT_NO_ERROR == error);
#endif // DEBUG
delete [] pbToSendBuf;
return TRUE;
}
BOOL SendX224DisconnectRequest(PSocket pSocket, unsigned int remote, USHORT usReason)
{
LPBYTE pbToSendBuf = NULL;
UINT cbToSendBuf = 0;
RegEntry re(CONFERENCING_KEY, HKEY_CURRENT_USER);
CNPPDU cnp_pdu;
LPBYTE encoded_pdu = NULL;
UINT encoded_pdu_length = 0;
TRACE_OUT(("SendX224DisconnectRequest"));
static X224_DR_FIXED dr_fixed =
{
{ 3, 0, 0, UNK }, // RFC1006 header
UNK,
{ DISCONNECT_REQUEST_PACKET, UNK, UNK, UNK, UNK, 0 },
};
ASSERT (pSocket->fExtendedX224);
ASSERT (sizeof(RFC_HEADER) == 4);
ASSERT (sizeof(X224_DATA_PACKET) == 7);
::OnProtocolControl(pSocket->XprtConn, PLUGXPRT_DISCONNECTING);
dr_fixed.disconn.msbDest = (UChar) (remote >> 8);
dr_fixed.disconn.lsbDest = (UChar) remote;
dr_fixed.disconn.msbSrc = (UChar) (pSocket->XprtConn.nLogicalHandle >> 8);
dr_fixed.disconn.lsbSrc = (UChar) pSocket->XprtConn.nLogicalHandle;
cnp_pdu.choice = disconnectRequest_chosen;
cnp_pdu.u.disconnectRequest.bit_mask = 0;
cnp_pdu.u.disconnectRequest.disconnectReason.choice = usReason;
if (! g_CNPCoder->Encode((LPVOID) &cnp_pdu,
CNPPDU_PDU,
PACKED_ENCODING_RULES,
&encoded_pdu,
&encoded_pdu_length))
{
ERROR_OUT(("SendX224DisconnectRequest: Can't encode cnp pdu"));
return FALSE;
}
cbToSendBuf = sizeof(X224_DR_FIXED) + sizeof(X224_VARIABLE_INFO) + encoded_pdu_length;
dr_fixed.rfc.lsbPacketSize = (UChar)cbToSendBuf;
dr_fixed.HeaderSize = (UChar)(sizeof(X224_DISCONN) + sizeof(X224_VARIABLE_INFO) + encoded_pdu_length);
ASSERT( cbToSendBuf <= 128 );
pbToSendBuf = new BYTE[cbToSendBuf];
if (NULL == pbToSendBuf)
{
ERROR_OUT(("SendX224DisconnectRequest: failed to allocate memory"));
return FALSE;
}
LPBYTE pbTemp = pbToSendBuf;
memcpy(pbTemp, (LPBYTE) &dr_fixed, sizeof(dr_fixed));
pbTemp += sizeof(dr_fixed);
X224_VARIABLE_INFO x224_var_info = { 0xe0, (UChar)encoded_pdu_length };
memcpy(pbTemp, (LPBYTE) &x224_var_info, sizeof(x224_var_info));
pbTemp += sizeof(x224_var_info);
memcpy(pbTemp, encoded_pdu, encoded_pdu_length);
g_CNPCoder->FreeEncoded(encoded_pdu);
/* Attempt to send data out the socket */
#ifdef DEBUG
TransportError error =
#endif // DEBUG
FlushSendBuffer(pSocket, pbToSendBuf, cbToSendBuf);
#ifdef DEBUG
ASSERT (TRANSPORT_NO_ERROR == error);
#endif // DEBUG
return TRUE;
}
/*
* void ContinueAuthentication (PSocket pSocket)
*
* Functional Description:
*/
void ContinueAuthentication (PSocket pSocket)
{
ULong packet_size;
PUChar Buffer;
PSecurityContext pSC = pSocket->pSC;
if (NULL != pSC) {
TRACE_OUT(("ContinueAuthentication: sending data packet"));
ASSERT(NULL != pSC->GetTokenBuf());
ASSERT(0 != pSC->GetTokenSiz());
/* We send an X224 data */
packet_size = sizeof(X224_DATA_PACKET) + pSC->GetTokenSiz();
DBG_SAVE_FILE_LINE
Buffer = new UChar[packet_size];
if (NULL != Buffer)
{
memcpy(Buffer + sizeof(X224_DATA_PACKET),
pSC->GetTokenBuf(),
pSC->GetTokenSiz());
/* X224 header */
memcpy (Buffer, g_X224Header, sizeof(X224_DATA_PACKET));
AddRFCSize (Buffer, packet_size);
/* Attempt to send data out the socket */
#ifdef DEBUG
TransportError error = FlushSendBuffer(pSocket, (LPBYTE) Buffer, packet_size);
ASSERT (TRANSPORT_NO_ERROR == error);
#else // DEBUG
FlushSendBuffer(pSocket, (LPBYTE) Buffer, packet_size);
#endif // DEBUG
delete [] Buffer;
}
else {
// bugbug: what do we need to do in case of a mem alloc failure?
WARNING_OUT (("ContinueAuthentication: memory allocation failure."));
}
}
else {
ERROR_OUT(("ContinueAuthentication called w/ bad socket"));
}
}
/*
* The following function processes the variable part of incoming X.224
* CONNECT_REQUEST and CONNECT_CONFIRM PDUs.
* For now, it can only process Max PDU size and security T_SELECTOR requests.
*/
BOOL ProcessX224ConnectPDU (PSocket pSocket, PUChar CP_ptr, UINT CP_length, ULONG *pNotify)
{
UChar length;
BOOL bSecurityInfoFound = FALSE;
PSecurityContext pSC = pSocket->pSC;
/* This structure must be accessed using byte-alignment */
#pragma pack(1)
X224_VARIABLE_INFO *pX224VarInfo;
/* return to normal alignment */
#pragma pack()
while (CP_length > 0) {
pX224VarInfo = (X224_VARIABLE_INFO *) CP_ptr;
/*
* Check the packet to see if it contains a valid TPDU_SIZE part. If it
* does, we need to reset the max packet size for this socket.
*/
if (TPDU_SIZE == pX224VarInfo->InfoType) {
/* This structure must be accessed using byte-alignment */
#pragma pack(1)
X224_TPDU_INFO *pX224TpduSize;
/* return to normal alignment */
#pragma pack()
pX224TpduSize = (X224_TPDU_INFO *) CP_ptr;
ASSERT (pX224TpduSize->InfoSize == 1);
if (pX224TpduSize->Info != DEFAULT_TPDU_SIZE) {
// We do not accept too small PDU sizes
if ((pX224TpduSize->Info < LOWEST_TPDU_SIZE) && (pX224TpduSize->Info < HIGHEST_TPDU_SIZE))
{
if (NULL != pNotify)
*pNotify = TPRT_NOTIFY_INCOMPATIBLE_T120_TPDU;
return FALSE;
}
pSocket->Max_Packet_Length = (1 << pX224TpduSize->Info);
}
}
/*
* Check the packet to see if it contains a valid
* TSELECTOR variable portion. If so, make sure it's security related
* and include one in the reply
*/
else if (T_SELECTOR == pX224VarInfo->InfoType || T_SELECTOR_2 == pX224VarInfo->InfoType)
{
// Try to decode
LPVOID pdecoding_buf = NULL;
UINT decoding_len = 0;
LPBYTE pbEncoded_data = CP_ptr + sizeof(X224_VARIABLE_INFO);
if ( g_CNPCoder->Decode (pbEncoded_data,
pX224VarInfo->InfoSize,
CNPPDU_PDU, PACKED_ENCODING_RULES,
(LPVOID *) &pdecoding_buf, &decoding_len))
{
bSecurityInfoFound = TRUE;
/* This structure must be accessed using byte-alignment */
#pragma pack(1)
CNPPDU *pCnp_pdu;
/* return to normal alignment */
#pragma pack()
pCnp_pdu = (CNPPDU *) pdecoding_buf;
if (pSocket->Read_State == CONNECTION_REQUEST) {
TRACE_OUT(("CR packet using TSELECTOR extension"));
pSocket->fExtendedX224 = TRUE;
if (pCnp_pdu->u.connectRequest.bit_mask & reliableSecurityProtocols_present)
{
PConnectRequestPDU_reliableSecurityProtocols pRSP = pCnp_pdu->u.connectRequest.reliableSecurityProtocols;
if (gssApiX224_chosen == pRSP->value.choice)
{
pSocket->fIncomingSecure = TRUE;
}
}
}
else {
ASSERT (pSocket->Read_State == CONNECTION_CONFIRM);
if ((NULL != pSC) && (pSC->ContinueNeeded())) {
ConnectConfirmPDU *pCnpCc = &pCnp_pdu->u.connectConfirm;
if ((pCnpCc->bit_mask & ConnectConfirmPDU_reliableSecurityProtocol_present )
&& gssApiX224_chosen == pCnpCc->reliableSecurityProtocol.choice)
{
// Everything is OK, we got an extended X224 response
// to our secure CR.
ContinueAuthentication(pSocket);
}
else {
WARNING_OUT(("No-support response to secure call attempt"));
if (NULL != pNotify)
*pNotify = TPRT_NOTIFY_REMOTE_NO_SECURITY;
return FALSE;
}
}
}
}
g_CNPCoder->FreeDecoded(CNPPDU_PDU, pdecoding_buf);
}
else {
ERROR_OUT (("ProcessX224ConnectPDU: Received X.224 Connect packet with unrecognizable parts."));
}
// Adjust the pointer and length and the X.224 CR packet.
length = pX224VarInfo->InfoSize + sizeof(X224_VARIABLE_INFO);
//x5:223196 - fix AV when invalid PDU is processed
if(CP_length < length)
{
CP_length = 0;
*pNotify = TPRT_NOTIFY_INCOMPATIBLE_T120_TPDU;
break;
}
CP_ptr += length;
CP_length -= length;
}
if (bSecurityInfoFound == FALSE) {
if ((pSocket->Read_State == CONNECTION_CONFIRM) && (pSC != NULL) && pSC->ContinueNeeded()) {
WARNING_OUT(("Downlevel response to secure call attempt"));
if (NULL != pNotify)
*pNotify = TPRT_NOTIFY_REMOTE_DOWNLEVEL_SECURITY;
return FALSE;
}
}
return TRUE;
}
void ProcessX224DisconnectPDU(PSocket pSocket, PUChar CP_ptr, UINT CP_length, ULONG *pNotify)
{
UChar length;
BOOL bSecurityInfoFound = FALSE;
PSecurityContext pSC = pSocket->pSC;
/* This structure must be accessed using byte-alignment */
#pragma pack(1)
X224_VARIABLE_INFO *pX224VarInfo;
/* return to normal alignment */
#pragma pack()
while (CP_length > 0) {
pX224VarInfo = (X224_VARIABLE_INFO *) CP_ptr;
if ( 0xe0 == pX224VarInfo->InfoType) {
LPVOID pdecoding_buf = NULL;
UINT decoding_len = 0;
LPBYTE pbEncoded_data = CP_ptr + sizeof(X224_VARIABLE_INFO);
if ( g_CNPCoder->Decode (pbEncoded_data,
pX224VarInfo->InfoSize,
CNPPDU_PDU, PACKED_ENCODING_RULES,
(LPVOID *) &pdecoding_buf, &decoding_len))
{
#pragma pack(1)
CNPPDU *pCnp_pdu;
/* return to normal alignment */
#pragma pack()
pCnp_pdu = (CNPPDU *) pdecoding_buf;
if (disconnectRequest_chosen == pCnp_pdu->choice)
{
switch (pCnp_pdu->u.disconnectRequest.disconnectReason.choice)
{
case securityDenied_chosen:
*pNotify = TPRT_NOTIFY_REMOTE_REQUIRE_SECURITY;
break;
default:
*pNotify = TPRT_NOTIFY_OTHER_REASON;
break;
}
}
}
g_CNPCoder->FreeDecoded(decoding_len, pdecoding_buf);
}
length = pX224VarInfo->InfoSize + sizeof(X224_VARIABLE_INFO);
CP_ptr += length;
//x5:223196 - fix AV when invalid PDU is processed
if(CP_length < length)
{
CP_length = 0;
*pNotify = TPRT_NOTIFY_INCOMPATIBLE_T120_TPDU;
break;
}
CP_length -= length;
}
}
/*
* void DisconnectRequest (TransportConnection XprtConn)
*
* Functional Description:
* This function closes the socket and deletes its connection node.
*/
void DisconnectRequest (TransportConnection XprtConn,
ULONG ulNotify)
{
PSocket pSocket;
TRACE_OUT(("DisconnectRequest"));
/* If the transport connection handle is not registered, return error */
if (NULL != (pSocket = g_pSocketList->FindByTransportConnection(XprtConn, TRUE)))
{
// LONCHANC: cannot do Remove in the above line because PurgeRequest() uses it again.
::PurgeRequest(XprtConn);
// SendStatusMessage (pSocket -> Remote_Address, TSTATE_NOT_CONNECTED, IDS_NULL_STRING);
if (IS_PLUGGABLE_PSTN(XprtConn))
{
CPluggableConnection *p = ::GetPluggableConnection(XprtConn.nLogicalHandle);
if (NULL != p)
{
p->TDisconnectRequest();
}
}
/* Free the structures and close the socket */
TransportConnection XprtConn2 = XprtConn;
if (IS_SOCKET(XprtConn2))
{
XprtConn2.nLogicalHandle = INVALID_SOCKET;
}
::freeSocket(pSocket, XprtConn2);
// Free up QoS resources if this disconnect was the
// last connected socket.
MaybeReleaseQoSResources();
// Notify the user
if (TPRT_NOTIFY_NONE != ulNotify && g_Transport)
{
TRACE_OUT (("TCP Callback: g_Transport->DisconnectIndication (%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
/* We issue a callback to the user to notify him of the message */
g_Transport->DisconnectIndication(XprtConn, ulNotify);
}
}
else
{
WARNING_OUT(("DisconnectRequest: logical handle (%d, %d) not found",
XprtConn.eType, XprtConn.nLogicalHandle));
}
::OnProtocolControl(XprtConn, PLUGXPRT_DISCONNECTED);
}
typedef enum {
RECVRET_CONTINUE = 0,
RECVRET_NON_FATAL_ERROR,
RECVRET_DISCONNECT,
RECVRET_NO_PLUGGABLE_CONNECTION,
} RecvReturn;
/* RecvReturn Call_recv (PSocket pSocket)
*
* Functional Description:
* This function calls recv once and checks for errors coming from the
* recv call. It knows about the socket's state from the "pSocket" argument
* and uses this info to create the arguments for the recv call.
*
* Return value:
* Continue, if everything went ok and we have new data
* Non_Fatal_Error, if no real error has happenned, but we did not recv all data we asked for
* Disconnect, if a real error has occurred, or the other side has disconnected.
*/
RecvReturn Call_recv (PSocket pSocket)
{
PUChar buffer;
int length;
int bytes_received;
BOOL bAllocationOK;
RecvReturn rrCode = RECVRET_NON_FATAL_ERROR;
PLUGXPRT_RESULT plug_rc = PLUGXPRT_RESULT_SUCCESSFUL;
TRACE_OUT(("Call_recv"));
if (READ_HEADER != pSocket->Read_State)
{
// Verify packet size is within acceptable limits (64K)
ASSERT((0 < pSocket->X224_Length) && (pSocket->X224_Length <= 65536));
if((pSocket->X224_Length <= 0) || (65536 < pSocket->X224_Length))
{
rrCode = RECVRET_DISCONNECT;
goto ExitLabel;
}
// Compute how much data we have to read from this X.224 pkt.
length = pSocket->X224_Length - sizeof(X224_DATA_PACKET);
// Space allocation
if (! pSocket->bSpaceAllocated)
{
// We need to allocate the space for the recv call.
if (NULL == pSocket->Data_Indication_Buffer)
{
DBG_SAVE_FILE_LINE
pSocket->Data_Memory = AllocateMemory (
NULL, pSocket->X224_Length,
((READ_DATA == pSocket->Read_State) ?
RECV_PRIORITY : HIGHEST_PRIORITY));
// Leave space for the X.224 header in the newly allocated data buffer
pSocket->Data_Indication_Length = sizeof (X224_DATA_PACKET);
bAllocationOK = (pSocket->Data_Memory != NULL);
}
else
{
// This is an MCS PDU broken up in many X.224 packets.
ASSERT (READ_DATA == pSocket->Read_State);
bAllocationOK = ReAllocateMemory (&(pSocket->Data_Memory), length);
}
// Check whether the allocations were successful.
if (bAllocationOK)
{
pSocket->bSpaceAllocated = TRUE;
pSocket->Data_Indication_Buffer = pSocket->Data_Memory->GetPointer();
/*
* If this is an X.224 CONNECT_REQUEST or CONNECT_CONFIRM packet,
* we need to copy the first 7 bytes into the buffer for the whole
* packet.
*/
if (READ_DATA != pSocket->Read_State)
{
memcpy ((void *) pSocket->Data_Indication_Buffer,
(void *) &(pSocket->X224_Header),
sizeof(X224_DATA_PACKET));
}
}
else
{
/*
* We will retry the operation later.
*/
WARNING_OUT (("Call_recv: Buffer allocation failed."));
g_pMCSController->HandleTransportWaitUpdateIndication(TRUE);
goto ExitLabel;
}
}
buffer = pSocket->Data_Indication_Buffer + pSocket->Data_Indication_Length;
}
else
{
buffer = (PUChar) &(pSocket->X224_Header);
length = sizeof(X224_DATA_PACKET);
}
// Adjust "buffer" and "length" for data already read from the current X.224 pkt.
buffer += pSocket->Current_Length;
length -= pSocket->Current_Length;
ASSERT (length > 0);
if (IS_SOCKET(pSocket->XprtConn))
{
// Issue the recv call.
bytes_received = recv (pSocket->XprtConn.nLogicalHandle, (char *) buffer, length, 0);
}
else
{
bytes_received = ::X224Recv(pSocket, buffer, length, &plug_rc);
}
if (bytes_received == length)
{
TRACE_OUT (("Call_recv: Received %d bytes on socket (%d, %d).", bytes_received,
pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
// We have received the whole X.224 packet.
if (READ_HEADER != pSocket->Read_State)
{
pSocket->Data_Indication_Length += pSocket->X224_Length - sizeof(X224_DATA_PACKET);
}
// Reset the current length variable for the next Call_recv().
pSocket->Current_Length = 0;
rrCode = RECVRET_CONTINUE;
}
// Handle errors
else
if (bytes_received == SOCKET_ERROR)
{
if (IS_SOCKET(pSocket->XprtConn))
{
if(WSAGetLastError() == WSAEWOULDBLOCK)
{
TRACE_OUT(("Call_recv: recv blocked on socket (%d, %d).",
pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
}
else
{
/* If the error is not WOULD BLOCK, we have a real error. */
WARNING_OUT (("Call_recv: Error %d on recv. Socket: (%d, %d). Disconnecting...",
WSAGetLastError(), pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
rrCode = RECVRET_DISCONNECT;
}
}
else
{
if (PLUGXPRT_RESULT_SUCCESSFUL == plug_rc)
{
// do nothing, treat it as WSAEWOULDBLOCK
}
else
{
/* If the error is not WOULD BLOCK, we have a real error. */
WARNING_OUT (("Call_recv: Error %d on recv. Socket: (%d, %d). Disconnecting...",
WSAGetLastError(), pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
rrCode = RECVRET_DISCONNECT;
}
}
}
else
if (bytes_received > 0)
{
TRACE_OUT(("Call_recv: Received %d bytes out of %d bytes requested on socket (%d, %d).",
bytes_received, length, pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
// We received only part of what we wanted. We retry later.
pSocket->Current_Length += bytes_received;
}
else
{
WARNING_OUT(("Call_recv: Socket (%d, %d) has been gracefully closed.",
pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
rrCode = RECVRET_DISCONNECT;
}
ExitLabel:
return rrCode;
}
int X224Recv(PSocket pSocket, LPBYTE buffer, int length, PLUGXPRT_RESULT *plug_rc)
{
TRACE_OUT(("X224Recv"));
if (IS_PLUGGABLE_X224(pSocket->XprtConn))
{
return ::SubmitPluggableRead(pSocket, buffer, length, plug_rc);
}
if (IS_PLUGGABLE_PSTN(pSocket->XprtConn))
{
return Q922Recv(pSocket, buffer, length, plug_rc);
}
ERROR_OUT(("X224Recv: invalid plugable type (%d, %d)",
pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
return SOCKET_ERROR;
}
int Q922Recv(PSocket pSocket, LPBYTE buffer, int length, PLUGXPRT_RESULT *plug_rc)
{
ERROR_OUT(("Q922Recv: NYI (%d, %d)",
pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
return SOCKET_ERROR;
}
typedef enum {
FreeX224AndExit,
ErrorExit,
ImmediateExit
} ExitWay;
/*
* void ReadRequest ( TransportConnection )
*
* Functional Description:
* This function will attempt to read and process a full X.224 packet.
* However, it may only be able to read part of a packet or fail to
* process it at this time. In this case, it must keep enough state
* info for the next entrance into this function, to be able to handle
* the partly-received or unprocessed X.224 packet.
*/
void ReadRequest (TransportConnection XprtConn)
{
PSocket pSocket;
ExitWay ew = ImmediateExit;
RecvReturn rrCode;
ULONG ulNotify = TPRT_NOTIFY_OTHER_REASON;
TRACE_OUT(("ReadRequest"));
if (IS_PLUGGABLE_PSTN(XprtConn))
{
ERROR_OUT(("ReadRequest: PSTN should not be here"));
return;
}
/* If the transport connection handle is not registered, return */
if (NULL != (pSocket = g_pSocketList->FindByTransportConnection(XprtConn)))
{
if (pSocket->State != WAITING_FOR_CONNECTION)
{
PSecurityContext pSC = pSocket->pSC;
/*
* If we haven't read the header of the incoming packet yet,
* we need to read it into the header space
*/
if (READ_HEADER == pSocket->Read_State)
{
rrCode = Call_recv (pSocket);
if (RECVRET_CONTINUE == rrCode)
{
// We need to allocate the space for the rest of the X.224 packet.
pSocket->bSpaceAllocated = FALSE;
// Find the length of the X.224 packet.
pSocket->X224_Length = (pSocket->X224_Header.rfc.msbPacketSize << 8) +
pSocket->X224_Header.rfc.lsbPacketSize;
/*
* We have the whole X.224 header. Compute the next state,
* based on the packet type.
*/
switch (pSocket->X224_Header.PacketType)
{
case DATA_PACKET:
pSocket->Read_State = READ_DATA;
break;
case CONNECTION_CONFIRM_PACKET:
if (pSocket->State != X224_CONNECTED)
{
pSocket->Read_State = CONNECTION_CONFIRM;
}
else
{
ERROR_OUT (("ReadRequest: Received X.224 CONNECTION_CONFIRM packet while already connected!! Socket: (%d, %d).",
XprtConn.eType, XprtConn.nLogicalHandle));
ew = ErrorExit;
}
break;
case CONNECTION_REQUEST_PACKET:
// we just received a X224 Connect request
pSocket->Read_State = CONNECTION_REQUEST;
::OnProtocolControl(XprtConn, PLUGXPRT_CONNECTING);
break;
case DISCONNECT_REQUEST_PACKET:
// we just received a X224 Disconnect request
pSocket->Read_State = DISCONNECT_REQUEST;
::OnProtocolControl(XprtConn, PLUGXPRT_DISCONNECTING);
break;
default:
// We have lost sync with the remote side.
ERROR_OUT (("ReadRequest: Bad X.224 packet on socket (%d, %d). Disconnecting...", XprtConn.eType, XprtConn.nLogicalHandle));
ew = ErrorExit;
break;
}
}
else
if (RECVRET_DISCONNECT == rrCode)
{
ew = ErrorExit;
}
}
if ((READ_DATA <= pSocket->Read_State) &&
(CONNECTION_REQUEST >= pSocket->Read_State))
{
rrCode = Call_recv (pSocket);
if (RECVRET_CONTINUE == rrCode)
{
// We now have the whole X.224 packet.
switch (pSocket->Read_State)
{
case READ_DATA:
// Check whether this is the final X.224 packet
if (pSocket->X224_Header.FinalPacket & EOT_BIT)
{
// If we're waiting for a security data packet we will process
// this internally without passing it up to the transport
// client.
if (NULL != pSC)
{
if (pSC->WaitingForPacket())
{
TransportSecurityError SecErr;
SecErr = pSC->AdvanceState((PBYTE) pSocket->Data_Indication_Buffer +
sizeof(X224_DATA_PACKET),
pSocket->Data_Indication_Length -
sizeof(X224_DATA_PACKET));
if (TPRTSEC_NOERROR != SecErr)
{
// Something has gone wrong. Need to disconnect
delete pSC;
pSocket->pSC = NULL;
ulNotify = TPRT_NOTIFY_AUTHENTICATION_FAILED;
ew = ErrorExit;
break;
}
if (pSC->ContinueNeeded())
{
// We need to send out another token
// bugbug: what should we do if this fails?
ContinueAuthentication(pSocket);
}
if (pSC->StateComplete())
{
// We're connected... inform the client
TRACE_OUT(("deferred g_Transport->ConnectConfirm"));
g_Transport->ConnectConfirm(XprtConn);
}
ew = FreeX224AndExit;
break;
}
// We must decrypt the data (in place)
TRACE_OUT(("Decrypting received data"));
if (! pSC->Decrypt(pSocket->Data_Indication_Buffer +
sizeof(X224_DATA_PACKET),
pSocket->Data_Indication_Length -
sizeof(X224_DATA_PACKET)))
{
TRACE_OUT(("Sending %d bytes to application",
pSocket->Data_Indication_Length - sizeof(X224_DATA_PACKET)));
}
else
{
ERROR_OUT(("Error decrypting packet"));
ew = ErrorExit;
break;
}
}
pSocket->Read_State = DATA_READY;
}
else
{
// This and the next X.224 packets are part of a bigger MCS data PDU.
ASSERT (NULL == pSC);
pSocket->Read_State = READ_HEADER;
}
break;
case CONNECTION_CONFIRM:
{
TRACE_OUT(("ReadRequest: X224 CONNECTION_CONFIRM_PACKET received"));
BOOL bCallback = ((NULL == pSC) || (! pSC->ContinueNeeded()));
// Process the CC packet.
if (FALSE == ProcessX224ConnectPDU (pSocket,
pSocket->Data_Indication_Buffer + sizeof(X224_CONNECT),
pSocket->X224_Length - sizeof (X224_CONNECT), &ulNotify))
{
ew = ErrorExit;
break;
}
// Issue the callback if the CC was not on a secure connection
// Otherwise, we don't notify the transport client yet... still need to
// exchange security information. TRANSPORT_CONNECT_CONFIRM will
// be sent when the final security data token is received and
// processed.
if (bCallback)
{
TRACE_OUT (("TCP Callback: g_Transport->ConnectConfirm (%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
/* We issue a callback to the user to notify him of the message */
g_Transport->ConnectConfirm(XprtConn);
}
pSocket->State = X224_CONNECTED;
::OnProtocolControl(XprtConn, PLUGXPRT_CONNECTED);
ew = FreeX224AndExit;
}
break;
case CONNECTION_REQUEST:
{
UINT remote;
/* This structure must be accessed using byte-alignment */
#pragma pack(1)
X224_CONNECT *pConnectRequest;
/* return to normal alignment */
#pragma pack()
/* Grab the remote connection ID */
TRACE_OUT (("ReadRequest: X224 CONNECTION_REQUEST_PACKET received"));
pConnectRequest = (X224_CONNECT *) pSocket->Data_Indication_Buffer;
remote = ((unsigned int) pConnectRequest->conn.msbSrc) << 8;
remote |= pConnectRequest->conn.lsbSrc;
if (FALSE == ProcessX224ConnectPDU (pSocket, (PUChar) (pConnectRequest + 1),
pSocket->X224_Length - sizeof (X224_CONNECT), &ulNotify))
{
ew = ErrorExit;
break;
}
if (::SendX224ConnectConfirm(pSocket, remote))
{
// success
if (IS_PLUGGABLE(pSocket->XprtConn))
{
pSocket->State = SOCKET_CONNECTED;
g_Transport->ConnectIndication(XprtConn);
ASSERT(X224_CONNECTED == pSocket->State);
}
::OnProtocolControl(XprtConn, PLUGXPRT_CONNECTED);
ew = FreeX224AndExit;
}
else
{
if (pSocket->fExtendedX224)
{
::SendX224DisconnectRequest(pSocket, remote, securityDenied_chosen);
}
ew = ErrorExit;
}
}
break;
case DISCONNECT_REQUEST:
{
UINT remote;
X224_DR_FIXED *pX224_DR_fixed;
TRACE_OUT(("ReadRequest: X224 DISCONNECT_REQUEST_PACKET received"));
pX224_DR_fixed = (X224_DR_FIXED *) pSocket->Data_Indication_Buffer;
remote = ((unsigned int) pX224_DR_fixed->disconn.msbSrc) << 8;
remote |= pX224_DR_fixed->disconn.lsbSrc;
ProcessX224DisconnectPDU(pSocket, pSocket->Data_Indication_Buffer + sizeof(X224_DR_FIXED),
pSocket->X224_Length - sizeof(X224_DR_FIXED), &ulNotify);
ew = ErrorExit;
}
break;
}
}
else if (RECVRET_DISCONNECT == rrCode)
{
ew = ErrorExit;
}
}
if (DATA_READY == pSocket->Read_State)
{
TransportData transport_data;
// Fill in the callback structure.
transport_data.transport_connection = XprtConn;
transport_data.user_data = pSocket->Data_Indication_Buffer;
transport_data.user_data_length = pSocket->Data_Indication_Length;
transport_data.memory = pSocket->Data_Memory;
/*
* If there is an incoming security context associated with this
* socket, we must adjust pointer by header and overall size by header and
* trailer.
*/
if (NULL != pSC)
{
transport_data.user_data += pSC->GetStreamHeaderSize();
transport_data.user_data_length -= (pSC->GetStreamHeaderSize() +
pSC->GetStreamTrailerSize());
}
if (TRANSPORT_NO_ERROR == g_Transport->DataIndication(&transport_data))
{
TRACE_OUT (("ReadRequest: %d bytes were accepted from socket (%d, %d)",
transport_data.user_data_length, XprtConn.eType, XprtConn.nLogicalHandle));
// Prepare for the next X.224 packet
pSocket->Read_State = READ_HEADER;
pSocket->Data_Indication_Buffer = NULL;
pSocket->Data_Memory = NULL;
}
else
{
WARNING_OUT(("ReadRequest: Error on g_Transport->DataIndication from socket (%d, %d)",
XprtConn.eType, XprtConn.nLogicalHandle));
}
}
}
else
{
WARNING_OUT (("ReadRequest: socket (%d, %d) is in WAITING_FOR_CONNECTION state.", XprtConn.eType, XprtConn.nLogicalHandle));
}
}
else
{
WARNING_OUT (("ReadRequest: socket (%d, %d) can not be found.", XprtConn.eType, XprtConn.nLogicalHandle));
}
switch (ew)
{
case FreeX224AndExit:
if (NULL != pSocket)
{
// Free the buffers we have allocated.
pSocket->FreeTransportBuffer();
// Prepare for the next X.224 packet
pSocket->Read_State = READ_HEADER;
}
break;
case ErrorExit:
// We get here only if we need to disconnect the socket (because of an error)
ASSERT(TPRT_NOTIFY_NONE != ulNotify);
::DisconnectRequest(XprtConn, ulNotify);
break;
}
if (NULL != pSocket)
{
pSocket->Release(); // offset the previous AddRef.
}
}
/*
* TransportError FlushSendBuffer ( PSocket pSocket )
*
* Functional Description:
* This function sends any pending data through the transport.
*/
TransportError FlushSendBuffer(PSocket pSocket, LPBYTE buffer, UINT length)
{
int bytes_sent = SOCKET_ERROR;
PLUGXPRT_RESULT plug_rc = PLUGXPRT_RESULT_SUCCESSFUL;
TRACE_OUT(("FlushSendBuffer"));
/* send the data */
if (IS_SOCKET(pSocket->XprtConn))
{
bytes_sent = ::send(pSocket->XprtConn.nLogicalHandle, (PChar) buffer,
(int) length, 0);
}
else
if (IS_PLUGGABLE_X224(pSocket->XprtConn))
{
bytes_sent = ::SubmitPluggableWrite(pSocket, buffer, length, &plug_rc);
}
else
if (IS_PLUGGABLE_PSTN(pSocket->XprtConn))
{
CPluggableConnection *p = ::GetPluggableConnection(pSocket);
if (NULL != p)
{
bytes_sent = p->TDataRequest(buffer, length, &plug_rc);
}
else
{
plug_rc = PLUGXPRT_RESULT_WRITE_FAILED;
}
}
if (bytes_sent == SOCKET_ERROR)
{
if (IS_SOCKET(pSocket->XprtConn))
{
/* If the error is not WOULD BLOCK, it is a real error! */
if (::WSAGetLastError() != WSAEWOULDBLOCK)
{
WARNING_OUT (("FlushSendBuffer: Error %d on write", ::WSAGetLastError()));
/* Notify the owner of the broken connection */
WARNING_OUT (("FlushSendBuffer: Sending up DISCONNECT_INDICATION"));
// SendStatusMessage (pSocket -> Remote_Address, TSTATE_REMOVED, IDS_NULL_STRING);
::DisconnectRequest(pSocket->XprtConn, TPRT_NOTIFY_OTHER_REASON);
return (TRANSPORT_WRITE_QUEUE_FULL);
}
}
else
{
// do nothing if it is WSAEWOULDBLOCK
if (PLUGXPRT_RESULT_SUCCESSFUL != plug_rc)
{
/* Notify the owner of the broken connection */
WARNING_OUT (("FlushSendBuffer: Sending up DISCONNECT_INDICATION"));
// SendStatusMessage (pSocket -> Remote_Address, TSTATE_REMOVED, IDS_NULL_STRING);
::DisconnectRequest(pSocket->XprtConn, TPRT_NOTIFY_OTHER_REASON);
return (TRANSPORT_WRITE_QUEUE_FULL);
}
}
bytes_sent = 0;
}
/* If the transport layer did not accept the data, its write buffers are full */
if (bytes_sent != (int) length)
{
ASSERT (bytes_sent == 0);
TRACE_OUT(("FlushSendBuffer: returning TRANSPORT_WRITE_QUEUE_FULL"));
return (TRANSPORT_WRITE_QUEUE_FULL);
}
// Increment our counter of bytes sent since last QoS notification
if (bytes_sent)
{
QoSLock();
g_dwSentSinceLastQoS += bytes_sent;
QoSUnlock();
}
TRACE_OUT (("FlushSendBuffer: %d bytes sent on Socket (%d, %d).",
length, pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
return (TRANSPORT_NO_ERROR);
}
/*
* SegmentX224Data
*
* This function segments outgoing data into X.224 packets of the appropriate size.
* It should not be called in a NM to NM call or in a call when we have negotiated an
* X.224 max PDU size of at least the size of a max MCS PDU. NM attempts to negotiate
* X.224 sizes of 8K, but will accept anything the other side proposes.
* This function does memcpy's so it will slow us down sending data.
*
* The 2 buffers specified by "ptr1" and "ptr2" and their lengths are used to create
* one stream of X.224 bytes. The function will return TRANSPORT_WRITE_QUEUE_FULL if
* it fails to allocate the necessary amount of memory.
*/
TransportError SegmentX224Data (PSocket pSocket,
LPBYTE *pPtr1, UINT *pLength1,
LPBYTE Ptr2, UINT Length2)
{
TransportError TransError;
UINT length;
LPBYTE ptr1 = *pPtr1 + sizeof (X224_DATA_PACKET);
UINT length1 = *pLength1 - sizeof (X224_DATA_PACKET);
LPBYTE ptr;
UINT max_pdu_length = pSocket->Max_Packet_Length;
X224_DATA_PACKET l_X224Header = {3, 0, (UChar) (max_pdu_length >> 8), (UChar) (max_pdu_length & 0xFF),
2, DATA_PACKET, 0};
UINT last_length;
/* This structure must be accessed using byte-alignment */
#pragma pack(1)
X224_DATA_PACKET *pX224Data;
/* return to normal alignment */
#pragma pack()
ASSERT(! IS_PLUGGABLE_PSTN(pSocket->XprtConn));
// Calculate how much space we need.
length = *pLength1 + Length2;
ASSERT (pSocket->Max_Packet_Length < length);
ASSERT (pSocket->Max_Packet_Length > sizeof(X224_DATA_PACKET));
max_pdu_length -= sizeof (X224_DATA_PACKET);
/*
* Calculate the space we need to allocate. Notice that the data already
* contains one X.224 header.
*/
length += (length / max_pdu_length) * sizeof (X224_DATA_PACKET);
*pPtr1 = Allocate (length);
if (*pPtr1 != NULL) {
TransError = TRANSPORT_NO_ERROR;
ptr = *pPtr1;
// Go through the 1st buffer.
while (length1 > 0) {
// Copy the X.224 header.
memcpy (ptr, &l_X224Header, sizeof(X224_DATA_PACKET));
pX224Data = (X224_DATA_PACKET *) ptr;
ptr += sizeof (X224_DATA_PACKET);
// Copy data
length = ((max_pdu_length > length1) ? length1 : max_pdu_length);
memcpy (ptr, ptr1, length);
last_length = length;
// Advance pointers
ptr1 += length;
ptr += length;
length1 -= length;
}
// If there is space in the current X.224 PDU, we need to use it.
length = max_pdu_length - length;
if (length > 0 && Length2 > 0) {
if (length > Length2)
length = Length2;
memcpy (ptr, Ptr2, length);
last_length += length;
Ptr2 += length;
ptr += length;
Length2 -= length;
}
// Go through the 2nd buffer.
while (Length2 > 0) {
// Copy the X.224 header.
memcpy (ptr, &l_X224Header, sizeof(X224_DATA_PACKET));
pX224Data = (X224_DATA_PACKET *) ptr;
ptr += sizeof (X224_DATA_PACKET);
// Copy data
length = ((max_pdu_length > Length2) ? Length2 : max_pdu_length);
memcpy (ptr, Ptr2, length);
last_length = length;
// Advance pointers
Ptr2 += length;
ptr += length;
Length2 -= length;
}
// Prepare for return
*pLength1 = (UINT)(ptr - *pPtr1);
// Set the last X.224 header
last_length += sizeof(X224_DATA_PACKET);
pX224Data->FinalPacket = EOT_BIT;
pX224Data->rfc.msbPacketSize = (UChar) (last_length >> 8);
pX224Data->rfc.lsbPacketSize = (UChar) (last_length & 0xFF);
}
else {
ERROR_OUT (("SegmentX224Data: Failed to allocate memory of length %d.", length));
TransError = TRANSPORT_WRITE_QUEUE_FULL;
}
return TransError;
}
/*
* SendSecureData
*
* This function segments secure data into X.224 packets, if needed, and flushes them through
* the transport. "pBuf" and "cbBuf" provide the encrypted data buffer and length.
*/
TransportError SendSecureData (PSocket pSocket, LPBYTE pBuf, UINT cbBuf)
{
TransportError TransError;
LPBYTE pBuf_Copy = pBuf;
UINT cbBuf_Copy = cbBuf;
// Do we need to segment the data into X.224 packets?
if (pSocket->Max_Packet_Length >= cbBuf) {
TransError = TRANSPORT_NO_ERROR;
}
else {
TransError = SegmentX224Data (pSocket, &pBuf, &cbBuf, NULL, 0);
}
// Flush the data, if everything OK so far.
if (TRANSPORT_NO_ERROR == TransError)
TransError = FlushSendBuffer (pSocket, pBuf, cbBuf);
// If we segmented the data, we need to free the segmented buffer.
if (pBuf != pBuf_Copy)
Free(pBuf);
// If there are errors, we need to store the decrypted data for the next time, so don't free it.
if (TRANSPORT_NO_ERROR == TransError) {
LocalFree(pBuf_Copy);
}
return TransError;
}
/*
* TransportError DataRequest ( TransportConnection XprtConn,
* PSimplePacket packet)
*
* Functional Description:
* This function is used to send a data packet to the remote site.
* If the user_data_length is zero, and we have no pending data,
* it sends a keep-alive (zero-length) packet.
*/
TransportError DataRequest (TransportConnection XprtConn,
PSimplePacket packet)
{
PSocket pSocket;
LPBYTE ptr1, ptr2;
UINT length1, length2;
TransportError TransError = TRANSPORT_NO_ERROR;
TRACE_OUT(("DataRequest: packet=0x%x", packet));
if (NULL != (pSocket = g_pSocketList->FindByTransportConnection(XprtConn)))
{
// First, we need to handle the retry operations.
if (NULL != pSocket->pSC) {
LPBYTE lpBuf;
/*
* Check to see whether we have already encrypted, but not sent
* the last piece of data.
*/
lpBuf = pSocket->Retry_Info.sbiBufferInfo.lpBuffer;
if (NULL != lpBuf) {
TransError = SendSecureData (pSocket, lpBuf,
pSocket->Retry_Info.sbiBufferInfo.uiLength);
if (TransError == TRANSPORT_NO_ERROR) {
TRACE_OUT(("DataRequest: Sent previously-encrypted piece of data."));
pSocket->Retry_Info.sbiBufferInfo.lpBuffer = NULL;
}
}
}
else {
PDataPacket pdpPacket = pSocket->Retry_Info.pUnfinishedPacket;
// Check to see whether we have half-sent the last packet.
if (NULL != pdpPacket) {
/*
* We need to send the rest of the unfinished packet,
* before we can go on. The 1st part of the packet
* must have already been sent.
*/
// The packet's encoded data must be in 2 buffers.
ASSERT (TRUE == pdpPacket->IsEncodedDataBroken());
TransError = FlushSendBuffer (pSocket, pdpPacket->GetUserData(),
pdpPacket->GetUserDataLength());
if (TransError == TRANSPORT_NO_ERROR) {
pdpPacket->Unlock();
TRACE_OUT(("DataRequest: 2nd part of data packet was sent out in separate request"));
pSocket->Retry_Info.pUnfinishedPacket = NULL;
}
}
}
if ((TransError == TRANSPORT_NO_ERROR) && (packet != NULL)) {
// Now, let's try to send this new packet.
ptr1 = packet->GetEncodedData();
length1 = packet->GetEncodedDataLength();
/*
* We need to find out whether the packet to send is a
* DataPacket or a Packet object. If it's a DataPacket, the
* encoded data may not be contiguous (may be broken in 2 parts)
*/
if ((packet->IsDataPacket()) &&
((PDataPacket) packet)->IsEncodedDataBroken()) {
// the data to send is broken into 2 parts.
ptr2 = ((PDataPacket) packet)->GetUserData();
length2 = ((PDataPacket) packet)->GetUserDataLength();
}
else {
// the data to send is contiguous.
ptr2 = NULL;
length2 = 0;
}
if (NULL != pSocket->pSC) {
LPBYTE pBuf;
UINT cbBuf;
TRACE_OUT(("Encrypting %d bytes of outgoing data",
(length1 + length2) - sizeof(X224_DATA_PACKET)));
if (!pSocket->pSC->Encrypt(ptr1 + sizeof(X224_DATA_PACKET),
length1 - sizeof(X224_DATA_PACKET),
ptr2, length2, &pBuf, &cbBuf))
{
ASSERT (TransError == TRANSPORT_NO_ERROR);
TransError = SendSecureData (pSocket, pBuf, cbBuf);
if (TRANSPORT_NO_ERROR != TransError) {
TRACE_OUT(("DataRequest: Failed to send encrypted data. Keeping buffer for retry."));
pSocket->Retry_Info.sbiBufferInfo.lpBuffer = pBuf;
pSocket->Retry_Info.sbiBufferInfo.uiLength = cbBuf;
// The caller needs to remove the packet from its queue.
TransError = TRANSPORT_NO_ERROR;
}
}
else
{
WARNING_OUT (("DataRequest: Encryption failed. Disconnecting..."));
::DisconnectRequest(pSocket->XprtConn, TPRT_NOTIFY_OTHER_REASON);
TransError = TRANSPORT_MEMORY_FAILURE;
}
}
else {
BOOL bNeedToFree = FALSE;
// Do we need to segment the data into X.224 packets?
if (pSocket->Max_Packet_Length >= length1 + length2)
;
else {
TransError = SegmentX224Data (pSocket, &ptr1, &length1, ptr2, length2);
if (TRANSPORT_NO_ERROR == TransError) {
// The data is now contiguous
ptr2 = NULL;
bNeedToFree = TRUE;
}
}
// Flush the data, if everything OK so far.
if (TRANSPORT_NO_ERROR == TransError)
TransError = FlushSendBuffer (pSocket, ptr1, length1);
// Free the temporary X.224 buffer if we need to.
if (bNeedToFree)
Free(ptr1);
if (TRANSPORT_NO_ERROR == TransError) {
// If there is more, send it, too.
if (NULL != ptr2) {
TransError = FlushSendBuffer (pSocket, ptr2, length2);
if (TRANSPORT_NO_ERROR != TransError) {
/*
* We need to keep the partial packet to send it later.
* Notice we have already sent a part of this packet.
*/
ASSERT (pSocket->Retry_Info.pUnfinishedPacket == NULL);
pSocket->Retry_Info.pUnfinishedPacket = (PDataPacket) packet;
packet->Lock();
// Return success.
TransError = TRANSPORT_NO_ERROR;
}
}
}
}
}
pSocket->Release();
}
else {
TransError = TRANSPORT_NO_SUCH_CONNECTION;
WARNING_OUT (("DataRequest: Attempt to send to unknown transport connection (%d, %d)",
XprtConn.eType, XprtConn.nLogicalHandle));
}
return TransError;
}
/*
* void PurgeRequest (TransportConnection XprtConn)
*
* Functional Description:
* This function purges the outbound packets for the given transport
* connection.
*/
void PurgeRequest (TransportConnection XprtConn)
{
PSocket pSocket;
TRACE_OUT (("In PurgeRequest for transport connection (%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
if (IS_PLUGGABLE_PSTN(XprtConn))
{
CPluggableConnection *p = ::GetPluggableConnection(XprtConn.nLogicalHandle);
if (NULL != p)
{
p->TPurgeRequest();
}
}
else
/* If the logical connection handle is not registered, return error */
if (NULL != (pSocket = g_pSocketList->FindByTransportConnection(XprtConn)))
{
/* Purge the pending data stored in the socket struct */
if (NULL != pSocket->pSC) {
if (NULL != pSocket->Retry_Info.sbiBufferInfo.lpBuffer) {
TRACE_OUT (("PurgeRequest: Purging data packet for secure connection"));
LocalFree (pSocket->Retry_Info.sbiBufferInfo.lpBuffer);
pSocket->Retry_Info.sbiBufferInfo.lpBuffer = NULL;
}
}
pSocket->Release();
}
}
/*
* void EnableReceiver (Void)
*
* Functional Description:
* This function allows packets to be sent to the user application.
*/
void EnableReceiver (void)
{
PSocket pSocket;
::EnterCriticalSection(&g_csTransport);
CSocketList Connection_List_Copy (*g_pSocketList);
::LeaveCriticalSection(&g_csTransport);
TRACE_OUT(("EnableReceiver"));
if (NULL != g_pLegacyTransport)
{
g_pLegacyTransport->TEnableReceiver();
}
/* Go thru all the sockets and enable receiving */
while (NULL != (pSocket = Connection_List_Copy.Get()))
{
/*
* If we had failed to deliver a data pkt to MCS before, we need
* an extra ReadRequest to recv and keep the FD_READ msgs coming.
*/
if (DATA_READY == pSocket->Read_State)
{
::ReadRequest(pSocket->XprtConn);
}
TRACE_OUT (("EnableReceiver: Calling ReadRequestEx on socket (%d, %d)",
pSocket->XprtConn.eType, pSocket->XprtConn.nLogicalHandle));
::ReadRequestEx(pSocket->XprtConn);
}
}
/*
* TransportError ShutdownAndClose (TransportConnection , BOOL fShutdown, int how)
*
* Functional Description
* This function shuts down the socket and closes it.
*
*/
void ShutdownAndClose (TransportConnection XprtConn, BOOL fShutdown, int how)
{
if (IS_SOCKET(XprtConn))
{
int error;
if (fShutdown)
{
error = ::shutdown(XprtConn.nLogicalHandle, how);
ASSERT(error != SOCKET_ERROR);
#ifdef DEBUG
if(error == SOCKET_ERROR)
{
error = WSAGetLastError();
WARNING_OUT (("ShutdownAndClose: shutdown returned %d", error));
}
#endif // DEBUG
}
error = ::closesocket(XprtConn.nLogicalHandle);
#ifdef DEBUG
if(error == SOCKET_ERROR)
{
WARNING_OUT(("ShutdownAndClose: closesocket returned %d", WSAGetLastError()));
}
#endif // DEBUG
}
}
/*
* TransportError GetLocalAddress (TransportConnection XprtConn,
* TransportAddress address,
* int * size)
*
* Functional Description:
* This function retrieves the local IP address associated with the given
* connection. It returns TRANSPORT_NO_SUCH_CONNECTION if the address is
* not available. If the address is available, the size parameter specifies
* the size of the address buffer on entry, and it is filled in with the size
* used for the address on exit.
*/
TransportError GetLocalAddress( TransportConnection XprtConn,
TransportAddress address,
int * size)
{
SOCKADDR_IN socket_control;
PChar szTemp;
int Length;
TransportError error = TRANSPORT_NO_SUCH_CONNECTION;
if (NULL != g_pSocketList->FindByTransportConnection(XprtConn, TRUE))
{
if (IS_SOCKET(XprtConn))
{
/* Get the local name for the socket */
Length = sizeof(socket_control);
if (getsockname(XprtConn.nLogicalHandle, (LPSOCKADDR) &socket_control, &Length) == 0) {
/* Convert it to an IP address string */
szTemp = inet_ntoa(socket_control.sin_addr);
ASSERT (szTemp);
Length = (int) strlen(szTemp) + 1;
ASSERT (*size >= Length);
ASSERT (address);
/* Copy it to the buffer */
lstrcpyn((PChar)address, szTemp, Length);
*size = Length;
error = TRANSPORT_NO_ERROR;
}
}
else
{
ASSERT(IS_PLUGGABLE(XprtConn));
// string should look like "xprt: 1"
char szConnStr[T120_CONNECTION_ID_LENGTH];
Length = ::CreateConnString((UINT)XprtConn.nLogicalHandle, szConnStr);
if (*size > ++Length)
{
::lstrcpyn(address, szConnStr, Length+1);
*size = Length;
error = TRANSPORT_NO_ERROR;
TRACE_OUT (("GetLocalAddress: plugable connection local address (%s)", address));
}
else
{
ERROR_OUT(("GetLocalAddress: buffer too small, given=%d, required=%d", *size, Length));
error = TRANSPORT_BUFFER_TOO_SMALL;
}
}
}
#ifdef DEBUG
if (error != TRANSPORT_NO_ERROR)
WARNING_OUT (("GetLocalAddress: Failure to obtain local address (%d)", WSAGetLastError()));
#endif // DEBUG
return (error);
}
/*
* void AcceptCall (BOOL fSecure)
*
* Functional Description:
* This function calls Winsock to answer an incoming call.
*/
void AcceptCall (TransportConnection XprtConn)
{
PSocket pSocket;
PSecurityContext pSC = NULL;
SOCKADDR_IN socket_control;
int size;
TRACE_OUT(("AcceptCall"));
if (IS_SOCKET(XprtConn))
{
ASSERT(XprtConn.nLogicalHandle == Listen_Socket);
ASSERT (Listen_Socket != INVALID_SOCKET);
/* Call accept() to see if anyone is calling us */
size = sizeof (socket_control);
XprtConn.nLogicalHandle = ::accept ( Listen_Socket,
(struct sockaddr *) &socket_control, &size);
/* Note that we expect accept to complete immediately */
if (XprtConn.nLogicalHandle == INVALID_SOCKET)
{
ERROR_OUT (("AcceptCall: Error on accept = %d", WSAGetLastError()));
// SendStatusMessage ("", TSTATE_NOT_READY, IDS_NULL_STRING);
return;
}
}
/* If the accept() received an incoming call, create a connection and notify our owner object. */
pSocket = newSocket(XprtConn, NULL);
if( pSocket == NULL )
{
/* Close the socket */
::ShutdownAndClose(XprtConn, TRUE, 2);
return;
}
pSocket -> State = SOCKET_CONNECTED;
if (IS_SOCKET(XprtConn))
{
/* Issue the getpeername() function to get the remote user's address */
size = sizeof (socket_control);
if (::getpeername(XprtConn.nLogicalHandle, (LPSOCKADDR) &socket_control, &size) == 0)
{
lstrcpyn (
pSocket -> Remote_Address,
inet_ntoa (socket_control.sin_addr),
MAXIMUM_IP_ADDRESS_SIZE-1);
pSocket -> Remote_Address[MAXIMUM_IP_ADDRESS_SIZE - 1] = NULL;
}
// SendStatusMessage(pSocket -> Remote_Address, TSTATE_CONNECTED, IDS_NULL_STRING);
}
/* Add to connection list */
// bugbug: we fail to insert.
g_pSocketList->SafeAppend(pSocket);
/* Notify the user */
TRACE_OUT (("TCP Callback: g_Transport->ConnectIndication (%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
/* We issue a callback to the user to notify him of the message */
g_Transport->ConnectIndication(XprtConn);
}
//
// ReadRequestEx() is for the plugable transport.
// Since we do not have the FD_ACCEPT notifcation, we try to make sure
// we have a valid transport connection for every read...
// The following piece of code is derived from AcceptCall().
//
void ReadRequestEx(TransportConnection XprtConn)
{
if (! IS_PLUGGABLE_PSTN(XprtConn))
{
::ReadRequest(XprtConn);
}
}
/*
* LRESULT WindowProcedure (
* HWND window_handle,
* UINT message,
* WPARAM wParam,
* LPARAM lParam)
*
* Public
*
* Functional Description:
* This function is called by Windows when we dispatch a TCP message from the
* event loop above. It gives us a chance to process the incoming socket messages.
*/
LRESULT WindowProcedure (HWND window_handle,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
TransportConnection XprtConn;
UShort error;
UShort event;
//PSocket pSocket;
switch (message)
{
#ifndef NO_TCP_TIMER
case WM_TIMER:
{
/*
** We are currently using a slow timer to keep reading even when
** FD_READ msgs get lost (this happens on Win95).
**
*/
if (NULL != g_Transport) {
TRACE_OUT(("MSMCSTCP: WM_TIMER"));
EnableReceiver ();
}
}
break;
#endif /* NO_TCP_TIMER */
case WM_SOCKET_NOTIFICATION:
{
/* This message is generated by WinSock */
event = WSAGETSELECTEVENT (lParam);
error = WSAGETSELECTERROR (lParam);
SET_SOCKET_CONNECTION(XprtConn, wParam);
/* We disconnect whenever a socket command generates an error message */
if (error)
{
WARNING_OUT (("TCP: error %d on socket (%d). Event: %d", error, XprtConn.nLogicalHandle, event));
::DisconnectRequest(XprtConn, TPRT_NOTIFY_OTHER_REASON);
break;
}
/* We get FD_CLOSE when the socket is closed by the remote site. */
if (event & FD_CLOSE)
{
TRACE_OUT (("TCP: FD_CLOSE(%d)", XprtConn.nLogicalHandle));
::DisconnectRequest(XprtConn, TPRT_NOTIFY_OTHER_REASON);
break;
}
/* We get FD_READ when there is data available for us to read. */
if (event & FD_READ)
{
// TRACE_OUT(("MSMCSTCP: FD_READ(%d)", (UINT) wParam));
::ReadRequest(XprtConn);
}
/* We get FD_ACCEPT when a remote site is connecting with us */
if (event & FD_ACCEPT)
{
TRACE_OUT (("TCP: FD_ACCEPT(%d)", XprtConn.nLogicalHandle));
/* Note that we always accept calls. Disconnect cancels them. */
TransportConnection XprtConn2;
SET_SOCKET_CONNECTION(XprtConn2, Listen_Socket);
::AcceptCall(XprtConn2);
}
/* We get FD_CONNECT when our connect completes */
if (event & FD_CONNECT)
{
TRACE_OUT (("TCP: FD_CONNECT(%d)", XprtConn.nLogicalHandle));
::SendX224ConnectRequest(XprtConn);
}
/* We get FD_WRITE when there is space available to write data to WinSock */
if (event & FD_WRITE)
{
/*
* We need to send a BUFFER_EMPTY_INDICATION to the connection associated
* with the socket
*/
TRACE_OUT (("TCP: FD_WRITE(%d)", XprtConn.nLogicalHandle));
// We need to flush the socket's pending data first.
if (TRANSPORT_NO_ERROR == ::DataRequest(XprtConn, NULL))
{
TRACE_OUT (("TCP: Sending BUFFER_EMPTY_INDICATION to transport."));
g_Transport->BufferEmptyIndication(XprtConn);
}
}
}
break;
case WM_PLUGGABLE_X224:
// for low level read and write,
{
XprtConn.eType = (TransportType) PLUGXPRT_WPARAM_TO_TYPE(wParam);
XprtConn.nLogicalHandle = PLUGXPRT_WPARAM_TO_ID(wParam);
ASSERT(IS_PLUGGABLE(XprtConn));
event = PLUGXPRT_LPARAM_TO_EVENT(lParam);
error = PLUGXPRT_LPARAM_TO_ERROR(lParam);
/* We disconnect whenever a socket command generates an error message */
if (error)
{
WARNING_OUT(("PluggableWndProc: error %d on socket (%d, %d). Event: %d",
error, XprtConn.eType, XprtConn.nLogicalHandle, event));
::DisconnectRequest(XprtConn, TPRT_NOTIFY_OTHER_REASON);
::PluggableShutdown(XprtConn);
break;
}
switch (event)
{
case PLUGXPRT_EVENT_READ:
TRACE_OUT(("PluggableWndProc: READ(%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
::ReadRequestEx(XprtConn);
break;
case PLUGXPRT_EVENT_WRITE:
TRACE_OUT(("PluggableWndProc: WRITE(%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
::PluggableWriteTheFirst(XprtConn);
break;
case PLUGXPRT_EVENT_CLOSE:
TRACE_OUT(("PluggableWndProc: CLOSE(%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
::DisconnectRequest(XprtConn, TPRT_NOTIFY_OTHER_REASON);
break;
case PLUGXPRT_HIGH_LEVEL_READ:
TRACE_OUT(("PluggableWndProc: READ_NEXT(%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
::ReadRequestEx(XprtConn);
break;
case PLUGXPRT_HIGH_LEVEL_WRITE:
TRACE_OUT(("PluggableWndProc: WRITE_NEXT(%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle));
// We need to flush the socket's pending data first.
if (TRANSPORT_NO_ERROR == ::DataRequest(XprtConn, NULL))
{
TRACE_OUT(("PluggableWndProc: Sending BUFFER_EMPTY_INDICATION to transport."));
g_Transport->BufferEmptyIndication(XprtConn);
}
break;
default:
ERROR_OUT(("PluggableWndProc: unknown event=%d.", event));
break;
}
}
break;
case WM_PLUGGABLE_PSTN:
{
extern void HandlePSTNCallback(WPARAM wParam, LPARAM lParam);
HandlePSTNCallback(wParam, lParam);
}
break;
default:
{
/*
** The message is not related to WinSock messages, so let
** the default window procedure handle it.
*/
return (DefWindowProc (window_handle, message, wParam, lParam));
}
}
return (0);
}
// GetSecurityInfo() takes a connection_handle and returns the security information associated with
// it.
//
// Returns TRUE if we can either find the information or we are not directly connected to the node
// represented by this connection handle.
//
// Returns FALSE if we are directly connected but for some reason could not get the info -- this
// result should be viewed as suspicious.
BOOL GetSecurityInfo(ConnectionHandle connection_handle, PBYTE pInfo, PDWORD pcbInfo)
{
PSocket pSocket;
SOCKET socket_number;
if (g_pMCSController->FindSocketNumber(connection_handle, &socket_number))
{
TransportConnection XprtConn;
SET_SOCKET_CONNECTION(XprtConn, socket_number);
BOOL fRet = FALSE;
if (NULL != (pSocket = g_pSocketList->FindByTransportConnection(XprtConn)))
{
if (NULL != pSocket->pSC)
{
fRet = pSocket->pSC->GetUserCert(pInfo, pcbInfo);
}
else
{
WARNING_OUT(("GetSecurityInfo: queried non-secure socket %d", socket_number));
}
pSocket->Release();
}
else
{
WARNING_OUT(("GetSecurityInfo: socket %d not found", socket_number ));
}
return fRet;
}
// In this case we are not directly connected, so will return length of NOT_DIRECTLY_CONNECTED
// but positive return value.
*pcbInfo = NOT_DIRECTLY_CONNECTED;
return TRUE;
}
// GetSecurityInfoFromGCCID() takes a GCCID and returns the security information associated with
// it.
//
// Returns TRUE if either (1) we successfully retrieve the information from a transport-level
// connection, or (2) we find that we are not directly connected to the node with this GCCID.
//
// Returns FALSE if we are directly connected but cannot retrieve the info, or some other error
// occurs. A FALSE return value should be treated as a security violation.
BOOL WINAPI T120_GetSecurityInfoFromGCCID(DWORD dwGCCID, PBYTE pInfo, PDWORD pcbInfo)
{
PSocket pSocket;
SOCKET socket_number;
if ( NULL != dwGCCID )
{
// Get the user info for a remote connection
ConnectionHandle connection_handle;
BOOL fConnected = FindSocketNumber(dwGCCID, &socket_number);
if (fConnected == FALSE) {
(* pcbInfo) = 0;
return TRUE;
}
TransportConnection XprtConn;
SET_SOCKET_CONNECTION(XprtConn, socket_number);
BOOL fRet = FALSE;
if (NULL != (pSocket = g_pSocketList->FindByTransportConnection(XprtConn)))
{
if (NULL != pSocket->pSC)
{
fRet = pSocket->pSC->GetUserCert(pInfo, pcbInfo);
}
else
{
WARNING_OUT(("GetSecurityInfoFromGCCID: queried non-secure socket %d", socket_number));
}
pSocket->Release();
}
else
{
ERROR_OUT(("GetSecurityInfoFromGCCID: socket %d not found", socket_number ));
}
return fRet;
}
else
{
// Get the user info for the local user
if ( NULL != g_Transport && NULL != g_Transport->pSecurityInterface )
return g_Transport->pSecurityInterface->GetUserCert( pInfo, pcbInfo );
else
return FALSE;
}
}
DWORD WINAPI T120_TprtSecCtrl ( DWORD dwCode, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
{
DWORD dwRet = TPRTSEC_NOERROR;
RegEntry re(CONFERENCING_KEY, HKEY_CURRENT_USER);
switch ( dwCode )
{
case TPRTCTRL_SETX509CREDENTIALS:
//
// Security. Create transport interface if we don't have one.
// Update credentials if we do.
//
if (!g_Transport->pSecurityInterface)
{
g_Transport->pSecurityInterface =
new SecurityInterface(g_Transport->bInServiceContext);
if ( TPRTSEC_NOERROR !=
g_Transport->pSecurityInterface->Initialize())
{
delete g_Transport->pSecurityInterface;
g_Transport->pSecurityInterface = NULL;
dwRet = TPRTSEC_SSPIFAIL;
}
}
else
{
//
// dwParam1 points to an encoded X509 cert
// create credentials from it.
//
dwRet = g_Transport->pSecurityInterface->
InitializeCreds((PCCERT_CONTEXT)dwParam1);
}
return dwRet;
break;
case TPRTCTRL_GETX509CREDENTIALS:
if ( g_Transport->pSecurityInterface )
{
DWORD cb;
PBYTE pb;
if ( g_Transport->pSecurityInterface->GetUserCert( NULL, &cb))
{
if ( pb = (PBYTE)CoTaskMemAlloc ( cb ))
{
if(g_Transport->pSecurityInterface->GetUserCert(pb,&cb))
{
*((PBYTE *)dwParam1) = pb;
*((PDWORD)dwParam2) = cb;
dwRet = TPRTSEC_NOERROR;
}
else
CoTaskMemFree(pb);
}
}
}
else
{
ERROR_OUT(("TPRTCTRL_GETX509CREDENTIALS w/ no infc"));
dwRet = TPRTSEC_SSPIFAIL;
}
return dwRet;
break;
default:
ERROR_OUT(("TprtSecCtrl: unrecognized command code"));
return 0;
}
ASSERT(FALSE); // Should not reach this
return 0;
}