1 //====== Copyright Valve Corporation, All rights reserved. ====================
2 
3 #include "csteamnetworkingsockets.h"
4 #include "steamnetworkingsockets_lowlevel.h"
5 #include "steamnetworkingsockets_connections.h"
6 #include "steamnetworkingsockets_udp.h"
7 #include "../steamnetworkingsockets_certstore.h"
8 #include "crypto.h"
9 
10 #ifdef STEAMNETWORKINGSOCKETS_STANDALONELIB
11 #include <steam/steamnetworkingsockets.h>
12 #endif
13 
14 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
15 #include "csteamnetworkingmessages.h"
16 #endif
17 
18 // Needed for the platform checks below
19 #if defined(__APPLE__)
20 	#include "AvailabilityMacros.h"
21 	#include "TargetConditionals.h"
22 #endif
23 
24 // memdbgon must be the last include file in a .cpp file!!!
25 #include "tier0/memdbgon.h"
26 
~ISteamNetworkingSockets()27 ISteamNetworkingSockets::~ISteamNetworkingSockets() {}
~ISteamNetworkingUtils()28 ISteamNetworkingUtils::~ISteamNetworkingUtils() {}
29 
30 // Put everything in a namespace, so we don't violate the one definition rule
31 namespace SteamNetworkingSocketsLib {
32 
33 /////////////////////////////////////////////////////////////////////////////
34 //
35 // Configuration Variables
36 //
37 /////////////////////////////////////////////////////////////////////////////
38 
39 DEFINE_GLOBAL_CONFIGVAL( float, FakePacketLoss_Send, 0.0f, 0.0f, 100.0f );
40 DEFINE_GLOBAL_CONFIGVAL( float, FakePacketLoss_Recv, 0.0f, 0.0f, 100.0f );
41 DEFINE_GLOBAL_CONFIGVAL( int32, FakePacketLag_Send, 0, 0, 5000 );
42 DEFINE_GLOBAL_CONFIGVAL( int32, FakePacketLag_Recv, 0, 0, 5000 );
43 DEFINE_GLOBAL_CONFIGVAL( float, FakePacketReorder_Send, 0.0f, 0.0f, 100.0f );
44 DEFINE_GLOBAL_CONFIGVAL( float, FakePacketReorder_Recv, 0.0f, 0.0f, 100.0f );
45 DEFINE_GLOBAL_CONFIGVAL( int32, FakePacketReorder_Time, 15, 0, 5000 );
46 DEFINE_GLOBAL_CONFIGVAL( float, FakePacketDup_Send, 0.0f, 0.0f, 100.0f );
47 DEFINE_GLOBAL_CONFIGVAL( float, FakePacketDup_Recv, 0.0f, 0.0f, 100.0f );
48 DEFINE_GLOBAL_CONFIGVAL( int32, FakePacketDup_TimeMax, 10, 0, 5000 );
49 DEFINE_GLOBAL_CONFIGVAL( int32, PacketTraceMaxBytes, -1, -1, 99999 );
50 DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Send_Rate, 0, 0, 1024*1024*1024 );
51 DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Send_Burst, 16*1024, 0, 1024*1024 );
52 DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Recv_Rate, 0, 0, 1024*1024*1024 );
53 DEFINE_GLOBAL_CONFIGVAL( int32, FakeRateLimit_Recv_Burst, 16*1024, 0, 1024*1024 );
54 
55 DEFINE_GLOBAL_CONFIGVAL( int32, EnumerateDevVars, 0, 0, 1 );
56 
57 DEFINE_GLOBAL_CONFIGVAL( void *, Callback_AuthStatusChanged, nullptr );
58 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
59 DEFINE_GLOBAL_CONFIGVAL( void*, Callback_MessagesSessionRequest, nullptr );
60 DEFINE_GLOBAL_CONFIGVAL( void*, Callback_MessagesSessionFailed, nullptr );
61 #endif
62 DEFINE_GLOBAL_CONFIGVAL( void *, Callback_CreateConnectionSignaling, nullptr );
63 
64 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, TimeoutInitial, 10000, 0, INT32_MAX );
65 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, TimeoutConnected, 10000, 0, INT32_MAX );
66 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SendBufferSize, 512*1024, 0, 0x10000000 );
67 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int64, ConnectionUserData, -1 ); // no limits here
68 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SendRateMin, 128*1024, 1024, 0x10000000 );
69 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SendRateMax, 1024*1024, 1024, 0x10000000 );
70 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, NagleTime, 5000, 0, 20000 );
71 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, MTU_PacketSize, 1300, k_cbSteamNetworkingSocketsMinMTUPacketSize, k_cbSteamNetworkingSocketsMaxUDPMsgLen );
72 #ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
73 	// We don't have a trusted third party, so allow this by default,
74 	// and don't warn about it
75 	DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, IP_AllowWithoutAuth, 2, 0, 2 );
76 #else
77 	DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, IP_AllowWithoutAuth, 0, 0, 2 );
78 #endif
79 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, Unencrypted, 0, 0, 3 );
80 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, SymmetricConnect, 0, 0, 1 );
81 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LocalVirtualPort, -1, -1, 65535 );
82 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_AckRTT, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
83 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_PacketDecode, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
84 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_Message, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
85 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_PacketGaps, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
86 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, LogLevel_P2PRendezvous, k_ESteamNetworkingSocketsDebugOutputType_Warning, k_ESteamNetworkingSocketsDebugOutputType_Error, k_ESteamNetworkingSocketsDebugOutputType_Everything );
87 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( void *, Callback_ConnectionStatusChanged, nullptr );
88 
89 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE
90 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( std::string, P2P_STUN_ServerList, "" );
91 
92 COMPILE_TIME_ASSERT( k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Default == -1 );
93 COMPILE_TIME_ASSERT( k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Disable == 0 );
94 #ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
95 	// There is no such thing as "default" if we don't have some sort of platform
96 	DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_ICE_Enable, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_All, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Disable, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_All );
97 #else
98 	DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_ICE_Enable, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Default, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Default, k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_All );
99 #endif
100 
101 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_ICE_Penalty, 0, 0, INT_MAX );
102 #endif
103 
104 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR
105 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( std::string, SDRClient_DebugTicketAddress, "" );
106 DEFINE_CONNECTON_DEFAULT_CONFIGVAL( int32, P2P_Transport_SDR_Penalty, 0, 0, INT_MAX );
107 #endif
108 
109 static GlobalConfigValueEntry *s_pFirstGlobalConfigEntry = nullptr;
110 static bool s_bConfigValueTableInitted = false;
111 static std::vector<GlobalConfigValueEntry *> s_vecConfigValueTable; // Sorted by value
112 static std::vector<GlobalConfigValueEntry *> s_vecConnectionConfigValueTable; // Sorted by offset
113 
GlobalConfigValueEntry(ESteamNetworkingConfigValue eValue,const char * pszName,ESteamNetworkingConfigDataType eDataType,ESteamNetworkingConfigScope eScope,int cbOffsetOf)114 GlobalConfigValueEntry::GlobalConfigValueEntry(
115 	ESteamNetworkingConfigValue eValue,
116 	const char *pszName,
117 	ESteamNetworkingConfigDataType eDataType,
118 	ESteamNetworkingConfigScope eScope,
119 	int cbOffsetOf
120 ) : m_eValue{ eValue }
121 , m_pszName{ pszName }
122 , m_eDataType{ eDataType }
123 , m_eScope{ eScope }
124 , m_cbOffsetOf{cbOffsetOf}
125 , m_pNextEntry( s_pFirstGlobalConfigEntry )
126 {
127 	s_pFirstGlobalConfigEntry = this;
128 	AssertMsg( !s_bConfigValueTableInitted, "Attempt to register more config values after table is already initialized" );
129 	s_bConfigValueTableInitted = false;
130 }
131 
EnsureConfigValueTableInitted()132 static void EnsureConfigValueTableInitted()
133 {
134 	if ( s_bConfigValueTableInitted )
135 		return;
136 	SteamNetworkingGlobalLock scopeLock;
137 	if ( s_bConfigValueTableInitted )
138 		return;
139 
140 	for ( GlobalConfigValueEntry *p = s_pFirstGlobalConfigEntry ; p ; p = p->m_pNextEntry )
141 	{
142 		s_vecConfigValueTable.push_back( p );
143 		if ( p->m_eScope == k_ESteamNetworkingConfig_Connection )
144 			s_vecConnectionConfigValueTable.push_back( p );
145 	}
146 
147 	// Sort in ascending order by value, so we can binary search
148 	std::sort( s_vecConfigValueTable.begin(), s_vecConfigValueTable.end(),
149 		[]( GlobalConfigValueEntry *a, GlobalConfigValueEntry *b ) { return a->m_eValue < b->m_eValue; } );
150 
151 	// Sort by struct offset, so that ConnectionConfig::Init will access memory in a sane way.
152 	// This doesn't really matter, though.
153 	std::sort( s_vecConnectionConfigValueTable.begin(), s_vecConnectionConfigValueTable.end(),
154 		[]( GlobalConfigValueEntry *a, GlobalConfigValueEntry *b ) { return a->m_cbOffsetOf < b->m_cbOffsetOf; } );
155 
156 	// Rebuild linked list, in order, and safety check for duplicates
157 	int N = len( s_vecConfigValueTable );
158 	for ( int i = 1 ; i < N ; ++i )
159 	{
160 		s_vecConfigValueTable[i-1]->m_pNextEntry = s_vecConfigValueTable[i];
161 		AssertMsg1( s_vecConfigValueTable[i-1]->m_eValue < s_vecConfigValueTable[i]->m_eValue, "Registered duplicate config value %d", s_vecConfigValueTable[i]->m_eValue );
162 	}
163 	s_vecConfigValueTable[N-1]->m_pNextEntry = nullptr;
164 
165 	s_pFirstGlobalConfigEntry = nullptr;
166 	s_bConfigValueTableInitted = true;
167 }
168 
FindConfigValueEntry(ESteamNetworkingConfigValue eSearchVal)169 static GlobalConfigValueEntry *FindConfigValueEntry( ESteamNetworkingConfigValue eSearchVal )
170 {
171 	EnsureConfigValueTableInitted();
172 
173 	// Binary search
174 	int l = 0;
175 	int r = len( s_vecConfigValueTable )-1;
176 	while ( l <= r )
177 	{
178 		int m = (l+r)>>1;
179 		GlobalConfigValueEntry *mp = s_vecConfigValueTable[m];
180 		if ( eSearchVal < mp->m_eValue )
181 			r = m-1;
182 		else if ( eSearchVal > mp->m_eValue )
183 			l = m+1;
184 		else
185 			return mp;
186 	}
187 
188 	// Not found
189 	return nullptr;
190 }
191 
Init(ConnectionConfig * pInherit)192 void ConnectionConfig::Init( ConnectionConfig *pInherit )
193 {
194 	EnsureConfigValueTableInitted();
195 
196 	for ( GlobalConfigValueEntry *pEntry : s_vecConnectionConfigValueTable )
197 	{
198 		ConfigValueBase *pVal = (ConfigValueBase *)((intptr_t)this + pEntry->m_cbOffsetOf );
199 		if ( pInherit )
200 		{
201 			pVal->m_pInherit = (ConfigValueBase *)((intptr_t)pInherit + pEntry->m_cbOffsetOf );
202 		}
203 		else
204 		{
205 			// Assume the relevant members are the same, no matter
206 			// what type T, so just use int32 arbitrarily
207 			pVal->m_pInherit = &( static_cast< GlobalConfigValueBase<int32> * >( pEntry ) )->m_value;
208 		}
209 	}
210 }
211 
212 /////////////////////////////////////////////////////////////////////////////
213 //
214 // Table of active sockets
215 //
216 /////////////////////////////////////////////////////////////////////////////
217 
218 CUtlHashMap<uint16, CSteamNetworkConnectionBase *, std::equal_to<uint16>, Identity<uint16> > g_mapConnections;
219 CUtlHashMap<int, CSteamNetworkPollGroup *, std::equal_to<int>, Identity<int> > g_mapPollGroups;
220 TableLock g_tables_lock;
221 
222 // Table of active listen sockets.  Listen sockets and this table are protected
223 // by the global lock.
224 CUtlHashMap<int, CSteamNetworkListenSocketBase *, std::equal_to<int>, Identity<int> > g_mapListenSockets;
225 
BConnectionStateExistsToAPI(ESteamNetworkingConnectionState eState)226 static bool BConnectionStateExistsToAPI( ESteamNetworkingConnectionState eState )
227 {
228 	switch ( eState )
229 	{
230 		default:
231 			Assert( false );
232 			return false;
233 		case k_ESteamNetworkingConnectionState_None:
234 		case k_ESteamNetworkingConnectionState_Dead:
235 		case k_ESteamNetworkingConnectionState_FinWait:
236 		case k_ESteamNetworkingConnectionState_Linger:
237 			return false;
238 
239 		case k_ESteamNetworkingConnectionState_Connecting:
240 		case k_ESteamNetworkingConnectionState_FindingRoute:
241 		case k_ESteamNetworkingConnectionState_Connected:
242 		case k_ESteamNetworkingConnectionState_ClosedByPeer:
243 		case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
244 			return true;
245 	}
246 
247 }
248 
InternalGetConnectionByHandle(HSteamNetConnection sock,ConnectionScopeLock & scopeLock,const char * pszLockTag,bool bForAPI)249 static CSteamNetworkConnectionBase *InternalGetConnectionByHandle( HSteamNetConnection sock, ConnectionScopeLock &scopeLock, const char *pszLockTag, bool bForAPI )
250 {
251 	if ( sock == 0 )
252 		return nullptr;
253 	TableScopeLock tableScopeLock( g_tables_lock );
254 	int idx = g_mapConnections.Find( uint16( sock ) );
255 	if ( idx == g_mapConnections.InvalidIndex() )
256 		return nullptr;
257 	CSteamNetworkConnectionBase *pResult = g_mapConnections[ idx ];
258 	if ( !pResult )
259 	{
260 		AssertMsg( false, "g_mapConnections corruption!" );
261 		return nullptr;
262 	}
263 	if ( uint16( pResult->m_hConnectionSelf ) != uint16( sock ) )
264 	{
265 		AssertMsg( false, "Connection map corruption!" );
266 		return nullptr;
267 	}
268 
269 	// Make sure connection is not in the process of being self-destructed
270 	bool bLocked = false;
271 	for (;;)
272 	{
273 
274 		// Fetch the state of the connection.  This is OK to do
275 		// even if we don't have the lock.
276 		ESteamNetworkingConnectionState s = pResult->GetState();
277 		if ( s == k_ESteamNetworkingConnectionState_Dead )
278 			break;
279 		if ( bForAPI )
280 		{
281 			if ( !BConnectionStateExistsToAPI( s ) )
282 				break;
283 		}
284 
285 		// Have we locked already?  Then we're good
286 		if ( bLocked )
287 		{
288 			// NOTE: We unlock the table lock here, OUT OF ORDER!
289 			return pResult;
290 		}
291 
292 		// State looks good, try to lock the connection.
293 		// NOTE: we still (briefly) hold the table lock!
294 		// We *should* be able to totally block here
295 		// without creating a deadlock, but looping here
296 		// isn't so bad
297 		bLocked = scopeLock.TryLock( *pResult->m_pLock, 5, pszLockTag );
298 	}
299 
300 	// Connection found in table, but should not be returned to the caller.
301 	// Unlock the connection, if we locked it
302 	if ( bLocked )
303 		scopeLock.Unlock();
304 
305 	return nullptr;
306 }
307 
GetConnectionByHandle(HSteamNetConnection sock,ConnectionScopeLock & scopeLock)308 CSteamNetworkConnectionBase *GetConnectionByHandle( HSteamNetConnection sock, ConnectionScopeLock &scopeLock )
309 {
310 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
311 	return InternalGetConnectionByHandle( sock, scopeLock, nullptr, false );
312 }
313 
GetConnectionByHandleForAPI(HSteamNetConnection sock,ConnectionScopeLock & scopeLock,const char * pszLockTag)314 inline CSteamNetworkConnectionBase *GetConnectionByHandleForAPI( HSteamNetConnection sock, ConnectionScopeLock &scopeLock, const char *pszLockTag )
315 {
316 	return InternalGetConnectionByHandle( sock, scopeLock, pszLockTag, true );
317 }
318 
GetListenSocketByHandle(HSteamListenSocket sock)319 static CSteamNetworkListenSocketBase *GetListenSocketByHandle( HSteamListenSocket sock )
320 {
321 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread(); // listen sockets are protected by the global lock!
322 	if ( sock == k_HSteamListenSocket_Invalid )
323 		return nullptr;
324 	AssertMsg( !(sock & 0x80000000), "A poll group handle was used where a listen socket handle was expected" );
325 	int idx = sock & 0xffff;
326 	if ( !g_mapListenSockets.IsValidIndex( idx ) )
327 		return nullptr;
328 	CSteamNetworkListenSocketBase *pResult = g_mapListenSockets[ idx ];
329 	if ( pResult->m_hListenSocketSelf != sock )
330 	{
331 		// Slot was reused, but this handle is now invalid
332 		return nullptr;
333 	}
334 	return pResult;
335 }
336 
GetPollGroupByHandle(HSteamNetPollGroup hPollGroup,PollGroupScopeLock & scopeLock,const char * pszLockTag)337 CSteamNetworkPollGroup *GetPollGroupByHandle( HSteamNetPollGroup hPollGroup, PollGroupScopeLock &scopeLock, const char *pszLockTag )
338 {
339 	if ( hPollGroup == k_HSteamNetPollGroup_Invalid )
340 		return nullptr;
341 	AssertMsg( (hPollGroup & 0x80000000), "A listen socket handle was used where a poll group handle was expected" );
342 	int idx = hPollGroup & 0xffff;
343 	TableScopeLock tableScopeLock( g_tables_lock );
344 	if ( !g_mapPollGroups.IsValidIndex( idx ) )
345 		return nullptr;
346 	CSteamNetworkPollGroup *pResult = g_mapPollGroups[ idx ];
347 
348 	// Make sure poll group is the one they really asked for, and also
349 	// handle deletion race condition
350 	while ( pResult->m_hPollGroupSelf == hPollGroup )
351 	{
352 		if ( scopeLock.TryLock( pResult->m_lock, 1, pszLockTag ) )
353 			return pResult;
354 	}
355 
356 	// Slot was reused, but this handle is now invalid,
357 	// or poll group deleted race condition
358 	return nullptr;
359 }
360 
361 /////////////////////////////////////////////////////////////////////////////
362 //
363 // CSteamSocketNetworkingBase
364 //
365 /////////////////////////////////////////////////////////////////////////////
366 
367 std::vector<CSteamNetworkingSockets *> CSteamNetworkingSockets::s_vecSteamNetworkingSocketsInstances;
368 
CSteamNetworkingSockets(CSteamNetworkingUtils * pSteamNetworkingUtils)369 CSteamNetworkingSockets::CSteamNetworkingSockets( CSteamNetworkingUtils *pSteamNetworkingUtils )
370 : m_bHaveLowLevelRef( false )
371 , m_pSteamNetworkingUtils( pSteamNetworkingUtils )
372 , m_pSteamNetworkingMessages( nullptr )
373 , m_bEverTriedToGetCert( false )
374 , m_bEverGotCert( false )
375 #ifdef STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
376 , m_scheduleCheckRenewCert( this, &CSteamNetworkingSockets::CheckAuthenticationPrerequisites )
377 #endif
378 , m_mutexPendingCallbacks( "pending_callbacks" )
379 {
380 	m_connectionConfig.Init( nullptr );
381 	InternalInitIdentity();
382 }
383 
InternalInitIdentity()384 void CSteamNetworkingSockets::InternalInitIdentity()
385 {
386 	m_identity.Clear();
387 	m_msgSignedCert.Clear();
388 	m_msgCert.Clear();
389 	m_keyPrivateKey.Wipe();
390 
391 	#ifdef STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
392 		m_CertStatus.m_eAvail = k_ESteamNetworkingAvailability_NeverTried;
393 		m_CertStatus.m_debugMsg[0] = '\0';
394 	#else
395 		m_CertStatus.m_eAvail = k_ESteamNetworkingAvailability_CannotTry;
396 		V_strcpy_safe( m_CertStatus.m_debugMsg, "No certificate authority" );
397 	#endif
398 	m_AuthenticationStatus = m_CertStatus;
399 	m_bEverTriedToGetCert = false;
400 	m_bEverGotCert = false;
401 }
402 
~CSteamNetworkingSockets()403 CSteamNetworkingSockets::~CSteamNetworkingSockets()
404 {
405 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
406 	Assert( !m_bHaveLowLevelRef ); // Called destructor directly?  Use Destroy()!
407 }
408 
409 #ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
BInitGameNetworkingSockets(const SteamNetworkingIdentity * pIdentity,SteamDatagramErrMsg & errMsg)410 bool CSteamNetworkingSockets::BInitGameNetworkingSockets( const SteamNetworkingIdentity *pIdentity, SteamDatagramErrMsg &errMsg )
411 {
412 	AssertMsg( !m_bHaveLowLevelRef, "Initted interface twice?" );
413 
414 	// Make sure low level socket support is ready
415 	if ( !BInitLowLevel( errMsg ) )
416 		return false;
417 
418 	if ( pIdentity )
419 		m_identity = *pIdentity;
420 	else
421 		CacheIdentity();
422 
423 	return true;
424 }
425 #endif
426 
BInitLowLevel(SteamNetworkingErrMsg & errMsg)427 bool CSteamNetworkingSockets::BInitLowLevel( SteamNetworkingErrMsg &errMsg )
428 {
429 	if ( m_bHaveLowLevelRef )
430 		return true;
431 	if ( !BSteamNetworkingSocketsLowLevelAddRef( errMsg) )
432 		return false;
433 
434 	// Add us to list of extant instances only after we have done some initialization
435 	if ( !has_element( s_vecSteamNetworkingSocketsInstances, this ) )
436 		s_vecSteamNetworkingSocketsInstances.push_back( this );
437 
438 	m_bHaveLowLevelRef = true;
439 	return true;
440 }
441 
KillConnections()442 void CSteamNetworkingSockets::KillConnections()
443 {
444 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "CSteamNetworkingSockets::KillConnections" );
445 	TableScopeLock tableScopeLock( g_tables_lock );
446 
447 	// Warn messages interface that it needs to clean up.  We need to do this
448 	// because that class has pointers to objects that we are about to destroy.
449 	#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
450 		if ( m_pSteamNetworkingMessages )
451 			m_pSteamNetworkingMessages->FreeResources();
452 	#endif
453 
454 	// Destroy all of my connections
455 	CSteamNetworkConnectionBase::ProcessDeletionList();
456 	FOR_EACH_HASHMAP( g_mapConnections, idx )
457 	{
458 		CSteamNetworkConnectionBase *pConn = g_mapConnections[idx];
459 		if ( pConn->m_pSteamNetworkingSocketsInterface == this )
460 		{
461 			ConnectionScopeLock connectionLock( *pConn );
462 			pConn->ConnectionQueueDestroy();
463 		}
464 	}
465 	CSteamNetworkConnectionBase::ProcessDeletionList();
466 
467 	// Destroy all of my listen sockets
468 	FOR_EACH_HASHMAP( g_mapListenSockets, idx )
469 	{
470 		CSteamNetworkListenSocketBase *pSock = g_mapListenSockets[idx];
471 		if ( pSock->m_pSteamNetworkingSocketsInterface == this )
472 		{
473 			DbgVerify( CloseListenSocket( pSock->m_hListenSocketSelf ) );
474 			Assert( !g_mapListenSockets.IsValidIndex( idx ) );
475 		}
476 	}
477 
478 	// Destroy all of my poll groups
479 	FOR_EACH_HASHMAP( g_mapPollGroups, idx )
480 	{
481 		CSteamNetworkPollGroup *pPollGroup = g_mapPollGroups[idx];
482 		if ( pPollGroup->m_pSteamNetworkingSocketsInterface == this )
483 		{
484 			DbgVerify( DestroyPollGroup( pPollGroup->m_hPollGroupSelf ) );
485 			Assert( !g_mapPollGroups.IsValidIndex( idx ) );
486 		}
487 	}
488 }
489 
Destroy()490 void CSteamNetworkingSockets::Destroy()
491 {
492 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "CSteamNetworkingSockets::Destroy" );
493 
494 	FreeResources();
495 
496 	// Nuke messages interface, if we had one.
497 	#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
498 		if ( m_pSteamNetworkingMessages )
499 		{
500 			delete m_pSteamNetworkingMessages;
501 			Assert( m_pSteamNetworkingMessages == nullptr ); // Destructor should sever this link
502 			m_pSteamNetworkingMessages = nullptr; // Buuuuut we'll slam it, too, in case there's a bug
503 		}
504 	#endif
505 
506 	// Remove from list of extant instances, if we are there
507 	find_and_remove_element( s_vecSteamNetworkingSocketsInstances, this );
508 
509 	delete this;
510 }
511 
FreeResources()512 void CSteamNetworkingSockets::FreeResources()
513 {
514 
515 	KillConnections();
516 
517 	// Clear identity and crypto stuff.
518 	// If we are re-initialized, we might get new ones
519 	InternalInitIdentity();
520 
521 	// Mark us as no longer being setup
522 	if ( m_bHaveLowLevelRef )
523 	{
524 		m_bHaveLowLevelRef = false;
525 		SteamNetworkingSocketsLowLevelDecRef();
526 	}
527 }
528 
BHasAnyConnections() const529 bool CSteamNetworkingSockets::BHasAnyConnections() const
530 {
531 	TableScopeLock tableScopeLock( g_tables_lock );
532 	for ( CSteamNetworkConnectionBase *pConn: g_mapConnections.IterValues() )
533 	{
534 		if ( pConn->m_pSteamNetworkingSocketsInterface == this )
535 			return true;
536 	}
537 	return false;
538 }
539 
BHasAnyListenSockets() const540 bool CSteamNetworkingSockets::BHasAnyListenSockets() const
541 {
542 	TableScopeLock tableScopeLock( g_tables_lock );
543 	for ( CSteamNetworkListenSocketBase *pSock: g_mapListenSockets.IterValues() )
544 	{
545 		if ( pSock->m_pSteamNetworkingSocketsInterface == this )
546 			return true;
547 	}
548 	return false;
549 }
550 
GetIdentity(SteamNetworkingIdentity * pIdentity)551 bool CSteamNetworkingSockets::GetIdentity( SteamNetworkingIdentity *pIdentity )
552 {
553 	SteamNetworkingGlobalLock scopeLock( "GetIdentity" );
554 	InternalGetIdentity();
555 	if ( pIdentity )
556 		*pIdentity = m_identity;
557 	return !m_identity.IsInvalid();
558 }
559 
GetSecondsUntilCertExpiry() const560 int CSteamNetworkingSockets::GetSecondsUntilCertExpiry() const
561 {
562 	if ( !m_msgSignedCert.has_cert() )
563 		return INT_MIN;
564 
565 	Assert( m_msgSignedCert.has_ca_signature() ); // Connections may use unsigned certs in certain situations, but we never use them here
566 	Assert( m_msgCert.has_key_data() );
567 	Assert( m_msgCert.has_time_expiry() ); // We should never generate keys without an expiry!
568 
569 	int nSeconduntilExpiry = (long)m_msgCert.time_expiry() - (long)m_pSteamNetworkingUtils->GetTimeSecure();
570 	return nSeconduntilExpiry;
571 }
572 
GetCertificateRequest(int * pcbBlob,void * pBlob,SteamNetworkingErrMsg & errMsg)573 bool CSteamNetworkingSockets::GetCertificateRequest( int *pcbBlob, void *pBlob, SteamNetworkingErrMsg &errMsg )
574 {
575 	SteamNetworkingGlobalLock scopeLock( "GetCertificateRequest" );
576 
577 	// If we don't have a private key, generate one now.
578 	CECSigningPublicKey pubKey;
579 	if ( m_keyPrivateKey.IsValid() )
580 	{
581 		DbgVerify( m_keyPrivateKey.GetPublicKey( &pubKey ) );
582 	}
583 	else
584 	{
585 		CCrypto::GenerateSigningKeyPair( &pubKey, &m_keyPrivateKey );
586 	}
587 
588 	// Fill out the request
589 	CMsgSteamDatagramCertificateRequest msgRequest;
590 	CMsgSteamDatagramCertificate &msgCert =*msgRequest.mutable_cert();
591 
592 	// Our public key
593 	msgCert.set_key_type( CMsgSteamDatagramCertificate_EKeyType_ED25519 );
594 	DbgVerify( pubKey.GetRawDataAsStdString( msgCert.mutable_key_data() ) );
595 
596 	// Our identity, if we know it
597 	InternalGetIdentity();
598 	if ( !m_identity.IsInvalid() && !m_identity.IsLocalHost() )
599 	{
600 		SteamNetworkingIdentityToProtobuf( m_identity, msgCert, identity_string, legacy_identity_binary, legacy_steam_id );
601 	}
602 
603 	// Check size
604 	int cb = ProtoMsgByteSize( msgRequest );
605 	if ( !pBlob )
606 	{
607 		*pcbBlob = cb;
608 		return true;
609 	}
610 	if ( cb > *pcbBlob )
611 	{
612 		*pcbBlob = cb;
613 		V_sprintf_safe( errMsg, "%d byte buffer not big enough; %d bytes required", *pcbBlob, cb );
614 		return false;
615 	}
616 
617 	*pcbBlob = cb;
618 	uint8 *p = (uint8 *)pBlob;
619 	DbgVerify( msgRequest.SerializeWithCachedSizesToArray( p ) == p + cb );
620 	return true;
621 }
622 
SetCertificate(const void * pCertificate,int cbCertificate,SteamNetworkingErrMsg & errMsg)623 bool CSteamNetworkingSockets::SetCertificate( const void *pCertificate, int cbCertificate, SteamNetworkingErrMsg &errMsg )
624 {
625 	// Crack the blob
626 	CMsgSteamDatagramCertificateSigned msgCertSigned;
627 	if ( !msgCertSigned.ParseFromArray( pCertificate, cbCertificate ) )
628 	{
629 		V_strcpy_safe( errMsg, "CMsgSteamDatagramCertificateSigned failed protobuf parse" );
630 		return false;
631 	}
632 
633 	SteamNetworkingGlobalLock scopeLock( "SetCertificate" );
634 
635 	// Crack the cert, and check the signature.  If *we* aren't even willing
636 	// to trust it, assume that our peers won't either
637 	CMsgSteamDatagramCertificate msgCert;
638 	time_t authTime = m_pSteamNetworkingUtils->GetTimeSecure();
639 	const CertAuthScope *pAuthScope = CertStore_CheckCert( msgCertSigned, msgCert, authTime, errMsg );
640 	if ( !pAuthScope )
641 	{
642 		SpewWarning( "SetCertificate: We are not currently able to verify our own cert!  %s.  Continuing anyway!", errMsg );
643 	}
644 
645 	// Extract the identity from the cert
646 	SteamNetworkingErrMsg tempErrMsg;
647 	SteamNetworkingIdentity certIdentity;
648 	int r = SteamNetworkingIdentityFromCert( certIdentity, msgCert, tempErrMsg );
649 	if ( r < 0 )
650 	{
651 		V_sprintf_safe( errMsg, "Cert has invalid identity.  %s", tempErrMsg );
652 		return false;
653 	}
654 
655 	// We currently only support one key type
656 	if ( msgCert.key_type() != CMsgSteamDatagramCertificate_EKeyType_ED25519 || msgCert.key_data().size() != 32 )
657 	{
658 		V_strcpy_safe( errMsg, "Cert has invalid public key" );
659 		return false;
660 	}
661 
662 	// Does cert contain a private key?
663 	if ( msgCertSigned.has_private_key_data() )
664 	{
665 		// The degree to which the key is actually "private" is not
666 		// really known to us.  However there are some use cases where
667 		// we will accept a cert
668 		const std::string &private_key_data = msgCertSigned.private_key_data();
669 		if ( m_keyPrivateKey.IsValid() )
670 		{
671 
672 			// We already chose a private key, so the cert must match.
673 			// For the most common use cases, we choose a private
674 			// key and it never leaves the current process.
675 			if ( m_keyPrivateKey.GetRawDataSize() != private_key_data.length()
676 				|| memcmp( m_keyPrivateKey.GetRawDataPtr(), private_key_data.c_str(), private_key_data.length() ) != 0 )
677 			{
678 				V_strcpy_safe( errMsg, "Private key mismatch" );
679 				return false;
680 			}
681 		}
682 		else
683 		{
684 			// We haven't chosen a private key yet, so we'll accept this one.
685 			if ( !m_keyPrivateKey.SetRawDataFromStdString( private_key_data ) )
686 			{
687 				V_strcpy_safe( errMsg, "Invalid private key" );
688 				return false;
689 			}
690 		}
691 	}
692 	else if ( !m_keyPrivateKey.IsValid() )
693 	{
694 		// WAT
695 		V_strcpy_safe( errMsg, "Cannot set cert.  No private key?" );
696 		return false;
697 	}
698 
699 	// Make sure the cert actually matches our public key.
700 	if ( memcmp( msgCert.key_data().c_str(), m_keyPrivateKey.GetPublicKeyRawData(), 32 ) != 0 )
701 	{
702 		V_strcpy_safe( errMsg, "Cert public key does not match our private key" );
703 		return false;
704 	}
705 
706 	// Make sure the cert authorizes us for the App we think we are running
707 	AppId_t nAppID = m_pSteamNetworkingUtils->GetAppID();
708 	if ( !CheckCertAppID( msgCert, pAuthScope, nAppID, tempErrMsg ) )
709 	{
710 		V_sprintf_safe( errMsg, "Cert does not authorize us for App %u", nAppID );
711 		return false;
712 	}
713 
714 	// If we don't know our identity, then set it now.  Otherwise,
715 	// it better match.
716 	if ( m_identity.IsInvalid() || m_identity.IsLocalHost() )
717 	{
718 		m_identity = certIdentity;
719 		SpewMsg( "Local identity established from certificate.  We are '%s'\n", SteamNetworkingIdentityRender( m_identity ).c_str() );
720 	}
721 	else if ( !( m_identity == certIdentity ) )
722 	{
723 		V_sprintf_safe( errMsg, "Cert is for identity '%s'.  We are '%s'", SteamNetworkingIdentityRender( certIdentity ).c_str(), SteamNetworkingIdentityRender( m_identity ).c_str() );
724 		return false;
725 	}
726 
727 	// Save it off
728 	m_msgSignedCert = std::move( msgCertSigned );
729 	m_msgCert = std::move( msgCert );
730 	// If shouldn't already be expired.
731 	AssertMsg( GetSecondsUntilCertExpiry() > 0, "Cert already invalid / expired?" );
732 
733 	// We've got a valid cert
734 	SetCertStatus( k_ESteamNetworkingAvailability_Current, "OK" );
735 
736 	// Make sure we have everything else we need to do authentication.
737 	// This will also make sure we have renewal scheduled
738 	AuthenticationNeeded();
739 
740 	// OK
741 	return true;
742 }
743 
ResetIdentity(const SteamNetworkingIdentity * pIdentity)744 void CSteamNetworkingSockets::ResetIdentity( const SteamNetworkingIdentity *pIdentity )
745 {
746 #ifdef STEAMNETWORKINGSOCKETS_STEAM
747 	Assert( !"Not supported on steam" );
748 #else
749 	KillConnections();
750 	InternalInitIdentity();
751 	if ( pIdentity )
752 		m_identity = *pIdentity;
753 #endif
754 }
755 
InitAuthentication()756 ESteamNetworkingAvailability CSteamNetworkingSockets::InitAuthentication()
757 {
758 	SteamNetworkingGlobalLock scopeLock( "InitAuthentication" );
759 
760 	// Check/fetch prerequisites
761 	AuthenticationNeeded();
762 
763 	// Return status
764 	return m_AuthenticationStatus.m_eAvail;
765 }
766 
CheckAuthenticationPrerequisites(SteamNetworkingMicroseconds usecNow)767 void CSteamNetworkingSockets::CheckAuthenticationPrerequisites( SteamNetworkingMicroseconds usecNow )
768 {
769 #ifdef STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
770 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
771 
772 	// Check if we're in flight already.
773 	bool bInFlight = BCertRequestInFlight();
774 
775 	// Do we already have a cert?
776 	if ( m_msgSignedCert.has_cert() )
777 	{
778 		//Assert( m_CertStatus.m_eAvail == k_ESteamNetworkingAvailability_Current );
779 
780 		// How much more life does it have in it?
781 		int nSeconduntilExpiry = GetSecondsUntilCertExpiry();
782 		if ( nSeconduntilExpiry < 0 )
783 		{
784 
785 			// It's already expired, we might as well discard it now.
786 			SpewMsg( "Cert expired %d seconds ago.  Discarding and requesting another\n", -nSeconduntilExpiry );
787 			m_msgSignedCert.Clear();
788 			m_msgCert.Clear();
789 			m_keyPrivateKey.Wipe();
790 
791 			// Update cert status
792 			SetCertStatus( k_ESteamNetworkingAvailability_Previously, "Expired" );
793 		}
794 		else
795 		{
796 
797 			// If request is already active, don't do any of the work below, and don't spam while we wait, since this function may be called frequently.
798 			if ( bInFlight )
799 				return;
800 
801 			// Check if it's time to renew
802 			SteamNetworkingMicroseconds usecTargetRenew = usecNow + ( nSeconduntilExpiry - k_nSecCertExpirySeekRenew ) * k_nMillion;
803 			if ( usecTargetRenew > usecNow )
804 			{
805 				SteamNetworkingMicroseconds usecScheduledRenew = m_scheduleCheckRenewCert.GetScheduleTime();
806 				SteamNetworkingMicroseconds usecLatestRenew = usecTargetRenew + 4*k_nMillion;
807 				if ( usecScheduledRenew <= usecLatestRenew )
808 				{
809 					// Currently scheduled time is good enough.  Don't constantly update the schedule time,
810 					// that involves a (small amount) of work.  Just wait for it
811 				}
812 				else
813 				{
814 					// Schedule a check later
815 					m_scheduleCheckRenewCert.Schedule( usecTargetRenew + 2*k_nMillion );
816 				}
817 				return;
818 			}
819 
820 			// Currently valid, but it's time to renew.  Spew about this.
821 			SpewMsg( "Cert expires in %d seconds.  Requesting another, but keeping current cert in case request fails\n", nSeconduntilExpiry );
822 		}
823 	}
824 
825 	// If a request is already active, then we just need to wait for it to complete
826 	if ( bInFlight )
827 		return;
828 
829 	// Invoke platform code to begin fetching a cert
830 	BeginFetchCertAsync();
831 #endif
832 }
833 
SetCertStatus(ESteamNetworkingAvailability eAvail,const char * pszFmt,...)834 void CSteamNetworkingSockets::SetCertStatus( ESteamNetworkingAvailability eAvail, const char *pszFmt, ... )
835 {
836 	char msg[ sizeof(m_CertStatus.m_debugMsg) ];
837 	va_list ap;
838 	va_start( ap, pszFmt );
839 	V_vsprintf_safe( msg, pszFmt, ap );
840 	va_end( ap );
841 
842 	// Mark success or an attempt
843 	if ( eAvail == k_ESteamNetworkingAvailability_Current )
844 		m_bEverGotCert = true;
845 	if ( eAvail == k_ESteamNetworkingAvailability_Attempting || eAvail == k_ESteamNetworkingAvailability_Retrying )
846 		m_bEverTriedToGetCert = true;
847 
848 	// If we failed, but we previously succeeded, convert to "previously"
849 	if ( eAvail == k_ESteamNetworkingAvailability_Failed && m_bEverGotCert )
850 		eAvail = k_ESteamNetworkingAvailability_Previously;
851 
852 	// No change?
853 	if ( m_CertStatus.m_eAvail == eAvail && V_stricmp( m_CertStatus.m_debugMsg, msg ) == 0 )
854 		return;
855 
856 	// Update
857 	m_CertStatus.m_eAvail = eAvail;
858 	V_strcpy_safe( m_CertStatus.m_debugMsg, msg );
859 
860 	// Check if our high level authentication status changed
861 	DeduceAuthenticationStatus();
862 }
863 
DeduceAuthenticationStatus()864 void CSteamNetworkingSockets::DeduceAuthenticationStatus()
865 {
866 	// For the base class, the overall authentication status is identical to the status of
867 	// our cert.  (Derived classes may add additional criteria)
868 	SetAuthenticationStatus( m_CertStatus );
869 }
870 
SetAuthenticationStatus(const SteamNetAuthenticationStatus_t & newStatus)871 void CSteamNetworkingSockets::SetAuthenticationStatus( const SteamNetAuthenticationStatus_t &newStatus )
872 {
873 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
874 
875 	// No change?
876 	bool bStatusChanged = newStatus.m_eAvail != m_AuthenticationStatus.m_eAvail;
877 	if ( !bStatusChanged && V_strcmp( m_AuthenticationStatus.m_debugMsg, newStatus.m_debugMsg ) == 0 )
878 		return;
879 
880 	// Update
881 	m_AuthenticationStatus = newStatus;
882 
883 	// Re-cache identity
884 	InternalGetIdentity();
885 
886 	// Post a callback, but only if the high level status changed.  Don't post a callback just
887 	// because the message changed
888 	if ( bStatusChanged )
889 	{
890 		// Spew
891 		SpewMsg( "AuthStatus (%s):  %s  (%s)",
892 			SteamNetworkingIdentityRender( m_identity ).c_str(),
893 			GetAvailabilityString( m_AuthenticationStatus.m_eAvail ), m_AuthenticationStatus.m_debugMsg );
894 
895 		QueueCallback( m_AuthenticationStatus, g_Config_Callback_AuthStatusChanged.Get() );
896 	}
897 }
898 
899 #ifdef STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
AsyncCertRequestFinished()900 void CSteamNetworkingSockets::AsyncCertRequestFinished()
901 {
902 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "AsyncCertRequestFinished" );
903 
904 	Assert( m_msgSignedCert.has_cert() );
905 	SetCertStatus( k_ESteamNetworkingAvailability_Current, "OK" );
906 
907 	// Check for any connections that we own that are waiting on a cert
908 	TableScopeLock tableScopeLock( g_tables_lock );
909 	for ( CSteamNetworkConnectionBase *pConn: g_mapConnections.IterValues() )
910 	{
911 		if ( pConn->m_pSteamNetworkingSocketsInterface == this )
912 			pConn->InterfaceGotCert();
913 	}
914 }
915 
CertRequestFailed(ESteamNetworkingAvailability eCertAvail,ESteamNetConnectionEnd nConnectionEndReason,const char * pszMsg)916 void CSteamNetworkingSockets::CertRequestFailed( ESteamNetworkingAvailability eCertAvail, ESteamNetConnectionEnd nConnectionEndReason, const char *pszMsg )
917 {
918 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "CertRequestFailed" );
919 
920 	SpewWarning( "Cert request for %s failed with reason code %d.  %s\n", SteamNetworkingIdentityRender( InternalGetIdentity() ).c_str(), nConnectionEndReason, pszMsg );
921 
922 	// Schedule a retry.  Note that if we have active connections that need for a cert,
923 	// we may end up retrying sooner.  If we don't have any active connections, spamming
924 	// retries way too frequently may be really bad; we might end up DoS-ing ourselves.
925 	// Do we need to make this configurable?
926 	m_scheduleCheckRenewCert.Schedule( SteamNetworkingSockets_GetLocalTimestamp() + k_nMillion*30 );
927 
928 	if ( m_msgSignedCert.has_cert() )
929 	{
930 		SpewMsg( "But we still have a valid cert, continuing with that one\n" );
931 		AsyncCertRequestFinished();
932 		return;
933 	}
934 
935 	// Set generic cert status, so we will post a callback
936 	SetCertStatus( eCertAvail, "%s", pszMsg );
937 
938 	TableScopeLock tableScopeLock( g_tables_lock );
939 	for ( CSteamNetworkConnectionBase *pConn: g_mapConnections.IterValues() )
940 	{
941 		if ( pConn->m_pSteamNetworkingSocketsInterface == this )
942 			pConn->CertRequestFailed( nConnectionEndReason, pszMsg );
943 	}
944 
945 	// FIXME If we have any listen sockets, we might need to let them know about this as well?
946 }
947 #endif
948 
GetAuthenticationStatus(SteamNetAuthenticationStatus_t * pDetails)949 ESteamNetworkingAvailability CSteamNetworkingSockets::GetAuthenticationStatus( SteamNetAuthenticationStatus_t *pDetails )
950 {
951 	SteamNetworkingGlobalLock scopeLock; // !SPEED! We could protect this with a more tightly scoped lock, if we think this is eomthing people might be polling
952 
953 	// Return details, if requested
954 	if ( pDetails )
955 		*pDetails = m_AuthenticationStatus;
956 
957 	// Return status
958 	return m_AuthenticationStatus.m_eAvail;
959 }
960 
CreateListenSocketIP(const SteamNetworkingIPAddr & localAddr,int nOptions,const SteamNetworkingConfigValue_t * pOptions)961 HSteamListenSocket CSteamNetworkingSockets::CreateListenSocketIP( const SteamNetworkingIPAddr &localAddr, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
962 {
963 	SteamNetworkingGlobalLock scopeLock( "CreateListenSocketIP" );
964 	SteamDatagramErrMsg errMsg;
965 
966 	CSteamNetworkListenSocketDirectUDP *pSock = new CSteamNetworkListenSocketDirectUDP( this );
967 	if ( !pSock )
968 		return k_HSteamListenSocket_Invalid;
969 	if ( !pSock->BInit( localAddr, nOptions, pOptions, errMsg ) )
970 	{
971 		SpewError( "Cannot create listen socket.  %s", errMsg );
972 		pSock->Destroy();
973 		return k_HSteamListenSocket_Invalid;
974 	}
975 
976 	return pSock->m_hListenSocketSelf;
977 }
978 
ConnectByIPAddress(const SteamNetworkingIPAddr & address,int nOptions,const SteamNetworkingConfigValue_t * pOptions)979 HSteamNetConnection CSteamNetworkingSockets::ConnectByIPAddress( const SteamNetworkingIPAddr &address, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
980 {
981 	SteamNetworkingGlobalLock scopeLock( "ConnectByIPAddress" );
982 	ConnectionScopeLock connectionLock;
983 	CSteamNetworkConnectionUDP *pConn = new CSteamNetworkConnectionUDP( this, connectionLock );
984 	if ( !pConn )
985 		return k_HSteamNetConnection_Invalid;
986 	SteamDatagramErrMsg errMsg;
987 	if ( !pConn->BInitConnect( address, nOptions, pOptions, errMsg ) )
988 	{
989 		SpewError( "Cannot create IPv4 connection.  %s", errMsg );
990 		pConn->ConnectionQueueDestroy();
991 		return k_HSteamNetConnection_Invalid;
992 	}
993 
994 	return pConn->m_hConnectionSelf;
995 }
996 
997 
AcceptConnection(HSteamNetConnection hConn)998 EResult CSteamNetworkingSockets::AcceptConnection( HSteamNetConnection hConn )
999 {
1000 	SteamNetworkingGlobalLock scopeLock( "AcceptConnection" ); // Take global lock, since this will lead to connection state transition
1001 	ConnectionScopeLock connectionLock;
1002 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, nullptr );
1003 	if ( !pConn )
1004 	{
1005 		SpewError( "Cannot accept connection #%u; invalid connection handle", hConn );
1006 		return k_EResultInvalidParam;
1007 	}
1008 
1009 	// Accept it
1010 	return pConn->APIAcceptConnection();
1011 }
1012 
CloseConnection(HSteamNetConnection hConn,int nReason,const char * pszDebug,bool bEnableLinger)1013 bool CSteamNetworkingSockets::CloseConnection( HSteamNetConnection hConn, int nReason, const char *pszDebug, bool bEnableLinger )
1014 {
1015 	SteamNetworkingGlobalLock scopeLock( "CloseConnection" ); // Take global lock, we are going to change connection state and/or destroy objects
1016 	ConnectionScopeLock connectionLock;
1017 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, nullptr );
1018 	if ( !pConn )
1019 		return false;
1020 
1021 	// Close it
1022 	pConn->APICloseConnection( nReason, pszDebug, bEnableLinger );
1023 	return true;
1024 }
1025 
CloseListenSocket(HSteamListenSocket hSocket)1026 bool CSteamNetworkingSockets::CloseListenSocket( HSteamListenSocket hSocket )
1027 {
1028 	SteamNetworkingGlobalLock scopeLock( "CloseListenSocket" ); // Take global lock, we are going to destroy objects
1029 	CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( hSocket );
1030 	if ( !pSock )
1031 		return false;
1032 
1033 	// Delete the socket itself
1034 	// NOTE: If you change this, look at CSteamSocketNetworking::Kill()!
1035 	pSock->Destroy();
1036 	return true;
1037 }
1038 
SetConnectionUserData(HSteamNetConnection hPeer,int64 nUserData)1039 bool CSteamNetworkingSockets::SetConnectionUserData( HSteamNetConnection hPeer, int64 nUserData )
1040 {
1041 	//SteamNetworkingGlobalLock scopeLock( "SetConnectionUserData" ); // NO, not necessary!
1042 	ConnectionScopeLock connectionLock;
1043 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hPeer, connectionLock, "SetConnectionUserData" );
1044 	if ( !pConn )
1045 		return false;
1046 	pConn->SetUserData( nUserData );
1047 	return true;
1048 }
1049 
GetConnectionUserData(HSteamNetConnection hPeer)1050 int64 CSteamNetworkingSockets::GetConnectionUserData( HSteamNetConnection hPeer )
1051 {
1052 	//SteamNetworkingGlobalLock scopeLock( "GetConnectionUserData" ); // NO, not necessary!
1053 	ConnectionScopeLock connectionLock;
1054 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hPeer, connectionLock, "GetConnectionUserData" );
1055 	if ( !pConn )
1056 		return -1;
1057 	return pConn->GetUserData();
1058 }
1059 
SetConnectionName(HSteamNetConnection hPeer,const char * pszName)1060 void CSteamNetworkingSockets::SetConnectionName( HSteamNetConnection hPeer, const char *pszName )
1061 {
1062 	SteamNetworkingGlobalLock scopeLock( "SetConnectionName" ); // NOTE: Yes, we must take global lock for this.  See CSteamNetworkConnectionBase::SetDescription
1063 	ConnectionScopeLock connectionLock;
1064 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hPeer, connectionLock, nullptr );
1065 	if ( !pConn )
1066 		return;
1067 	pConn->SetAppName( pszName );
1068 }
1069 
GetConnectionName(HSteamNetConnection hPeer,char * pszName,int nMaxLen)1070 bool CSteamNetworkingSockets::GetConnectionName( HSteamNetConnection hPeer, char *pszName, int nMaxLen )
1071 {
1072 	//SteamNetworkingGlobalLock scopeLock( "GetConnectionName" ); // NO, not necessary!
1073 	ConnectionScopeLock connectionLock;
1074 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hPeer, connectionLock, "GetConnectionName" );
1075 	if ( !pConn )
1076 		return false;
1077 	V_strncpy( pszName, pConn->GetAppName(), nMaxLen );
1078 	return true;
1079 }
1080 
SendMessageToConnection(HSteamNetConnection hConn,const void * pData,uint32 cbData,int nSendFlags,int64 * pOutMessageNumber)1081 EResult CSteamNetworkingSockets::SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, int nSendFlags, int64 *pOutMessageNumber )
1082 {
1083 	//SteamNetworkingGlobalLock scopeLock( "SendMessageToConnection" ); // NO, not necessary!
1084 	ConnectionScopeLock connectionLock;
1085 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "SendMessageToConnection" );
1086 	if ( !pConn )
1087 		return k_EResultInvalidParam;
1088 	return pConn->APISendMessageToConnection( pData, cbData, nSendFlags, pOutMessageNumber );
1089 }
1090 
SendMessages(int nMessages,SteamNetworkingMessage_t * const * pMessages,int64 * pOutMessageNumberOrResult)1091 void CSteamNetworkingSockets::SendMessages( int nMessages, SteamNetworkingMessage_t *const *pMessages, int64 *pOutMessageNumberOrResult )
1092 {
1093 
1094 	// Get list of messages, grouped by connection.
1095 	// But within the connection, it is important that we
1096 	// keep them in the same order!
1097 	struct SortMsg_t
1098 	{
1099 		HSteamNetConnection m_hConn;
1100 		int m_idx;
1101 		inline bool operator<(const SortMsg_t &x ) const
1102 		{
1103 			if ( m_hConn < x.m_hConn ) return true;
1104 			if ( m_hConn > x.m_hConn ) return false;
1105 			return m_idx < x.m_idx;
1106 		}
1107 	};
1108 	SortMsg_t *pSortMessages = (SortMsg_t *)alloca( nMessages * sizeof(SortMsg_t) );
1109 	int nSortMessages = 0;
1110 
1111 	for ( int i = 0 ; i < nMessages ; ++i )
1112 	{
1113 
1114 		// Sanity check that message is valid
1115 		CSteamNetworkingMessage *pMsg = static_cast<CSteamNetworkingMessage*>( pMessages[i] );
1116 		if ( !pMsg )
1117 		{
1118 			if ( pOutMessageNumberOrResult )
1119 				pOutMessageNumberOrResult[i] = -k_EResultInvalidParam;
1120 			continue;
1121 		}
1122 
1123 		if ( pMsg->m_conn == k_HSteamNetConnection_Invalid )
1124 		{
1125 			if ( pOutMessageNumberOrResult )
1126 				pOutMessageNumberOrResult[i] = -k_EResultInvalidParam;
1127 			pMsg->Release();
1128 			continue;
1129 		}
1130 
1131 		pSortMessages[ nSortMessages ].m_hConn = pMsg->m_conn;
1132 		pSortMessages[ nSortMessages ].m_idx = i;
1133 		++nSortMessages;
1134 	}
1135 
1136 	if ( nSortMessages < 1 )
1137 		return;
1138 
1139 	SortMsg_t *const pSortEnd = pSortMessages+nSortMessages;
1140 	std::sort( pSortMessages, pSortEnd );
1141 
1142 	// OK, we are ready to begin
1143 
1144 	// SteamNetworkingGlobalLock scopeLock( "SendMessages" ); // NO, not necessary!
1145 	SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp();
1146 
1147 	CSteamNetworkConnectionBase *pConn = nullptr;
1148 	HSteamNetConnection hConn = k_HSteamNetConnection_Invalid;
1149 	ConnectionScopeLock connectionLock;
1150 	bool bConnectionThinkImmediately = false;
1151 	for ( SortMsg_t *pSort = pSortMessages ; pSort < pSortEnd ; ++pSort )
1152 	{
1153 
1154 		// Switched to a different connection?
1155 		if ( hConn != pSort->m_hConn )
1156 		{
1157 
1158 			// Flush out previous connection, if any
1159 			if ( pConn )
1160 			{
1161 				if ( bConnectionThinkImmediately )
1162 					pConn->CheckConnectionStateOrScheduleWakeUp( usecNow );
1163 				connectionLock.Unlock();
1164 				bConnectionThinkImmediately = false;
1165 			}
1166 
1167 			// Locate the connection
1168 			hConn = pSort->m_hConn;
1169 			pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "SendMessages" );
1170 		}
1171 
1172 		CSteamNetworkingMessage *pMsg = static_cast<CSteamNetworkingMessage*>( pMessages[pSort->m_idx] );
1173 
1174 		// Current connection is valid?
1175 		int64 result;
1176 		if ( pConn )
1177 		{
1178 
1179 			// Attempt to send
1180 			bool bThinkImmediately = false;
1181 			result = pConn->APISendMessageToConnection( pMsg, usecNow, &bThinkImmediately );
1182 			if ( bThinkImmediately )
1183 				bConnectionThinkImmediately = true;
1184 		}
1185 		else
1186 		{
1187 			pMsg->Release();
1188 			result = -k_EResultInvalidParam;
1189 		}
1190 
1191 		// Return result for this message if they asked for it
1192 		if ( pOutMessageNumberOrResult )
1193 			pOutMessageNumberOrResult[pSort->m_idx] = result;
1194 	}
1195 
1196 	// Flush out last connection, if any
1197 	if ( bConnectionThinkImmediately )
1198 		pConn->CheckConnectionStateOrScheduleWakeUp( usecNow );
1199 }
1200 
FlushMessagesOnConnection(HSteamNetConnection hConn)1201 EResult CSteamNetworkingSockets::FlushMessagesOnConnection( HSteamNetConnection hConn )
1202 {
1203 	//SteamNetworkingGlobalLock scopeLock( "FlushMessagesOnConnection" ); // NO, not necessary!
1204 	ConnectionScopeLock connectionLock;
1205 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "FlushMessagesOnConnection" );
1206 	if ( !pConn )
1207 		return k_EResultInvalidParam;
1208 	return pConn->APIFlushMessageOnConnection();
1209 }
1210 
ReceiveMessagesOnConnection(HSteamNetConnection hConn,SteamNetworkingMessage_t ** ppOutMessages,int nMaxMessages)1211 int CSteamNetworkingSockets::ReceiveMessagesOnConnection( HSteamNetConnection hConn, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
1212 {
1213 	//SteamNetworkingGlobalLock scopeLock( "ReceiveMessagesOnConnection" ); // NO, not necessary!
1214 	ConnectionScopeLock connectionLock;
1215 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "ReceiveMessagesOnConnection" );
1216 	if ( !pConn )
1217 		return -1;
1218 	return pConn->APIReceiveMessages( ppOutMessages, nMaxMessages );
1219 }
1220 
CreatePollGroup()1221 HSteamNetPollGroup CSteamNetworkingSockets::CreatePollGroup()
1222 {
1223 	SteamNetworkingGlobalLock scopeLock( "CreatePollGroup" ); // Take global lock, because we will be creating objects
1224 	PollGroupScopeLock pollGroupScopeLock;
1225 	CSteamNetworkPollGroup *pPollGroup = InternalCreatePollGroup( pollGroupScopeLock );
1226 	return pPollGroup->m_hPollGroupSelf;
1227 }
1228 
InternalCreatePollGroup(PollGroupScopeLock & scopeLock)1229 CSteamNetworkPollGroup *CSteamNetworkingSockets::InternalCreatePollGroup( PollGroupScopeLock &scopeLock )
1230 {
1231 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
1232 	TableScopeLock tableScopeLock( g_tables_lock );
1233 	CSteamNetworkPollGroup *pPollGroup = new CSteamNetworkPollGroup( this );
1234 	scopeLock.Lock( pPollGroup->m_lock );
1235 	pPollGroup->AssignHandleAndAddToGlobalTable();
1236 	return pPollGroup;
1237 }
1238 
DestroyPollGroup(HSteamNetPollGroup hPollGroup)1239 bool CSteamNetworkingSockets::DestroyPollGroup( HSteamNetPollGroup hPollGroup )
1240 {
1241 	SteamNetworkingGlobalLock scopeLock( "DestroyPollGroup" ); // Take global lock, since we'll be destroying objects
1242 	TableScopeLock tableScopeLock( g_tables_lock ); // We'll need to be able to remove the poll group from the tables list
1243 	PollGroupScopeLock pollGroupLock;
1244 	CSteamNetworkPollGroup *pPollGroup = GetPollGroupByHandle( hPollGroup, pollGroupLock, nullptr );
1245 	if ( !pPollGroup )
1246 		return false;
1247 	pollGroupLock.Abandon(); // We're about to destroy the lock itself.  The Destructor will unlock -- we don't want to do it again.
1248 	delete pPollGroup;
1249 	return true;
1250 }
1251 
SetConnectionPollGroup(HSteamNetConnection hConn,HSteamNetPollGroup hPollGroup)1252 bool CSteamNetworkingSockets::SetConnectionPollGroup( HSteamNetConnection hConn, HSteamNetPollGroup hPollGroup )
1253 {
1254 	SteamNetworkingGlobalLock scopeLock( "SetConnectionPollGroup" ); // Take global lock, since we'll need to take multiple object locks
1255 	ConnectionScopeLock connectionLock;
1256 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, nullptr );
1257 	if ( !pConn )
1258 		return false;
1259 
1260 	// NOTE: We are allowed to take multiple locks here, in any order, because we have the global
1261 	// lock.  Code that does not hold the global lock may only lock one object at a time
1262 
1263 	// Special case for removing the poll group
1264 	if ( hPollGroup == k_HSteamNetPollGroup_Invalid )
1265 	{
1266 		pConn->RemoveFromPollGroup();
1267 		return true;
1268 	}
1269 
1270 
1271 	PollGroupScopeLock pollGroupLock;
1272 	CSteamNetworkPollGroup *pPollGroup = GetPollGroupByHandle( hPollGroup, pollGroupLock, nullptr );
1273 	if ( !pPollGroup )
1274 		return false;
1275 
1276 	pConn->SetPollGroup( pPollGroup );
1277 
1278 	return true;
1279 }
1280 
ReceiveMessagesOnPollGroup(HSteamNetPollGroup hPollGroup,SteamNetworkingMessage_t ** ppOutMessages,int nMaxMessages)1281 int CSteamNetworkingSockets::ReceiveMessagesOnPollGroup( HSteamNetPollGroup hPollGroup, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
1282 {
1283 	//SteamNetworkingGlobalLock scopeLock( "ReceiveMessagesOnPollGroup" ); // NO, not necessary!
1284 	PollGroupScopeLock pollGroupLock;
1285 	CSteamNetworkPollGroup *pPollGroup = GetPollGroupByHandle( hPollGroup, pollGroupLock, "ReceiveMessagesOnPollGroup" );
1286 	if ( !pPollGroup )
1287 		return -1;
1288 	g_lockAllRecvMessageQueues.lock();
1289 	int nMessagesReceived = pPollGroup->m_queueRecvMessages.RemoveMessages( ppOutMessages, nMaxMessages );
1290 	g_lockAllRecvMessageQueues.unlock();
1291 	return nMessagesReceived;
1292 }
1293 
1294 #ifdef STEAMNETWORKINGSOCKETS_STEAMCLIENT
ReceiveMessagesOnListenSocketLegacyPollGroup(HSteamListenSocket hSocket,SteamNetworkingMessage_t ** ppOutMessages,int nMaxMessages)1295 int CSteamNetworkingSockets::ReceiveMessagesOnListenSocketLegacyPollGroup( HSteamListenSocket hSocket, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
1296 {
1297 	SteamNetworkingGlobalLock scopeLock( "ReceiveMessagesOnListenSocket" );
1298 	CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( hSocket );
1299 	if ( !pSock )
1300 		return -1;
1301 	if ( !pSock->m_pLegacyPollGroup )
1302 		return 0;
1303 	g_lockAllRecvMessageQueues.lock();
1304 	int nMessagesReceived = pSock->m_pLegacyPollGroup->m_queueRecvMessages.RemoveMessages( ppOutMessages, nMaxMessages );
1305 	g_lockAllRecvMessageQueues.unlock();
1306 	return nMessagesReceived;
1307 }
1308 #endif
1309 
GetConnectionInfo(HSteamNetConnection hConn,SteamNetConnectionInfo_t * pInfo)1310 bool CSteamNetworkingSockets::GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo_t *pInfo )
1311 {
1312 	//SteamNetworkingGlobalLock scopeLock( "GetConnectionInfo" ); // NO, not necessary!
1313 	ConnectionScopeLock connectionLock;
1314 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "GetConnectionInfo" );
1315 	if ( !pConn )
1316 		return false;
1317 	if ( pInfo )
1318 		pConn->ConnectionPopulateInfo( *pInfo );
1319 	return true;
1320 }
1321 
GetQuickConnectionStatus(HSteamNetConnection hConn,SteamNetworkingQuickConnectionStatus * pStats)1322 bool CSteamNetworkingSockets::GetQuickConnectionStatus( HSteamNetConnection hConn, SteamNetworkingQuickConnectionStatus *pStats )
1323 {
1324 	//SteamNetworkingGlobalLock scopeLock( "GetQuickConnectionStatus" ); // NO, not necessary!
1325 	ConnectionScopeLock connectionLock;
1326 	CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, "GetQuickConnectionStatus" );
1327 	if ( !pConn )
1328 		return false;
1329 	if ( pStats )
1330 		pConn->APIGetQuickConnectionStatus( *pStats );
1331 	return true;
1332 }
1333 
GetDetailedConnectionStatus(HSteamNetConnection hConn,char * pszBuf,int cbBuf)1334 int CSteamNetworkingSockets::GetDetailedConnectionStatus( HSteamNetConnection hConn, char *pszBuf, int cbBuf )
1335 {
1336 	SteamNetworkingDetailedConnectionStatus stats;
1337 
1338 	// Only hold the lock for as long as we need.
1339 	{
1340 		SteamNetworkingGlobalLock scopeLock( "GetDetailedConnectionStatus" ); // In some use cases (SDR), we need to touch some shared data structures.  It's easier to just protect this with the global lock than to try to sort that out
1341 		ConnectionScopeLock connectionLock;
1342 		CSteamNetworkConnectionBase *pConn = GetConnectionByHandleForAPI( hConn, connectionLock, nullptr );
1343 		if ( !pConn )
1344 			return -1;
1345 
1346 		pConn->APIGetDetailedConnectionStatus( stats, SteamNetworkingSockets_GetLocalTimestamp() );
1347 
1348 	} // Release lock.  We don't need it, and printing can take a while!
1349 	int r = stats.Print( pszBuf, cbBuf );
1350 
1351 	/// If just asking for buffer size, pad it a bunch
1352 	/// because connection status can change at any moment.
1353 	if ( r > 0 )
1354 		r += 1024;
1355 	return r;
1356 }
1357 
GetListenSocketAddress(HSteamListenSocket hSocket,SteamNetworkingIPAddr * pAddress)1358 bool CSteamNetworkingSockets::GetListenSocketAddress( HSteamListenSocket hSocket, SteamNetworkingIPAddr *pAddress )
1359 {
1360 	SteamNetworkingGlobalLock scopeLock( "GetListenSocketAddress" );
1361 	CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( hSocket );
1362 	if ( !pSock )
1363 		return false;
1364 	return pSock->APIGetAddress( pAddress );
1365 }
1366 
CreateSocketPair(HSteamNetConnection * pOutConnection1,HSteamNetConnection * pOutConnection2,bool bUseNetworkLoopback,const SteamNetworkingIdentity * pIdentity1,const SteamNetworkingIdentity * pIdentity2)1367 bool CSteamNetworkingSockets::CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection *pOutConnection2, bool bUseNetworkLoopback, const SteamNetworkingIdentity *pIdentity1, const SteamNetworkingIdentity *pIdentity2 )
1368 {
1369 	SteamNetworkingGlobalLock scopeLock( "CreateSocketPair" );
1370 
1371 	// Assume failure
1372 	*pOutConnection1 = k_HSteamNetConnection_Invalid;
1373 	*pOutConnection2 = k_HSteamNetConnection_Invalid;
1374 	SteamNetworkingIdentity identity[2];
1375 	if ( pIdentity1 )
1376 		identity[0] = *pIdentity1;
1377 	else
1378 		identity[0].SetLocalHost();
1379 	if ( pIdentity2 )
1380 		identity[1] = *pIdentity2;
1381 	else
1382 		identity[1].SetLocalHost();
1383 
1384 	// Create network connections?
1385 	if ( bUseNetworkLoopback )
1386 	{
1387 		// Create two connection objects
1388 		CSteamNetworkConnectionlocalhostLoopback *pConn[2];
1389 		if ( !CSteamNetworkConnectionlocalhostLoopback::APICreateSocketPair( this, pConn, identity ) )
1390 			return false;
1391 
1392 		// Return their handles
1393 		*pOutConnection1 = pConn[0]->m_hConnectionSelf;
1394 		*pOutConnection2 = pConn[1]->m_hConnectionSelf;
1395 	}
1396 	else
1397 	{
1398 		// Create two connection objects
1399 		CSteamNetworkConnectionPipe *pConn[2];
1400 		if ( !CSteamNetworkConnectionPipe::APICreateSocketPair( this, pConn, identity ) )
1401 			return false;
1402 
1403 		// Return their handles
1404 		*pOutConnection1 = pConn[0]->m_hConnectionSelf;
1405 		*pOutConnection2 = pConn[1]->m_hConnectionSelf;
1406 	}
1407 	return true;
1408 }
1409 
BCertHasIdentity() const1410 bool CSteamNetworkingSockets::BCertHasIdentity() const
1411 {
1412 	// We should actually have a cert, otherwise this question cannot be answered
1413 	Assert( m_msgSignedCert.has_cert() );
1414 	Assert( m_msgCert.has_key_data() );
1415 	return m_msgCert.has_identity_string() || m_msgCert.has_legacy_identity_binary() || m_msgCert.has_legacy_steam_id();
1416 }
1417 
1418 
SetCertificateAndPrivateKey(const void * pCert,int cbCert,void * pPrivateKey,int cbPrivateKey)1419 bool CSteamNetworkingSockets::SetCertificateAndPrivateKey( const void *pCert, int cbCert, void *pPrivateKey, int cbPrivateKey )
1420 {
1421 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread( "SetCertificateAndPrivateKey" );
1422 
1423 	m_msgCert.Clear();
1424 	m_msgSignedCert.Clear();
1425 	m_keyPrivateKey.Wipe();
1426 
1427 	//
1428 	// Decode the private key
1429 	//
1430 	if ( !m_keyPrivateKey.LoadFromAndWipeBuffer( pPrivateKey, cbPrivateKey ) )
1431 	{
1432 		SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Invalid private key" );
1433 		return false;
1434 	}
1435 
1436 	//
1437 	// Decode the cert
1438 	//
1439 	SteamNetworkingErrMsg parseErrMsg;
1440 	if ( !ParseCertFromPEM( pCert, cbCert, m_msgSignedCert, parseErrMsg ) )
1441 	{
1442 		SetCertStatus( k_ESteamNetworkingAvailability_Failed, parseErrMsg );
1443 		return false;
1444 	}
1445 
1446 	if (
1447 		!m_msgSignedCert.has_cert()
1448 		|| !m_msgCert.ParseFromString( m_msgSignedCert.cert() )
1449 		|| !m_msgCert.has_time_expiry()
1450 		|| !m_msgCert.has_key_data()
1451 	) {
1452 		SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Invalid cert" );
1453 		return false;
1454 	}
1455 	if ( m_msgCert.key_type() != CMsgSteamDatagramCertificate_EKeyType_ED25519 )
1456 	{
1457 		SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Invalid cert or unsupported public key type" );
1458 		return false;
1459 	}
1460 
1461 	//
1462 	// Make sure that the private key and the cert match!
1463 	//
1464 
1465 	CECSigningPublicKey pubKey;
1466 	if ( !pubKey.SetRawDataWithoutWipingInput( m_msgCert.key_data().c_str(), m_msgCert.key_data().length() ) )
1467 	{
1468 		SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Invalid public key" );
1469 		return false;
1470 	}
1471 	if ( !m_keyPrivateKey.MatchesPublicKey( pubKey ) )
1472 	{
1473 		SetCertStatus( k_ESteamNetworkingAvailability_Failed, "Private key doesn't match public key from cert" );
1474 		return false;
1475 	}
1476 
1477 	SetCertStatus( k_ESteamNetworkingAvailability_Current, "OK" );
1478 
1479 	return true;
1480 }
1481 
GetP2P_Transport_ICE_Enable(const SteamNetworkingIdentity & identityRemote,int * pOutUserFlags)1482 int CSteamNetworkingSockets::GetP2P_Transport_ICE_Enable( const SteamNetworkingIdentity &identityRemote, int *pOutUserFlags )
1483 {
1484 	// We really shouldn't get here, because this is only a question that makes sense
1485 	// to ask if we have also overridden this function in a derived class, or slammed
1486 	// it before making the connection
1487 	Assert( false );
1488 	if ( pOutUserFlags )
1489 		*pOutUserFlags = 0;
1490 	return k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Disable;
1491 }
1492 
RunCallbacks()1493 void CSteamNetworkingSockets::RunCallbacks()
1494 {
1495 
1496 	// Swap into a temp, so that we only hold lock for
1497 	// a brief period.
1498 	std_vector<QueuedCallback> listTemp;
1499 	m_mutexPendingCallbacks.lock();
1500 	listTemp.swap( m_vecPendingCallbacks );
1501 	m_mutexPendingCallbacks.unlock();
1502 
1503 	// Dispatch the callbacks
1504 	for ( QueuedCallback &x: listTemp )
1505 	{
1506 		// NOTE: this switch statement is probably not necessary, if we are willing to make
1507 		// some (almost certainly reasonable in practice) assumptions about the parameter
1508 		// passing ABI.  All of these function calls basically have the same signature except
1509 		// for the actual type of the argument being pointed to.
1510 
1511 		#define DISPATCH_CALLBACK( structType, fnType ) \
1512 			case structType::k_iCallback: \
1513 				COMPILE_TIME_ASSERT( sizeof(structType) <= sizeof(x.data) ); \
1514 				((fnType)x.fnCallback)( (structType*)x.data ); \
1515 				break; \
1516 
1517 		switch ( x.nCallback )
1518 		{
1519 			DISPATCH_CALLBACK( SteamNetConnectionStatusChangedCallback_t, FnSteamNetConnectionStatusChanged )
1520 		#ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR
1521 			DISPATCH_CALLBACK( SteamNetAuthenticationStatus_t, FnSteamNetAuthenticationStatusChanged )
1522 			DISPATCH_CALLBACK( SteamRelayNetworkStatus_t, FnSteamRelayNetworkStatusChanged )
1523 		#endif
1524 		#ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
1525 			DISPATCH_CALLBACK( SteamNetworkingMessagesSessionRequest_t, FnSteamNetworkingMessagesSessionRequest )
1526 			DISPATCH_CALLBACK( SteamNetworkingMessagesSessionFailed_t, FnSteamNetworkingMessagesSessionFailed )
1527 		#endif
1528 			default:
1529 				AssertMsg1( false, "Unknown callback type %d!", x.nCallback );
1530 		}
1531 
1532 		#undef DISPATCH_CALLBACK
1533 	}
1534 }
1535 
InternalQueueCallback(int nCallback,int cbCallback,const void * pvCallback,void * fnRegisteredFunctionPtr)1536 void CSteamNetworkingSockets::InternalQueueCallback( int nCallback, int cbCallback, const void *pvCallback, void *fnRegisteredFunctionPtr )
1537 {
1538 	SteamNetworkingGlobalLock::AssertHeldByCurrentThread();
1539 
1540 	if ( !fnRegisteredFunctionPtr )
1541 		return;
1542 	if ( cbCallback > sizeof( ((QueuedCallback*)0)->data ) )
1543 	{
1544 		AssertMsg( false, "Callback doesn't fit!" );
1545 		return;
1546 	}
1547 	AssertMsg( len( m_vecPendingCallbacks ) < 100, "Callbacks backing up and not being checked.  Need to check them more frequently!" );
1548 
1549 	m_mutexPendingCallbacks.lock();
1550 	QueuedCallback &q = *push_back_get_ptr( m_vecPendingCallbacks );
1551 	q.nCallback = nCallback;
1552 	q.fnCallback = fnRegisteredFunctionPtr;
1553 	memcpy( q.data, pvCallback, cbCallback );
1554 	m_mutexPendingCallbacks.unlock();
1555 }
1556 
1557 /////////////////////////////////////////////////////////////////////////////
1558 //
1559 // CSteamNetworkingUtils
1560 //
1561 /////////////////////////////////////////////////////////////////////////////
1562 
~CSteamNetworkingUtils()1563 CSteamNetworkingUtils::~CSteamNetworkingUtils() {}
1564 
AllocateMessage(int cbAllocateBuffer)1565 SteamNetworkingMessage_t *CSteamNetworkingUtils::AllocateMessage( int cbAllocateBuffer )
1566 {
1567 	return CSteamNetworkingMessage::New( cbAllocateBuffer );
1568 }
1569 
GetLocalTimestamp()1570 SteamNetworkingMicroseconds CSteamNetworkingUtils::GetLocalTimestamp()
1571 {
1572 	return SteamNetworkingSockets_GetLocalTimestamp();
1573 }
1574 
SetDebugOutputFunction(ESteamNetworkingSocketsDebugOutputType eDetailLevel,FSteamNetworkingSocketsDebugOutput pfnFunc)1575 void CSteamNetworkingUtils::SetDebugOutputFunction( ESteamNetworkingSocketsDebugOutputType eDetailLevel, FSteamNetworkingSocketsDebugOutput pfnFunc )
1576 {
1577 	SteamNetworkingSockets_SetDebugOutputFunction( eDetailLevel, pfnFunc );
1578 }
1579 
1580 
1581 template<typename T>
GetConnectionVar(const GlobalConfigValueEntry * pEntry,ConnectionConfig * pConnectionConfig)1582 static ConfigValue<T> *GetConnectionVar( const GlobalConfigValueEntry *pEntry, ConnectionConfig *pConnectionConfig )
1583 {
1584 	Assert( pEntry->m_eScope == k_ESteamNetworkingConfig_Connection );
1585 	intptr_t ptr = intptr_t( pConnectionConfig );
1586 	return (ConfigValue<T> *)( ptr + pEntry->m_cbOffsetOf );
1587 }
1588 
1589 template<typename T>
EvaluateScopeConfigValue(GlobalConfigValueEntry * pEntry,ESteamNetworkingConfigScope eScopeType,intptr_t scopeObj,ConnectionScopeLock & connectionLock)1590 static ConfigValue<T> *EvaluateScopeConfigValue( GlobalConfigValueEntry *pEntry,
1591 	ESteamNetworkingConfigScope eScopeType,
1592 	intptr_t scopeObj,
1593 	ConnectionScopeLock &connectionLock // Lock this, if it's a connection
1594 )
1595 {
1596 	switch ( eScopeType )
1597 	{
1598 		case k_ESteamNetworkingConfig_Global:
1599 		{
1600 			auto *pGlobalVal = static_cast< GlobalConfigValueBase<T> * >( pEntry );
1601 			return &pGlobalVal->m_value;
1602 		}
1603 
1604 		case k_ESteamNetworkingConfig_SocketsInterface:
1605 		{
1606 			CSteamNetworkingSockets *pInterface = (CSteamNetworkingSockets *)scopeObj;
1607 			if ( pEntry->m_eScope == k_ESteamNetworkingConfig_Connection )
1608 			{
1609 				return GetConnectionVar<T>( pEntry, &pInterface->m_connectionConfig );
1610 			}
1611 			break;
1612 		}
1613 
1614 		case k_ESteamNetworkingConfig_ListenSocket:
1615 		{
1616 			CSteamNetworkListenSocketBase *pSock = GetListenSocketByHandle( HSteamListenSocket( scopeObj ) );
1617 			if ( pSock )
1618 			{
1619 				if ( pEntry->m_eScope == k_ESteamNetworkingConfig_Connection )
1620 				{
1621 					return GetConnectionVar<T>( pEntry, &pSock->m_connectionConfig );
1622 				}
1623 			}
1624 			break;
1625 		}
1626 
1627 		case k_ESteamNetworkingConfig_Connection:
1628 		{
1629 			// NOTE: Not using GetConnectionByHandleForAPI here.  In a few places in the code,
1630 			// we need to be able to set config options for connections that are being created.
1631 			// Really, we ought to plumb through these calls to an internal interface, so that
1632 			// we would know that they should be given access.  Right now they are coming in
1633 			// the "front door".  So this means if the app tries to set a config option on a
1634 			// connection that technically no longer exists, we will actually allow that, when
1635 			// we probably should fail the call.
1636 			CSteamNetworkConnectionBase *pConn = GetConnectionByHandle( HSteamNetConnection( scopeObj ), connectionLock );
1637 			if ( pConn )
1638 			{
1639 				if ( pEntry->m_eScope == k_ESteamNetworkingConfig_Connection )
1640 				{
1641 					return GetConnectionVar<T>( pEntry, &pConn->m_connectionConfig );
1642 				}
1643 			}
1644 			break;
1645 		}
1646 
1647 	}
1648 
1649 	// Bad scope argument
1650 	return nullptr;
1651 }
1652 
AssignConfigValueTyped(int32 * pVal,ESteamNetworkingConfigDataType eDataType,const void * pArg)1653 static bool AssignConfigValueTyped( int32 *pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
1654 {
1655 	switch ( eDataType )
1656 	{
1657 		case k_ESteamNetworkingConfig_Int32:
1658 			*pVal = *(int32*)pArg;
1659 			break;
1660 
1661 		case k_ESteamNetworkingConfig_Int64:
1662 		{
1663 			int64 arg = *(int64*)pArg;
1664 			if ( (int32)arg != arg )
1665 				return false; // Cannot truncate!
1666 			*pVal = *(int32*)arg;
1667 			break;
1668 		}
1669 
1670 		case k_ESteamNetworkingConfig_Float:
1671 			*pVal = (int32)floor( *(float*)pArg + .5f );
1672 			break;
1673 
1674 		case k_ESteamNetworkingConfig_String:
1675 		{
1676 			int x;
1677 			if ( sscanf( (const char *)pArg, "%d", &x ) != 1 )
1678 				return false;
1679 			*pVal = x;
1680 			break;
1681 		}
1682 
1683 		default:
1684 			return false;
1685 	}
1686 
1687 	return true;
1688 }
1689 
AssignConfigValueTyped(int64 * pVal,ESteamNetworkingConfigDataType eDataType,const void * pArg)1690 static bool AssignConfigValueTyped( int64 *pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
1691 {
1692 	switch ( eDataType )
1693 	{
1694 		case k_ESteamNetworkingConfig_Int32:
1695 			*pVal = *(int32*)pArg;
1696 			break;
1697 
1698 		case k_ESteamNetworkingConfig_Int64:
1699 		{
1700 			*pVal = *(int64*)pArg;
1701 			break;
1702 		}
1703 
1704 		case k_ESteamNetworkingConfig_Float:
1705 			*pVal = (int64)floor( *(float*)pArg + .5f );
1706 			break;
1707 
1708 		case k_ESteamNetworkingConfig_String:
1709 		{
1710 			long long x;
1711 			if ( sscanf( (const char *)pArg, "%lld", &x ) != 1 )
1712 				return false;
1713 			*pVal = (int64)x;
1714 			break;
1715 		}
1716 
1717 		default:
1718 			return false;
1719 	}
1720 
1721 	return true;
1722 }
1723 
AssignConfigValueTyped(float * pVal,ESteamNetworkingConfigDataType eDataType,const void * pArg)1724 static bool AssignConfigValueTyped( float *pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
1725 {
1726 	switch ( eDataType )
1727 	{
1728 		case k_ESteamNetworkingConfig_Int32:
1729 			*pVal = (float)( *(int32*)pArg );
1730 			break;
1731 
1732 		case k_ESteamNetworkingConfig_Int64:
1733 		{
1734 			*pVal = (float)( *(int64*)pArg );
1735 			break;
1736 		}
1737 
1738 		case k_ESteamNetworkingConfig_Float:
1739 			*pVal = *(float*)pArg;
1740 			break;
1741 
1742 		case k_ESteamNetworkingConfig_String:
1743 		{
1744 			float x;
1745 			if ( sscanf( (const char *)pArg, "%f", &x ) != 1 )
1746 				return false;
1747 			*pVal = x;
1748 			break;
1749 		}
1750 
1751 		default:
1752 			return false;
1753 	}
1754 
1755 	return true;
1756 }
1757 
AssignConfigValueTyped(std::string * pVal,ESteamNetworkingConfigDataType eDataType,const void * pArg)1758 static bool AssignConfigValueTyped( std::string *pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
1759 {
1760 	char temp[64];
1761 
1762 	switch ( eDataType )
1763 	{
1764 		case k_ESteamNetworkingConfig_Int32:
1765 			V_sprintf_safe( temp, "%d", *(int32*)pArg );
1766 			*pVal = temp;
1767 			break;
1768 
1769 		case k_ESteamNetworkingConfig_Int64:
1770 			V_sprintf_safe( temp, "%lld", (long long)*(int64*)pArg );
1771 			*pVal = temp;
1772 			break;
1773 
1774 		case k_ESteamNetworkingConfig_Float:
1775 			V_sprintf_safe( temp, "%g", *(float*)pArg );
1776 			*pVal = temp;
1777 			break;
1778 
1779 		case k_ESteamNetworkingConfig_String:
1780 			*pVal = (const char *)pArg;
1781 			break;
1782 
1783 		default:
1784 			return false;
1785 	}
1786 
1787 	return true;
1788 }
1789 
AssignConfigValueTyped(void ** pVal,ESteamNetworkingConfigDataType eDataType,const void * pArg)1790 static bool AssignConfigValueTyped( void **pVal, ESteamNetworkingConfigDataType eDataType, const void *pArg )
1791 {
1792 	switch ( eDataType )
1793 	{
1794 		case k_ESteamNetworkingConfig_Ptr:
1795 			*pVal = *(void **)pArg;
1796 			break;
1797 
1798 		default:
1799 			return false;
1800 	}
1801 
1802 	return true;
1803 }
1804 
1805 template<typename T>
SetConfigValueTyped(GlobalConfigValueEntry * pEntry,ESteamNetworkingConfigScope eScopeType,intptr_t scopeObj,ESteamNetworkingConfigDataType eDataType,const void * pArg)1806 bool SetConfigValueTyped(
1807 	GlobalConfigValueEntry *pEntry,
1808 	ESteamNetworkingConfigScope eScopeType,
1809 	intptr_t scopeObj,
1810 	ESteamNetworkingConfigDataType eDataType,
1811 	const void *pArg
1812 ) {
1813 	ConnectionScopeLock connectionLock;
1814 	ConfigValue<T> *pVal = EvaluateScopeConfigValue<T>( pEntry, eScopeType, scopeObj, connectionLock );
1815 	if ( !pVal )
1816 		return false;
1817 
1818 	// Locked values cannot be changed
1819 	if ( pVal->IsLocked() )
1820 		return false;
1821 
1822 	// Clearing the value?
1823 	if ( pArg == nullptr )
1824 	{
1825 		if ( eScopeType == k_ESteamNetworkingConfig_Global )
1826 		{
1827 			auto *pGlobal = (typename GlobalConfigValueBase<T>::Value *)( pVal );
1828 			Assert( pGlobal->m_pInherit == nullptr );
1829 			Assert( pGlobal->IsSet() );
1830 			pGlobal->m_data = pGlobal->m_defaultValue;
1831 		}
1832 		else if ( eScopeType == k_ESteamNetworkingConfig_Connection && pEntry->m_eValue == k_ESteamNetworkingConfig_ConnectionUserData )
1833 		{
1834 			// Once this is set, we cannot clear it or inherit it.
1835 			SpewError( "Cannot clear connection user data\n" );
1836 			return false;
1837 		}
1838 		else
1839 		{
1840 			Assert( pVal->m_pInherit );
1841 			pVal->m_eState = ConfigValueBase::kENotSet;
1842 		}
1843 		return true;
1844 	}
1845 
1846 	// Call type-specific method to set it
1847 	if ( !AssignConfigValueTyped( &pVal->m_data, eDataType, pArg ) )
1848 		return false;
1849 
1850 	// Mark it as set
1851 	pVal->m_eState = ConfigValueBase::kESet;
1852 
1853 	// Apply limits
1854 	pEntry->Clamp<T>( pVal->m_data );
1855 
1856 	// OK
1857 	return true;
1858 }
1859 
1860 template<typename T>
ReturnConfigValueTyped(const T & data,void * pData,size_t * cbData)1861 ESteamNetworkingGetConfigValueResult ReturnConfigValueTyped( const T &data, void *pData, size_t *cbData )
1862 {
1863 	ESteamNetworkingGetConfigValueResult eResult;
1864 	if ( !pData || *cbData < sizeof(T) )
1865 	{
1866 		eResult = k_ESteamNetworkingGetConfigValue_BufferTooSmall;
1867 	}
1868 	else
1869 	{
1870 		*(T*)pData = data;
1871 		eResult = k_ESteamNetworkingGetConfigValue_OK;
1872 	}
1873 	*cbData = sizeof(T);
1874 	return eResult;
1875 }
1876 
1877 template<>
ReturnConfigValueTyped(const std::string & data,void * pData,size_t * cbData)1878 ESteamNetworkingGetConfigValueResult ReturnConfigValueTyped<std::string>( const std::string &data, void *pData, size_t *cbData )
1879 {
1880 	size_t l = data.length() + 1;
1881 	ESteamNetworkingGetConfigValueResult eResult;
1882 	if ( !pData || *cbData < l )
1883 	{
1884 		eResult = k_ESteamNetworkingGetConfigValue_BufferTooSmall;
1885 	}
1886 	else
1887 	{
1888 		memcpy( pData, data.c_str(), l );
1889 		eResult = k_ESteamNetworkingGetConfigValue_OK;
1890 	}
1891 	*cbData = l;
1892 	return eResult;
1893 }
1894 
1895 template<typename T>
GetConfigValueTyped(GlobalConfigValueEntry * pEntry,ESteamNetworkingConfigScope eScopeType,intptr_t scopeObj,void * pResult,size_t * cbResult)1896 ESteamNetworkingGetConfigValueResult GetConfigValueTyped(
1897 	GlobalConfigValueEntry *pEntry,
1898 	ESteamNetworkingConfigScope eScopeType,
1899 	intptr_t scopeObj,
1900 	void *pResult, size_t *cbResult
1901 ) {
1902 	ConnectionScopeLock connectionLock;
1903 	ConfigValue<T> *pVal = EvaluateScopeConfigValue<T>( pEntry, eScopeType, scopeObj, connectionLock );
1904 	if ( !pVal )
1905 	{
1906 		*cbResult = 0;
1907 		return k_ESteamNetworkingGetConfigValue_BadScopeObj;
1908 	}
1909 
1910 	// Remember if it was set at this level
1911 	bool bValWasSet = pVal->IsSet();
1912 
1913 	// Find the place where the actual value comes from
1914 	while ( !pVal->IsSet() )
1915 	{
1916 		Assert( pVal->m_pInherit );
1917 		pVal = static_cast<ConfigValue<T> *>( pVal->m_pInherit );
1918 	}
1919 
1920 	// Call type-specific method to return it
1921 	ESteamNetworkingGetConfigValueResult eResult = ReturnConfigValueTyped( pVal->m_data, pResult, cbResult );
1922 	if ( eResult == k_ESteamNetworkingGetConfigValue_OK && !bValWasSet )
1923 		eResult = k_ESteamNetworkingGetConfigValue_OKInherited;
1924 	return eResult;
1925 }
1926 
SetConfigValue(ESteamNetworkingConfigValue eValue,ESteamNetworkingConfigScope eScopeType,intptr_t scopeObj,ESteamNetworkingConfigDataType eDataType,const void * pValue)1927 bool CSteamNetworkingUtils::SetConfigValue( ESteamNetworkingConfigValue eValue,
1928 	ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj,
1929 	ESteamNetworkingConfigDataType eDataType, const void *pValue )
1930 {
1931 
1932 	// Check for special values
1933 	switch ( eValue )
1934 	{
1935 		case k_ESteamNetworkingConfig_MTU_DataSize:
1936 			SpewWarning( "MTU_DataSize is readonly" );
1937 			return false;
1938 
1939 		case k_ESteamNetworkingConfig_ConnectionUserData:
1940 		{
1941 
1942 			// We only need special handling when modifying a connection
1943 			if ( eScopeType != k_ESteamNetworkingConfig_Connection )
1944 				break;
1945 
1946 			// Process the user argument, maybe performing type conversion
1947 			int64 newData;
1948 			if ( !AssignConfigValueTyped( &newData, eDataType, pValue ) )
1949 				return false;
1950 
1951 			// Lookup the connection
1952 			ConnectionScopeLock connectionLock;
1953 			CSteamNetworkConnectionBase *pConn = GetConnectionByHandle( HSteamNetConnection( scopeObj ), connectionLock );
1954 			if ( !pConn )
1955 				return false;
1956 
1957 			// Set the data, possibly fixing up existing queued messages, etc
1958 			pConn->SetUserData( pConn->m_connectionConfig.m_ConnectionUserData.m_data );
1959 			return true;
1960 		}
1961 
1962 	}
1963 
1964 	GlobalConfigValueEntry *pEntry = FindConfigValueEntry( eValue );
1965 	if ( pEntry == nullptr )
1966 		return false;
1967 
1968 	SteamNetworkingGlobalLock scopeLock( "SetConfigValue" );
1969 
1970 	switch ( pEntry->m_eDataType )
1971 	{
1972 		case k_ESteamNetworkingConfig_Int32: return SetConfigValueTyped<int32>( pEntry, eScopeType, scopeObj, eDataType, pValue );
1973 		case k_ESteamNetworkingConfig_Int64: return SetConfigValueTyped<int64>( pEntry, eScopeType, scopeObj, eDataType, pValue );
1974 		case k_ESteamNetworkingConfig_Float: return SetConfigValueTyped<float>( pEntry, eScopeType, scopeObj, eDataType, pValue );
1975 		case k_ESteamNetworkingConfig_String: return SetConfigValueTyped<std::string>( pEntry, eScopeType, scopeObj, eDataType, pValue );
1976 		case k_ESteamNetworkingConfig_Ptr: return SetConfigValueTyped<void *>( pEntry, eScopeType, scopeObj, eDataType, pValue );
1977 	}
1978 
1979 	Assert( false );
1980 	return false;
1981 }
1982 
GetConfigValue(ESteamNetworkingConfigValue eValue,ESteamNetworkingConfigScope eScopeType,intptr_t scopeObj,ESteamNetworkingConfigDataType * pOutDataType,void * pResult,size_t * cbResult)1983 ESteamNetworkingGetConfigValueResult CSteamNetworkingUtils::GetConfigValue(
1984 	ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType,
1985 	intptr_t scopeObj, ESteamNetworkingConfigDataType *pOutDataType,
1986 	void *pResult, size_t *cbResult )
1987 {
1988 	// Take the global lock.
1989 	SteamNetworkingGlobalLock scopeLock( "GetConfigValue" );
1990 
1991 	if ( eValue == k_ESteamNetworkingConfig_MTU_DataSize )
1992 	{
1993 		int32 MTU_packetsize;
1994 		size_t cbMTU_packetsize = sizeof(MTU_packetsize);
1995 		ESteamNetworkingGetConfigValueResult rFetch = GetConfigValueTyped<int32>( &g_ConfigDefault_MTU_PacketSize, eScopeType, scopeObj, &MTU_packetsize, &cbMTU_packetsize );
1996 		if ( rFetch < 0 )
1997 			return rFetch;
1998 
1999 		int32 MTU_DataSize = std::max( 0, MTU_packetsize - k_cbSteamNetworkingSocketsNoFragmentHeaderReserve );
2000 		ESteamNetworkingGetConfigValueResult rStore = ReturnConfigValueTyped<int32>( MTU_DataSize, pResult, cbResult );
2001 		if ( rStore != k_ESteamNetworkingGetConfigValue_OK )
2002 			return rStore;
2003 		return rFetch;
2004 	}
2005 
2006 	GlobalConfigValueEntry *pEntry = FindConfigValueEntry( eValue );
2007 	if ( pEntry == nullptr )
2008 		return k_ESteamNetworkingGetConfigValue_BadValue;
2009 
2010 	if ( pOutDataType )
2011 		*pOutDataType = pEntry->m_eDataType;
2012 
2013 	switch ( pEntry->m_eDataType )
2014 	{
2015 		case k_ESteamNetworkingConfig_Int32: return GetConfigValueTyped<int32>( pEntry, eScopeType, scopeObj, pResult, cbResult );
2016 		case k_ESteamNetworkingConfig_Int64: return GetConfigValueTyped<int64>( pEntry, eScopeType, scopeObj, pResult, cbResult );
2017 		case k_ESteamNetworkingConfig_Float: return GetConfigValueTyped<float>( pEntry, eScopeType, scopeObj, pResult, cbResult );
2018 		case k_ESteamNetworkingConfig_String: return GetConfigValueTyped<std::string>( pEntry, eScopeType, scopeObj, pResult, cbResult );
2019 		case k_ESteamNetworkingConfig_Ptr: return GetConfigValueTyped<void *>( pEntry, eScopeType, scopeObj, pResult, cbResult );
2020 	}
2021 
2022 	Assert( false ); // FIXME
2023 	return k_ESteamNetworkingGetConfigValue_BadValue;
2024 }
2025 
BEnumerateConfigValue(const GlobalConfigValueEntry * pVal)2026 static bool BEnumerateConfigValue( const GlobalConfigValueEntry *pVal )
2027 {
2028 	if ( pVal->m_eDataType == k_ESteamNetworkingConfig_Ptr )
2029 		return false;
2030 
2031 	switch  ( pVal->m_eValue )
2032 	{
2033 		// Never enumerate these
2034 		case k_ESteamNetworkingConfig_SymmetricConnect:
2035 		case k_ESteamNetworkingConfig_LocalVirtualPort:
2036 		case k_ESteamNetworkingConfig_ConnectionUserData:
2037 			return false;
2038 
2039 		// Dev var?
2040 		case k_ESteamNetworkingConfig_IP_AllowWithoutAuth:
2041 		case k_ESteamNetworkingConfig_Unencrypted:
2042 		case k_ESteamNetworkingConfig_EnumerateDevVars:
2043 		case k_ESteamNetworkingConfig_SDRClient_FakeClusterPing:
2044 			return g_Config_EnumerateDevVars.Get();
2045 	}
2046 
2047 	return true;
2048 }
2049 
GetConfigValueInfo(ESteamNetworkingConfigValue eValue,const char ** pOutName,ESteamNetworkingConfigDataType * pOutDataType,ESteamNetworkingConfigScope * pOutScope,ESteamNetworkingConfigValue * pOutNextValue)2050 bool CSteamNetworkingUtils::GetConfigValueInfo( ESteamNetworkingConfigValue eValue,
2051 	const char **pOutName, ESteamNetworkingConfigDataType *pOutDataType,
2052 	ESteamNetworkingConfigScope *pOutScope, ESteamNetworkingConfigValue *pOutNextValue )
2053 {
2054 	const GlobalConfigValueEntry *pVal = FindConfigValueEntry( eValue );
2055 	if ( pVal == nullptr )
2056 		return false;
2057 
2058 	if ( pOutName )
2059 		*pOutName = pVal->m_pszName;
2060 	if ( pOutDataType )
2061 		*pOutDataType = pVal->m_eDataType;
2062 	if ( pOutScope )
2063 		*pOutScope = pVal->m_eScope;
2064 
2065 	if ( pOutNextValue )
2066 	{
2067 		const GlobalConfigValueEntry *pNext = pVal;
2068 		for (;;)
2069 		{
2070 			pNext = pNext->m_pNextEntry;
2071 			if ( !pNext )
2072 			{
2073 				*pOutNextValue = k_ESteamNetworkingConfig_Invalid;
2074 				break;
2075 			}
2076 			if ( BEnumerateConfigValue( pNext ) )
2077 			{
2078 				*pOutNextValue = pNext->m_eValue;
2079 				break;
2080 			}
2081 		};
2082 	}
2083 
2084 	return true;
2085 }
2086 
GetFirstConfigValue()2087 ESteamNetworkingConfigValue CSteamNetworkingUtils::GetFirstConfigValue()
2088 {
2089 	EnsureConfigValueTableInitted();
2090 	Assert( BEnumerateConfigValue( s_vecConfigValueTable[0] ) );
2091 	return s_vecConfigValueTable[0]->m_eValue;
2092 }
2093 
2094 
SteamNetworkingIPAddr_ToString(const SteamNetworkingIPAddr & addr,char * buf,size_t cbBuf,bool bWithPort)2095 void CSteamNetworkingUtils::SteamNetworkingIPAddr_ToString( const SteamNetworkingIPAddr &addr, char *buf, size_t cbBuf, bool bWithPort )
2096 {
2097 	::SteamNetworkingIPAddr_ToString( &addr, buf, cbBuf, bWithPort );
2098 }
2099 
SteamNetworkingIPAddr_ParseString(SteamNetworkingIPAddr * pAddr,const char * pszStr)2100 bool CSteamNetworkingUtils::SteamNetworkingIPAddr_ParseString( SteamNetworkingIPAddr *pAddr, const char *pszStr )
2101 {
2102 	return ::SteamNetworkingIPAddr_ParseString( pAddr, pszStr );
2103 }
2104 
SteamNetworkingIdentity_ToString(const SteamNetworkingIdentity & identity,char * buf,size_t cbBuf)2105 void CSteamNetworkingUtils::SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf )
2106 {
2107 	return ::SteamNetworkingIdentity_ToString( &identity, buf, cbBuf );
2108 }
2109 
SteamNetworkingIdentity_ParseString(SteamNetworkingIdentity * pIdentity,const char * pszStr)2110 bool CSteamNetworkingUtils::SteamNetworkingIdentity_ParseString( SteamNetworkingIdentity *pIdentity, const char *pszStr )
2111 {
2112 	return ::SteamNetworkingIdentity_ParseString( pIdentity, sizeof(SteamNetworkingIdentity), pszStr );
2113 }
2114 
GetAppID()2115 AppId_t CSteamNetworkingUtils::GetAppID()
2116 {
2117 	return m_nAppID;
2118 }
2119 
TEST_ResetSelf()2120 void CSteamNetworkingUtils::TEST_ResetSelf()
2121 {
2122 	m_nAppID = 0;
2123 }
2124 
GetTimeSecure()2125 time_t CSteamNetworkingUtils::GetTimeSecure()
2126 {
2127 	// Trusting local user's clock!
2128 	return time(nullptr);
2129 }
2130 
GetBuildString()2131 const char *CSteamNetworkingUtils::GetBuildString()
2132 {
2133 	#if defined( STEAMNETWORKINGSOCKETS_OPENSOURCE )
2134 		return "opensource " __DATE__ " " __TIME__;
2135 	#elif defined( STEAMNETWORKINGSOCKETS_PARTNER )
2136 		return "partner " __DATE__ " " __TIME__;
2137 	#elif defined( STEAMNETWORKINGSOCKETS_STANDALONELIB )
2138 		return "lib " __DATE__ " " __TIME__;
2139 	#elif defined( STEAMNETWORKINGSOCKETS_STEAMCLIENT )
2140 		return "steam "
2141 		#ifdef BRANCH_MAIN
2142 			"(main) "
2143 		#elif !defined( BRANCH_REL_CLIENT )
2144 			"(branch?) "
2145 		#endif
2146 		__DATE__ " " __TIME__;
2147 	#elif defined( STEAMNETWORKINGSOCKETS_STREAMINGCLIENT )
2148 		return "stream "
2149 		#ifdef BRANCH_MAIN
2150 			"(main) "
2151 		#elif !defined( BRANCH_REL_CLIENT )
2152 			"(branch?) "
2153 		#endif
2154 		__DATE__ " " __TIME__;
2155 	#else
2156 		#error "Huh?"
2157 	#endif
2158 }
2159 
GetPlatformString()2160 const char *CSteamNetworkingUtils::GetPlatformString()
2161 {
2162 	#if defined( NN_NINTENDO_SDK )
2163 		return "nswitch";
2164 	#elif defined( _GAMECORE )
2165 		// Is this right?  This might actually require a system call.
2166 		return "xboxx";
2167 	#elif defined( _STADIA )
2168 		// Not sure if this works.
2169 		return "stadia";
2170 	#elif defined( _XBOX_ONE )
2171 		return "xbone";
2172 	#elif defined( _PS4 )
2173 		return "ps4";
2174 	#elif defined( _PS5 )
2175 		return "ps5";
2176 	#elif defined( TVOS ) || defined( __TVOS__ )
2177 		return "tvos";
2178 	#elif defined( __APPLE__ )
2179 		#if TARGET_OS_TV
2180 			return "tvos";
2181 		#elif TARGET_OS_IPHONE
2182 			return "ios";
2183 		#else
2184 			return "osx";
2185 		#endif
2186 	#elif defined( OSX )
2187 		return "osx";
2188 	#elif defined( ANDROID ) || defined( __ANDROID__ )
2189 		return "android";
2190 	#elif defined( _WINDOWS )
2191 		return "windows";
2192 	#elif defined( LINUX ) || defined( __LINUX__ ) || defined(linux) || defined(__linux) || defined(__linux__)
2193 		return "linux";
2194 	#elif defined( FREEBSD ) || defined( __FreeBSD__ )
2195 		return "freebsd";
2196 	#else
2197 		#error "Unknown platform"
2198 	#endif
2199 }
2200 
2201 } // namespace SteamNetworkingSocketsLib
2202 using namespace SteamNetworkingSocketsLib;
2203 
2204 /////////////////////////////////////////////////////////////////////////////
2205 //
2206 // Global API interface
2207 //
2208 /////////////////////////////////////////////////////////////////////////////
2209 
2210 #ifdef STEAMNETWORKINGSOCKETS_OPENSOURCE
2211 
2212 static CSteamNetworkingSockets *s_pSteamNetworkingSockets = nullptr;
2213 
GameNetworkingSockets_Init(const SteamNetworkingIdentity * pIdentity,SteamNetworkingErrMsg & errMsg)2214 STEAMNETWORKINGSOCKETS_INTERFACE bool GameNetworkingSockets_Init( const SteamNetworkingIdentity *pIdentity, SteamNetworkingErrMsg &errMsg )
2215 {
2216 	SteamNetworkingGlobalLock lock( "GameNetworkingSockets_Init" );
2217 
2218 	// Already initted?
2219 	if ( s_pSteamNetworkingSockets )
2220 	{
2221 		AssertMsg( false, "GameNetworkingSockets_init called multiple times?" );
2222 		return true;
2223 	}
2224 
2225 	// Init basic functionality
2226 	CSteamNetworkingSockets *pSteamNetworkingSockets = new CSteamNetworkingSockets( ( CSteamNetworkingUtils *)SteamNetworkingUtils() );
2227 	if ( !pSteamNetworkingSockets->BInitGameNetworkingSockets( pIdentity, errMsg ) )
2228 	{
2229 		pSteamNetworkingSockets->Destroy();
2230 		return false;
2231 	}
2232 
2233 	s_pSteamNetworkingSockets = pSteamNetworkingSockets;
2234 	return true;
2235 }
2236 
GameNetworkingSockets_Kill()2237 STEAMNETWORKINGSOCKETS_INTERFACE void GameNetworkingSockets_Kill()
2238 {
2239 	SteamNetworkingGlobalLock lock( "GameNetworkingSockets_Kill" );
2240 	if ( s_pSteamNetworkingSockets )
2241 	{
2242 		s_pSteamNetworkingSockets->Destroy();
2243 		s_pSteamNetworkingSockets = nullptr;
2244 	}
2245 }
2246 
SteamNetworkingSockets_LibV9()2247 STEAMNETWORKINGSOCKETS_INTERFACE ISteamNetworkingSockets *SteamNetworkingSockets_LibV9()
2248 {
2249 	return s_pSteamNetworkingSockets;
2250 }
2251 
SteamNetworkingUtils_LibV3()2252 STEAMNETWORKINGSOCKETS_INTERFACE ISteamNetworkingUtils *SteamNetworkingUtils_LibV3()
2253 {
2254 	static CSteamNetworkingUtils s_utils;
2255 	return &s_utils;
2256 }
2257 
2258 #endif
2259