From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- engine/socketcreator.cpp | 358 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 engine/socketcreator.cpp (limited to 'engine/socketcreator.cpp') diff --git a/engine/socketcreator.cpp b/engine/socketcreator.cpp new file mode 100644 index 0000000..a6445b9 --- /dev/null +++ b/engine/socketcreator.cpp @@ -0,0 +1,358 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Utility class to help in socket creation. Works for clients + servers +// +//===========================================================================// + + +#if defined(_WIN32) +#if !defined(_X360) +#include +#endif +#undef SetPort // winsock screws with the SetPort string... *sigh* +#define socklen_t int +#define MSG_NOSIGNAL 0 +#elif POSIX +#include +#include +#include +#include +#include +#include +#define closesocket close +#define WSAGetLastError() errno +#define ioctlsocket ioctl +#ifdef OSX +#define MSG_NOSIGNAL 0 +#endif +#endif +#include +#include "socketcreator.h" +#include "server.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +bool SocketWouldBlock() +{ +#ifdef _WIN32 + return (WSAGetLastError() == WSAEWOULDBLOCK); +#elif POSIX + return (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSocketCreator::CSocketCreator( ISocketCreatorListener *pListener ) +{ + m_hListenSocket = -1; + m_pListener = pListener; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CSocketCreator::~CSocketCreator() +{ + Disconnect(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the listening socket is created and listening +//----------------------------------------------------------------------------- +bool CSocketCreator::IsListening() const +{ + return m_hListenSocket != -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Bind to a TCP port and accept incoming connections +//----------------------------------------------------------------------------- +bool CSocketCreator::CreateListenSocket( const netadr_t &netAdr ) +{ + CloseListenSocket(); + + m_ListenAddress = netAdr; + m_hListenSocket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if ( m_hListenSocket == -1 ) + { + Warning( "Socket unable to create socket (%s)\n", NET_ErrorString( WSAGetLastError() ) ); + return false; + } + + if ( !ConfigureSocket( m_hListenSocket ) ) + { + CloseListenSocket(); + return false; + } + + struct sockaddr_in s; + m_ListenAddress.ToSockadr( (struct sockaddr *)&s ); + int ret = bind( m_hListenSocket, (struct sockaddr *)&s, sizeof(struct sockaddr_in) ); + if ( ret == -1 ) + { + Warning( "Socket bind failed (%s)\n", NET_ErrorString( WSAGetLastError() ) ); + CloseListenSocket(); + return false; + } + + ret = listen( m_hListenSocket, SOCKET_TCP_MAX_ACCEPTS ); + if ( ret == -1 ) + { + Warning( "Socket listen failed (%s)\n", NET_ErrorString( WSAGetLastError() ) ); + CloseListenSocket(); + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Configures a socket for use +//----------------------------------------------------------------------------- +bool CSocketCreator::ConfigureSocket( int sock ) +{ + // disable NAGLE (rcon cmds are small in size) + int nodelay = 1; + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay)); + + nodelay = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&nodelay, sizeof(nodelay)); + + int opt = 1, ret; + ret = ioctlsocket( sock, FIONBIO, (unsigned long*)&opt ); // non-blocking + if ( ret == -1 ) + { + Warning( "Socket accept ioctl(FIONBIO) failed (%i)\n", WSAGetLastError() ); + return false; + } + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle a new connection +//----------------------------------------------------------------------------- +void CSocketCreator::ProcessAccept() +{ + int newSocket; + sockaddr sa; + int nLengthAddr = sizeof(sa); + + newSocket = accept( m_hListenSocket, &sa, (socklen_t *)&nLengthAddr ); + if ( newSocket == -1 ) + { + if ( !SocketWouldBlock() +#ifdef POSIX + && errno != EINTR +#endif + ) + { + Warning ("Socket ProcessAccept Error: %s\n", NET_ErrorString( WSAGetLastError() ) ); + } + return; + } + + if ( !ConfigureSocket( newSocket ) ) + { + closesocket( newSocket ); + return; + } + + netadr_t adr; + adr.SetFromSockadr( &sa ); + if ( m_pListener && !m_pListener->ShouldAcceptSocket( newSocket, adr ) ) + { + closesocket( newSocket ); + return; + } + + // new connection TCP request, put in accepted queue + int nIndex = m_hAcceptedSockets.AddToTail(); + AcceptedSocket_t *pNewEntry = &m_hAcceptedSockets[nIndex]; + pNewEntry->m_hSocket = newSocket; + pNewEntry->m_Address = adr; + pNewEntry->m_pData = NULL; + + void* pData = NULL; + if ( m_pListener ) + { + m_pListener->OnSocketAccepted( newSocket, adr, &pData ); + } + pNewEntry->m_pData = pData; +} + + +//----------------------------------------------------------------------------- +// Purpose: connect to the remote server +//----------------------------------------------------------------------------- +int CSocketCreator::ConnectSocket( const netadr_t &netAdr, bool bSingleSocket ) +{ + if ( bSingleSocket ) + { + CloseAllAcceptedSockets(); + } + + SocketHandle_t hSocket = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); + if ( hSocket == -1 ) + { + Warning( "Unable to create socket (%s)\n", NET_ErrorString( WSAGetLastError() ) ); + return -1; + } + + int opt = 1, ret; + ret = ioctlsocket( hSocket, FIONBIO, (unsigned long*)&opt ); // non-blocking + if ( ret == -1 ) + { + Warning( "Socket ioctl(FIONBIO) failed (%s)\n", NET_ErrorString( WSAGetLastError() ) ); + closesocket( hSocket ); + return -1; + } + + // disable NAGLE (rcon cmds are small in size) + int nodelay = 1; + setsockopt( hSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay) ); + + struct sockaddr_in s; + netAdr.ToSockadr( (struct sockaddr *)&s ); + + ret = connect( hSocket, (struct sockaddr *)&s, sizeof(s)); + if ( ret == -1 ) + { + if ( !SocketWouldBlock() ) + { + Warning( "Socket connection failed (%s)\n", NET_ErrorString( WSAGetLastError() ) ); + closesocket( hSocket ); + return -1; + } + + fd_set writefds; + struct timeval tv; + tv.tv_usec = 0; + tv.tv_sec = 1; + FD_ZERO( &writefds ); + FD_SET( static_cast( hSocket ), &writefds ); + if ( select ( hSocket + 1, NULL, &writefds, NULL, &tv ) < 1 ) // block for at most 1 second + { + closesocket( hSocket ); // took too long to connect to, give up + return -1; + } + } + + if ( m_pListener && !m_pListener->ShouldAcceptSocket( hSocket, netAdr ) ) + { + closesocket( hSocket ); + return -1; + } + + // new connection TCP request, put in accepted queue + void *pData = NULL; + int nIndex = m_hAcceptedSockets.AddToTail(); + AcceptedSocket_t *pNewEntry = &m_hAcceptedSockets[nIndex]; + pNewEntry->m_hSocket = hSocket; + pNewEntry->m_Address = netAdr; + pNewEntry->m_pData = NULL; + + if ( m_pListener ) + { + m_pListener->OnSocketAccepted( hSocket, netAdr, &pData ); + } + + pNewEntry->m_pData = pData; + return nIndex; +} + + +//----------------------------------------------------------------------------- +// Purpose: close an open rcon connection +//----------------------------------------------------------------------------- +void CSocketCreator::CloseListenSocket() +{ + if ( m_hListenSocket != -1 ) + { + closesocket( m_hListenSocket ); + m_hListenSocket = -1; + } +} + +void CSocketCreator::CloseAcceptedSocket( int nIndex ) +{ + if ( nIndex >= m_hAcceptedSockets.Count() ) + return; + + AcceptedSocket_t& connected = m_hAcceptedSockets[nIndex]; + if ( m_pListener ) + { + m_pListener->OnSocketClosed( connected.m_hSocket, connected.m_Address, connected.m_pData ); + } + closesocket( connected.m_hSocket ); + m_hAcceptedSockets.Remove( nIndex ); +} + +void CSocketCreator::CloseAllAcceptedSockets() +{ + int nCount = m_hAcceptedSockets.Count(); + for ( int i = 0; i < nCount; ++i ) + { + AcceptedSocket_t& connected = m_hAcceptedSockets[i]; + if ( m_pListener ) + { + m_pListener->OnSocketClosed( connected.m_hSocket, connected.m_Address, connected.m_pData ); + } + closesocket( connected.m_hSocket ); + } + m_hAcceptedSockets.RemoveAll(); +} + + +void CSocketCreator::Disconnect() +{ + CloseListenSocket(); + CloseAllAcceptedSockets(); +} + + +//----------------------------------------------------------------------------- +// Purpose: accept new connections and walk open sockets and handle any incoming data +//----------------------------------------------------------------------------- +void CSocketCreator::RunFrame() +{ + if ( IsListening() ) + { + ProcessAccept(); // handle any new connection requests + } +} + + +//----------------------------------------------------------------------------- +// Returns socket info +//----------------------------------------------------------------------------- +int CSocketCreator::GetAcceptedSocketCount() const +{ + return m_hAcceptedSockets.Count(); +} + +SocketHandle_t CSocketCreator::GetAcceptedSocketHandle( int nIndex ) const +{ + return m_hAcceptedSockets[nIndex].m_hSocket; +} + +const netadr_t& CSocketCreator::GetAcceptedSocketAddress( int nIndex ) const +{ + return m_hAcceptedSockets[nIndex].m_Address; +} + +void* CSocketCreator::GetAcceptedSocketData( int nIndex ) +{ + return m_hAcceptedSockets[nIndex].m_pData; +} + + -- cgit v1.2.3