#include "precomp.h" DEBUG_FILEZONE(ZONE_T120_MSMCSTCP); #include #include #include #include "tprtsec.h" #include #include #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; }