summaryrefslogtreecommitdiff
path: root/utils/vmpi/iphelpers.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/vmpi/iphelpers.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/vmpi/iphelpers.cpp')
-rw-r--r--utils/vmpi/iphelpers.cpp610
1 files changed, 610 insertions, 0 deletions
diff --git a/utils/vmpi/iphelpers.cpp b/utils/vmpi/iphelpers.cpp
new file mode 100644
index 0000000..119bba8
--- /dev/null
+++ b/utils/vmpi/iphelpers.cpp
@@ -0,0 +1,610 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#pragma warning (disable:4127)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#pragma warning (default:4127)
+
+#include "iphelpers.h"
+#include "basetypes.h"
+#include <assert.h>
+#include "utllinkedlist.h"
+#include "utlvector.h"
+#include "tier1/strtools.h"
+
+
+// This automatically calls WSAStartup for the app at startup.
+class CIPStarter
+{
+public:
+ CIPStarter()
+ {
+ WSADATA wsaData;
+ WSAStartup( WINSOCK_VERSION, &wsaData );
+ }
+};
+static CIPStarter g_Starter;
+
+
+unsigned long SampleMilliseconds()
+{
+ CCycleCount cnt;
+ cnt.Sample();
+ return cnt.GetMilliseconds();
+}
+
+
+// ------------------------------------------------------------------------------------------ //
+// CChunkWalker.
+// ------------------------------------------------------------------------------------------ //
+
+CChunkWalker::CChunkWalker( void const * const *pChunks, const int *pChunkLengths, int nChunks )
+{
+ m_TotalLength = 0;
+ for ( int i=0; i < nChunks; i++ )
+ m_TotalLength += pChunkLengths[i];
+
+ m_iCurChunk = 0;
+ m_iCurChunkPos = 0;
+ m_pChunks = pChunks;
+ m_pChunkLengths = pChunkLengths;
+ m_nChunks = nChunks;
+}
+
+int CChunkWalker::GetTotalLength() const
+{
+ return m_TotalLength;
+}
+
+void CChunkWalker::CopyTo( void *pOut, int nBytes )
+{
+ unsigned char *pOutPos = (unsigned char*)pOut;
+
+ int nBytesLeft = nBytes;
+ while ( nBytesLeft > 0 )
+ {
+ int toCopy = nBytesLeft;
+ int curChunkLen = m_pChunkLengths[m_iCurChunk];
+
+ int amtLeft = curChunkLen - m_iCurChunkPos;
+ if ( nBytesLeft > amtLeft )
+ {
+ toCopy = amtLeft;
+ }
+
+ unsigned char *pCurChunkData = (unsigned char*)m_pChunks[m_iCurChunk];
+ memcpy( pOutPos, &pCurChunkData[m_iCurChunkPos], toCopy );
+ nBytesLeft -= toCopy;
+ pOutPos += toCopy;
+
+ // Slide up to the next chunk if we're done with the one we're on.
+ m_iCurChunkPos += toCopy;
+ assert( m_iCurChunkPos <= curChunkLen );
+ if ( m_iCurChunkPos == curChunkLen )
+ {
+ ++m_iCurChunk;
+ m_iCurChunkPos = 0;
+ if ( m_iCurChunk == m_nChunks )
+ {
+ assert( nBytesLeft == 0 );
+ }
+ }
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------ //
+// CWaitTimer
+// ------------------------------------------------------------------------------------------ //
+
+bool g_bForceWaitTimers = false;
+
+CWaitTimer::CWaitTimer( double flSeconds )
+{
+ m_StartTime = SampleMilliseconds();
+ m_WaitMS = (unsigned long)( flSeconds * 1000.0 );
+}
+
+
+bool CWaitTimer::ShouldKeepWaiting()
+{
+ if ( m_WaitMS == 0 )
+ {
+ return false;
+ }
+ else
+ {
+ return ( SampleMilliseconds() - m_StartTime ) <= m_WaitMS || g_bForceWaitTimers;
+ }
+}
+
+
+
+// ------------------------------------------------------------------------------------------ //
+// CIPAddr.
+// ------------------------------------------------------------------------------------------ //
+
+CIPAddr::CIPAddr()
+{
+ Init( 0, 0, 0, 0, 0 );
+}
+
+
+CIPAddr::CIPAddr( const int inputIP[4], const int inputPort )
+{
+ Init( inputIP[0], inputIP[1], inputIP[2], inputIP[3], inputPort );
+}
+
+
+CIPAddr::CIPAddr( int ip0, int ip1, int ip2, int ip3, int ipPort )
+{
+ Init( ip0, ip1, ip2, ip3, ipPort );
+}
+
+
+void CIPAddr::Init( int ip0, int ip1, int ip2, int ip3, int ipPort )
+{
+ ip[0] = (unsigned char)ip0;
+ ip[1] = (unsigned char)ip1;
+ ip[2] = (unsigned char)ip2;
+ ip[3] = (unsigned char)ip3;
+ port = (unsigned short)ipPort;
+}
+
+bool CIPAddr::operator==( const CIPAddr &o ) const
+{
+ return ip[0] == o.ip[0] && ip[1] == o.ip[1] && ip[2] == o.ip[2] && ip[3] == o.ip[3] && port == o.port;
+}
+
+
+bool CIPAddr::operator!=( const CIPAddr &o ) const
+{
+ return !( *this == o );
+}
+
+
+void CIPAddr::SetupLocal( int inPort )
+{
+ ip[0] = 0x7f;
+ ip[1] = 0;
+ ip[2] = 0;
+ ip[3] = 1;
+ port = inPort;
+}
+
+
+// ------------------------------------------------------------------------------------------ //
+// Static helpers.
+// ------------------------------------------------------------------------------------------ //
+
+static double IP_FloatTime()
+{
+ CCycleCount cnt;
+ cnt.Sample();
+ return cnt.GetSeconds();
+}
+
+TIMEVAL SetupTimeVal( double flTimeout )
+{
+ TIMEVAL timeVal;
+ timeVal.tv_sec = (long)flTimeout;
+ timeVal.tv_usec = (long)( (flTimeout - (long)flTimeout) * 1000.0 );
+ return timeVal;
+}
+
+// Convert a CIPAddr to a sockaddr_in.
+void IPAddrToInAddr( const CIPAddr *pIn, in_addr *pOut )
+{
+ u_char *p = (u_char*)pOut;
+ p[0] = pIn->ip[0];
+ p[1] = pIn->ip[1];
+ p[2] = pIn->ip[2];
+ p[3] = pIn->ip[3];
+}
+
+// Convert a CIPAddr to a sockaddr_in.
+void IPAddrToSockAddr( const CIPAddr *pIn, struct sockaddr_in *pOut )
+{
+ memset( pOut, 0, sizeof(*pOut) );
+ pOut->sin_family = AF_INET;
+ pOut->sin_port = htons( pIn->port );
+
+ IPAddrToInAddr( pIn, &pOut->sin_addr );
+}
+
+// Convert a CIPAddr to a sockaddr_in.
+void SockAddrToIPAddr( const struct sockaddr_in *pIn, CIPAddr *pOut )
+{
+ const u_char *p = (const u_char*)&pIn->sin_addr;
+ pOut->ip[0] = p[0];
+ pOut->ip[1] = p[1];
+ pOut->ip[2] = p[2];
+ pOut->ip[3] = p[3];
+ pOut->port = ntohs( pIn->sin_port );
+}
+
+
+class CIPSocket : public ISocket
+{
+public:
+ CIPSocket()
+ {
+ m_Socket = INVALID_SOCKET;
+ m_bSetupToBroadcast = false;
+ }
+
+ virtual ~CIPSocket()
+ {
+ Term();
+ }
+
+
+// ISocket implementation.
+public:
+
+ virtual void Release()
+ {
+ delete this;
+ }
+
+
+ virtual bool CreateSocket()
+ {
+ // Clear any old socket we had around.
+ Term();
+
+ // Create a socket to send and receive through.
+ SOCKET sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_IP );
+ if ( sock == INVALID_SOCKET )
+ {
+ Assert( false );
+ return false;
+ }
+
+ // Nonblocking please..
+ int status;
+ DWORD val = 1;
+ status = ioctlsocket( sock, FIONBIO, &val );
+ if ( status != 0 )
+ {
+ assert( false );
+ closesocket( sock );
+ return false;
+ }
+
+ m_Socket = sock;
+ return true;
+ }
+
+
+ // Called after we have a socket.
+ virtual bool BindPart2( const CIPAddr *pAddr )
+ {
+ Assert( m_Socket != INVALID_SOCKET );
+
+ // bind to it!
+ sockaddr_in addr;
+ IPAddrToSockAddr( pAddr, &addr );
+
+ int status = bind( m_Socket, (sockaddr*)&addr, sizeof(addr) );
+ if ( status == 0 )
+ {
+ return true;
+ }
+ else
+ {
+ Term();
+ return false;
+ }
+ }
+
+
+ virtual bool Bind( const CIPAddr *pAddr )
+ {
+ if ( !CreateSocket() )
+ return false;
+
+ return BindPart2( pAddr );
+ }
+
+ virtual bool BindToAny( const unsigned short port )
+ {
+ // (INADDR_ANY)
+ CIPAddr addr;
+ addr.ip[0] = addr.ip[1] = addr.ip[2] = addr.ip[3] = 0;
+ addr.port = port;
+ return Bind( &addr );
+ }
+
+ virtual bool ListenToMulticastStream( const CIPAddr &addr, const CIPAddr &localInterface )
+ {
+ ip_mreq mr;
+ IPAddrToInAddr( &addr, &mr.imr_multiaddr );
+ IPAddrToInAddr( &localInterface, &mr.imr_interface );
+
+ // This helps a lot if the stream is sending really fast.
+ int rcvBuf = 1024*1024*2;
+ setsockopt( m_Socket, SOL_SOCKET, SO_RCVBUF, (char*)&rcvBuf, sizeof( rcvBuf ) );
+
+ if ( setsockopt( m_Socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mr, sizeof( mr ) ) == 0 )
+ {
+ // Remember this so we do IP_DEL_MEMBERSHIP on shutdown.
+ m_bMulticastGroupMembership = true;
+ m_MulticastGroupMREQ = mr;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ virtual bool Broadcast( const void *pData, const int len, const unsigned short port )
+ {
+ assert( m_Socket != INVALID_SOCKET );
+
+ // Make sure we're setup to broadcast.
+ if ( !m_bSetupToBroadcast )
+ {
+ BOOL bBroadcast = true;
+ if ( setsockopt( m_Socket, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof( bBroadcast ) ) != 0 )
+ {
+ assert( false );
+ return false;
+ }
+
+ m_bSetupToBroadcast = true;
+ }
+
+ CIPAddr addr;
+ addr.ip[0] = addr.ip[1] = addr.ip[2] = addr.ip[3] = 0xFF;
+ addr.port = port;
+ return SendTo( &addr, pData, len );
+ }
+
+ virtual bool SendTo( const CIPAddr *pAddr, const void *pData, const int len )
+ {
+ return SendChunksTo( pAddr, &pData, &len, 1 );
+ }
+
+ virtual bool SendChunksTo( const CIPAddr *pAddr, void const * const *pChunks, const int *pChunkLengths, int nChunks )
+ {
+ WSABUF bufs[32];
+ if ( nChunks > 32 )
+ {
+ Error( "CIPSocket::SendChunksTo: too many chunks (%d).", nChunks );
+ }
+
+ int nTotalBytes = 0;
+ for ( int i=0; i < nChunks; i++ )
+ {
+ bufs[i].len = pChunkLengths[i];
+ bufs[i].buf = (char*)pChunks[i];
+ nTotalBytes += pChunkLengths[i];
+ }
+
+ assert( m_Socket != INVALID_SOCKET );
+
+ // Translate the address.
+ sockaddr_in addr;
+ IPAddrToSockAddr( pAddr, &addr );
+
+ DWORD dwNumBytesSent = 0;
+ DWORD ret = WSASendTo(
+ m_Socket,
+ bufs,
+ nChunks,
+ &dwNumBytesSent,
+ 0,
+ (sockaddr*)&addr,
+ sizeof( addr ),
+ NULL,
+ NULL
+ );
+
+ return ret == 0 && (int)dwNumBytesSent == nTotalBytes;
+ }
+
+ virtual int RecvFrom( void *pData, int maxDataLen, CIPAddr *pFrom )
+ {
+ assert( m_Socket != INVALID_SOCKET );
+
+ fd_set readSet;
+ readSet.fd_count = 1;
+ readSet.fd_array[0] = m_Socket;
+
+ TIMEVAL timeVal = SetupTimeVal( 0 );
+
+ // See if it has a packet waiting.
+ int status = select( 0, &readSet, NULL, NULL, &timeVal );
+ if ( status == 0 || status == SOCKET_ERROR )
+ return -1;
+
+ // Get the data.
+ sockaddr_in sender;
+ int fromSize = sizeof( sockaddr_in );
+ status = recvfrom( m_Socket, (char*)pData, maxDataLen, 0, (struct sockaddr*)&sender, &fromSize );
+ if ( status == 0 || status == SOCKET_ERROR )
+ {
+ return -1;
+ }
+ else
+ {
+ if ( pFrom )
+ {
+ SockAddrToIPAddr( &sender, pFrom );
+ }
+
+ m_flLastRecvTime = IP_FloatTime();
+ return status;
+ }
+ }
+
+ virtual double GetRecvTimeout()
+ {
+ return IP_FloatTime() - m_flLastRecvTime;
+ }
+
+
+private:
+
+ void Term()
+ {
+ if ( m_Socket != INVALID_SOCKET )
+ {
+ if ( m_bMulticastGroupMembership )
+ {
+ // Undo our multicast group membership.
+ setsockopt( m_Socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&m_MulticastGroupMREQ, sizeof( m_MulticastGroupMREQ ) );
+ }
+
+ closesocket( m_Socket );
+ m_Socket = INVALID_SOCKET;
+ }
+
+ m_bSetupToBroadcast = false;
+ m_bMulticastGroupMembership = false;
+ }
+
+
+private:
+
+ SOCKET m_Socket;
+
+ bool m_bMulticastGroupMembership; // Did we join a multicast group?
+ ip_mreq m_MulticastGroupMREQ;
+
+ bool m_bSetupToBroadcast;
+ double m_flLastRecvTime;
+ bool m_bListenSocket;
+};
+
+
+
+ISocket* CreateIPSocket()
+{
+ return new CIPSocket;
+}
+
+
+ISocket* CreateMulticastListenSocket(
+ const CIPAddr &addr,
+ const CIPAddr &localInterface )
+{
+ CIPSocket *pSocket = new CIPSocket;
+
+ CIPAddr bindAddr = localInterface;
+ bindAddr.port = addr.port;
+
+ if ( pSocket->Bind( &bindAddr ) &&
+ pSocket->ListenToMulticastStream( addr, localInterface )
+ )
+ {
+ return pSocket;
+ }
+ else
+ {
+ pSocket->Release();
+ return NULL;
+ }
+}
+
+
+bool ConvertStringToIPAddr( const char *pStr, CIPAddr *pOut )
+{
+ char ipStr[512];
+
+ const char *pColon = strchr( pStr, ':' );
+ if ( pColon )
+ {
+ int toCopy = pColon - pStr;
+ if ( toCopy < 2 || toCopy > sizeof(ipStr)-1 )
+ {
+ assert( false );
+ return false;
+ }
+
+ memcpy( ipStr, pStr, toCopy );
+ ipStr[toCopy] = 0;
+
+ pOut->port = (unsigned short)atoi( pColon+1 );
+ }
+ else
+ {
+ strncpy( ipStr, pStr, sizeof( ipStr ) );
+ ipStr[ sizeof(ipStr)-1 ] = 0;
+ }
+
+ if ( ipStr[0] >= '0' && ipStr[0] <= '9' )
+ {
+ // It's numbers.
+ int ip[4];
+ sscanf( ipStr, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3] );
+ pOut->ip[0] = (unsigned char)ip[0];
+ pOut->ip[1] = (unsigned char)ip[1];
+ pOut->ip[2] = (unsigned char)ip[2];
+ pOut->ip[3] = (unsigned char)ip[3];
+ }
+ else
+ {
+ // It's a text string.
+ struct hostent *pHost = gethostbyname( ipStr );
+ if( !pHost )
+ return false;
+
+ pOut->ip[0] = pHost->h_addr_list[0][0];
+ pOut->ip[1] = pHost->h_addr_list[0][1];
+ pOut->ip[2] = pHost->h_addr_list[0][2];
+ pOut->ip[3] = pHost->h_addr_list[0][3];
+ }
+
+ return true;
+}
+
+
+bool ConvertIPAddrToString( const CIPAddr *pIn, char *pOut, int outLen )
+{
+ in_addr addr;
+ addr.S_un.S_un_b.s_b1 = pIn->ip[0];
+ addr.S_un.S_un_b.s_b2 = pIn->ip[1];
+ addr.S_un.S_un_b.s_b3 = pIn->ip[2];
+ addr.S_un.S_un_b.s_b4 = pIn->ip[3];
+
+ HOSTENT *pEnt = gethostbyaddr( (char*)&addr, sizeof( addr ), AF_INET );
+ if ( pEnt )
+ {
+ Q_strncpy( pOut, pEnt->h_name, outLen );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void IP_GetLastErrorString( char *pStr, int maxLen )
+{
+ char *lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ Q_strncpy( pStr, lpMsgBuf, maxLen );
+ LocalFree( lpMsgBuf );
+}
+