247 lines
8.5 KiB
C++
247 lines
8.5 KiB
C++
//====== 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 _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.)
|
|
#ifndef IS_STEAMDATAGRAMROUTER
|
|
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.
|
|
/// Note: by "string", we don't mean that it's text. Ut's a binary
|
|
/// 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 );
|
|
}
|
|
|
|
};
|
|
#endif
|
|
|
|
/// 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 );
|
|
return nullptr;
|
|
}
|
|
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)
|
|
|
|
#endif // STEAMDATAGRAM_TICKETS_H
|