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