1 //====== Copyright Valve Corporation, All rights reserved. ====================
2 
3 #ifndef STEAMNETWORKINGSOCKETS_CONNECTIONS_H
4 #define STEAMNETWORKINGSOCKETS_CONNECTIONS_H
5 #pragma once
6 
7 #include "../steamnetworkingsockets_internal.h"
8 #ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE
9 #include "../steamdatagram_internal.h"
10 #include <steam/steamdatagram_tickets.h>
11 #endif
12 #include "../steamnetworking_statsutils.h"
13 #include <tier1/utlhashmap.h>
14 #include "steamnetworkingsockets_lowlevel.h"
15 #include "../steamnetworkingsockets_thinker.h"
16 #include "keypair.h"
17 #include "crypto.h"
18 #include "crypto_25519.h"
19 #include <tier0/memdbgoff.h>
20 #include <steamnetworkingsockets_messages.pb.h>
21 #include <tier0/memdbgon.h>
22 
23 #include "steamnetworkingsockets_snp.h"
24 
25 struct SteamNetConnectionStatusChangedCallback_t;
26 class ISteamNetworkingSocketsSerialized;
27 class CGameNetworkingUI_ConnectionState;
28 
29 namespace SteamNetworkingSocketsLib {
30 
31 const SteamNetworkingMicroseconds k_usecConnectRetryInterval = k_nMillion/2;
32 const SteamNetworkingMicroseconds k_usecFinWaitTimeout = 5*k_nMillion;
33 
34 typedef char ConnectionEndDebugMsg[ k_cchSteamNetworkingMaxConnectionCloseReason ];
35 typedef char ConnectionTypeDescription_t[64];
36 
37 class CSteamNetworkingSockets;
38 class CSteamNetworkingMessages;
39 class CSteamNetworkConnectionBase;
40 class CSteamNetworkConnectionP2P;
41 class CSharedSocket;
42 class CConnectionTransport;
43 struct SNPAckSerializerHelper;
44 struct CertAuthScope;
45 
46 enum EUnsignedCert
47 {
48 	k_EUnsignedCert_Disallow,
49 	k_EUnsignedCert_AllowWarn,
50 	k_EUnsignedCert_Allow,
51 };
52 
53 // Fixed size byte array that automatically wipes itself upon destruction.
54 // Used for storage of secret keys, etc.
55 template <int N>
56 class AutoWipeFixedSizeBuffer
57 {
58 public:
59 	enum { k_nSize = N };
60 	uint8 m_buf[ N ];
61 
62 	// You can wipe before destruction if you want
Wipe()63 	inline void Wipe() { SecureZeroMemory( m_buf, N ); }
64 
65 	// Wipe on destruction
~AutoWipeFixedSizeBuffer()66 	inline ~AutoWipeFixedSizeBuffer() { Wipe(); }
67 };
68 
69 /// In various places, we need a key in a map of remote connections.
70 struct RemoteConnectionKey_t
71 {
72 	SteamNetworkingIdentity m_identity;
73 	uint32 m_unConnectionID;
74 
75 	// NOTE: If we assume that peers are well behaved, then we
76 	// could just use the connection ID, which is a random number.
77 	// but let's not assume that.  In fact, if we really need to
78 	// protect against malicious clients we might have to include
79 	// some random private data so that they don't know how our hash
80 	// function works.  We'll assume for now that this isn't a problem
operatorRemoteConnectionKey_t::Hash81 	struct Hash { uint32 operator()( const RemoteConnectionKey_t &x ) const { return SteamNetworkingIdentityHash{}( x.m_identity ) ^ x.m_unConnectionID; } };
82 	inline bool operator ==( const RemoteConnectionKey_t &x ) const
83 	{
84 		return m_unConnectionID == x.m_unConnectionID && m_identity == x.m_identity;
85 	}
86 };
87 
88 /// Base class for connection-type-specific context structure
89 struct SendPacketContext_t
90 {
SendPacketContext_tSendPacketContext_t91 	inline SendPacketContext_t( SteamNetworkingMicroseconds usecNow, const char *pszReason ) : m_usecNow( usecNow ), m_pszReason( pszReason ) {}
92 	const SteamNetworkingMicroseconds m_usecNow;
93 	int m_cbMaxEncryptedPayload;
94 	const char *m_pszReason; // Why are we sending this packet?
95 };
96 
97 /// Context used when receiving a data packet
98 struct RecvPacketContext_t
99 {
100 
101 //
102 // Must be filled in by transport
103 //
104 
105 	/// Current time
106 	SteamNetworkingMicroseconds m_usecNow;
107 
108 	/// What transport is receiving this packet?
109 	CConnectionTransport *m_pTransport;
110 
111 	/// Jitter measurement, if present
112 	//int m_usecTimeSinceLast;
113 
114 //
115 // Output of DecryptDataChunk
116 //
117 
118 	/// Expanded packet number
119 	int64 m_nPktNum;
120 
121 	/// Pointer to decrypted data.  Will either point to to the caller's original packet,
122 	/// if the packet was not encrypted, or m_decrypted, if it was encrypted and we
123 	/// decrypted it
124 	const void *m_pPlainText;
125 
126 	/// Size of plaintext
127 	int m_cbPlainText;
128 
129 	// Temporary buffer to hold decrypted data, if we were actually encrypted
130 	uint8 m_decrypted[ k_cbSteamNetworkingSocketsMaxPlaintextPayloadRecv ];
131 };
132 
133 template<typename TStatsMsg>
134 struct SendPacketContext : SendPacketContext_t
135 {
SendPacketContextSendPacketContext136 	inline SendPacketContext( SteamNetworkingMicroseconds usecNow, const char *pszReason ) : SendPacketContext_t( usecNow, pszReason ) {}
137 
138 	uint32 m_nFlags; // Message flags that we need to set.
139 	TStatsMsg msg; // Type-specific stats message
140 	int m_cbMsgSize; // Size of message
141 	int m_cbTotalSize; // Size needed in the header, including the serialized size field
142 
SlamFlagsAndCalcSizeSendPacketContext143 	void SlamFlagsAndCalcSize()
144 	{
145 		SetStatsMsgFlagsIfNotImplied( msg, m_nFlags );
146 		m_cbTotalSize = m_cbMsgSize = ProtoMsgByteSize( msg );
147 		if ( m_cbMsgSize > 0 )
148 			m_cbTotalSize += VarIntSerializedSize( (uint32)m_cbMsgSize );
149 	}
150 
SerializeSendPacketContext151 	bool Serialize( byte *&p )
152 	{
153 		if ( m_cbTotalSize <= 0 )
154 			return false;
155 
156 		// Serialize the stats size, var-int encoded
157 		byte *pOut = SerializeVarInt( p, uint32( m_cbMsgSize ) );
158 
159 		// Serialize the actual message
160 		pOut = msg.SerializeWithCachedSizesToArray( pOut );
161 
162 		// Make sure we wrote the number of bytes we expected
163 		if ( pOut != p + m_cbTotalSize )
164 		{
165 			// ABORT!
166 			AssertMsg( false, "Size mismatch after serializing inline stats blob" );
167 			return false;
168 		}
169 
170 		// Advance pointer
171 		p = pOut;
172 		return true;
173 	}
174 
175 	void CalcMaxEncryptedPayloadSize( size_t cbHdrReserve, CSteamNetworkConnectionBase *pConnection );
176 };
177 
178 /// Replace internal states that are not visible outside of the API with
179 /// the corresponding state that we show the the application.
CollapseConnectionStateToAPIState(ESteamNetworkingConnectionState eState)180 inline ESteamNetworkingConnectionState CollapseConnectionStateToAPIState( ESteamNetworkingConnectionState eState )
181 {
182 	// All the hidden internal states are assigned negative values
183 	if ( eState < 0 )
184 		return k_ESteamNetworkingConnectionState_None;
185 	return eState;
186 }
187 
188 /// We use one global lock to protect all queues of
189 /// received messages.  (On connections and poll groups!)
190 extern ShortDurationLock g_lockAllRecvMessageQueues;
191 
192 /////////////////////////////////////////////////////////////////////////////
193 //
194 // CSteamNetworkPollGroup
195 //
196 /////////////////////////////////////////////////////////////////////////////
197 
198 class CSteamNetworkPollGroup;
199 struct PollGroupLock : Lock<RecursiveTimedMutexImpl> {
PollGroupLockPollGroupLock200 	PollGroupLock() : Lock<RecursiveTimedMutexImpl>( "pollgroup", LockDebugInfo::k_nFlag_PollGroup ) {}
201 };
202 using PollGroupScopeLock = ScopeLock<PollGroupLock>;
203 
204 class CSteamNetworkPollGroup
205 {
206 public:
207 	CSteamNetworkPollGroup( CSteamNetworkingSockets *pInterface );
208 	~CSteamNetworkPollGroup();
209 
210 	PollGroupLock m_lock;
211 
212 	/// What interface is responsible for this listen socket?
213 	CSteamNetworkingSockets *const m_pSteamNetworkingSocketsInterface;
214 
215 	/// Linked list of messages received through any connection on this listen socket
216 	SteamNetworkingMessageQueue m_queueRecvMessages;
217 
218 	/// Index into the global list
219 	HSteamNetPollGroup m_hPollGroupSelf;
220 
221 	/// List of connections that are in this poll group
222 	CUtlVector<CSteamNetworkConnectionBase *> m_vecConnections;
223 
224 	void AssignHandleAndAddToGlobalTable();
225 };
226 
227 /////////////////////////////////////////////////////////////////////////////
228 //
229 // CSteamNetworkListenSocketBase
230 //
231 /////////////////////////////////////////////////////////////////////////////
232 
233 /// Abstract base class for a listen socket that can accept connections.
234 class CSteamNetworkListenSocketBase
235 {
236 public:
237 
238 	/// Destroy the listen socket, and all of its accepted connections
239 	virtual void Destroy();
240 
241 	/// Called when we receive a connection attempt, to setup the linkage.
242 	bool BAddChildConnection( CSteamNetworkConnectionBase *pConn, SteamNetworkingErrMsg &errMsg );
243 
244 	/// This gets called on an accepted connection before it gets destroyed
245 	virtual void AboutToDestroyChildConnection( CSteamNetworkConnectionBase *pConn );
246 
247 	virtual bool APIGetAddress( SteamNetworkingIPAddr *pAddress );
248 
249 	/// Map of child connections
250 	CUtlHashMap<RemoteConnectionKey_t, CSteamNetworkConnectionBase *, std::equal_to<RemoteConnectionKey_t>, RemoteConnectionKey_t::Hash > m_mapChildConnections;
251 
252 	/// Index into the global list
253 	HSteamListenSocket m_hListenSocketSelf;
254 
255 	/// What interface is responsible for this listen socket?
256 	CSteamNetworkingSockets *const m_pSteamNetworkingSocketsInterface;
257 
258 	/// Configuration options that will apply to all connections accepted through this listen socket
259 	ConnectionConfig m_connectionConfig;
260 
261 	/// Symmetric mode
BSymmetricMode()262 	inline bool BSymmetricMode() const { return m_connectionConfig.m_SymmetricConnect.Get() != 0; }
263 	virtual bool BSupportsSymmetricMode();
264 
265 	/// For legacy interface.
266 	#ifdef STEAMNETWORKINGSOCKETS_STEAMCLIENT
267 	std::unique_ptr<CSteamNetworkPollGroup> m_pLegacyPollGroup;
268 	#endif
269 
270 protected:
271 	CSteamNetworkListenSocketBase( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface );
272 	virtual ~CSteamNetworkListenSocketBase(); // hidden destructor, don't call directly.  Use Destroy()
273 
274 	bool BInitListenSocketCommon( int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg );
275 };
276 
277 /////////////////////////////////////////////////////////////////////////////
278 //
279 // CSteamNetworkConnectionBase
280 //
281 /////////////////////////////////////////////////////////////////////////////
282 
283 struct ConnectionLock : Lock<RecursiveTimedMutexImpl> {
ConnectionLockConnectionLock284 	ConnectionLock() : Lock<RecursiveTimedMutexImpl>( "connection", LockDebugInfo::k_nFlag_Connection ) {}
285 };
286 struct ConnectionScopeLock : ScopeLock<ConnectionLock>
287 {
288 	ConnectionScopeLock() = default;
289 	ConnectionScopeLock( ConnectionLock &lock, const char *pszTag = nullptr ) : ScopeLock<ConnectionLock>( lock, pszTag ) {}
290 	ConnectionScopeLock( CSteamNetworkConnectionBase &conn, const char *pszTag = nullptr );
291 	void Lock( ConnectionLock &lock, const char *pszTag = nullptr ) { ScopeLock<ConnectionLock>::Lock( lock, pszTag ); }
292 	void Lock( CSteamNetworkConnectionBase &conn, const char *pszTag = nullptr );
293 };
294 
295 /// Abstract interface for a connection to a remote host over any underlying
296 /// transport.  Most of the common functionality for implementing reliable
297 /// connections on top of unreliable datagrams, connection quality measurement,
298 /// etc is implemented here.
299 class CSteamNetworkConnectionBase : public ILockableThinker< ConnectionLock >
300 {
301 public:
302 
303 //
304 // API entry points
305 //
306 
307 	/// Called when we close the connection locally
308 	void APICloseConnection( int nReason, const char *pszDebug, bool bEnableLinger );
309 
310 	/// Send a message
311 	EResult APISendMessageToConnection( const void *pData, uint32 cbData, int nSendFlags, int64 *pOutMessageNumber );
312 
313 	/// Send a message.  Returns the assigned message number, or a negative EResult value
314 	int64 APISendMessageToConnection( CSteamNetworkingMessage *pMsg, SteamNetworkingMicroseconds usecNow, bool *pbThinkImmediately = nullptr );
315 
316 	/// Flush any messages queued for Nagle
317 	EResult APIFlushMessageOnConnection();
318 
319 	/// Receive the next message(s)
320 	int APIReceiveMessages( SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages );
321 
322 	/// Accept a connection.  This will involve sending a message
323 	/// to the client, and calling ConnectionState_Connected on the connection
324 	/// to transition it to the connected state.
325 	EResult APIAcceptConnection();
326 	virtual EResult AcceptConnection( SteamNetworkingMicroseconds usecNow );
327 
328 	/// Fill in quick connection stats
329 	void APIGetQuickConnectionStatus( SteamNetworkingQuickConnectionStatus &stats );
330 
331 	/// Fill in detailed connection stats
332 	virtual void APIGetDetailedConnectionStatus( SteamNetworkingDetailedConnectionStatus &stats, SteamNetworkingMicroseconds usecNow );
333 
334 	/// Hook to allow connections to customize message sending.
335 	/// (E.g. loopback.)
336 	virtual int64 _APISendMessageToConnection( CSteamNetworkingMessage *pMsg, SteamNetworkingMicroseconds usecNow, bool *pbThinkImmediately );
337 
338 //
339 // Accessor
340 //
341 
342 	// Get/set user data
GetUserData()343 	inline int64 GetUserData() const
344 	{
345 		// User data is locked when we create a connection!
346 		Assert( m_connectionConfig.m_ConnectionUserData.IsSet() );
347 		return m_connectionConfig.m_ConnectionUserData.m_data;
348 	}
349 	void SetUserData( int64 nUserData );
350 
351 	// Get/set name
GetAppName()352 	inline const char *GetAppName() const { return m_szAppName; }
353 	void SetAppName( const char *pszName );
354 
355 	// Debug description
GetDescription()356 	inline const char *GetDescription() const { return m_szDescription; }
357 
358 	/// When something changes that goes into the description, call this to rebuild the description
359 	void SetDescription();
360 
361 	/// High level state of the connection
GetState()362 	ESteamNetworkingConnectionState GetState() const { return m_eConnectionState; }
GetWireState()363 	ESteamNetworkingConnectionState GetWireState() const { return m_eConnectionWireState; }
364 
365 	/// Check if the connection is 'connected' from the perspective of the wire protocol.
366 	/// (The wire protocol doesn't care about local states such as linger)
BStateIsConnectedForWirePurposes()367 	bool BStateIsConnectedForWirePurposes() const { return m_eConnectionWireState == k_ESteamNetworkingConnectionState_Connected; }
368 
369 	/// Return true if the connection is still "active" in some way.
BStateIsActive()370 	bool BStateIsActive() const
371 	{
372 		return
373 			m_eConnectionWireState == k_ESteamNetworkingConnectionState_Connecting
374 			|| m_eConnectionWireState == k_ESteamNetworkingConnectionState_FindingRoute
375 			|| m_eConnectionWireState == k_ESteamNetworkingConnectionState_Connected;
376 	}
377 
378 	/// Reason connection ended
GetConnectionEndReason()379 	ESteamNetConnectionEnd GetConnectionEndReason() const { return m_eEndReason; }
GetConnectionEndDebugString()380 	const char *GetConnectionEndDebugString() const { return m_szEndDebug; }
381 
382 	/// When did we enter the current state?
GetTimeEnteredConnectionState()383 	inline SteamNetworkingMicroseconds GetTimeEnteredConnectionState() const { return m_usecWhenEnteredConnectionState; }
384 
385 	/// Fill in connection details
386 	void ConnectionPopulateInfo( SteamNetConnectionInfo_t &info ) const;
387 
388 //
389 // Lifetime management
390 //
391 
392 	/// Schedule destruction at the next possible opportunity
393 	void ConnectionQueueDestroy();
394 	static void ProcessDeletionList();
395 
396 	/// Free up all resources.  Close sockets, etc
397 	virtual void FreeResources();
398 
399 	/// Nuke all transports
400 	virtual void DestroyTransport();
401 
402 //
403 // Connection state machine
404 // Functions to transition to the specified state.
405 //
406 
407 	void ConnectionState_ProblemDetectedLocally( ESteamNetConnectionEnd eReason, PRINTF_FORMAT_STRING const char *pszFmt, ... ) FMTFUNCTION( 3, 4 );
408 	void ConnectionState_ClosedByPeer( int nReason, const char *pszDebug );
409 	void ConnectionState_FindingRoute( SteamNetworkingMicroseconds usecNow );
410 	bool BConnectionState_Connecting( SteamNetworkingMicroseconds usecNow, SteamNetworkingErrMsg &errMsg );
411 	void ConnectionState_Connected( SteamNetworkingMicroseconds usecNow );
412 	void ConnectionState_FinWait();
413 
414 //
415 // Misc internal stuff
416 //
417 
418 	/// What interface is responsible for this connection?
419 	CSteamNetworkingSockets *const m_pSteamNetworkingSocketsInterface;
420 
421 	/// Current active transport for this connection.
422 	/// MIGHT BE NULL in certain failure / edge cases!
423 	/// Might change during the connection lifetime.
424 	CConnectionTransport *m_pTransport;
425 
426 	/// Our public handle
427 	HSteamNetConnection m_hConnectionSelf;
428 
429 	/// Who is on the other end?  This might be invalid if we don't know yet.  (E.g. direct UDP connections.)
430 	SteamNetworkingIdentity m_identityRemote;
431 
432 	/// Who are we?
433 	SteamNetworkingIdentity m_identityLocal;
434 
435 	/// The listen socket through which we were accepted, if any.
436 	CSteamNetworkListenSocketBase *m_pParentListenSocket;
437 
438 	/// What poll group are we assigned to?
439 	CSteamNetworkPollGroup *m_pPollGroup;
440 
441 	/// Assign poll group
442 	void SetPollGroup( CSteamNetworkPollGroup *pPollGroup );
443 
444 	/// Remove us from the poll group we are in (if any)
445 	void RemoveFromPollGroup();
446 
447 	/// Was this connection initiated locally (we are the "client") or remotely (we are the "server")?
448 	/// In *most* use cases, "server" connections have a listen socket, but not always.
449 	bool m_bConnectionInitiatedRemotely;
450 
451 	/// Our handle in our parent's m_listAcceptedConnections (if we were accepted on a listen socket)
452 	int m_hSelfInParentListenSocketMap;
453 
454 	// Linked list of received messages
455 	SteamNetworkingMessageQueue m_queueRecvMessages;
456 
457 	/// The unique 64-bit end-to-end connection ID.  Each side picks 32 bits
458 	uint32 m_unConnectionIDLocal;
459 	uint32 m_unConnectionIDRemote;
460 
461 	/// Track end-to-end stats for this connection.
462 	LinkStatsTracker<LinkStatsTrackerEndToEnd> m_statsEndToEnd;
463 
464 	/// When we accept a connection, they will send us a timestamp we should send back
465 	/// to them, so that they can estimate the ping
466 	uint64 m_ulHandshakeRemoteTimestamp;
467 	SteamNetworkingMicroseconds m_usecWhenReceivedHandshakeRemoteTimestamp;
468 
469 	/// Connection configuration
470 	ConnectionConfig m_connectionConfig;
471 
472 	/// The reason code for why the connection was closed.
473 	ESteamNetConnectionEnd m_eEndReason;
474 	ConnectionEndDebugMsg m_szEndDebug;
475 
476 	/// MTU values for this connection
477 	int m_cbMTUPacketSize = 0;
478 	int m_cbMaxPlaintextPayloadSend = 0;
479 	int m_cbMaxMessageNoFragment = 0;
480 	int m_cbMaxReliableMessageSegment = 0;
481 
482 	void UpdateMTUFromConfig();
483 
484 	// Each connection is protected by a lock.  The actual lock to use is IThinker::m_pLock.
485 	// Almost all connections use this default lock.  (A few special cases use a different lock
486 	// so that they are locked at the same time as other objects.)
487 	ConnectionLock m_defaultLock;
488 	void _AssertLocksHeldByCurrentThread( const char *pszFile, int line, const char *pszTag = nullptr ) const
489 	{
490 		SteamNetworkingGlobalLock::_AssertHeldByCurrentThread( pszFile, line, pszTag );
491 		m_pLock->_AssertHeldByCurrentThread( pszFile, line );
492 	}
493 
494 	/// Expand the packet number, and decrypt the data chunk.
495 	/// Returns true if everything is OK and we should continue
496 	/// processing the packet
497 	bool DecryptDataChunk( uint16 nWireSeqNum, int cbPacketSize, const void *pChunk, int cbChunk, RecvPacketContext_t &ctx );
498 
499 	/// Decode the plaintext.  Returns false if the packet seems corrupt or bogus, or should abort further
500 	/// processing.
501 	bool ProcessPlainTextDataChunk( int usecTimeSinceLast, RecvPacketContext_t &ctx );
502 
503 	/// Called when we receive an (end-to-end) packet with a sequence number
504 	void RecvNonDataSequencedPacket( int64 nPktNum, SteamNetworkingMicroseconds usecNow );
505 
506 	// Called from SNP to update transmit/receive speeds
507 	void UpdateSpeeds( int nTXSpeed, int nRXSpeed );
508 
509 	/// Called when the async process to request a cert has failed.
510 	void CertRequestFailed( ESteamNetConnectionEnd nConnectionEndReason, const char *pszMsg );
BHasLocalCert()511 	bool BHasLocalCert() const { return m_msgSignedCertLocal.has_cert(); }
512 	void SetLocalCert( const CMsgSteamDatagramCertificateSigned &msgSignedCert, const CECSigningPrivateKey &keyPrivate, bool bCertHasIdentity );
513 	void InterfaceGotCert();
514 
SNP_BHasAnyBufferedRecvData()515 	bool SNP_BHasAnyBufferedRecvData() const
516 	{
517 		return !m_receiverState.m_bufReliableStream.empty();
518 	}
SNP_BHasAnyUnackedSentReliableData()519 	bool SNP_BHasAnyUnackedSentReliableData() const
520 	{
521 		return m_senderState.m_cbPendingReliable > 0 || m_senderState.m_cbSentUnackedReliable > 0;
522 	}
523 
524 	/// Return true if we have any reason to send a packet.  This doesn't mean we have the bandwidth
525 	/// to send it now, it just means we would like to send something ASAP
SNP_WantsToSendPacket()526 	inline bool SNP_WantsToSendPacket() const
527 	{
528 		return m_receiverState.TimeWhenFlushAcks() < INT64_MAX || SNP_TimeWhenWantToSendNextPacket() < INT64_MAX;
529 	}
530 
531 	/// Send a data packet now, even if we don't have the bandwidth available.  Returns true if a packet was
532 	/// sent successfully, false if there was a problem.  This will call SendEncryptedDataChunk to do the work
533 	bool SNP_SendPacket( CConnectionTransport *pTransport, SendPacketContext_t &ctx );
534 
535 	/// Record that we sent a non-data packet.  This is so that if the peer acks,
536 	/// we can record it as a ping
537 	void SNP_SentNonDataPacket( CConnectionTransport *pTransport, int cbPkt, SteamNetworkingMicroseconds usecNow );
538 
539 	/// Called after the connection state changes.  Default behavior is to notify
540 	/// the active transport, if any
541 	virtual void ConnectionStateChanged( ESteamNetworkingConnectionState eOldState );
542 
543 	/// Called to post a callback
544 	int m_nSupressStateChangeCallbacks;
545 	void PostConnectionStateChangedCallback( ESteamNetworkingConnectionState eOldAPIState, ESteamNetworkingConnectionState eNewAPIState );
546 
QueueEndToEndAck(bool bImmediate,SteamNetworkingMicroseconds usecNow)547 	void QueueEndToEndAck( bool bImmediate, SteamNetworkingMicroseconds usecNow )
548 	{
549 		if ( bImmediate )
550 		{
551 			m_receiverState.QueueFlushAllAcks( k_nThinkTime_ASAP );
552 			SetNextThinkTimeASAP();
553 		}
554 		else
555 		{
556 			QueueFlushAllAcks( usecNow + k_usecMaxDataAckDelay );
557 		}
558 	}
559 
QueueFlushAllAcks(SteamNetworkingMicroseconds usecWhen)560 	void QueueFlushAllAcks( SteamNetworkingMicroseconds usecWhen )
561 	{
562 		m_receiverState.QueueFlushAllAcks( usecWhen );
563 		EnsureMinThinkTime( m_receiverState.TimeWhenFlushAcks() );
564 	}
565 
GetSignedCryptLocal()566 	inline const CMsgSteamDatagramSessionCryptInfoSigned &GetSignedCryptLocal() { return m_msgSignedCryptLocal; }
GetSignedCertLocal()567 	inline const CMsgSteamDatagramCertificateSigned &GetSignedCertLocal() { return m_msgSignedCertLocal; }
BCertHasIdentity()568 	inline bool BCertHasIdentity() const { return m_bCertHasIdentity; }
BCryptKeysValid()569 	inline bool BCryptKeysValid() const { return m_bCryptKeysValid; }
570 
571 	/// Called when we send an end-to-end connect request
SentEndToEndConnectRequest(SteamNetworkingMicroseconds usecNow)572 	void SentEndToEndConnectRequest( SteamNetworkingMicroseconds usecNow )
573 	{
574 
575 		// Reset timeout/retry for this reply.  But if it fails, we'll start
576 		// the whole handshake over again.  It keeps the code simpler, and the
577 		// challenge value has a relatively short expiry anyway.
578 		m_usecWhenSentConnectRequest = usecNow;
579 		EnsureMinThinkTime( usecNow + k_usecConnectRetryInterval );
580 	}
581 
582 	/// Symmetric mode
BSymmetricMode()583 	inline bool BSymmetricMode() const { return m_connectionConfig.m_SymmetricConnect.Get() != 0; }
584 	virtual bool BSupportsSymmetricMode();
585 
586 	// Check the certs, save keys, etc
587 	bool BRecvCryptoHandshake( const CMsgSteamDatagramCertificateSigned &msgCert, const CMsgSteamDatagramSessionCryptInfoSigned &msgSessionInfo, bool bServer );
588 	bool BFinishCryptoHandshake( bool bServer );
589 
590 	/// Check state of connection.  Check for timeouts, and schedule time when we
591 	/// should think next
592 	void CheckConnectionStateAndSetNextThinkTime( SteamNetworkingMicroseconds usecNow );
593 
594 	/// Same as CheckConnectionStateAndSetNextThinkTime, but can be called when we don't
595 	/// already have the global lock.  If we can take the necessary locks now, without
596 	/// blocking, then we'll go ahead and take action now.  If we cannot, we will
597 	/// just schedule a wakeup call
598 	void CheckConnectionStateOrScheduleWakeUp( SteamNetworkingMicroseconds usecNow );
599 
600 	// Upcasts.  So we don't have to compile with RTTI
601 	virtual CSteamNetworkConnectionP2P *AsSteamNetworkConnectionP2P();
602 
603 	/// Check if this connection is an internal connection for the
604 	/// ISteamMessages interface.  The messages layer *mostly* works
605 	/// on top of the sockets system, but in a few places we need
606 	/// to break the abstraction and do things other clients of the
607 	/// API could not do easily
IsConnectionForMessagesSession()608 	inline bool IsConnectionForMessagesSession() const { return m_connectionConfig.m_LocalVirtualPort.Get() == k_nVirtualPort_Messages; }
609 
610 	/// Time when we would like to send our next connection diagnostics
611 	/// update.  This is initialized the first time we enter the "connecting"
612 	/// state and we are on a platform that wants those updates.
613 	/// Once we wish to stop sending them, we set it to "never"
614 	#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI
615 		SteamNetworkingMicroseconds m_usecWhenNextDiagnosticsUpdate;
616 		void CheckScheduleDiagnosticsUpdateASAP();
617 
618 		/// Fill out diagnostics message to send to steam client with current state of the
619 		/// connection, and also schedule the next check, if the connection is active.
620 		virtual void ConnectionPopulateDiagnostics( ESteamNetworkingConnectionState eOldState, CGameNetworkingUI_ConnectionState &msgConnectionState, SteamNetworkingMicroseconds usecNow );
621 	#else
CheckScheduleDiagnosticsUpdateASAP()622 		inline void CheckScheduleDiagnosticsUpdateASAP() {}
623 		static constexpr SteamNetworkingMicroseconds m_usecWhenNextDiagnosticsUpdate = k_nThinkTime_Never;
624 	#endif
625 
626 	/// Timestamp when we were created
627 	SteamNetworkingMicroseconds m_usecWhenCreated;
628 
629 protected:
630 	CSteamNetworkConnectionBase( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface, ConnectionScopeLock &scopeLock );
631 	virtual ~CSteamNetworkConnectionBase(); // hidden destructor, don't call directly.  Use ConnectionQueueDestroy()
632 
633 	/// Initialize connection bookkeeping
634 	bool BInitConnection( SteamNetworkingMicroseconds usecNow, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg );
635 
636 	/// Called from BInitConnection, to start obtaining certs, etc
637 	virtual void InitConnectionCrypto( SteamNetworkingMicroseconds usecNow );
638 
639 	/// Name assigned by app (for debugging)
640 	char m_szAppName[ k_cchSteamNetworkingMaxConnectionDescription ];
641 
642 	/// More complete debug description (for debugging)
643 	char m_szDescription[ k_cchSteamNetworkingMaxConnectionDescription ];
644 
645 	/// Set the connection description.  Should include the connection type and peer address.
646 	virtual void GetConnectionTypeDescription( ConnectionTypeDescription_t &szDescription ) const = 0;
647 
648 	/// Misc periodic processing.
649 	/// Called from within CheckConnectionStateAndSetNextThinkTime.
650 	/// Will be called in any connection state.
651 	virtual void ThinkConnection( SteamNetworkingMicroseconds usecNow );
652 
653 	/// Called from the connection Think() state machine, for connections that have been
654 	/// initiated locally and that are in the connecting state.
655 	///
656 	/// Should return the next time when it needs to be woken up.  Or it can set the next
657 	/// think time directly, if it is awkward to return.  That is slightly
658 	/// less efficient.
659 	///
660 	/// Base class sends connect requests (including periodic retry) through the current
661 	/// transport.
662 	virtual SteamNetworkingMicroseconds ThinkConnection_ClientConnecting( SteamNetworkingMicroseconds usecNow );
663 
664 	/// Called from the connection Think() state machine, when the connection is in the finding
665 	/// route state.  The connection should return the next time when it needs to be woken up.
666 	/// Or it can set the next think time directly, if it is awkward to return.  That is slightly
667 	/// less efficient.
668 	virtual SteamNetworkingMicroseconds ThinkConnection_FindingRoute( SteamNetworkingMicroseconds usecNow );
669 
670 	/// Called when a timeout is detected
671 	void ConnectionTimedOut( SteamNetworkingMicroseconds usecNow );
672 
673 	/// Called when a timeout is detected to tried to provide a more specific error
674 	/// message.
675 	virtual void ConnectionGuessTimeoutReason( ESteamNetConnectionEnd &nReasonCode, ConnectionEndDebugMsg &msg, SteamNetworkingMicroseconds usecNow );
676 
677 	/// Called when we receive a complete message.  Should allocate a message object and put it into the proper queues
678 	bool ReceivedMessage( const void *pData, int cbData, int64 nMsgNum, int nFlags, SteamNetworkingMicroseconds usecNow );
679 	void ReceivedMessage( CSteamNetworkingMessage *pMsg );
680 
681 	/// Timestamp when we last sent an end-to-end connection request packet
682 	SteamNetworkingMicroseconds m_usecWhenSentConnectRequest;
683 
684 	//
685 	// Crypto
686 	//
687 
688 	void ClearCrypto();
689 	bool BThinkCryptoReady( SteamNetworkingMicroseconds usecNow );
690 	void SetLocalCertUnsigned();
691 	void ClearLocalCrypto();
692 	void FinalizeLocalCrypto();
693 	void SetCryptoCipherList();
694 
695 	// Remote cert and crypt info.  We need to hand on to the original serialized version briefly
696 	std::string m_sCertRemote;
697 	std::string m_sCryptRemote;
698 	CMsgSteamDatagramCertificate m_msgCertRemote;
699 	CMsgSteamDatagramSessionCryptInfo m_msgCryptRemote;
700 	bool m_bRemoteCertHasTrustedCASignature; // Could expand this to an enum of different states
701 
702 	// Local crypto info for this connection
703 	CECSigningPrivateKey m_keyPrivate; // Private key corresponding to our cert.  We'll wipe this in FinalizeLocalCrypto, as soon as we've locked in the crypto properties we're going to use
704 	CECKeyExchangePrivateKey m_keyExchangePrivateKeyLocal;
705 	CMsgSteamDatagramSessionCryptInfo m_msgCryptLocal;
706 	CMsgSteamDatagramSessionCryptInfoSigned m_msgSignedCryptLocal;
707 	CMsgSteamDatagramCertificateSigned m_msgSignedCertLocal;
708 	bool m_bCertHasIdentity; // Does the cert contain the identity we will use for this connection?
709 	ESteamNetworkingSocketsCipher m_eNegotiatedCipher;
710 
711 	// AES keys used in each direction
712 	bool m_bCryptKeysValid;
713 	AES_GCM_EncryptContext m_cryptContextSend;
714 	AES_GCM_DecryptContext m_cryptContextRecv;
715 
716 	// Initialization vector for AES-GCM.  These are combined with
717 	// the packet number so that the effective IV is unique per
718 	// packet.  We use a 96-bit IV, which is what TLS uses (RFC5288),
719 	// what NIST recommends (https://dl.acm.org/citation.cfm?id=2206251),
720 	// and what makes GCM the most efficient.
721 	AutoWipeFixedSizeBuffer<12> m_cryptIVSend;
722 	AutoWipeFixedSizeBuffer<12> m_cryptIVRecv;
723 
724 	/// Check if the remote cert (m_msgCertRemote) is acceptable.  If not, return the
725 	/// appropriate connection code and error message.  If pCACertAuthScope is NULL, the
726 	/// cert is not signed.  (The base class will check if this is allowed.)  If pCACertAuthScope
727 	/// is present, the cert was signed and the chain of trust has been verified, and the CA trust
728 	/// chain has authorized the specified rights.
729 	virtual ESteamNetConnectionEnd CheckRemoteCert( const CertAuthScope *pCACertAuthScope, SteamNetworkingErrMsg &errMsg );
730 
731 	/// Called when we the remote host presents us with an unsigned cert.
732 	virtual EUnsignedCert AllowRemoteUnsignedCert();
733 
734 	/// Called to decide if we want to try to proceed without a signed cert for ourselves
735 	virtual EUnsignedCert AllowLocalUnsignedCert();
736 
737 	//
738 	// "SNP" - Steam Networking Protocol.  (Sort of audacious to stake out this acronym, don't you think...?)
739 	//         The layer that does end-to-end reliability and bandwidth estimation
740 	//
741 
742 	void SNP_InitializeConnection( SteamNetworkingMicroseconds usecNow );
743 	void SNP_ShutdownConnection();
744 	int64 SNP_SendMessage( CSteamNetworkingMessage *pSendMessage, SteamNetworkingMicroseconds usecNow, bool *pbThinkImmediately );
745 	SteamNetworkingMicroseconds SNP_ThinkSendState( SteamNetworkingMicroseconds usecNow );
746 	SteamNetworkingMicroseconds SNP_GetNextThinkTime( SteamNetworkingMicroseconds usecNow );
747 	SteamNetworkingMicroseconds SNP_TimeWhenWantToSendNextPacket() const;
748 	void SNP_PrepareFeedback( SteamNetworkingMicroseconds usecNow );
749 	void SNP_ReceiveUnreliableSegment( int64 nMsgNum, int nOffset, const void *pSegmentData, int cbSegmentSize, bool bLastSegmentInMessage, SteamNetworkingMicroseconds usecNow );
750 	bool SNP_ReceiveReliableSegment( int64 nPktNum, int64 nSegBegin, const uint8 *pSegmentData, int cbSegmentSize, SteamNetworkingMicroseconds usecNow );
751 	int SNP_ClampSendRate();
752 	void SNP_PopulateDetailedStats( SteamDatagramLinkStats &info );
753 	void SNP_PopulateQuickStats( SteamNetworkingQuickConnectionStatus &info, SteamNetworkingMicroseconds usecNow );
754 	void SNP_RecordReceivedPktNum( int64 nPktNum, SteamNetworkingMicroseconds usecNow, bool bScheduleAck );
755 	EResult SNP_FlushMessage( SteamNetworkingMicroseconds usecNow );
756 
757 	/// Accumulate "tokens" into our bucket base on the current calculated send rate
758 	void SNP_TokenBucket_Accumulate( SteamNetworkingMicroseconds usecNow );
759 
760 	/// Mark a packet as dropped
761 	void SNP_SenderProcessPacketNack( int64 nPktNum, SNPInFlightPacket_t &pkt, const char *pszDebug );
762 
763 	/// Check in flight packets.  Expire any that need to be, and return the time when the
764 	/// next one that is not yet expired will be expired.
765 	SteamNetworkingMicroseconds SNP_SenderCheckInFlightPackets( SteamNetworkingMicroseconds usecNow );
766 
767 	SSNPSenderState m_senderState;
768 	SSNPReceiverState m_receiverState;
769 
770 	/// Bandwidth estimation data
771 	SSendRateData m_sendRateData; // FIXME Move this to transport!
772 
773 	/// Called from SNP layer when it decodes a packet that serves as a ping measurement
774 	virtual void ProcessSNPPing( int msPing, RecvPacketContext_t &ctx );
775 
776 private:
777 
778 	void SNP_GatherAckBlocks( SNPAckSerializerHelper &helper, SteamNetworkingMicroseconds usecNow );
779 	uint8 *SNP_SerializeAckBlocks( const SNPAckSerializerHelper &helper, uint8 *pOut, const uint8 *pOutEnd, SteamNetworkingMicroseconds usecNow );
780 	uint8 *SNP_SerializeStopWaitingFrame( uint8 *pOut, const uint8 *pOutEnd, SteamNetworkingMicroseconds usecNow );
781 
782 	void SetState( ESteamNetworkingConnectionState eNewState, SteamNetworkingMicroseconds usecNow );
783 	ESteamNetworkingConnectionState m_eConnectionState;
784 
785 	/// State of the connection as our peer would observe it.
786 	/// (Certain local state transitions are not meaningful.)
787 	///
788 	/// Differs from m_eConnectionState in two ways:
789 	/// - Linger is not used.  Instead, to the peer we are "connected."
790 	/// - When the local connection state transitions
791 	///   from ProblemDetectedLocally or ClosedByPeer to FinWait,
792 	///   when the application closes the connection, this value
793 	///   will not change.  It will retain the previous state,
794 	///   so that while we are in the FinWait state, we can send
795 	///   appropriate cleanup messages.
796 	ESteamNetworkingConnectionState m_eConnectionWireState;
797 
798 	/// Timestamp when we entered the current state.  Used for various
799 	/// timeouts.
800 	SteamNetworkingMicroseconds m_usecWhenEnteredConnectionState;
801 
802 	// !DEBUG! Log of packets we sent.
803 	#ifdef SNP_ENABLE_PACKETSENDLOG
804 	struct PacketSendLog
805 	{
806 		// State before we sent anything
807 		SteamNetworkingMicroseconds m_usecTime;
808 		int m_cbPendingReliable;
809 		int m_cbPendingUnreliable;
810 		int m_nPacketGaps;
811 		float m_fltokens;
812 		int64 m_nPktNumNextPendingAck;
813 		SteamNetworkingMicroseconds m_usecNextPendingAckTime;
814 		int64 m_nMaxPktRecv;
815 		int64 m_nMinPktNumToSendAcks;
816 
817 		int m_nAckBlocksNeeded;
818 
819 		// What we sent
820 		int m_nAckBlocksSent;
821 		int64 m_nAckEnd;
822 		int m_nReliableSegmentsRetry;
823 		int m_nSegmentsSent;
824 		int m_cbSent;
825 	};
826 	std_vector<PacketSendLog> m_vecSendLog;
827 	#endif
828 
829 	// Implements IThinker.
830 	// Connections must not override this, or call it directly.
831 	// Do any periodic work in ThinkConnection()
832 	virtual void Think( SteamNetworkingMicroseconds usecNow ) override final;
833 };
834 
835 /// Abstract base class for sending end-to-end data for a connection.
836 ///
837 /// Many connection classes only have one transport, but some may
838 /// may have more than one transport, and dynamically switch between
839 /// them.  (E.g. it will try local LAN, NAT piercing, then fallback to relay)
840 class CConnectionTransport
841 {
842 public:
843 
844 	/// The connection we were created to service.  A given transport object
845 	/// is always created for a single connection (and that will not change,
846 	/// hence this is a reference and not a pointer).  However, a connection may
847 	/// create more than one transport.
848 	CSteamNetworkConnectionBase &m_connection;
849 
850 	/// Use this function to actually delete the object.  Do not use operator delete
851 	void TransportDestroySelfNow();
852 
853 	/// Free up transport resources.  Called just before destruction.  If you have cleanup
854 	/// that might involved calling virtual methods, do it in here
855 	virtual void TransportFreeResources();
856 
857 	/// Called by SNP pacing layer, when it has some data to send and there is bandwidth available.
858 	/// The derived class should setup a context, reserving the space it needs, and then call SNP_SendPacket.
859 	/// Returns true if a packet was sent successfully, false if there was a problem.
860 	virtual bool SendDataPacket( SteamNetworkingMicroseconds usecNow ) = 0;
861 
862 	/// Connection will call this to ask the transport to surround the
863 	/// "chunk" with the appropriate framing, and route it to the
864 	/// appropriate host.  A "chunk" might contain a mix of reliable
865 	/// and unreliable data.  We use the same framing for data
866 	/// payloads for all connection types.  Return value is
867 	/// the number of bytes written to the network layer, UDP/IP
868 	/// header is not included.
869 	///
870 	/// ctx is whatever the transport passed to SNP_SendPacket, if the
871 	/// connection initiated the sending of the packet
872 	virtual int SendEncryptedDataChunk( const void *pChunk, int cbChunk, SendPacketContext_t &ctx ) = 0;
873 
874 	/// Return true if we are currently able to send end-to-end messages.
875 	virtual bool BCanSendEndToEndConnectRequest() const;
876 	virtual bool BCanSendEndToEndData() const = 0;
877 	virtual void SendEndToEndConnectRequest( SteamNetworkingMicroseconds usecNow );
878 	virtual void SendEndToEndStatsMsg( EStatsReplyRequest eRequest, SteamNetworkingMicroseconds usecNow, const char *pszReason ) = 0;
879 	virtual void TransportPopulateConnectionInfo( SteamNetConnectionInfo_t &info ) const;
880 	virtual void GetDetailedConnectionStatus( SteamNetworkingDetailedConnectionStatus &stats, SteamNetworkingMicroseconds usecNow );
881 	#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI
882 		virtual void TransportPopulateDiagnostics( CGameNetworkingUI_ConnectionState &msgConnectionState, SteamNetworkingMicroseconds usecNow );
883 	#endif
884 
885 	/// Called when the connection state changes.  Some transports need to do stuff
886 	virtual void TransportConnectionStateChanged( ESteamNetworkingConnectionState eOldState );
887 
888 	/// Called when a timeout is detected to tried to provide a more specific error
889 	/// message
890 	virtual void TransportGuessTimeoutReason( ESteamNetConnectionEnd &nReasonCode, ConnectionEndDebugMsg &msg, SteamNetworkingMicroseconds usecNow );
891 
892 	// Some accessors for commonly needed info
ConnectionState()893 	inline ESteamNetworkingConnectionState ConnectionState() const { return m_connection.GetState(); }
ConnectionWireState()894 	inline ESteamNetworkingConnectionState ConnectionWireState() const { return m_connection.GetWireState(); }
ConnectionIDLocal()895 	inline uint32 ConnectionIDLocal() const { return m_connection.m_unConnectionIDLocal; }
ConnectionIDRemote()896 	inline uint32 ConnectionIDRemote() const { return m_connection.m_unConnectionIDRemote; }
ListenSocket()897 	inline CSteamNetworkListenSocketBase *ListenSocket() const { return m_connection.m_pParentListenSocket; }
IdentityLocal()898 	inline const SteamNetworkingIdentity &IdentityLocal() const { return m_connection.m_identityLocal; }
IdentityRemote()899 	inline const SteamNetworkingIdentity &IdentityRemote() const { return m_connection.m_identityRemote; }
ConnectionDescription()900 	inline const char *ConnectionDescription() const { return m_connection.GetDescription(); }
901 
902 	void _AssertLocksHeldByCurrentThread( const char *pszFile, int line, const char *pszTag = nullptr ) const
903 	{
904 		m_connection._AssertLocksHeldByCurrentThread( pszFile, line, pszTag );
905 	}
906 
907 	// Useful so we can use ScheduledMethodThinkerLockable
TryLock()908 	bool TryLock() { return m_connection.TryLock(); }
Unlock()909 	void Unlock() { m_connection.Unlock(); }
910 
911 protected:
912 
CConnectionTransport(CSteamNetworkConnectionBase & conn)913 	inline CConnectionTransport( CSteamNetworkConnectionBase &conn ) : m_connection( conn ) {}
~CConnectionTransport()914 	virtual ~CConnectionTransport() {} // Destructor protected -- use TransportDestroySelfNow()
915 };
916 
917 // Delayed inline
ConnectionScopeLock(CSteamNetworkConnectionBase & conn,const char * pszTag)918 inline ConnectionScopeLock::ConnectionScopeLock( CSteamNetworkConnectionBase &conn, const char *pszTag ) : ScopeLock<ConnectionLock>( *conn.m_pLock, pszTag ) {}
Lock(CSteamNetworkConnectionBase & conn,const char * pszTag)919 inline void ConnectionScopeLock::Lock( CSteamNetworkConnectionBase &conn, const char *pszTag ) { ScopeLock<ConnectionLock>::Lock( *conn.m_pLock, pszTag ); }
920 
921 /// Dummy loopback/pipe connection that doesn't actually do any network work.
922 /// For these types of connections, the distinction between connection and transport
923 /// is not really useful
924 class CSteamNetworkConnectionPipe final : public CSteamNetworkConnectionBase, public CConnectionTransport
925 {
926 public:
927 
928 	/// Create a pair of loopback connections that are immediately connected to each other
929 	/// No callbacks are posted.
930 	static bool APICreateSocketPair( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface, CSteamNetworkConnectionPipe **pOutConnections, const SteamNetworkingIdentity pIdentity[2] );
931 
932 	/// Create a pair of loopback connections that act like normal connections, but use internal transport.
933 	/// The two connections will be placed in the "connecting" state, and will go through the ordinary
934 	/// state machine.
935 	///
936 	/// The client connection is returned.
937 	static CSteamNetworkConnectionPipe *CreateLoopbackConnection(
938 		CSteamNetworkingSockets *pClientInstance, int nOptions, const SteamNetworkingConfigValue_t *pOptions,
939 		CSteamNetworkListenSocketBase *pListenSocket,
940 		SteamNetworkingErrMsg &errMsg,
941 		ConnectionScopeLock &scopeLock );
942 
943 	/// The guy who is on the other end.
944 	CSteamNetworkConnectionPipe *m_pPartner;
945 
946 	// CSteamNetworkConnectionBase overrides
947 	virtual int64 _APISendMessageToConnection( CSteamNetworkingMessage *pMsg, SteamNetworkingMicroseconds usecNow, bool *pbThinkImmediately ) override;
948 	virtual EResult AcceptConnection( SteamNetworkingMicroseconds usecNow ) override;
949 	virtual void InitConnectionCrypto( SteamNetworkingMicroseconds usecNow ) override;
950 	virtual EUnsignedCert AllowRemoteUnsignedCert() override;
951 	virtual EUnsignedCert AllowLocalUnsignedCert() override;
952 	virtual void GetConnectionTypeDescription( ConnectionTypeDescription_t &szDescription ) const override;
953 	virtual void DestroyTransport() override;
954 	virtual void ConnectionStateChanged( ESteamNetworkingConnectionState eOldState ) override;
955 
956 	// CConnectionTransport
957 	virtual bool SendDataPacket( SteamNetworkingMicroseconds usecNow ) override;
958 	virtual bool BCanSendEndToEndConnectRequest() const override;
959 	virtual bool BCanSendEndToEndData() const override;
960 	virtual void SendEndToEndConnectRequest( SteamNetworkingMicroseconds usecNow ) override;
961 	virtual void SendEndToEndStatsMsg( EStatsReplyRequest eRequest, SteamNetworkingMicroseconds usecNow, const char *pszReason ) override;
962 	virtual int SendEncryptedDataChunk( const void *pChunk, int cbChunk, SendPacketContext_t &ctx ) override;
963 	virtual void TransportPopulateConnectionInfo( SteamNetConnectionInfo_t &info ) const override;
964 
965 private:
966 
967 	// Use CreateSocketPair!
968 	CSteamNetworkConnectionPipe( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface, const SteamNetworkingIdentity &identity, ConnectionScopeLock &scopeLock );
969 	virtual ~CSteamNetworkConnectionPipe();
970 
971 	/// Setup the server side of a loopback connection
972 	bool BBeginAccept( CSteamNetworkListenSocketBase *pListenSocket, SteamNetworkingMicroseconds usecNow, SteamDatagramErrMsg &errMsg );
973 
974 	/// Act like we sent a sequenced packet
975 	void FakeSendStats( SteamNetworkingMicroseconds usecNow, int cbPktSize );
976 };
977 
978 // Had to delay this until CSteamNetworkConnectionBase was defined
979 template<typename TStatsMsg>
CalcMaxEncryptedPayloadSize(size_t cbHdrReserve,CSteamNetworkConnectionBase * pConnection)980 inline void SendPacketContext<TStatsMsg>::CalcMaxEncryptedPayloadSize( size_t cbHdrReserve, CSteamNetworkConnectionBase *pConnection )
981 {
982 	Assert( m_cbTotalSize >= 0 );
983 	m_cbMaxEncryptedPayload = pConnection->m_cbMTUPacketSize - (int)cbHdrReserve - m_cbTotalSize;
984 	Assert( m_cbMaxEncryptedPayload >= 0 );
985 }
986 
987 /////////////////////////////////////////////////////////////////////////////
988 //
989 // Misc globals
990 //
991 /////////////////////////////////////////////////////////////////////////////
992 
993 extern CUtlHashMap<uint16, CSteamNetworkConnectionBase *, std::equal_to<uint16>, Identity<uint16> > g_mapConnections;
994 extern CUtlHashMap<int, CSteamNetworkPollGroup *, std::equal_to<int>, Identity<int> > g_mapPollGroups;
995 
996 // All of the tables above are projected by the same lock, since we expect to only access it briefly
997 struct TableLock : Lock<RecursiveMutexImpl> {
TableLockTableLock998 	TableLock() : Lock<RecursiveMutexImpl>( "table", LockDebugInfo::k_nFlag_Table ) {}
999 };
1000 using TableScopeLock = ScopeLock<TableLock>;
1001 extern TableLock g_tables_lock;
1002 
1003 // This table is protected by the global lock
1004 extern CUtlHashMap<int, CSteamNetworkListenSocketBase *, std::equal_to<int>, Identity<int> > g_mapListenSockets;
1005 
1006 extern bool BCheckGlobalSpamReplyRateLimit( SteamNetworkingMicroseconds usecNow );
1007 extern CSteamNetworkConnectionBase *GetConnectionByHandle( HSteamNetConnection sock, ConnectionScopeLock &scopeLock );
1008 extern CSteamNetworkPollGroup *GetPollGroupByHandle( HSteamNetPollGroup hPollGroup, PollGroupScopeLock &scopeLock, const char *pszLockTag );
1009 
FindConnectionByLocalID(uint32 nLocalConnectionID,ConnectionScopeLock & scopeLock)1010 inline CSteamNetworkConnectionBase *FindConnectionByLocalID( uint32 nLocalConnectionID, ConnectionScopeLock &scopeLock )
1011 {
1012 	// We use the wire connection ID as the API handle, so these two operations
1013 	// are currently the same.
1014 	return GetConnectionByHandle( HSteamNetConnection( nLocalConnectionID ), scopeLock );
1015 }
1016 
1017 } // namespace SteamNetworkingSocketsLib
1018 
1019 #endif // STEAMNETWORKINGSOCKETS_CONNECTIONS_H
1020