mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2645 lines
95 KiB
2645 lines
95 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);
|
|
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;
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
ASSERT ((pSocket->X224_Length) > 0 && (pSocket->X224_Length <= 8192));
|
|
|
|
// 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;
|
|
}
|
|
|