2019-04-13 18:21:56 +02:00
//====== Copyright Valve Corporation, All rights reserved. ====================
//
// Types and utilities for handling steam datagram tickets. These are
// useful for both the client and the backend ticket generating authority.
//
//=============================================================================
# ifndef STEAMDATAGRAM_TICKETS_H
# define STEAMDATAGRAM_TICKETS_H
# ifdef STEAM_WIN32
# pragma once
# endif
# ifndef assert
# include <assert.h>
# endif
# include <string.h>
# include "steamnetworkingtypes.h"
# if defined( VALVE_CALLBACK_PACK_SMALL )
# pragma pack( push, 4 )
# elif defined( VALVE_CALLBACK_PACK_LARGE )
# pragma pack( push, 8 )
# else
# error "Must define VALVE_CALLBACK_PACK_SMALL or VALVE_CALLBACK_PACK_LARGE"
# endif
/// Max length of serialized auth ticket. This is important so that we
/// can ensure that we always fit into a single UDP datagram (along with
/// other certs and signatures) and keep the implementation simple.
const size_t k_cbSteamDatagramMaxSerializedTicket = 512 ;
/// Network-routable identifier for a service. This is an intentionally
/// opaque byte blob. The relays know how to use this to forward it on
/// to the intended destination, but otherwise clients really should not
/// need to know what's inside. (Indeed, we don't really want them to
/// know, as it could reveal information useful to an attacker.)
struct SteamDatagramHostedAddress
{
// Size of data blob.
int m_cbSize ;
// Opaque data
char m_data [ 128 ] ;
// Reset to empty state
void Clear ( ) { memset ( this , 0 , sizeof ( * this ) ) ; }
// Parse the data center out of the blob.
SteamNetworkingPOPID GetPopID ( ) const { return CalculateSteamNetworkingPOPIDFromString ( m_data ) ; }
/// Set a dummy routing blob with a hardcoded IP:port. You should only use
/// this in a dev environment, since the address is in plaintext!
/// In production this information should come from the server,
/// using ISteamNetworkingSockets::GetHostedDedicatedServerAddress
void SetDevAddress ( uint32 nIP , uint16 nPort , SteamNetworkingPOPID popid = 0 )
{
GetSteamNetworkingLocationPOPStringFromID ( popid , m_data ) ;
m_cbSize = 4 ;
m_data [ m_cbSize + + ] = 1 ;
m_data [ m_cbSize + + ] = 1 ;
m_data [ m_cbSize + + ] = char ( nPort ) ;
m_data [ m_cbSize + + ] = char ( nPort > > 8 ) ;
m_data [ m_cbSize + + ] = char ( nIP ) ;
m_data [ m_cbSize + + ] = char ( nIP > > 8 ) ;
m_data [ m_cbSize + + ] = char ( nIP > > 16 ) ;
m_data [ m_cbSize + + ] = char ( nIP > > 24 ) ;
}
/// Convert to/from std::string (or anything that acts like it).
/// Useful for interfacing with google protobuf. It's a template
/// mainly so that we don't have to include <string> in the header.
2019-07-28 15:02:09 +02:00
/// Note: by "string", we don't mean that it's text. It's a binary
2019-04-13 18:21:56 +02:00
/// blob, and it might have zeros in it. (std::string can handle that.)
template < typename T > bool SetFromStdString ( const T & str )
{
if ( str . length ( ) > = sizeof ( m_data ) )
{
m_cbSize = 0 ;
return false ;
}
m_cbSize = ( int ) str . length ( ) ;
memcpy ( m_data , str . c_str ( ) , m_cbSize ) ;
return true ;
}
template < typename T > void GetAsStdString ( T * str ) const
{
str - > assign ( m_data , m_cbSize ) ;
}
} ;
/// Ticket used to gain access to the relay network.
struct SteamDatagramRelayAuthTicket
{
SteamDatagramRelayAuthTicket ( ) { Clear ( ) ; }
/// Reset all fields
void Clear ( ) { memset ( this , 0 , sizeof ( * this ) ) ; m_nRestrictToVirtualPort = - 1 ; }
/// Identity of the gameserver we want to talk to. This is required.
SteamNetworkingIdentity m_identityGameserver ;
/// Identity of the person who was authorized. This is required.
SteamNetworkingIdentity m_identityAuthorizedClient ;
/// SteamID is authorized to send from a particular public IP. If this
/// is 0, then the sender is not restricted to a particular IP.
///
/// Recommend to leave this set to zero.
uint32 m_unPublicIP ;
/// Time when the ticket expires. Recommended: take the current
/// time and add 6 hours, or maybe a bit longer if your gameplay
/// sessions are longer.
///
/// NOTE: relays may reject tickets with expiry times excessively
/// far in the future, so contact us if you wish to use an expiry
/// longer than, say, 24 hours.
RTime32 m_rtimeTicketExpiry ;
/// Routing information where the gameserver is listening for
/// relayed traffic. You should fill this in when generating
/// a ticket.
///
/// When generating tickets on your backend:
/// - In production: The gameserver knows the proper routing
/// information, so you need to call
/// ISteamNetworkingSockets::GetHostedDedicatedServerAddress
/// and send the info to your backend.
/// - In development, you will need to provide public IP
/// of the server using SteamDatagramServiceNetID::SetDevAddress.
/// Relays need to be able to send UDP
/// packets to this server. Since it's very likely that
/// your server is behind a firewall/NAT, make sure that
/// the address is the one that the outside world can use.
/// The traffic from the relays will be "unsolicited", so
/// stateful firewalls won't work -- you will probably have
/// to set up an explicit port forward.
/// On the client:
/// - this field will always be blank.
SteamDatagramHostedAddress m_routing ;
/// App ID this is for. This is required, and should be the
/// App ID the client is running. (Even if your gameserver
/// uses a different App ID.)
uint32 m_nAppID ;
/// Restrict this ticket to be used for a particular virtual port?
/// Set to -1 to allow any virtual port.
///
/// This is useful as a security measure, and also so the client will
/// use the right ticket (which might have extra fields that are useful
/// for proper analytics), if the client happens to have more than one
/// appropriate ticket.
///
/// Note: if a client has more that one acceptable ticket, they will
/// always use the one expiring the latest.
int m_nRestrictToVirtualPort ;
//
// Extra fields.
//
// These are collected for backend analytics. For example, you might
// send a MatchID so that all of the records for a particular match can
// be located. Or send a game mode field so that you can compare
// the network characteristics of different game modes.
//
// (At the time of this writing we don't have a way to expose the data
// we collect to partners, but we hope to in the future so that you can
// get visibility into network conditions.)
//
struct ExtraField
{
enum EType
{
k_EType_String ,
k_EType_Int , // For most small integral values. Uses google protobuf sint64, so it's small on the wire. WARNING: In some places this value may be transmitted in JSON, in which case precision may be lost in backend analytics. Don't use this for an "identifier", use it for a scalar quantity.
k_EType_Fixed64 , // 64 arbitrary bits. This value is treated as an "identifier". In places where JSON format is used, it will be serialized as a string. No aggregation / analytics can be performed on this value.
} ;
int /* EType */ m_eType ;
char m_szName [ 28 ] ;
union {
char m_szStringValue [ 128 ] ;
int64 m_nIntValue ;
uint64 m_nFixed64Value ;
} ;
} ;
enum { k_nMaxExtraFields = 16 } ;
int m_nExtraFields ;
ExtraField m_vecExtraFields [ k_nMaxExtraFields ] ;
/// Helper to add an extra field in a single call
void AddExtraField_Int ( const char * pszName , int64 val )
{
ExtraField * p = AddExtraField ( pszName , ExtraField : : k_EType_Int ) ;
if ( p )
p - > m_nIntValue = val ;
}
void AddExtraField_Fixed64 ( const char * pszName , uint64 val )
{
ExtraField * p = AddExtraField ( pszName , ExtraField : : k_EType_Fixed64 ) ;
if ( p )
p - > m_nFixed64Value = val ;
}
void AddExtraField_String ( const char * pszName , const char * val )
{
ExtraField * p = AddExtraField ( pszName , ExtraField : : k_EType_String ) ;
if ( p )
{
size_t l = strlen ( val ) ;
if ( l > sizeof ( p - > m_szStringValue ) - 1 )
l = sizeof ( p - > m_szStringValue ) - 1 ;
memcpy ( p - > m_szStringValue , val , l ) ;
p - > m_szStringValue [ l ] = ' \0 ' ;
}
}
private :
ExtraField * AddExtraField ( const char * pszName , ExtraField : : EType eType )
{
if ( m_nExtraFields > = k_nMaxExtraFields )
{
assert ( false ) ;
2019-07-28 15:02:09 +02:00
return NULL ;
2019-04-13 18:21:56 +02:00
}
ExtraField * p = & m_vecExtraFields [ m_nExtraFields + + ] ;
p - > m_eType = eType ;
size_t l = strlen ( pszName ) ;
if ( l > sizeof ( p - > m_szName ) - 1 )
l = sizeof ( p - > m_szName ) - 1 ;
memcpy ( p - > m_szName , pszName , l ) ;
p - > m_szName [ l ] = ' \0 ' ;
return p ;
}
} ;
# pragma pack(pop)
2019-07-28 15:02:09 +02:00
/// Max size of user data blob
const size_t k_cbMaxSteamDatagramGameCoordinatorServerLoginAppData = 2048 ;
/// Max size of serialized data blob
const size_t k_cbMaxSteamDatagramGameCoordinatorServerLoginSerialized = 4096 ;
/// Structure that describes a gameserver attempting to authenticate
/// with your central server allocator / matchmaking service ("game coordinator").
/// This is useful because the game coordinator needs to know:
///
/// - What data center is the gameserver running in?
/// - The routing blob of the gameserver
/// - Is the gameserver actually trusted?
///
/// Using this structure, you can securely communicate this information
/// to your server, and you can do this WITHOUT maintaining any
/// whitelists or tables of IP addresses.
///
/// See ISteamNetworkingSockets::GetGameCoordinatorServerLogin
struct SteamDatagramGameCoordinatorServerLogin
{
/// Server's identity
SteamNetworkingIdentity m_identity ;
/// Routing info. Note that this includes the POPID
SteamDatagramHostedAddress m_routing ;
/// AppID that the server thinks it is running
AppId_t m_nAppID ;
/// Unix timestamp when this was generated
RTime32 m_rtime ;
/// Size of application data
int m_cbAppData ;
/// Application data. This is any additional information
/// that you need to identify the server not contained above.
/// (E.g. perhaps a public IP as seen by the coordinator service.)
char m_appData [ k_cbMaxSteamDatagramGameCoordinatorServerLoginAppData ] ;
} ;
2019-04-13 18:21:56 +02:00
# endif // STEAMDATAGRAM_TICKETS_H