#include "precomp.h" DEBUG_FILEZONE(ZONE_T120_MSMCSTCP); /* Socket.cpp * * Copyright (c) 1996 by Microsoft Corporation * * Abstract: * This is the implementation of our socket constructor/destructor functions. * */ #include "socket.h" #include "plgxprt.h" /* Size of listen queue */ #define LISTEN_QUEUE_SIZE 3 /* External definitions */ extern HWND TCP_Window_Handle; extern PTransportInterface g_Transport; /* * void CreateAndConfigureListenSocket (VOID) * * Functional Description * This function sets up a listening socket. * returns INVALID_SOCKET if there is any error. */ SOCKET CreateAndConfigureListenSocket (VOID) { SOCKADDR_IN socket_control; SOCKET Socket; // Create the listening socket. Socket = socket (AF_INET, SOCK_STREAM, 0); if (Socket == INVALID_SOCKET) { WARNING_OUT (("Socket: error creating listening socket (errno = %d)", WSAGetLastError())); goto Error; } // The listen socket only waits for FD_ACCEPT msgs. ASSERT(TCP_Window_Handle); if (WSAAsyncSelect (Socket, TCP_Window_Handle, WM_SOCKET_NOTIFICATION, FD_ACCEPT) != 0) { WARNING_OUT (("CreateAndConfigureListenSocket: Error on WSAAsyncSelect = %d", WSAGetLastError())); goto Error; } /* * Load the socket control structure with the parameters necessary. * - Internet socket * - Let it assign any address to this socket * - Assign our port number */ socket_control.sin_family = AF_INET; socket_control.sin_addr.s_addr = INADDR_ANY; socket_control.sin_port = htons ( DEFAULT_LISTEN_PORT ); /* Issue the bind call */ if (bind (Socket, (LPSOCKADDR) &socket_control, sizeof(SOCKADDR_IN)) != 0) { WARNING_OUT (("Socket::Listen: bind failed: Unable to use WinSock")); goto Error; } /* * Issue a listen to WinSock to tell it we are willing to accept calls. * This is a non-blocking listen, therefore we will receive FD_ACCEPT * if someone is trying to call us. */ if (listen (Socket, LISTEN_QUEUE_SIZE) != 0) { WARNING_OUT (("Socket::Listen: listen failed: Unable to use WinSock")); goto Error; } ASSERT(Socket != INVALID_SOCKET); return Socket; Error: if (INVALID_SOCKET != Socket) { ::closesocket(Socket); } return INVALID_SOCKET; } /* * PSocket newSocket (SOCKET socket_number) * * Functional Description: * This is a constructor for the Socket object. It allocates the * send and receive buffers and sets up internal variables. */ PSocket newSocket(TransportConnection XprtConn, PSecurityContext pSC) { if (IS_SOCKET(XprtConn)) { return ::newSocketEx(XprtConn, pSC); } return g_pSocketList->FindByTransportConnection(XprtConn, TRUE); } PSocket newPluggableSocket(TransportConnection XprtConn) { PSocket pSocket = ::newSocketEx(XprtConn, NULL); if (NULL != pSocket) { g_pSocketList->SafeAppend(pSocket); } return pSocket; } PSocket newSocketEx(TransportConnection XprtConn, PSecurityContext pSC) { BOOL fRet; DBG_SAVE_FILE_LINE PSocket pSocket = new CSocket(&fRet, XprtConn, pSC); if (NULL != pSocket) { if (fRet) { return pSocket; } pSocket->Release(); } ERROR_OUT(("newSocket: Unable to allocate memory for Socket struct, pSocket=0x%x", pSocket)); return NULL; } CSocket::CSocket(BOOL *_pfRet, TransportConnection _XprtConn, PSecurityContext _pSC) : CRefCount(MAKE_STAMP_ID('S','o','c','k')), State(IS_SOCKET(_XprtConn) ? NOT_CONNECTED : SOCKET_CONNECTED), SecState((NULL == _pSC) ? SC_UNDETERMINED : SC_SECURE), pSC(_pSC), Max_Packet_Length(DEFAULT_MAX_X224_SIZE), Current_Length(0), Data_Indication_Buffer(NULL), Data_Indication_Length(0), Read_State(READ_HEADER), X224_Length(0), bSpaceAllocated(FALSE), Data_Memory(NULL), fExtendedX224(FALSE), fIncomingSecure(FALSE), XprtConn(_XprtConn) { // assume failure *_pfRet = FALSE; // zero out sub structures ::ZeroMemory(&X224_Header, sizeof(X224_Header)); ::ZeroMemory(&Retry_Info, sizeof(Retry_Info)); Remote_Address[0] = '\0'; if (IS_SOCKET(XprtConn)) { if (INVALID_SOCKET == XprtConn.nLogicalHandle) { /* Create a STREAM socket (fully reliable, full duplex, and sequenced) */ if ((XprtConn.nLogicalHandle = ::socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { ERROR_OUT (("CSocket: error acquiring INET socket # (errno = %d)", WSAGetLastError())); return; } } /* Enable Tx and Rx messages to the window */ ASSERT(TCP_Window_Handle); if (::WSAAsyncSelect(XprtConn.nLogicalHandle, TCP_Window_Handle, WM_SOCKET_NOTIFICATION, FD_READ | FD_WRITE | FD_CLOSE | FD_CONNECT) != 0) { WARNING_OUT (("CSocket: Error on WSAAsyncSelect = %d", WSAGetLastError())); } } else { ASSERT(IS_PLUGGABLE(XprtConn)); CPluggableConnection *p = ::GetPluggableConnection(this); if (NULL == p) { ERROR_OUT(("newSocket: Unable to find plugable transport (%d, %d)", XprtConn.eType, XprtConn.nLogicalHandle)); return; } } // success *_pfRet = TRUE; } /* * void freeSocket (PSocket, TransportConnection) * * Functional Description: * This is a destructor for the Socket object. It frees the send * and receive buffers and connection structure. * It will also cleanup the listening socket. In this case, * "pSocket" is set to NULL and "trash_packets" should be set to TRUE. */ void freeSocket(PSocket pSocket, TransportConnection XprtConn) { if (IS_SOCKET(XprtConn)) { if (NULL != g_pSocketList) { g_pSocketList->SafeRemove(pSocket); } freeSocketEx(pSocket, XprtConn); } } void freeListenSocket(TransportConnection XprtConn) { ASSERT(IS_SOCKET(XprtConn)); freeSocketEx(NULL, XprtConn); } void freePluggableSocket(PSocket pSocket) { freeSocketEx(pSocket, pSocket->XprtConn); if (NULL != g_pSocketList) { g_pSocketList->SafeRemove(pSocket); } } void freeSocketEx(PSocket pSocket, TransportConnection XprtConn) { // Either "pSocket" is NULL, or the socket is not invalid. #ifdef _DEBUG if (IS_SOCKET(XprtConn)) { if (NULL != pSocket) { ASSERT(INVALID_SOCKET != pSocket->XprtConn.nLogicalHandle); } else { // it is a listen socket ASSERT(INVALID_SOCKET != XprtConn.nLogicalHandle); } } #endif // Determine the socket number to use... Either the socket is the // socket indicated in the PSocket structure, or it is a structure-less // listen socket. Note: both cannot be valid! if (IS_SOCKET(XprtConn)) { SOCKET socket = (pSocket) ? pSocket->XprtConn.nLogicalHandle : XprtConn.nLogicalHandle; XprtConn.nLogicalHandle = socket; /* Disable notifications to our window */ if (::IsWindow(TCP_Window_Handle)) { ::WSAAsyncSelect(socket, TCP_Window_Handle, 0, 0); } } if (pSocket != NULL) { pSocket->Release(); } else { // This is the listening socket ::ShutdownAndClose (XprtConn, FALSE, 0); } } CSocket::~CSocket(void) { switch (State) { case SOCKET_CONNECTED: // case WAITING_FOR_DISCONNECT: /* All physically connected states issue a shutdown() first */ ::ShutdownAndClose(XprtConn, TRUE, SD_BOTH); break; case X224_CONNECTED: // Shutdown disable reception only. ::ShutdownAndClose(XprtConn, TRUE, SD_RECEIVE); break; default: ::ShutdownAndClose(XprtConn, FALSE, 0); break; } /* Free the structures */ FreeTransportBuffer(); delete pSC; } void CSocket::FreeTransportBuffer(void) { if (NULL != Data_Memory) { ::FreeMemory(Data_Memory); Data_Memory = NULL; Data_Indication_Buffer = NULL; } } void CSocketList::SafeAppend(PSocket pSocket) { ::EnterCriticalSection(&g_csTransport); if (! Find(pSocket)) { Append(pSocket); } ::LeaveCriticalSection(&g_csTransport); } BOOL CSocketList::SafeRemove(PSocket pSocket) { ::EnterCriticalSection(&g_csTransport); BOOL fRet = Remove(pSocket); ::LeaveCriticalSection(&g_csTransport); return fRet; } PSocket CSocketList::FindByTransportConnection(TransportConnection XprtConn, BOOL fNoAddRef) { PSocket pSocket; ::EnterCriticalSection(&g_csTransport); Reset(); while (NULL != (pSocket = Iterate())) { if (IS_SAME_TRANSPORT_CONNECTION(pSocket->XprtConn, XprtConn)) { if (! fNoAddRef) { pSocket->AddRef(); } break; } } ::LeaveCriticalSection(&g_csTransport); return pSocket; } PSocket CSocketList::RemoveByTransportConnection(TransportConnection XprtConn) { PSocket pSocket; ::EnterCriticalSection(&g_csTransport); Reset(); while (NULL != (pSocket = Iterate())) { if (IS_SAME_TRANSPORT_CONNECTION(pSocket->XprtConn, XprtConn)) { Remove(pSocket); break; } } ::LeaveCriticalSection(&g_csTransport); return pSocket; }