1 //====== Copyright Valve Corporation, All rights reserved. ====================
2 
3 #ifndef STEAMNETWORKINGSOCKETS_UDP_H
4 #define STEAMNETWORKINGSOCKETS_UDP_H
5 #pragma once
6 
7 #include "steamnetworkingsockets_connections.h"
8 #include <steamnetworkingsockets_messages_udp.pb.h>
9 
10 namespace SteamNetworkingSocketsLib {
11 
12 class CConnectionTransportUDPBase;
13 
14 #pragma pack( push, 1 )
15 
16 const int k_cbSteamNetworkingMinPaddedPacketSize = 512;
17 
18 /// A protobuf-encoded message that is padded to ensure a minimum length
19 struct UDPPaddedMessageHdr
20 {
21 	uint8 m_nMsgID;
22 	uint16 m_nMsgLength;
23 };
24 
25 struct UDPDataMsgHdr
26 {
27 	enum
28 	{
29 		kFlag_ProtobufBlob  = 0x01, // Protobuf-encoded message is inline (CMsgSteamSockets_UDP_Stats)
30 	};
31 
32 	uint8 m_unMsgFlags;
33 	uint32 m_unToConnectionID; // Recipient's portion of the connection ID
34 	uint16 m_unSeqNum;
35 
36 	// [optional, if flags&kFlag_ProtobufBlob]  varint-encoded protobuf blob size, followed by blob
37 	// Data frame(s)
38 	// End of packet
39 };
40 #pragma pack( pop )
41 
42 template<>
43 inline uint32 StatsMsgImpliedFlags<CMsgSteamSockets_UDP_Stats>( const CMsgSteamSockets_UDP_Stats &msg )
44 {
45 	return msg.has_stats() ? msg.ACK_REQUEST_E2E : 0;
46 }
47 
48 struct UDPSendPacketContext_t : SendPacketContext<CMsgSteamSockets_UDP_Stats>
49 {
UDPSendPacketContext_tUDPSendPacketContext_t50 	inline explicit UDPSendPacketContext_t( SteamNetworkingMicroseconds usecNow, const char *pszReason ) : SendPacketContext<CMsgSteamSockets_UDP_Stats>( usecNow, pszReason ) {}
51 	int m_nStatsNeed;
52 
53 	void Populate( size_t cbHdrtReserve, EStatsReplyRequest eReplyRequested, CConnectionTransportUDPBase *pTransport );
54 
55 	void Trim( int cbHdrOutSpaceRemaining );
56 };
57 
58 struct UDPRecvPacketContext_t : RecvPacketContext_t
59 {
60 	CMsgSteamSockets_UDP_Stats *m_pStatsIn;
61 };
62 
63 extern std::string DescribeStatsContents( const CMsgSteamSockets_UDP_Stats &msg );
64 extern bool BCheckRateLimitReportBadPacket( SteamNetworkingMicroseconds usecNow );
65 extern void ReallyReportBadUDPPacket( const char *pszFrom, const char *pszMsgType, const char *pszFmt, ... );
66 
67 #define ReportBadUDPPacketFrom( pszFrom, pszMsgType, /* fmt */ ... ) \
68 	( BCheckRateLimitReportBadPacket( usecNow ) ? ReallyReportBadUDPPacket( pszFrom, pszMsgType, __VA_ARGS__ ) : (void)0 )
69 
70 #define ReportBadUDPPacketFromConnectionPeer( pszMsgType, /* fmt */ ... ) \
71 	ReportBadUDPPacketFrom( ConnectionDescription(), pszMsgType, __VA_ARGS__ )
72 
73 /////////////////////////////////////////////////////////////////////////////
74 //
75 // Listen socket used for direct IP connectivity
76 //
77 /////////////////////////////////////////////////////////////////////////////
78 
79 class CSteamNetworkListenSocketDirectUDP : public CSteamNetworkListenSocketBase
80 {
81 public:
82 	CSteamNetworkListenSocketDirectUDP( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface );
83 	virtual bool APIGetAddress( SteamNetworkingIPAddr *pAddress ) override;
84 
85 	/// Setup
86 	bool BInit( const SteamNetworkingIPAddr &localAddr, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg );
87 
88 private:
89 	virtual ~CSteamNetworkListenSocketDirectUDP(); // hidden destructor, don't call directly.  Use Destroy()
90 
91 	/// The socket we are bound to.  We own this socket.
92 	/// Any connections accepted through us become clients of this shared socket.
93 	CSharedSocket *m_pSock;
94 
95 	/// Secret used to generate challenges
96 	uint64_t m_argbChallengeSecret[ 2 ];
97 
98 	/// Generate a challenge
99 	uint64 GenerateChallenge( uint16 nTime, const netadr_t &adr ) const;
100 
101 	// Callback to handle a packet when it doesn't match
102 	// any known address
103 	static void ReceivedFromUnknownHost( const RecvPktInfo_t &info, CSteamNetworkListenSocketDirectUDP *pSock );
104 
105 	// Process packets from a source address that does not already correspond to a session
106 	void Received_ChallengeRequest( const CMsgSteamSockets_UDP_ChallengeRequest &msg, const netadr_t &adrFrom, SteamNetworkingMicroseconds usecNow );
107 	void Received_ConnectRequest( const CMsgSteamSockets_UDP_ConnectRequest &msg, const netadr_t &adrFrom, int cbPkt, SteamNetworkingMicroseconds usecNow );
108 	void Received_ConnectionClosed( const CMsgSteamSockets_UDP_ConnectionClosed &msg, const netadr_t &adrFrom, SteamNetworkingMicroseconds usecNow );
109 	void SendMsg( uint8 nMsgID, const google::protobuf::MessageLite &msg, const netadr_t &adrTo );
110 	void SendPaddedMsg( uint8 nMsgID, const google::protobuf::MessageLite &msg, const netadr_t adrTo );
111 };
112 
113 /////////////////////////////////////////////////////////////////////////////
114 //
115 // IP connections
116 //
117 /////////////////////////////////////////////////////////////////////////////
118 
119 class CSteamNetworkConnectionUDP;
120 
121 /// Base class for transports that (might) end up sending packets
122 /// directly on the wire.
123 class CConnectionTransportUDPBase : public CConnectionTransport
124 {
125 public:
126 	CConnectionTransportUDPBase( CSteamNetworkConnectionBase &connection );
127 
128 	// Implements CConnectionTransport
129 	virtual bool SendDataPacket( SteamNetworkingMicroseconds usecNow ) override;
130 	virtual int SendEncryptedDataChunk( const void *pChunk, int cbChunk, SendPacketContext_t &ctx ) override;
131 	virtual void SendEndToEndStatsMsg( EStatsReplyRequest eRequest, SteamNetworkingMicroseconds usecNow, const char *pszReason ) override;
132 	virtual void GetDetailedConnectionStatus( SteamNetworkingDetailedConnectionStatus &stats, SteamNetworkingMicroseconds usecNow ) override;
133 
134 protected:
135 	void Received_Data( const uint8 *pPkt, int cbPkt, SteamNetworkingMicroseconds usecNow );
136 	void Received_ConnectionClosed( const CMsgSteamSockets_UDP_ConnectionClosed &msg, SteamNetworkingMicroseconds usecNow );
137 	void Received_NoConnection( const CMsgSteamSockets_UDP_NoConnection &msg, SteamNetworkingMicroseconds usecNow );
138 
139 	void SendPaddedMsg( uint8 nMsgID, const google::protobuf::MessageLite &msg );
140 	void SendMsg( uint8 nMsgID, const google::protobuf::MessageLite &msg );
141 	void SendConnectionClosedOrNoConnection();
142 	void SendNoConnection( uint32 unFromConnectionID, uint32 unToConnectionID );
143 
144 	virtual bool SendPacket( const void *pkt, int cbPkt ) = 0;
145 	virtual bool SendPacketGather( int nChunks, const iovec *pChunks, int cbSendTotal ) = 0;
146 
147 	/// Process stats message, either inline or standalone
148 	void RecvStats( const CMsgSteamSockets_UDP_Stats &msgStatsIn, SteamNetworkingMicroseconds usecNow );
149 	virtual void TrackSentStats( UDPSendPacketContext_t &ctx );
150 
151 	virtual void RecvValidUDPDataPacket( UDPRecvPacketContext_t &ctx );
152 };
153 
154 
155 /// Actual, ordinary UDP transport
156 class CConnectionTransportUDP final : public CConnectionTransportUDPBase
157 {
158 public:
159 	CConnectionTransportUDP( CSteamNetworkConnectionUDP &connection );
160 
161 	// Implements CConnectionTransport
162 	virtual void TransportFreeResources() override;
163 	virtual bool BCanSendEndToEndConnectRequest() const override;
164 	virtual bool BCanSendEndToEndData() const override;
165 	virtual void SendEndToEndConnectRequest( SteamNetworkingMicroseconds usecNow ) override;
166 	virtual void TransportConnectionStateChanged( ESteamNetworkingConnectionState eOldState ) override;
167 	virtual void TransportPopulateConnectionInfo( SteamNetConnectionInfo_t &info ) const override;
168 
169 	/// Interface used to talk to the remote host
170 	IBoundUDPSocket *m_pSocket;
171 
172 	bool BConnect( const netadr_t &netadrRemote, SteamDatagramErrMsg &errMsg );
173 	bool BAccept( CSharedSocket *pSharedSock, const netadr_t &netadrRemote, SteamDatagramErrMsg &errMsg );
174 
175 	void SendConnectOK( SteamNetworkingMicroseconds usecNow );
176 
177 	static bool CreateLoopbackPair( CConnectionTransportUDP *pTransport[2] );
178 
179 protected:
180 	virtual ~CConnectionTransportUDP(); // Don't call operator delete directly
181 
182 	static void PacketReceived( const RecvPktInfo_t &info, CConnectionTransportUDP *pSelf );
183 
184 	void Received_ChallengeReply( const CMsgSteamSockets_UDP_ChallengeReply &msg, SteamNetworkingMicroseconds usecNow );
185 	void Received_ConnectOK( const CMsgSteamSockets_UDP_ConnectOK &msg, SteamNetworkingMicroseconds usecNow );
186 	void Received_ChallengeOrConnectRequest( const char *pszDebugPacketType, uint32 unPacketConnectionID, SteamNetworkingMicroseconds usecNow );
187 
188 	// Implements CConnectionTransportUDPBase
189 	virtual bool SendPacket( const void *pkt, int cbPkt ) override;
190 	virtual bool SendPacketGather( int nChunks, const iovec *pChunks, int cbSendTotal ) override;
191 };
192 
193 /// A connection over ordinary UDP
194 class CSteamNetworkConnectionUDP : public CSteamNetworkConnectionBase
195 {
196 public:
197 	CSteamNetworkConnectionUDP( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface, ConnectionScopeLock &scopeLock );
198 
199 	/// Convenience wrapper to do the upcast, since we know what sort of
200 	/// listen socket we were connected on.
ListenSocket()201 	inline CSteamNetworkListenSocketDirectUDP *ListenSocket() const { return assert_cast<CSteamNetworkListenSocketDirectUDP *>( m_pParentListenSocket ); }
Transport()202 	inline CConnectionTransportUDP *Transport() const { return assert_cast<CConnectionTransportUDP *>( m_pTransport ); }
203 
204 	/// Implements CSteamNetworkConnectionBase
205 	virtual EResult AcceptConnection( SteamNetworkingMicroseconds usecNow ) override;
206 	virtual void GetConnectionTypeDescription( ConnectionTypeDescription_t &szDescription ) const override;
207 	virtual EUnsignedCert AllowRemoteUnsignedCert() override;
208 	virtual EUnsignedCert AllowLocalUnsignedCert() override;
209 
210 	/// Initiate a connection
211 	bool BInitConnect( const SteamNetworkingIPAddr &addressRemote, int nOptions, const SteamNetworkingConfigValue_t *pOptions, SteamDatagramErrMsg &errMsg );
212 
213 	/// Accept a connection that has passed the handshake phase
214 	bool BBeginAccept(
215 		CSteamNetworkListenSocketDirectUDP *pParent,
216 		const netadr_t &adrFrom,
217 		CSharedSocket *pSharedSock,
218 		const SteamNetworkingIdentity &identityRemote,
219 		uint32 unConnectionIDRemote,
220 		const CMsgSteamDatagramCertificateSigned &msgCert,
221 		const CMsgSteamDatagramSessionCryptInfoSigned &msgSessionInfo,
222 		SteamDatagramErrMsg &errMsg
223 	);
224 protected:
225 	virtual ~CSteamNetworkConnectionUDP(); // hidden destructor, don't call directly.  Use ConnectionQueueDestroy()
226 };
227 
228 /// A connection over loopback
229 class CSteamNetworkConnectionlocalhostLoopback final : public CSteamNetworkConnectionUDP
230 {
231 public:
232 	CSteamNetworkConnectionlocalhostLoopback( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface, const SteamNetworkingIdentity &identity, ConnectionScopeLock &scopeLock );
233 
234 	/// Setup two connections to be talking to each other
235 	static bool APICreateSocketPair( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface, CSteamNetworkConnectionlocalhostLoopback *pConn[2], const SteamNetworkingIdentity pIdentity[2] );
236 
237 	/// Base class overrides
238 	virtual EUnsignedCert AllowRemoteUnsignedCert() override;
239 	virtual EUnsignedCert AllowLocalUnsignedCert() override;
240 };
241 
242 } // namespace SteamNetworkingSocketsLib
243 
244 #endif // STEAMNETWORKINGSOCKETS_UDP_H
245