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