#include <netcode.h>
#include <stdlib.h>
#include <assert.h>
#include <memory.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <math.h>
#include <time.h>
#ifdef _MSC_VER
#define SODIUM_STATIC
#pragma warning(disable:4996)
#endif
#include <sodium.h>
#if defined(__386__) || defined(i386) || defined(__i386__) \
|| defined(__X86) || defined(_M_IX86) \
|| defined(_M_X64) || defined(__x86_64__) \
|| defined(alpha) || defined(__alpha) || defined(__alpha__) \
|| defined(_M_ALPHA) \
|| defined(ARM) || defined(_ARM) || defined(__arm__) \
|| defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
|| defined(_WIN32_WCE) || defined(__NT__) \
|| defined(__MIPSEL__)
#define NETCODE_LITTLE_ENDIAN 1
#else
#define NETCODE_BIG_ENDIAN 1
#endif
#define NETCODE_PLATFORM_WINDOWS 1
#define NETCODE_PLATFORM_MAC 2
#define NETCODE_PLATFORM_UNIX 3
#if defined(_WIN32)
#define NETCODE_PLATFORM NETCODE_PLATFORM_WINDOWS
#elif defined(__APPLE__)
#define NETCODE_PLATFORM NETCODE_PLATFORM_MAC
#else
#define NETCODE_PLATFORM NETCODE_PLATFORM_UNIX
#endif
#define NETCODE_CONNECT_TOKEN_PRIVATE_BYTES 1024
#define NETCODE_CHALLENGE_TOKEN_BYTES 300
#define NETCODE_VERSION_INFO_BYTES 13
#define NETCODE_USER_DATA_BYTES 256
#define NETCODE_MAX_PACKET_BYTES 1220
#define NETCODE_MAX_PAYLOAD_BYTES 1200
#define NETCODE_MAX_ADDRESS_STRING_LENGTH 256
#define NETCODE_PACKET_QUEUE_SIZE 256
#define NETCODE_REPLAY_PROTECTION_BUFFER_SIZE 256
#define NETCODE_CLIENT_MAX_RECEIVE_PACKETS 64
#define NETCODE_SERVER_MAX_RECEIVE_PACKETS ( 64 * NETCODE_MAX_CLIENTS )
#define NETCODE_VERSION_INFO ( (uint8_t*) "NETCODE 1.00" )
#define NETCODE_PACKET_SEND_RATE 10.0
#define NETCODE_TIMEOUT_SECONDS 5.0
#define NETCODE_NUM_DISCONNECT_PACKETS 10
#define NETCODE_ENABLE_LOGGING 1
#ifndef NETCODE_ENABLE_TESTS
#define NETCODE_ENABLE_TESTS 1
#endif
static int log_level = 0;
void netcode_log_level( int level )
{
log_level = level;
}
#if NETCODE_ENABLE_LOGGING
void netcode_printf( int level, const char * format, ... )
{
if ( level > log_level )
return;
va_list args;
va_start( args, format );
vprintf( format, args );
va_end( args );
}
#else
void netcode_printf( int level, const char * format, ... )
{
(void) level;
(void) format;
}
#endif
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
#define NOMINMAX
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>
#pragma comment( lib, "WS2_32.lib" )
#pragma comment( lib, "IPHLPAPI.lib" )
#ifdef SetPort
#undef SetPort
#endif
#include <iphlpapi.h>
#pragma comment( lib, "IPHLPAPI.lib" )
#elif NETCODE_PLATFORM == NETCODE_PLATFORM_MAC || NETCODE_PLATFORM == NETCODE_PLATFORM_UNIX
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <fcntl.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#else
#error netcode.io - unknown platform!
#endif
#define NETCODE_ADDRESS_NONE 0
#define NETCODE_ADDRESS_IPV4 1
#define NETCODE_ADDRESS_IPV6 2
struct netcode_address_t
{
uint8_t type;
union
{
uint8_t ipv4[4];
uint16_t ipv6[8];
} data;
uint16_t port;
};
int netcode_parse_address( const char * address_string_in, struct netcode_address_t * address )
{
assert( address_string_in );
assert( address );
memset( address, 0, sizeof( struct netcode_address_t ) );
#define NETCODE_ADDRESS_BUFFER_SAFETY 32
char buffer[NETCODE_MAX_ADDRESS_STRING_LENGTH + NETCODE_ADDRESS_BUFFER_SAFETY*2];
char * address_string = buffer + NETCODE_ADDRESS_BUFFER_SAFETY;
strncpy( address_string, address_string_in, NETCODE_MAX_ADDRESS_STRING_LENGTH - 1 );
address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH-1] = '\0';
int address_string_length = (int) strlen( address_string );
if ( address_string[0] == '[' )
{
const int base_index = address_string_length - 1;
int i;
for ( i = 0; i < 6; ++i ) {
const int index = base_index - i;
if ( index < 3 )
return 0;
if ( address_string[index] == ':' )
{
address->port = (uint16_t) ( atoi( &address_string[index + 1] ) );
address_string[index-1] = '\0';
}
}
address_string += 1;
}
struct in6_addr sockaddr6;
if ( inet_pton( AF_INET6, address_string, &sockaddr6 ) == 1 )
{
address->type = NETCODE_ADDRESS_IPV6;
int i;
for ( i = 0; i < 8; ++i )
{
address->data.ipv6[i] = ntohs( ( (uint16_t*) &sockaddr6 ) [i] );
}
return 1;
}
address_string_length = (int) strlen( address_string );
const int base_index = address_string_length - 1;
int i;
for ( i = 0; i < 6; ++i )
{
const int index = base_index - i;
if ( index < 0 )
break;
if ( address_string[index] == ':' )
{
address->port = (uint16_t) atoi( &address_string[index+1] );
address_string[index] = '\0';
}
}
struct sockaddr_in sockaddr4;
if ( inet_pton( AF_INET, address_string, &sockaddr4.sin_addr ) == 1 )
{
address->type = NETCODE_ADDRESS_IPV4;
address->data.ipv4[3] = (uint8_t) ( ( sockaddr4.sin_addr.s_addr & 0xFF000000 ) >> 24 );
address->data.ipv4[2] = (uint8_t) ( ( sockaddr4.sin_addr.s_addr & 0x00FF0000 ) >> 16 );
address->data.ipv4[1] = (uint8_t) ( ( sockaddr4.sin_addr.s_addr & 0x0000FF00 ) >> 8 );
address->data.ipv4[0] = (uint8_t) ( ( sockaddr4.sin_addr.s_addr & 0x000000FF ) );
return 1;
}
return 0;
}
char * netcode_address_to_string( struct netcode_address_t * address, char * buffer )
{
assert( address );
assert( buffer );
if ( address->type == NETCODE_ADDRESS_IPV6 )
{
if ( address->port == 0 )
{
uint16_t ipv6_network_order[8];
int i;
for ( i = 0; i < 8; ++i )
ipv6_network_order[i] = htons( address->data.ipv6[i] );
inet_ntop( AF_INET6, (void*) ipv6_network_order, buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH );
return buffer;
}
else
{
char address_string[INET6_ADDRSTRLEN];
uint16_t ipv6_network_order[8];
int i;
for ( i = 0; i < 8; ++i )
ipv6_network_order[i] = htons( address->data.ipv6[i] );
inet_ntop( AF_INET6, (void*) ipv6_network_order, address_string, INET6_ADDRSTRLEN );
snprintf( buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH, "[%s]:%d", address_string, address->port );
return buffer;
}
}
else if ( address->type == NETCODE_ADDRESS_IPV4 )
{
if ( address->port != 0 )
snprintf( buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH, "%d.%d.%d.%d:%d", address->data.ipv4[0], address->data.ipv4[1], address->data.ipv4[2], address->data.ipv4[3], address->port );
else
snprintf( buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH, "%d.%d.%d.%d", address->data.ipv4[0], address->data.ipv4[1], address->data.ipv4[2], address->data.ipv4[3] );
return buffer;
}
else
{
snprintf( buffer, NETCODE_MAX_ADDRESS_STRING_LENGTH, "%s", "NONE" );
return buffer;
}
}
int netcode_address_equal( struct netcode_address_t * a, struct netcode_address_t * b )
{
assert( a );
assert( b );
if ( a->type != b->type )
return 0;
if ( a->port != b->port )
return 0;
if ( a->type == NETCODE_ADDRESS_IPV4 )
{
int i;
for ( i = 0; i < 4; ++i )
{
if ( a->data.ipv4[i] != b->data.ipv4[i] )
return 0;
}
}
else if ( a->type == NETCODE_ADDRESS_IPV6 )
{
int i;
for ( i = 0; i < 8; ++i )
{
if ( a->data.ipv6[i] != b->data.ipv6[i] )
return 0;
}
}
else
{
return 0;
}
return 1;
}
struct netcode_t
{
int initialized;
};
static struct netcode_t netcode;
int netcode_init()
{
assert( !netcode.initialized );
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
WSADATA WsaData;
if ( WSAStartup( MAKEWORD(2,2), &WsaData ) != NO_ERROR )
return 0;
#endif
if ( sodium_init() == -1 )
return 0;
netcode.initialized = 1;
return 1;
}
void netcode_term()
{
assert( netcode.initialized );
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
WSACleanup();
#endif
netcode.initialized = 0;
}
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
typedef uint64_t netcode_socket_handle_t;
#else
typedef int netcode_socket_handle_t;
#endif
struct netcode_socket_t
{
struct netcode_address_t address;
netcode_socket_handle_t handle;
};
#define NETCODE_SOCKET_SNDBUF_SIZE ( 1024 * 1024 )
#define NETCODE_SOCKET_RCVBUF_SIZE ( 1024 * 1024 )
#define NETCODE_SOCKET_ERROR_NONE 0
#define NETCODE_SOCKET_ERROR_CREATE_FAILED 1
#define NETCODE_SOCKET_ERROR_SET_NON_BLOCKING_FAILED 2
#define NETCODE_SOCKET_ERROR_SOCKOPT_IPV6_ONLY_FAILED 3
#define NETCODE_SOCKET_ERROR_SOCKOPT_RCVBUF_FAILED 4
#define NETCODE_SOCKET_ERROR_SOCKOPT_SNDBUF_FAILED 5
#define NETCODE_SOCKET_ERROR_BIND_IPV4_FAILED 6
#define NETCODE_SOCKET_ERROR_BIND_IPV6_FAILED 7
#define NETCODE_SOCKET_ERROR_GET_SOCKNAME_IPV4_FAILED 8
#define NETCODE_SOCKET_ERROR_GET_SOCKNAME_IPV6_FAILED 7
void netcode_socket_destroy( struct netcode_socket_t * socket )
{
assert( socket );
assert( netcode.initialized );
if ( socket->handle != 0 )
{
#if NETCODE_PLATFORM == NETCODE_PLATFORM_MAC || NETCODE_PLATFORM == NETCODE_PLATFORM_UNIX
close( socket->handle );
#elif NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
closesocket( socket->handle );
#else
#error unsupported platform
#endif
socket->handle = 0;
}
}
int netcode_socket_create( struct netcode_socket_t * s, struct netcode_address_t * address, int send_buffer_size, int receive_buffer_size )
{
assert( s );
assert( address );
assert( netcode.initialized );
assert( address->type != NETCODE_ADDRESS_NONE );
s->address = *address;
s->handle = socket( ( address->type == NETCODE_ADDRESS_IPV6 ) ? AF_INET6 : AF_INET, SOCK_DGRAM, IPPROTO_UDP );
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
if ( s->handle == INVALID_SOCKET )
#else
if ( s->handle <= 0 )
#endif {
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to create socket\n" );
return NETCODE_SOCKET_ERROR_CREATE_FAILED;
}
if ( address->type == NETCODE_ADDRESS_IPV6 )
{
int yes = 1;
if ( setsockopt( s->handle, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&yes, sizeof(yes) ) != 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to set socket ipv6 only\n" );
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_SOCKOPT_IPV6_ONLY_FAILED;
}
}
if ( setsockopt( s->handle, SOL_SOCKET, SO_SNDBUF, (char*)&send_buffer_size, sizeof(int) ) != 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to set socket send buffer size\n" );
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_SOCKOPT_SNDBUF_FAILED;
}
if ( setsockopt( s->handle, SOL_SOCKET, SO_RCVBUF, (char*)&receive_buffer_size, sizeof(int) ) != 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to set socket receive buffer size\n" );
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_SOCKOPT_RCVBUF_FAILED;
}
if ( address->type == NETCODE_ADDRESS_IPV6 )
{
struct sockaddr_in6 sock_address;
memset( &sock_address, 0, sizeof( struct sockaddr_in6 ) );
sock_address.sin6_family = AF_INET6;
int i;
for ( i = 0; i < 8; ++i )
{
( (uint16_t*) &sock_address.sin6_addr ) [i] = htons( address->data.ipv6[i] );
}
sock_address.sin6_port = htons( address->port );
if ( bind( s->handle, (struct sockaddr*) &sock_address, sizeof( sock_address ) ) < 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to bind socket (ipv6)\n" );
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_BIND_IPV6_FAILED;
}
}
else
{
struct sockaddr_in sock_address;
sock_address.sin_family = AF_INET;
sock_address.sin_addr.s_addr = ( ( (uint32_t) address->data.ipv4[0] ) << 24 ) | ( ( (uint32_t) address->data.ipv4[1] ) << 16 ) | ( ( (uint32_t) address->data.ipv4[2] ) << 8 ) | ( (uint32_t) address->data.ipv4[3] );
sock_address.sin_port = htons( address->port );
if ( bind( s->handle, (struct sockaddr*) &sock_address, sizeof( sock_address ) ) < 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to bind socket (ipv4)\n" );
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_BIND_IPV4_FAILED;
}
}
if ( address->port == 0 )
{
if ( address->type == NETCODE_ADDRESS_IPV6 )
{
struct sockaddr_in6 sin;
socklen_t len = sizeof( sin );
if ( getsockname( s->handle, (struct sockaddr*)&sin, &len ) == -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to get socket port (ipv6)\n" );
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_GET_SOCKNAME_IPV6_FAILED;
}
s->address.port = ntohs( sin.sin6_port );
}
else
{
struct sockaddr_in sin;
socklen_t len = sizeof( sin );
if ( getsockname( s->handle, (struct sockaddr*)&sin, &len ) == -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to get socket port (ipv4)\n" );
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_GET_SOCKNAME_IPV4_FAILED;
}
s->address.port = ntohs( sin.sin_port );
}
}
#if NETCODE_PLATFORM == NETCODE_PLATFORM_MAC || NETCODE_PLATFORM == NETCODE_PLATFORM_UNIX
int non_blocking = 1;
if ( fcntl( s->handle, F_SETFL, O_NONBLOCK, non_blocking ) == -1 )
{
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_SET_NON_BLOCKING_FAILED;
}
#elif NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
DWORD nonBlocking = 1;
if ( ioctlsocket( s->handle, FIONBIO, &nonBlocking ) != 0 )
{
netcode_socket_destroy( s );
return NETCODE_SOCKET_ERROR_SET_NON_BLOCKING_FAILED;
}
#else
#error unsupported platform
#endif
return NETCODE_SOCKET_ERROR_NONE;
}
void netcode_socket_send_packet( struct netcode_socket_t * socket, struct netcode_address_t * to, void * packet_data, int packet_bytes )
{
assert( socket );
assert( socket->handle != 0 );
assert( to );
assert( to->type == NETCODE_ADDRESS_IPV6 || to->type == NETCODE_ADDRESS_IPV4 );
assert( packet_data );
assert( packet_bytes > 0 );
if ( to->type == NETCODE_ADDRESS_IPV6 )
{
struct sockaddr_in6 socket_address;
memset( &socket_address, 0, sizeof( socket_address ) );
socket_address.sin6_family = AF_INET6;
int i;
for ( i = 0; i < 8; ++i )
{
( (uint16_t*) &socket_address.sin6_addr ) [i] = htons( to->data.ipv6[i] );
}
socket_address.sin6_port = htons( to->port );
sendto( socket->handle, (char*) packet_data, packet_bytes, 0, (struct sockaddr*) &socket_address, sizeof( struct sockaddr_in6 ) );
}
else if ( to->type == NETCODE_ADDRESS_IPV4 )
{
struct sockaddr_in socket_address;
memset( &socket_address, 0, sizeof( socket_address ) );
socket_address.sin_family = AF_INET;
socket_address.sin_addr.s_addr = ( ( (uint32_t) to->data.ipv4[0] ) << 24 ) | ( ( (uint32_t) to->data.ipv4[1] ) << 16 ) | ( ( (uint32_t) to->data.ipv4[2] ) << 8 ) | ( (uint32_t) to->data.ipv4[3] );
socket_address.sin_port = htons( to->port );
sendto( socket->handle, (const char*) packet_data, packet_bytes, 0, (struct sockaddr*) &socket_address, sizeof( struct sockaddr_in ) );
}
}
int netcode_socket_receive_packet( struct netcode_socket_t * socket, struct netcode_address_t * from, void * packet_data, int max_packet_size )
{
assert( socket );
assert( socket->handle != 0 );
assert( from );
assert( packet_data );
assert( max_packet_size > 0 );
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
typedef int socklen_t;
#endif
struct sockaddr_storage sockaddr_from;
socklen_t from_length = sizeof( sockaddr_from );
int result = recvfrom( socket->handle, (char*) packet_data, max_packet_size, 0, (struct sockaddr*) &sockaddr_from, &from_length );
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
if ( result == SOCKET_ERROR )
{
int error = WSAGetLastError();
if ( error == WSAEWOULDBLOCK )
return 0;
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: recvfrom failed with error %d\n", error );
return 0;
}
#else
if ( result <= 0 )
{
if ( errno == EAGAIN )
return 0;
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: recvfrom failed with error %d\n", errno );
return 0;
}
#endif
if ( sockaddr_from.ss_family == AF_INET6 )
{
struct sockaddr_in6 * addr_ipv6 = (struct sockaddr_in6*) &sockaddr_from;
from->type = NETCODE_ADDRESS_IPV6;
int i;
for ( i = 0; i < 8; ++i )
{
from->data.ipv6[i] = ntohs( ( (uint16_t*) &addr_ipv6->sin6_addr ) [i] );
}
from->port = ntohs( addr_ipv6->sin6_port );
}
else if ( sockaddr_from.ss_family == AF_INET )
{
struct sockaddr_in * addr_ipv4 = (struct sockaddr_in*) &sockaddr_from;
from->type = NETCODE_ADDRESS_IPV4;
from->data.ipv4[0] = (uint8_t) ( ( addr_ipv4->sin_addr.s_addr & 0xFF000000 ) >> 24 );
from->data.ipv4[1] = (uint8_t) ( ( addr_ipv4->sin_addr.s_addr & 0x00FF0000 ) >> 16 );
from->data.ipv4[2] = (uint8_t) ( ( addr_ipv4->sin_addr.s_addr & 0x0000FF00 ) >> 8 );
from->data.ipv4[3] = (uint8_t) ( ( addr_ipv4->sin_addr.s_addr & 0x000000FF ) );
from->port = ntohs( addr_ipv4->sin_port );
}
else
{
assert( 0 );
return 0;
}
assert( result >= 0 );
int bytes_read = result;
return bytes_read;
}
void netcode_write_uint8( uint8_t ** p, uint8_t value )
{
**p = value;
++(*p);
}
void netcode_write_uint16( uint8_t ** p, uint16_t value )
{
(*p)[0] = value & 0xFF;
(*p)[1] = value >> 8;
*p += 2;
}
void netcode_write_uint32( uint8_t ** p, uint32_t value )
{
(*p)[0] = value & 0xFF;
(*p)[1] = ( value >> 8 ) & 0xFF;
(*p)[2] = ( value >> 16 ) & 0xFF;
(*p)[3] = value >> 24;
*p += 4;
}
void netcode_write_uint64( uint8_t ** p, uint64_t value )
{
(*p)[0] = value & 0xFF;
(*p)[1] = ( value >> 8 ) & 0xFF;
(*p)[2] = ( value >> 16 ) & 0xFF;
(*p)[3] = ( value >> 24 ) & 0xFF;
(*p)[4] = ( value >> 32 ) & 0xFF;
(*p)[5] = ( value >> 40 ) & 0xFF;
(*p)[6] = ( value >> 48 ) & 0xFF;
(*p)[7] = value >> 56;
*p += 8;
}
void netcode_write_bytes( uint8_t ** p, uint8_t * byte_array, int num_bytes )
{
int i;
for ( i = 0; i < num_bytes; ++i )
{
netcode_write_uint8( p, byte_array[i] );
}
}
uint8_t netcode_read_uint8( uint8_t ** p )
{
uint8_t value = **p;
++(*p);
return value;
}
uint16_t netcode_read_uint16( uint8_t ** p )
{
uint16_t value;
value = (*p)[0];
value |= ( ( (uint16_t)( (*p)[1] ) ) << 8 );
*p += 2;
return value;
}
uint32_t netcode_read_uint32( uint8_t ** p )
{
uint32_t value;
value = (*p)[0];
value |= ( ( (uint32_t)( (*p)[1] ) ) << 8 );
value |= ( ( (uint32_t)( (*p)[2] ) ) << 16 );
value |= ( ( (uint32_t)( (*p)[3] ) ) << 24 );
*p += 4;
return value;
}
uint64_t netcode_read_uint64( uint8_t ** p )
{
uint64_t value;
value = (*p)[0];
value |= ( ( (uint64_t)( (*p)[1] ) ) << 8 );
value |= ( ( (uint64_t)( (*p)[2] ) ) << 16 );
value |= ( ( (uint64_t)( (*p)[3] ) ) << 24 );
value |= ( ( (uint64_t)( (*p)[4] ) ) << 32 );
value |= ( ( (uint64_t)( (*p)[5] ) ) << 40 );
value |= ( ( (uint64_t)( (*p)[6] ) ) << 48 );
value |= ( ( (uint64_t)( (*p)[7] ) ) << 56 );
*p += 8;
return value;
}
void netcode_read_bytes( uint8_t ** p, uint8_t * byte_array, int num_bytes )
{
int i;
for ( i = 0; i < num_bytes; ++i )
{
byte_array[i] = netcode_read_uint8( p );
}
}
#if SODIUM_LIBRARY_VERSION_MAJOR > 7 || ( SODIUM_LIBRARY_VERSION_MAJOR && SODIUM_LIBRARY_VERSION_MINOR >= 3 )
#define SODIUM_SUPPORTS_OVERLAPPING_BUFFERS 1
#endif
void netcode_generate_key( uint8_t * key )
{
assert( key );
randombytes_buf( key, NETCODE_KEY_BYTES );
}
void netcode_random_bytes( uint8_t * data, int bytes )
{
assert( data );
assert( bytes > 0 );
randombytes_buf( data, bytes );
}
int netcode_encrypt_aead( uint8_t * message, uint64_t message_length,
uint8_t * additional, uint64_t additional_length,
uint8_t * nonce,
uint8_t * key )
{
assert( NETCODE_KEY_BYTES == crypto_aead_chacha20poly1305_KEYBYTES );
assert( NETCODE_MAC_BYTES == crypto_aead_chacha20poly1305_ABYTES );
unsigned long long encrypted_length;
#if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS
int result = crypto_aead_chacha20poly1305_encrypt( message, &encrypted_length,
message, (unsigned long long) message_length,
additional, (unsigned long long) additional_length,
NULL, nonce, key );
if ( result != 0 )
return 0;
#else
uint8_t * temp = alloca( message_length + NETCODE_MAC_BYTES );
int result = crypto_aead_chacha20poly1305_encrypt( temp, &encrypted_length,
message, (unsigned long long) message_length,
additional, (unsigned long long) additional_length,
NULL, nonce, key );
if ( result != 0 )
return 0;
memcpy( message, temp, message_length + NETCODE_MAC_BYTES );
#endif
assert( encrypted_length == message_length + NETCODE_MAC_BYTES );
return 1;
}
int netcode_decrypt_aead( uint8_t * message, uint64_t message_length,
uint8_t * additional, uint64_t additional_length,
uint8_t * nonce,
uint8_t * key )
{
assert( NETCODE_KEY_BYTES == crypto_aead_chacha20poly1305_KEYBYTES );
assert( NETCODE_MAC_BYTES == crypto_aead_chacha20poly1305_ABYTES );
unsigned long long decrypted_length;
#if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS
int result = crypto_aead_chacha20poly1305_decrypt( message, &decrypted_length,
NULL,
message, (unsigned long long) message_length,
additional, (unsigned long long) additional_length,
nonce, key );
if ( result != 0 )
return 0;
#else
uint8_t * temp = alloca( message_length );
int result = crypto_aead_chacha20poly1305_decrypt( temp, &decrypted_length,
NULL,
message, (unsigned long long) message_length,
additional, (unsigned long long) additional_length,
nonce, key );
if ( result != 0 )
return 0;
memcpy( message, temp, decrypted_length );
#endif
assert( decrypted_length == message_length - NETCODE_MAC_BYTES );
return 1;
}
struct netcode_connect_token_private_t
{
uint64_t client_id;
int num_server_addresses;
struct netcode_address_t server_addresses[NETCODE_MAX_SERVERS_PER_CONNECT];
uint8_t client_to_server_key[NETCODE_KEY_BYTES];
uint8_t server_to_client_key[NETCODE_KEY_BYTES];
uint8_t user_data[NETCODE_USER_DATA_BYTES];
};
void netcode_generate_connect_token_private( struct netcode_connect_token_private_t * connect_token, uint64_t client_id, int num_server_addresses, struct netcode_address_t * server_addresses, uint8_t * user_data )
{
assert( connect_token );
assert( num_server_addresses > 0 );
assert( num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT );
assert( server_addresses );
assert( user_data );
connect_token->client_id = client_id;
connect_token->num_server_addresses = num_server_addresses;
int i;
for ( i = 0; i < num_server_addresses; ++i )
{
memcpy( &connect_token->server_addresses[i], &server_addresses[i], sizeof( struct netcode_address_t ) );
}
netcode_generate_key( connect_token->client_to_server_key );
netcode_generate_key( connect_token->server_to_client_key );
if ( user_data != NULL )
{
memcpy( connect_token->user_data, user_data, NETCODE_USER_DATA_BYTES );
}
else
{
memset( connect_token->user_data, 0, NETCODE_USER_DATA_BYTES );
}
}
void netcode_write_connect_token_private( struct netcode_connect_token_private_t * connect_token, uint8_t * buffer, int buffer_length )
{
(void) buffer_length;
assert( connect_token );
assert( connect_token->num_server_addresses > 0 );
assert( connect_token->num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT );
assert( buffer );
assert( buffer_length >= NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
memset( buffer, 0, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
uint8_t * start = buffer;
(void) start;
netcode_write_uint64( &buffer, connect_token->client_id );
netcode_write_uint32( &buffer, connect_token->num_server_addresses );
int i,j;
for ( i = 0; i < connect_token->num_server_addresses; ++i )
{
if ( connect_token->server_addresses[i].type == NETCODE_ADDRESS_IPV4 )
{
netcode_write_uint8( &buffer, NETCODE_ADDRESS_IPV4 );
for ( j = 0; j < 4; ++j )
{
netcode_write_uint8( &buffer, connect_token->server_addresses[i].data.ipv4[j] );
}
netcode_write_uint16( &buffer, connect_token->server_addresses[i].port );
}
else if ( connect_token->server_addresses[i].type == NETCODE_ADDRESS_IPV6 )
{
netcode_write_uint8( &buffer, NETCODE_ADDRESS_IPV6 );
for ( j = 0; j < 8; ++j )
{
netcode_write_uint16( &buffer, connect_token->server_addresses[i].data.ipv6[j] );
}
netcode_write_uint16( &buffer, connect_token->server_addresses[i].port );
}
else
{
assert( 0 );
}
}
netcode_write_bytes( &buffer, connect_token->client_to_server_key, NETCODE_KEY_BYTES );
netcode_write_bytes( &buffer, connect_token->server_to_client_key, NETCODE_KEY_BYTES );
netcode_write_bytes( &buffer, connect_token->user_data, NETCODE_USER_DATA_BYTES );
assert( buffer - start <= NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES );
memset( buffer, 0, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - ( buffer - start ) );
}
int netcode_encrypt_connect_token_private( uint8_t * buffer, int buffer_length, uint8_t * version_info, uint64_t protocol_id, uint64_t expire_timestamp, uint64_t sequence, uint8_t * key )
{
assert( buffer );
assert( buffer_length == NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
assert( key );
(void) buffer_length;
uint8_t additional_data[NETCODE_VERSION_INFO_BYTES+8+8];
{
uint8_t * p = additional_data;
netcode_write_bytes( &p, version_info, NETCODE_VERSION_INFO_BYTES );
netcode_write_uint64( &p, protocol_id );
netcode_write_uint64( &p, expire_timestamp );
}
uint8_t nonce[8];
{
uint8_t * p = nonce;
netcode_write_uint64( &p, sequence );
}
if ( !netcode_encrypt_aead( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES, additional_data, sizeof( additional_data ), nonce, key ) )
return 0;
return 1;
}
int netcode_decrypt_connect_token_private( uint8_t * buffer, int buffer_length, uint8_t * version_info, uint64_t protocol_id, uint64_t expire_timestamp, uint64_t sequence, uint8_t * key )
{
assert( buffer );
assert( buffer_length == NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
assert( key );
(void) buffer_length;
uint8_t additional_data[NETCODE_VERSION_INFO_BYTES+8+8];
{
uint8_t * p = additional_data;
netcode_write_bytes( &p, version_info, NETCODE_VERSION_INFO_BYTES );
netcode_write_uint64( &p, protocol_id );
netcode_write_uint64( &p, expire_timestamp );
}
uint8_t nonce[8];
{
uint8_t * p = nonce;
netcode_write_uint64( &p, sequence );
}
if ( !netcode_decrypt_aead( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, additional_data, sizeof( additional_data ), nonce, key ) )
return 0;
return 1;
}
int netcode_read_connect_token_private( uint8_t * buffer, int buffer_length, struct netcode_connect_token_private_t * connect_token )
{
assert( buffer );
assert( connect_token );
if ( buffer_length < NETCODE_CONNECT_TOKEN_PRIVATE_BYTES )
return 0;
connect_token->client_id = netcode_read_uint64( &buffer );
connect_token->num_server_addresses = netcode_read_uint32( &buffer );
if ( connect_token->num_server_addresses <= 0 )
return 0;
if ( connect_token->num_server_addresses > NETCODE_MAX_SERVERS_PER_CONNECT )
return 0;
int i,j;
for ( i = 0; i < connect_token->num_server_addresses; ++i )
{
connect_token->server_addresses[i].type = netcode_read_uint8( &buffer );
if ( connect_token->server_addresses[i].type == NETCODE_ADDRESS_IPV4 )
{
for ( j = 0; j < 4; ++j )
{
connect_token->server_addresses[i].data.ipv4[j] = netcode_read_uint8( &buffer );
}
connect_token->server_addresses[i].port = netcode_read_uint16( &buffer );
}
else if ( connect_token->server_addresses[i].type == NETCODE_ADDRESS_IPV6 )
{
for ( j = 0; j < 8; ++j )
{
connect_token->server_addresses[i].data.ipv6[j] = netcode_read_uint16( &buffer );
}
connect_token->server_addresses[i].port = netcode_read_uint16( &buffer );
}
else
{
return 0;
}
}
netcode_read_bytes( &buffer, connect_token->client_to_server_key, NETCODE_KEY_BYTES );
netcode_read_bytes( &buffer, connect_token->server_to_client_key, NETCODE_KEY_BYTES );
netcode_read_bytes( &buffer, connect_token->user_data, NETCODE_USER_DATA_BYTES );
return 1;
}
struct netcode_challenge_token_t
{
uint64_t client_id;
uint8_t user_data[NETCODE_USER_DATA_BYTES];
};
void netcode_write_challenge_token( struct netcode_challenge_token_t * challenge_token, uint8_t * buffer, int buffer_length )
{
(void) buffer_length;
assert( challenge_token );
assert( buffer );
assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES );
memset( buffer, 0, NETCODE_CHALLENGE_TOKEN_BYTES );
uint8_t * start = buffer;
(void) start;
netcode_write_uint64( &buffer, challenge_token->client_id );
netcode_write_bytes( &buffer, challenge_token->user_data, NETCODE_USER_DATA_BYTES );
assert( buffer - start <= NETCODE_CHALLENGE_TOKEN_BYTES - NETCODE_MAC_BYTES );
}
int netcode_encrypt_challenge_token( uint8_t * buffer, int buffer_length, uint64_t sequence, uint8_t * key )
{
assert( buffer );
assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES );
assert( key );
(void) buffer_length;
uint8_t nonce[8];
{
uint8_t * p = nonce;
netcode_write_uint64( &p, sequence );
}
if ( !netcode_encrypt_aead( buffer, NETCODE_CHALLENGE_TOKEN_BYTES - NETCODE_MAC_BYTES, NULL, 0, nonce, key ) )
return 0;
return 1;
}
int netcode_decrypt_challenge_token( uint8_t * buffer, int buffer_length, uint64_t sequence, uint8_t * key )
{
assert( buffer );
assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES );
assert( key );
(void) buffer_length;
uint8_t nonce[8];
{
uint8_t * p = nonce;
netcode_write_uint64( &p, sequence );
}
if ( !netcode_decrypt_aead( buffer, NETCODE_CHALLENGE_TOKEN_BYTES, NULL, 0, nonce, key ) )
return 0;
return 1;
}
int netcode_read_challenge_token( uint8_t * buffer, int buffer_length, struct netcode_challenge_token_t * challenge_token )
{
assert( buffer );
assert( challenge_token );
if ( buffer_length < NETCODE_CHALLENGE_TOKEN_BYTES )
return 0;
uint8_t * start = buffer;
(void) start;
challenge_token->client_id = netcode_read_uint64( &buffer );
netcode_read_bytes( &buffer, challenge_token->user_data, NETCODE_USER_DATA_BYTES );
assert( buffer - start == 8 + NETCODE_USER_DATA_BYTES );
return 1;
}
#define NETCODE_CONNECTION_REQUEST_PACKET 0
#define NETCODE_CONNECTION_DENIED_PACKET 1
#define NETCODE_CONNECTION_CHALLENGE_PACKET 2
#define NETCODE_CONNECTION_RESPONSE_PACKET 3
#define NETCODE_CONNECTION_KEEP_ALIVE_PACKET 4
#define NETCODE_CONNECTION_PAYLOAD_PACKET 5
#define NETCODE_CONNECTION_DISCONNECT_PACKET 6
#define NETCODE_CONNECTION_NUM_PACKETS 7
struct netcode_connection_request_packet_t
{
uint8_t packet_type;
uint8_t version_info[NETCODE_VERSION_INFO_BYTES];
uint64_t protocol_id;
uint64_t connect_token_expire_timestamp;
uint64_t connect_token_sequence;
uint8_t connect_token_data[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES];
};
struct netcode_connection_denied_packet_t
{
uint8_t packet_type;
};
struct netcode_connection_challenge_packet_t
{
uint8_t packet_type;
uint64_t challenge_token_sequence;
uint8_t challenge_token_data[NETCODE_CHALLENGE_TOKEN_BYTES];
};
struct netcode_connection_response_packet_t
{
uint8_t packet_type;
uint64_t challenge_token_sequence;
uint8_t challenge_token_data[NETCODE_CHALLENGE_TOKEN_BYTES];
};
struct netcode_connection_keep_alive_packet_t
{
uint8_t packet_type;
int client_index;
int max_clients;
};
struct netcode_connection_payload_packet_t
{
uint8_t packet_type;
uint32_t payload_bytes;
uint8_t payload_data[1];
};
struct netcode_connection_disconnect_packet_t
{
uint8_t packet_type;
};
struct netcode_connection_payload_packet_t * netcode_create_payload_packet( int payload_bytes )
{
assert( payload_bytes >= 0 );
assert( payload_bytes <= NETCODE_MAX_PAYLOAD_BYTES );
struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) malloc( sizeof( struct netcode_connection_payload_packet_t ) + payload_bytes );
if ( !packet )
return NULL;
packet->packet_type = NETCODE_CONNECTION_PAYLOAD_PACKET;
packet->payload_bytes = payload_bytes;
return packet;
}
struct netcode_context_t
{
uint8_t write_packet_key[NETCODE_KEY_BYTES];
uint8_t read_packet_key[NETCODE_KEY_BYTES];
};
int netcode_sequence_number_bytes_required( uint64_t sequence )
{
int i;
uint64_t mask = 0xFF00000000000000UL;
for ( i = 0; i < 7; ++i )
{
if ( sequence & mask )
break;
mask >>= 8;
}
return 8 - i;
}
int netcode_write_packet( void * packet, uint8_t * buffer, int buffer_length, uint64_t sequence, uint8_t * write_packet_key, uint64_t protocol_id )
{
assert( packet );
assert( buffer );
assert( write_packet_key );
(void) buffer_length;
uint8_t packet_type = ((uint8_t*)packet)[0];
if ( packet_type == NETCODE_CONNECTION_REQUEST_PACKET )
{
assert( buffer_length >= 1 + 13 + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
struct netcode_connection_request_packet_t * p = (struct netcode_connection_request_packet_t*) packet;
uint8_t * start = buffer;
netcode_write_uint8( &buffer, NETCODE_CONNECTION_REQUEST_PACKET );
netcode_write_bytes( &buffer, p->version_info, NETCODE_VERSION_INFO_BYTES );
netcode_write_uint64( &buffer, p->protocol_id );
netcode_write_uint64( &buffer, p->connect_token_expire_timestamp );
netcode_write_uint64( &buffer, p->connect_token_sequence );
netcode_write_bytes( &buffer, p->connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
assert( buffer - start == 1 + 13 + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
return (int) ( buffer - start );
}
else
{
uint8_t * start = buffer;
uint8_t sequence_bytes = (uint8_t) netcode_sequence_number_bytes_required( sequence );
assert( sequence_bytes >= 1 );
assert( sequence_bytes <= 8 );
assert( packet_type <= 0xF );
uint8_t prefix_byte = packet_type | ( sequence_bytes << 4 );
netcode_write_uint8( &buffer, prefix_byte );
uint64_t sequence_temp = sequence;
int i;
for ( i = 0; i < sequence_bytes; ++i )
{
netcode_write_uint8( &buffer, (uint8_t) ( sequence_temp & 0xFF ) );
sequence_temp >>= 8;
}
uint8_t * encrypted_start = buffer;
switch ( packet_type )
{
case NETCODE_CONNECTION_DENIED_PACKET:
{
}
break;
case NETCODE_CONNECTION_CHALLENGE_PACKET:
{
struct netcode_connection_challenge_packet_t * p = (struct netcode_connection_challenge_packet_t*) packet;
netcode_write_uint64( &buffer, p->challenge_token_sequence );
netcode_write_bytes( &buffer, p->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
}
break;
case NETCODE_CONNECTION_RESPONSE_PACKET:
{
struct netcode_connection_response_packet_t * p = (struct netcode_connection_response_packet_t*) packet;
netcode_write_uint64( &buffer, p->challenge_token_sequence );
netcode_write_bytes( &buffer, p->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
}
break;
case NETCODE_CONNECTION_KEEP_ALIVE_PACKET:
{
struct netcode_connection_keep_alive_packet_t * p = (struct netcode_connection_keep_alive_packet_t*) packet;
netcode_write_uint32( &buffer, p->client_index );
netcode_write_uint32( &buffer, p->max_clients );
}
break;
case NETCODE_CONNECTION_PAYLOAD_PACKET:
{
struct netcode_connection_payload_packet_t * p = (struct netcode_connection_payload_packet_t*) packet;
assert( p->payload_bytes <= NETCODE_MAX_PAYLOAD_BYTES );
netcode_write_bytes( &buffer, p->payload_data, p->payload_bytes );
}
break;
case NETCODE_CONNECTION_DISCONNECT_PACKET:
{
}
break;
default:
assert( 0 );
}
assert( buffer - start <= buffer_length - NETCODE_MAC_BYTES );
uint8_t * encrypted_finish = buffer;
uint8_t additional_data[NETCODE_VERSION_INFO_BYTES+8+1];
{
uint8_t * p = additional_data;
netcode_write_bytes( &p, NETCODE_VERSION_INFO, NETCODE_VERSION_INFO_BYTES );
netcode_write_uint64( &p, protocol_id );
netcode_write_uint8( &p, prefix_byte );
}
uint8_t nonce[8];
{
uint8_t * p = nonce;
netcode_write_uint64( &p, sequence );
}
if ( !netcode_encrypt_aead( encrypted_start, encrypted_finish - encrypted_start, additional_data, sizeof( additional_data ), nonce, write_packet_key ) )
return 0;
buffer += NETCODE_MAC_BYTES;
assert( buffer - start <= buffer_length );
return (int) ( buffer - start );
}
}
struct netcode_replay_protection_t
{
uint64_t most_recent_sequence;
uint64_t received_packet[NETCODE_REPLAY_PROTECTION_BUFFER_SIZE];
};
void netcode_replay_protection_reset( struct netcode_replay_protection_t * replay_protection )
{
assert( replay_protection );
replay_protection->most_recent_sequence = 0;
memset( replay_protection->received_packet, 0xFF, sizeof( replay_protection->received_packet ) );
}
int netcode_replay_protection_packet_already_received( struct netcode_replay_protection_t * replay_protection, uint64_t sequence )
{
assert( replay_protection );
if ( sequence & ( 1ULL << 63 ) )
return 0;
if ( sequence + NETCODE_REPLAY_PROTECTION_BUFFER_SIZE <= replay_protection->most_recent_sequence )
return 1;
if ( sequence > replay_protection->most_recent_sequence )
replay_protection->most_recent_sequence = sequence;
const int index = (int) ( sequence % NETCODE_REPLAY_PROTECTION_BUFFER_SIZE );
if ( replay_protection->received_packet[index] == 0xFFFFFFFFFFFFFFFFLL )
{
replay_protection->received_packet[index] = sequence;
return 0;
}
if ( replay_protection->received_packet[index] >= sequence )
return 1;
replay_protection->received_packet[index] = sequence;
return 0;
}
void * netcode_read_packet( uint8_t * buffer, int buffer_length, uint64_t * sequence, uint8_t * read_packet_key, uint64_t protocol_id, uint64_t current_timestamp, uint8_t * private_key, uint8_t * allowed_packets, struct netcode_replay_protection_t * replay_protection )
{
assert( sequence );
assert( allowed_packets );
*sequence = 0;
if ( buffer_length < 1 )
return NULL;
uint8_t * start = buffer;
uint8_t prefix_byte = netcode_read_uint8( &buffer );
if ( prefix_byte == NETCODE_CONNECTION_REQUEST_PACKET )
{
if ( !allowed_packets[0] )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. packet type is not allowed\n" );
return NULL;
}
if ( buffer_length != 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. bad packet length\n" );
return NULL;
}
if ( !private_key )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. no private key\n" );
return NULL;
}
uint8_t version_info[NETCODE_VERSION_INFO_BYTES];
netcode_read_bytes( &buffer, version_info, NETCODE_VERSION_INFO_BYTES );
if ( version_info[0] != 'N' ||
version_info[1] != 'E' ||
version_info[2] != 'T' ||
version_info[3] != 'C' ||
version_info[4] != 'O' ||
version_info[5] != 'D' ||
version_info[6] != 'E' ||
version_info[7] != ' ' ||
version_info[8] != '1' ||
version_info[9] != '.' ||
version_info[10] != '0' ||
version_info[11] != '0' ||
version_info[12] != '\0' )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. bad version info\n" );
return NULL;
}
uint64_t packet_protocol_id = netcode_read_uint64( &buffer );
if ( packet_protocol_id != protocol_id )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. wrong protocol id\n" );
return NULL;
}
uint64_t packet_connect_token_expire_timestamp = netcode_read_uint64( &buffer );
if ( packet_connect_token_expire_timestamp <= current_timestamp )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. connect token expired\n" );
return NULL;
}
uint64_t packet_connect_token_sequence = netcode_read_uint64( &buffer );
assert( buffer - start == 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 );
if ( !netcode_decrypt_connect_token_private( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, version_info, protocol_id, packet_connect_token_expire_timestamp, packet_connect_token_sequence, private_key ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. connect token failed to decrypt\n" );
return NULL;
}
struct netcode_connection_request_packet_t * packet = (struct netcode_connection_request_packet_t*) malloc( sizeof( struct netcode_connection_request_packet_t ) );
if ( !packet )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection request packet. failed to allocate packet\n" );
return NULL;
}
packet->packet_type = NETCODE_CONNECTION_REQUEST_PACKET;
memcpy( packet->version_info, version_info, NETCODE_VERSION_INFO_BYTES );
packet->protocol_id = packet_protocol_id;
packet->connect_token_expire_timestamp = packet_connect_token_expire_timestamp;
packet->connect_token_sequence = packet_connect_token_sequence;
netcode_read_bytes( &buffer, packet->connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
assert( buffer - start == 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
return packet;
}
else
{
if ( !read_packet_key )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. read packet key is NULL\n" );
return NULL;
}
if ( buffer_length < 1 + 1 + NETCODE_MAC_BYTES )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. packet is too small to be valid\n" );
return NULL;
}
int packet_type = prefix_byte & 0xF;
if ( packet_type >= NETCODE_CONNECTION_NUM_PACKETS )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. packet type %d is invalid\n", packet_type );
return NULL;
}
if ( !allowed_packets[packet_type] )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. packet type %d is not allowed\n", packet_type );
return NULL;
}
int sequence_bytes = prefix_byte >> 4;
if ( sequence_bytes < 1 || sequence_bytes > 8 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. sequence bytes %d is out of range [1,8]\n", sequence_bytes );
return NULL;
}
if ( buffer_length < 1 + sequence_bytes + NETCODE_MAC_BYTES )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. buffer is too small for sequence bytes + encryption mac\n" );
return NULL;
}
int i;
for ( i = 0; i < sequence_bytes; ++i )
{
uint8_t value = netcode_read_uint8( &buffer );
(*sequence) |= ( uint64_t) ( value ) << ( 8 * i );
}
if ( replay_protection && packet_type >= NETCODE_CONNECTION_KEEP_ALIVE_PACKET )
{
if ( netcode_replay_protection_packet_already_received( replay_protection, *sequence ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection payload packet. sequence %.16" PRIx64 " already received (replay protection)\n", *sequence );
return NULL;
}
}
uint8_t additional_data[NETCODE_VERSION_INFO_BYTES+8+1];
{
uint8_t * p = additional_data;
netcode_write_bytes( &p, NETCODE_VERSION_INFO, NETCODE_VERSION_INFO_BYTES );
netcode_write_uint64( &p, protocol_id );
netcode_write_uint8( &p, prefix_byte );
}
uint8_t nonce[8];
{
uint8_t * p = nonce;
netcode_write_uint64( &p, *sequence );
}
int encrypted_bytes = (int) ( buffer_length - ( buffer - start ) );
if ( encrypted_bytes < NETCODE_MAC_BYTES )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. encrypted payload is too small\n" );
return NULL;
}
if ( !netcode_decrypt_aead( buffer, encrypted_bytes, additional_data, sizeof( additional_data ), nonce, read_packet_key ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. failed to decrypt\n" );
return NULL;
}
int decrypted_bytes = encrypted_bytes - NETCODE_MAC_BYTES;
switch ( packet_type )
{
case NETCODE_CONNECTION_DENIED_PACKET:
{
if ( decrypted_bytes != 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection denied packet. decrypted packet data is wrong size\n" );
return NULL;
}
struct netcode_connection_denied_packet_t * packet = (struct netcode_connection_denied_packet_t*) malloc( sizeof( struct netcode_connection_denied_packet_t ) );
if ( !packet )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection denied packet. could not allocate packet struct\n" );
return NULL;
}
packet->packet_type = NETCODE_CONNECTION_DENIED_PACKET;
return packet;
}
break;
case NETCODE_CONNECTION_CHALLENGE_PACKET:
{
if ( decrypted_bytes != 8 + NETCODE_CHALLENGE_TOKEN_BYTES )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection challenge packet. decrypted packet data is wrong size\n" );
return NULL;
}
struct netcode_connection_challenge_packet_t * packet = (struct netcode_connection_challenge_packet_t*) malloc( sizeof( struct netcode_connection_challenge_packet_t ) );
if ( !packet )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection challenge packet. could not allocate packet struct\n" );
return NULL;
}
packet->packet_type = NETCODE_CONNECTION_CHALLENGE_PACKET;
packet->challenge_token_sequence = netcode_read_uint64( &buffer );
netcode_read_bytes( &buffer, packet->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
return packet;
}
break;
case NETCODE_CONNECTION_RESPONSE_PACKET:
{
if ( decrypted_bytes != 8 + NETCODE_CHALLENGE_TOKEN_BYTES )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection response packet. decrypted packet data is wrong size\n" );
return NULL;
}
struct netcode_connection_response_packet_t * packet = (struct netcode_connection_response_packet_t*) malloc( sizeof( struct netcode_connection_response_packet_t ) );
if ( !packet )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection response packet. could not allocate packet struct\n" );
return NULL;
}
packet->packet_type = NETCODE_CONNECTION_RESPONSE_PACKET;
packet->challenge_token_sequence = netcode_read_uint64( &buffer );
netcode_read_bytes( &buffer, packet->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
return packet;
}
break;
case NETCODE_CONNECTION_KEEP_ALIVE_PACKET:
{
if ( decrypted_bytes != 8 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection keep alive packet. decrypted packet data is wrong size\n" );
return NULL;
}
struct netcode_connection_keep_alive_packet_t * packet = (struct netcode_connection_keep_alive_packet_t*) malloc( sizeof( struct netcode_connection_keep_alive_packet_t ) );
if ( !packet )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection keep alive packet. could not allocate packet struct\n" );
return NULL;
}
packet->packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET;
packet->client_index = netcode_read_uint32( &buffer );
packet->max_clients = netcode_read_uint32( &buffer );
return packet;
}
break;
case NETCODE_CONNECTION_PAYLOAD_PACKET:
{
if ( decrypted_bytes < 1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection payload packet. payload is too small\n" );
return NULL;
}
if ( decrypted_bytes > NETCODE_MAX_PAYLOAD_BYTES )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection payload packet. payload is too large\n" );
return NULL;
}
struct netcode_connection_payload_packet_t * packet = netcode_create_payload_packet( decrypted_bytes );
if ( !packet )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection payload packet. could not allocate packet struct\n" );
return NULL;
}
memcpy( packet->payload_data, buffer, decrypted_bytes );
return packet;
}
break;
case NETCODE_CONNECTION_DISCONNECT_PACKET:
{
if ( decrypted_bytes != 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection disconnect packet. decrypted packet data is wrong size\n" );
return NULL;
}
struct netcode_connection_disconnect_packet_t * packet = (struct netcode_connection_disconnect_packet_t*) malloc( sizeof( struct netcode_connection_disconnect_packet_t ) );
if ( !packet )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored connection disconnect packet. could not allocate packet struct\n" );
return NULL;
}
packet->packet_type = NETCODE_CONNECTION_DISCONNECT_PACKET;
return packet;
}
break;
default:
return NULL;
}
}
}
struct netcode_connect_token_t
{
uint8_t version_info[NETCODE_VERSION_INFO_BYTES];
uint64_t protocol_id;
uint64_t create_timestamp;
uint64_t expire_timestamp;
uint64_t sequence;
uint8_t private_data[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES];
int num_server_addresses;
struct netcode_address_t server_addresses[NETCODE_MAX_SERVERS_PER_CONNECT];
uint8_t client_to_server_key[NETCODE_KEY_BYTES];
uint8_t server_to_client_key[NETCODE_KEY_BYTES];
int timeout_seconds;
};
void netcode_write_connect_token( struct netcode_connect_token_t * connect_token, uint8_t * buffer, int buffer_length )
{
assert( connect_token );
assert( buffer );
assert( buffer_length >= NETCODE_CONNECT_TOKEN_BYTES );
uint8_t * start = buffer;
(void) start;
(void) buffer_length;
netcode_write_bytes( &buffer, connect_token->version_info, NETCODE_VERSION_INFO_BYTES );
netcode_write_uint64( &buffer, connect_token->protocol_id );
netcode_write_uint64( &buffer, connect_token->create_timestamp );
netcode_write_uint64( &buffer, connect_token->expire_timestamp );
netcode_write_uint64( &buffer, connect_token->sequence );
netcode_write_bytes( &buffer, connect_token->private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
int i,j;
netcode_write_uint32( &buffer, connect_token->num_server_addresses );
for ( i = 0; i < connect_token->num_server_addresses; ++i )
{
if ( connect_token->server_addresses[i].type == NETCODE_ADDRESS_IPV4 )
{
netcode_write_uint8( &buffer, NETCODE_ADDRESS_IPV4 );
for ( j = 0; j < 4; ++j )
{
netcode_write_uint8( &buffer, connect_token->server_addresses[i].data.ipv4[j] );
}
netcode_write_uint16( &buffer, connect_token->server_addresses[i].port );
}
else if ( connect_token->server_addresses[i].type == NETCODE_ADDRESS_IPV6 )
{
netcode_write_uint8( &buffer, NETCODE_ADDRESS_IPV6 );
for ( j = 0; j < 8; ++j )
{
netcode_write_uint16( &buffer, connect_token->server_addresses[i].data.ipv6[j] );
}
netcode_write_uint16( &buffer, connect_token->server_addresses[i].port );
}
else
{
assert( 0 );
}
}
netcode_write_bytes( &buffer, connect_token->client_to_server_key, NETCODE_KEY_BYTES );
netcode_write_bytes( &buffer, connect_token->server_to_client_key, NETCODE_KEY_BYTES );
netcode_write_uint32( &buffer, connect_token->timeout_seconds );
assert( buffer - start <= NETCODE_CONNECT_TOKEN_BYTES );
memset( buffer, 0, NETCODE_CONNECT_TOKEN_BYTES - ( buffer - start ) );
}
int netcode_read_connect_token( uint8_t * buffer, int buffer_length, struct netcode_connect_token_t * connect_token )
{
assert( buffer );
assert( connect_token );
if ( buffer_length != NETCODE_CONNECT_TOKEN_BYTES )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad buffer length (%d)\n", buffer_length );
return 0;
}
netcode_read_bytes( &buffer, connect_token->version_info, NETCODE_VERSION_INFO_BYTES );
if ( connect_token->version_info[0] != 'N' ||
connect_token->version_info[1] != 'E' ||
connect_token->version_info[2] != 'T' ||
connect_token->version_info[3] != 'C' ||
connect_token->version_info[4] != 'O' ||
connect_token->version_info[5] != 'D' ||
connect_token->version_info[6] != 'E' ||
connect_token->version_info[7] != ' ' ||
connect_token->version_info[8] != '1' ||
connect_token->version_info[9] != '.' ||
connect_token->version_info[10] != '0' ||
connect_token->version_info[11] != '0' ||
connect_token->version_info[12] != '\0' )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad version info\n" );
return 0;
}
connect_token->protocol_id = netcode_read_uint64( &buffer );
connect_token->create_timestamp = netcode_read_uint64( &buffer );
connect_token->expire_timestamp = netcode_read_uint64( &buffer );
if ( connect_token->create_timestamp > connect_token->expire_timestamp )
return 0;
connect_token->sequence = netcode_read_uint64( &buffer );
netcode_read_bytes( &buffer, connect_token->private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
connect_token->num_server_addresses = netcode_read_uint32( &buffer );
if ( connect_token->num_server_addresses <= 0 || connect_token->num_server_addresses > NETCODE_MAX_SERVERS_PER_CONNECT )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad num server addresses (%d)\n", connect_token->num_server_addresses );
return 0;
}
int i,j;
for ( i = 0; i < connect_token->num_server_addresses; ++i )
{
connect_token->server_addresses[i].type = netcode_read_uint8( &buffer );
if ( connect_token->server_addresses[i].type == NETCODE_ADDRESS_IPV4 )
{
for ( j = 0; j < 4; ++j )
{
connect_token->server_addresses[i].data.ipv4[j] = netcode_read_uint8( &buffer );
}
connect_token->server_addresses[i].port = netcode_read_uint16( &buffer );
}
else if ( connect_token->server_addresses[i].type == NETCODE_ADDRESS_IPV6 )
{
for ( j = 0; j < 8; ++j )
{
connect_token->server_addresses[i].data.ipv6[j] = netcode_read_uint16( &buffer );
}
connect_token->server_addresses[i].port = netcode_read_uint16( &buffer );
}
else
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad address type (%d)\n", connect_token->server_addresses[i].type );
return 0;
}
}
netcode_read_bytes( &buffer, connect_token->client_to_server_key, NETCODE_KEY_BYTES );
netcode_read_bytes( &buffer, connect_token->server_to_client_key, NETCODE_KEY_BYTES );
connect_token->timeout_seconds = (int) netcode_read_uint32( &buffer );
return 1;
}
struct netcode_packet_queue_t
{
int num_packets;
int start_index;
void * packet_data[NETCODE_PACKET_QUEUE_SIZE];
uint64_t packet_sequence[NETCODE_PACKET_QUEUE_SIZE];
};
void netcode_packet_queue_init( struct netcode_packet_queue_t * queue )
{
assert( queue );
queue->num_packets = 0;
queue->start_index = 0;
memset( queue->packet_data, 0, sizeof( queue->packet_data ) );
memset( queue->packet_sequence, 0, sizeof( queue->packet_sequence ) );
}
void netcode_packet_queue_clear( struct netcode_packet_queue_t * queue )
{
queue->num_packets = 0;
queue->start_index = 0;
memset( queue->packet_data, 0, sizeof( queue->packet_data ) );
memset( queue->packet_sequence, 0, sizeof( queue->packet_sequence ) );
}
int netcode_packet_queue_push( struct netcode_packet_queue_t * queue, void * packet_data, uint64_t packet_sequence )
{
assert( queue );
assert( packet_data );
if ( queue->num_packets == NETCODE_PACKET_QUEUE_SIZE )
{
free( packet_data );
return 0;
}
int index = ( queue->start_index + queue->num_packets ) % NETCODE_PACKET_QUEUE_SIZE;
queue->packet_data[index] = packet_data;
queue->packet_sequence[index] = packet_sequence;
queue->num_packets++;
return 1;
}
void * netcode_packet_queue_pop( struct netcode_packet_queue_t * queue, uint64_t * packet_sequence )
{
if ( queue->num_packets == 0 )
return NULL;
void * packet = queue->packet_data[queue->start_index];
if ( packet_sequence )
*packet_sequence = queue->packet_sequence[queue->start_index];
queue->start_index = ( queue->start_index + 1 ) % NETCODE_PACKET_QUEUE_SIZE;
queue->num_packets--;
return packet;
}
#define NETCODE_NETWORK_SIMULATOR_NUM_PACKET_ENTRIES ( NETCODE_MAX_CLIENTS * 256 )
#define NETCODE_NETWORK_SIMULATOR_NUM_PENDING_RECEIVE_PACKETS ( NETCODE_MAX_CLIENTS * 64 )
struct netcode_network_simulator_packet_entry_t
{
struct netcode_address_t from;
struct netcode_address_t to;
double delivery_time;
uint8_t * packet_data;
int packet_bytes;
};
struct netcode_network_simulator_t
{
float latency_milliseconds;
float jitter_milliseconds;
float packet_loss_percent;
float duplicate_packet_percent;
double time;
int current_index;
int num_pending_receive_packets;
struct netcode_network_simulator_packet_entry_t packet_entries[NETCODE_NETWORK_SIMULATOR_NUM_PACKET_ENTRIES];
struct netcode_network_simulator_packet_entry_t pending_receive_packets[NETCODE_NETWORK_SIMULATOR_NUM_PENDING_RECEIVE_PACKETS];
};
struct netcode_network_simulator_t * netcode_network_simulator_create()
{
struct netcode_network_simulator_t * network_simulator = (struct netcode_network_simulator_t*) malloc( sizeof( struct netcode_network_simulator_t ) );
assert( network_simulator );
memset( network_simulator, 0, sizeof( struct netcode_network_simulator_t ) );
return network_simulator;
}
void netcode_network_simulator_destroy( struct netcode_network_simulator_t * network_simulator )
{
assert( network_simulator );
free( network_simulator );
}
float netcode_random_float( float a, float b )
{
assert( a < b );
float random = ( (float) rand() ) / (float) RAND_MAX;
float diff = b - a;
float r = random * diff;
return a + r;
}
void netcode_network_simulator_queue_packet( struct netcode_network_simulator_t * network_simulator, struct netcode_address_t * from, struct netcode_address_t * to, uint8_t * packet_data, int packet_bytes, float delay )
{
network_simulator->packet_entries[network_simulator->current_index].from = *from;
network_simulator->packet_entries[network_simulator->current_index].to = *to;
network_simulator->packet_entries[network_simulator->current_index].packet_data = (uint8_t*) malloc( packet_bytes );
memcpy( network_simulator->packet_entries[network_simulator->current_index].packet_data, packet_data, packet_bytes );
network_simulator->packet_entries[network_simulator->current_index].packet_bytes = packet_bytes;
network_simulator->packet_entries[network_simulator->current_index].delivery_time = network_simulator->time + delay;
network_simulator->current_index++;
network_simulator->current_index %= NETCODE_NETWORK_SIMULATOR_NUM_PACKET_ENTRIES;
}
void netcode_network_simulator_send_packet( struct netcode_network_simulator_t * network_simulator, struct netcode_address_t * from, struct netcode_address_t * to, uint8_t * packet_data, int packet_bytes )
{
assert( network_simulator );
assert( from );
assert( from->type != 0 );
assert( to );
assert( to->type != 0 );
assert( packet_data );
assert( packet_bytes > 0 );
assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES );
if ( netcode_random_float( 0.0f, 100.0f ) <= network_simulator->packet_loss_percent )
return;
if ( network_simulator->packet_entries[network_simulator->current_index].packet_data )
{
free( network_simulator->packet_entries[network_simulator->current_index].packet_data );
network_simulator->packet_entries[network_simulator->current_index].packet_data = NULL;
}
float delay = network_simulator->latency_milliseconds / 1000.0f;
if ( network_simulator->jitter_milliseconds > 0.0 )
delay += netcode_random_float( -network_simulator->jitter_milliseconds, +network_simulator->jitter_milliseconds ) / 1000.0f;
netcode_network_simulator_queue_packet( network_simulator, from, to, packet_data, packet_bytes, delay );
if ( netcode_random_float( 0.0f, 100.0f ) <= network_simulator->duplicate_packet_percent )
{
netcode_network_simulator_queue_packet( network_simulator, from, to, packet_data, packet_bytes, delay + netcode_random_float( 0, 1.0 ) );
}
}
int netcode_network_simulator_receive_packets( struct netcode_network_simulator_t * network_simulator, struct netcode_address_t * to, int max_packets, uint8_t ** packet_data, int * packet_bytes, struct netcode_address_t * from )
{
assert( network_simulator );
assert( max_packets >= 0 );
assert( packet_data );
assert( packet_bytes );
assert( from );
assert( to );
int num_packets = 0;
int i;
for ( i = 0; i < network_simulator->num_pending_receive_packets; ++i )
{
if ( num_packets == max_packets )
break;
if ( !network_simulator->pending_receive_packets[i].packet_data )
continue;
if ( !netcode_address_equal( &network_simulator->pending_receive_packets[i].to, to ) )
continue;
packet_data[num_packets] = network_simulator->pending_receive_packets[i].packet_data;
packet_bytes[num_packets] = network_simulator->pending_receive_packets[i].packet_bytes;
from[num_packets] = network_simulator->pending_receive_packets[i].from;
network_simulator->pending_receive_packets[i].packet_data = NULL;
num_packets++;
}
assert( num_packets <= max_packets );
return num_packets;
}
void netcode_network_simulator_update( struct netcode_network_simulator_t * network_simulator, double time )
{
assert( network_simulator );
network_simulator->time = time;
int i;
for ( i = 0; i < network_simulator->num_pending_receive_packets; ++i )
{
if ( network_simulator->pending_receive_packets[i].packet_data )
{
free( network_simulator->pending_receive_packets[i].packet_data );
network_simulator->pending_receive_packets[i].packet_data = NULL;
}
}
network_simulator->num_pending_receive_packets = 0;
for ( i = 0; i < NETCODE_NETWORK_SIMULATOR_NUM_PACKET_ENTRIES; ++i )
{
if ( !network_simulator->packet_entries[i].packet_data )
continue;
if ( network_simulator->num_pending_receive_packets == NETCODE_NETWORK_SIMULATOR_NUM_PENDING_RECEIVE_PACKETS )
break;
if ( network_simulator->packet_entries[i].packet_data && network_simulator->packet_entries[i].delivery_time <= time )
{
network_simulator->pending_receive_packets[network_simulator->num_pending_receive_packets] = network_simulator->packet_entries[i];
network_simulator->num_pending_receive_packets++;
network_simulator->packet_entries[i].packet_data = NULL;
}
}
}
void netcode_network_simulator_discard_packets( struct netcode_network_simulator_t * network_simulator )
{
assert( network_simulator );
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "network simulator discard packets\n" );
int i;
for ( i = 0; i < NETCODE_NETWORK_SIMULATOR_NUM_PACKET_ENTRIES; ++i )
{
free( network_simulator->packet_entries[i].packet_data );
memset( &network_simulator->packet_entries[i], 0, sizeof( struct netcode_network_simulator_packet_entry_t ) );
}
for ( i = 0; i < network_simulator->num_pending_receive_packets; ++i )
{
free( network_simulator->pending_receive_packets[i].packet_data );
memset( &network_simulator->pending_receive_packets[i], 0, sizeof( struct netcode_network_simulator_packet_entry_t ) );
}
network_simulator->current_index = 0;
network_simulator->num_pending_receive_packets = 0;
}
char * netcode_client_state_name( int client_state )
{
switch ( client_state )
{
case NETCODE_CLIENT_STATE_CONNECT_TOKEN_EXPIRED: return "connect token expired";
case NETCODE_CLIENT_STATE_INVALID_CONNECT_TOKEN: return "invalid connect token";
case NETCODE_CLIENT_STATE_CONNECTION_TIMED_OUT: return "connection timed out";
case NETCODE_CLIENT_STATE_CONNECTION_REQUEST_TIMED_OUT: return "connection request timed out";
case NETCODE_CLIENT_STATE_CONNECTION_RESPONSE_TIMED_OUT: return "connection response timed out";
case NETCODE_CLIENT_STATE_CONNECTION_DENIED: return "connection denied";
case NETCODE_CLIENT_STATE_DISCONNECTED: return "disconnected";
case NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST: return "sending connection request";
case NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE: return "sending connection response";
case NETCODE_CLIENT_STATE_CONNECTED: return "connected";
default:
assert( 0 );
return "???";
}
}
struct netcode_client_t
{
int state;
double time;
double connect_start_time;
double last_packet_send_time;
double last_packet_receive_time;
int should_disconnect;
int should_disconnect_state;
uint64_t sequence;
int client_index;
int max_clients;
int server_address_index;
struct netcode_address_t address;
struct netcode_address_t server_address;
struct netcode_connect_token_t connect_token;
struct netcode_network_simulator_t * network_simulator;
struct netcode_socket_t socket;
struct netcode_context_t context;
struct netcode_replay_protection_t replay_protection;
struct netcode_packet_queue_t packet_receive_queue;
uint64_t challenge_token_sequence;
uint8_t challenge_token_data[NETCODE_CHALLENGE_TOKEN_BYTES];
uint8_t * receive_packet_data[NETCODE_CLIENT_MAX_RECEIVE_PACKETS];
int receive_packet_bytes[NETCODE_CLIENT_MAX_RECEIVE_PACKETS];
struct netcode_address_t receive_from[NETCODE_CLIENT_MAX_RECEIVE_PACKETS];
};
struct netcode_client_t * netcode_client_create_internal( char * address_string, double time, struct netcode_network_simulator_t * network_simulator )
{
assert( netcode.initialized );
struct netcode_address_t address;
if ( !netcode_parse_address( address_string, &address ) )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to parse client address\n" );
return NULL;
}
struct netcode_socket_t socket;
memset( &socket, 0, sizeof( struct netcode_socket_t ) );
if ( !network_simulator )
{
if ( netcode_socket_create( &socket, &address, NETCODE_SOCKET_SNDBUF_SIZE, NETCODE_SOCKET_RCVBUF_SIZE ) != NETCODE_SOCKET_ERROR_NONE )
{
return NULL;
}
}
else
{
if ( address.port == 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: client must bind to a specific port when using network simulator\n" );
return NULL;
}
}
struct netcode_client_t * client = (struct netcode_client_t*) malloc( sizeof( struct netcode_client_t ) );
if ( !client )
{
netcode_socket_destroy( &socket );
return NULL;
}
if ( !network_simulator )
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client started on port %d\n", socket.address.port );
}
else
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client started on port %d (network simulator)\n", address.port );
}
client->socket = socket;
client->address = network_simulator ? address : socket.address;
client->network_simulator = network_simulator;
client->state = NETCODE_CLIENT_STATE_DISCONNECTED;
client->time = time;
client->connect_start_time = 0.0;
client->last_packet_send_time = -1000.0;
client->last_packet_receive_time = -1000.0;
client->should_disconnect = 0;
client->should_disconnect_state = NETCODE_CLIENT_STATE_DISCONNECTED;
client->sequence = 0;
client->client_index = 0;
client->max_clients = 0;
client->server_address_index = 0;
client->challenge_token_sequence = 0;
memset( &client->server_address, 0, sizeof( struct netcode_address_t ) );
memset( &client->connect_token, 0, sizeof( struct netcode_connect_token_t ) );
memset( &client->context, 0, sizeof( struct netcode_context_t ) );
memset( client->challenge_token_data, 0, NETCODE_CHALLENGE_TOKEN_BYTES );
netcode_packet_queue_init( &client->packet_receive_queue );
netcode_replay_protection_reset( &client->replay_protection );
return client;
}
struct netcode_client_t * netcode_client_create( char * address_string, double time )
{
return netcode_client_create_internal( address_string, time, NULL );
}
void netcode_client_destroy( struct netcode_client_t * client )
{
assert( client );
netcode_client_disconnect( client );
netcode_socket_destroy( &client->socket );
netcode_packet_queue_clear( &client->packet_receive_queue );
free( client );
}
void netcode_client_set_state( struct netcode_client_t * client, int client_state )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client changed state from '%s' to '%s'\n", netcode_client_state_name( client->state ), netcode_client_state_name( client_state ) );
client->state = client_state;
}
void netcode_client_reset_before_next_connect( struct netcode_client_t * client )
{
client->last_packet_send_time = client->time - 1.0f;
client->last_packet_receive_time = client->time;
client->should_disconnect = 0;
client->should_disconnect_state = NETCODE_CLIENT_STATE_DISCONNECTED;
client->challenge_token_sequence = 0;
memset( client->challenge_token_data, 0, NETCODE_CHALLENGE_TOKEN_BYTES );
netcode_replay_protection_reset( &client->replay_protection );
}
void netcode_client_reset_connection_data( struct netcode_client_t * client, int client_state )
{
assert( client );
client->sequence = 0;
client->client_index = 0;
client->max_clients = 0;
client->connect_start_time = 0.0;
client->server_address_index = 0;
memset( &client->server_address, 0, sizeof( struct netcode_address_t ) );
memset( &client->connect_token, 0, sizeof( struct netcode_connect_token_t ) );
memset( &client->context, 0, sizeof( struct netcode_context_t ) );
netcode_client_set_state( client, client_state );
netcode_client_reset_before_next_connect( client );
while ( 1 )
{
void * packet = netcode_packet_queue_pop( &client->packet_receive_queue, NULL );
if ( !packet )
break;
free( packet );
}
netcode_packet_queue_clear( &client->packet_receive_queue );
}
void netcode_client_disconnect_internal( struct netcode_client_t * client, int destination_state, int send_disconnect_packets );
void netcode_client_connect( struct netcode_client_t * client, uint8_t * connect_token )
{
assert( client );
assert( connect_token );
netcode_client_disconnect( client );
if ( !netcode_read_connect_token( connect_token, NETCODE_CONNECT_TOKEN_BYTES, &client->connect_token ) )
{
netcode_client_set_state( client, NETCODE_CLIENT_STATE_INVALID_CONNECT_TOKEN );
return;
}
client->server_address_index = 0;
client->server_address = client->connect_token.server_addresses[0];
memcpy( client->context.read_packet_key, client->connect_token.server_to_client_key, NETCODE_KEY_BYTES );
memcpy( client->context.write_packet_key, client->connect_token.client_to_server_key, NETCODE_KEY_BYTES );
netcode_client_reset_before_next_connect( client );
netcode_client_set_state( client, NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST );
}
void netcode_client_process_packet( struct netcode_client_t * client, struct netcode_address_t * from, void * packet, uint64_t sequence )
{
assert( client );
assert( packet );
uint8_t packet_type = ( (uint8_t*) packet ) [0];
switch ( packet_type )
{
case NETCODE_CONNECTION_DENIED_PACKET:
{
if ( ( client->state == NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST || client->state == NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE ) && netcode_address_equal( from, &client->server_address ) )
{
client->should_disconnect = 1;
client->should_disconnect_state = NETCODE_CLIENT_STATE_CONNECTION_DENIED;
client->last_packet_receive_time = client->time;
}
}
break;
case NETCODE_CONNECTION_CHALLENGE_PACKET:
{
if ( client->state == NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST && netcode_address_equal( from, &client->server_address ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client received connection challenge packet from server\n" );
struct netcode_connection_challenge_packet_t * p = (struct netcode_connection_challenge_packet_t*) packet;
client->challenge_token_sequence = p->challenge_token_sequence;
memcpy( client->challenge_token_data, p->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
client->last_packet_receive_time = client->time;
netcode_client_set_state( client, NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE );
}
}
break;
case NETCODE_CONNECTION_KEEP_ALIVE_PACKET:
{
if ( netcode_address_equal( from, &client->server_address ) )
{
struct netcode_connection_keep_alive_packet_t * p = (struct netcode_connection_keep_alive_packet_t*) packet;
if ( client->state == NETCODE_CLIENT_STATE_CONNECTED )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client received connection keep alive packet from server\n" );
client->last_packet_receive_time = client->time;
}
else if ( client->state == NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client received connection keep alive packet from server\n" );
client->last_packet_receive_time = client->time;
client->client_index = p->client_index;
client->max_clients = p->max_clients;
netcode_client_set_state( client, NETCODE_CLIENT_STATE_CONNECTED );
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connected to server\n" );
}
}
}
break;
case NETCODE_CONNECTION_PAYLOAD_PACKET:
{
if ( client->state == NETCODE_CLIENT_STATE_CONNECTED && netcode_address_equal( from, &client->server_address ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client received connection payload packet from server\n" );
netcode_packet_queue_push( &client->packet_receive_queue, packet, sequence );
client->last_packet_receive_time = client->time;
return;
}
}
break;
case NETCODE_CONNECTION_DISCONNECT_PACKET:
{
if ( client->state == NETCODE_CLIENT_STATE_CONNECTED && netcode_address_equal( from, &client->server_address ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client received disconnect packet from server\n" );
client->should_disconnect = 1;
client->should_disconnect_state = NETCODE_CLIENT_STATE_DISCONNECTED;
client->last_packet_receive_time = client->time;
}
}
break;
default:
break;
}
free( packet );
}
void netcode_client_receive_packets( struct netcode_client_t * client )
{
assert( client );
uint8_t allowed_packets[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packets, 0, sizeof( allowed_packets ) );
allowed_packets[NETCODE_CONNECTION_DENIED_PACKET] = 1;
allowed_packets[NETCODE_CONNECTION_CHALLENGE_PACKET] = 1;
allowed_packets[NETCODE_CONNECTION_KEEP_ALIVE_PACKET] = 1;
allowed_packets[NETCODE_CONNECTION_PAYLOAD_PACKET] = 1;
allowed_packets[NETCODE_CONNECTION_DISCONNECT_PACKET] = 1;
uint64_t current_timestamp = (uint64_t) time( NULL );
if ( !client->network_simulator )
{
while ( 1 )
{
struct netcode_address_t from;
uint8_t packet_data[NETCODE_MAX_PACKET_BYTES];
int packet_bytes = netcode_socket_receive_packet( &client->socket, &from, packet_data, NETCODE_MAX_PACKET_BYTES );
if ( packet_bytes == 0 )
break;
uint64_t sequence;
void * packet = netcode_read_packet( packet_data, packet_bytes, &sequence, client->context.read_packet_key, client->connect_token.protocol_id, current_timestamp, NULL, allowed_packets, &client->replay_protection );
if ( !packet )
continue;
netcode_client_process_packet( client, &from, packet, sequence );
}
}
else
{
int num_packets_received = netcode_network_simulator_receive_packets( client->network_simulator, &client->address, NETCODE_CLIENT_MAX_RECEIVE_PACKETS, client->receive_packet_data, client->receive_packet_bytes, client->receive_from );
int i;
for ( i = 0; i < num_packets_received; ++i )
{
uint64_t sequence;
void * packet = netcode_read_packet( client->receive_packet_data[i], client->receive_packet_bytes[i], &sequence, client->context.read_packet_key, client->connect_token.protocol_id, current_timestamp, NULL, allowed_packets, &client->replay_protection );
free( client->receive_packet_data[i] );
if ( !packet )
continue;
netcode_client_process_packet( client, &client->receive_from[i], packet, sequence );
}
}
}
void netcode_client_send_packet_to_server_internal( struct netcode_client_t * client, void * packet )
{
assert( client );
uint8_t packet_data[NETCODE_MAX_PACKET_BYTES];
int packet_bytes = netcode_write_packet( packet, packet_data, NETCODE_MAX_PACKET_BYTES, client->sequence, client->context.write_packet_key, client->connect_token.protocol_id );
assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES );
if ( client->network_simulator )
{
netcode_network_simulator_send_packet( client->network_simulator, &client->address, &client->server_address, packet_data, packet_bytes );
}
else
{
netcode_socket_send_packet( &client->socket, &client->server_address, packet_data, packet_bytes );
}
client->last_packet_send_time = client->time;
client->sequence++;
}
void netcode_client_send_packets( struct netcode_client_t * client )
{
assert( client );
switch ( client->state )
{
case NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST:
{
if ( client->last_packet_send_time + ( 1.0 / NETCODE_PACKET_SEND_RATE ) >= client->time )
return;
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client sent connection request packet to server\n" );
struct netcode_connection_request_packet_t packet;
packet.packet_type = NETCODE_CONNECTION_REQUEST_PACKET;
memcpy( packet.version_info, NETCODE_VERSION_INFO, NETCODE_VERSION_INFO_BYTES );
packet.protocol_id = client->connect_token.protocol_id;
packet.connect_token_expire_timestamp = client->connect_token.expire_timestamp;
packet.connect_token_sequence = client->connect_token.sequence;
memcpy( packet.connect_token_data, client->connect_token.private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
netcode_client_send_packet_to_server_internal( client, &packet );
}
break;
case NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE:
{
if ( client->last_packet_send_time + ( 1.0 / NETCODE_PACKET_SEND_RATE ) >= client->time )
return;
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client sent connection response packet to server\n" );
struct netcode_connection_response_packet_t packet;
packet.packet_type = NETCODE_CONNECTION_RESPONSE_PACKET;
packet.challenge_token_sequence = client->challenge_token_sequence;
memcpy( packet.challenge_token_data, client->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
netcode_client_send_packet_to_server_internal( client, &packet );
}
break;
case NETCODE_CLIENT_STATE_CONNECTED:
{
if ( client->last_packet_send_time + ( 1.0 / NETCODE_PACKET_SEND_RATE ) >= client->time )
return;
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client sent connection keep-alive packet to server\n" );
struct netcode_connection_keep_alive_packet_t packet;
packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET;
packet.client_index = 0;
packet.max_clients = 0;
netcode_client_send_packet_to_server_internal( client, &packet );
}
break;
default:
break;
}
}
int netcode_client_connect_to_next_server( struct netcode_client_t * client )
{
assert( client );
if ( client->server_address_index + 1 >= client->connect_token.num_server_addresses )
return 0;
client->server_address_index++;
client->server_address = client->connect_token.server_addresses[client->server_address_index];
netcode_client_reset_before_next_connect( client );
char server_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH];
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client connecting to next server %s (%d/%d)\n", netcode_address_to_string( &client->server_address, server_address_string ), client->server_address_index, client->connect_token.num_server_addresses );
netcode_client_set_state( client, NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST );
return 1;
}
void netcode_client_update( struct netcode_client_t * client, double time )
{
assert( client );
client->time = time;
netcode_client_receive_packets( client );
netcode_client_send_packets( client );
if ( client->state > NETCODE_CLIENT_STATE_DISCONNECTED && client->state < NETCODE_CLIENT_STATE_CONNECTED )
{
uint64_t connect_token_expire_seconds = ( client->connect_token.expire_timestamp - client->connect_token.create_timestamp );
if ( client->connect_start_time + connect_token_expire_seconds <= client->time )
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connect failed. connect token expired\n" );
netcode_client_disconnect_internal( client, NETCODE_CLIENT_STATE_CONNECT_TOKEN_EXPIRED, 0 );
return;
}
}
if ( client->should_disconnect )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client should disconnect -> %s\n", netcode_client_state_name( client->should_disconnect_state ) );
if ( netcode_client_connect_to_next_server( client ) )
return;
netcode_client_disconnect_internal( client, client->should_disconnect_state, 0 );
return;
}
switch ( client->state )
{
case NETCODE_CLIENT_STATE_SENDING_CONNECTION_REQUEST:
{
if ( client->last_packet_receive_time + client->connect_token.timeout_seconds < time )
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connect failed. connection request timed out\n" );
if ( netcode_client_connect_to_next_server( client ) )
return;
netcode_client_disconnect_internal( client, NETCODE_CLIENT_STATE_CONNECTION_REQUEST_TIMED_OUT, 0 );
return;
}
}
break;
case NETCODE_CLIENT_STATE_SENDING_CONNECTION_RESPONSE:
{
if ( client->last_packet_receive_time + client->connect_token.timeout_seconds < time )
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connect failed. connection response timed out\n" );
if ( netcode_client_connect_to_next_server( client ) )
return;
netcode_client_disconnect_internal( client, NETCODE_CLIENT_STATE_CONNECTION_RESPONSE_TIMED_OUT, 0 );
return;
}
}
break;
case NETCODE_CLIENT_STATE_CONNECTED:
{
if ( client->last_packet_receive_time + client->connect_token.timeout_seconds < time )
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connection timed out\n" );
netcode_client_disconnect_internal( client, NETCODE_CLIENT_STATE_CONNECTION_TIMED_OUT, 0 );
return;
}
}
break;
default:
break;
}
}
uint64_t netcode_client_next_send_packet_sequence( struct netcode_client_t * client )
{
assert( client );
return client->sequence;
}
void netcode_client_send_packet( struct netcode_client_t * client, uint8_t * packet_data, int packet_bytes )
{
assert( client );
assert( packet_data );
assert( NETCODE_MAX_PACKET_SIZE == NETCODE_MAX_PAYLOAD_BYTES );
assert( packet_bytes >= 0 );
assert( packet_bytes <= NETCODE_MAX_PACKET_SIZE );
if ( client->state != NETCODE_CLIENT_STATE_CONNECTED )
return;
uint8_t buffer[NETCODE_MAX_PAYLOAD_BYTES*2];
struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) buffer;
packet->packet_type = NETCODE_CONNECTION_PAYLOAD_PACKET;
packet->payload_bytes = packet_bytes;
memcpy( packet->payload_data, packet_data, packet_bytes );
netcode_client_send_packet_to_server_internal( client, packet );
}
void * netcode_client_receive_packet( struct netcode_client_t * client, int * packet_bytes, uint64_t * packet_sequence )
{
assert( client );
assert( packet_bytes );
struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) netcode_packet_queue_pop( &client->packet_receive_queue, packet_sequence );
if ( packet )
{
assert( packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET );
*packet_bytes = packet->payload_bytes;
assert( *packet_bytes >= 0 );
assert( *packet_bytes <= NETCODE_MAX_PACKET_BYTES );
return &packet->payload_data;
}
else
{
return NULL;
}
}
void netcode_client_free_packet( struct netcode_client_t * client, void * packet )
{
assert( client );
assert( packet );
(void) client;
int offset = offsetof( struct netcode_connection_payload_packet_t, payload_data );
free( ( (uint8_t*) packet ) - offset );
}
void netcode_client_disconnect( struct netcode_client_t * client )
{
assert( client );
netcode_client_disconnect_internal( client, NETCODE_CLIENT_STATE_DISCONNECTED, 1 );
}
void netcode_client_disconnect_internal( struct netcode_client_t * client, int destination_state, int send_disconnect_packets )
{
assert( destination_state <= NETCODE_CLIENT_STATE_DISCONNECTED );
if ( client->state <= NETCODE_CLIENT_STATE_DISCONNECTED || client->state == destination_state )
return;
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client disconnected\n" );
if ( send_disconnect_packets && client->state > NETCODE_CLIENT_STATE_DISCONNECTED )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client sent disconnect packets to server\n" );
int i;
for ( i = 0; i < NETCODE_NUM_DISCONNECT_PACKETS; ++i )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client sent disconnect packet %d\n", i );
struct netcode_connection_disconnect_packet_t packet;
packet.packet_type = NETCODE_CONNECTION_DISCONNECT_PACKET;
netcode_client_send_packet_to_server_internal( client, &packet );
}
}
netcode_client_reset_connection_data( client, destination_state );
}
int netcode_client_state( struct netcode_client_t * client )
{
assert( client );
return client->state;
}
int netcode_client_index( struct netcode_client_t * client )
{
assert( client );
return client->client_index;
}
int netcode_client_max_clients( struct netcode_client_t * client )
{
assert( client );
return client->max_clients;
}
#define NETCODE_MAX_ENCRYPTION_MAPPINGS ( NETCODE_MAX_CLIENTS * 4 )
struct netcode_encryption_manager_t
{
int num_encryption_mappings;
double expire_time[NETCODE_MAX_ENCRYPTION_MAPPINGS];
double last_access_time[NETCODE_MAX_ENCRYPTION_MAPPINGS];
struct netcode_address_t address[NETCODE_MAX_ENCRYPTION_MAPPINGS];
uint8_t send_key[NETCODE_KEY_BYTES*NETCODE_MAX_ENCRYPTION_MAPPINGS];
uint8_t receive_key[NETCODE_KEY_BYTES*NETCODE_MAX_ENCRYPTION_MAPPINGS];
};
void netcode_encryption_manager_reset( struct netcode_encryption_manager_t * encryption_manager )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "reset encryption manager\n" );
assert( encryption_manager );
encryption_manager->num_encryption_mappings = 0;
int i;
for ( i = 0; i < NETCODE_MAX_ENCRYPTION_MAPPINGS; ++i )
{
encryption_manager->expire_time[i] = -1.0;
encryption_manager->last_access_time[i] = -1000.0;
memset( &encryption_manager->address[i], 0, sizeof( struct netcode_address_t ) );
}
memset( encryption_manager->send_key, 0, sizeof( encryption_manager->send_key ) );
memset( encryption_manager->receive_key, 0, sizeof( encryption_manager->receive_key ) );
}
int netcode_encryption_manager_add_encryption_mapping( struct netcode_encryption_manager_t * encryption_manager, struct netcode_address_t * address, uint8_t * send_key, uint8_t * receive_key, double time, double expire_time )
{
int i;
for ( i = 0; i < encryption_manager->num_encryption_mappings; ++i )
{
if ( netcode_address_equal( &encryption_manager->address[i], address ) && encryption_manager->last_access_time[i] + NETCODE_TIMEOUT_SECONDS >= time )
{
encryption_manager->expire_time[i] = expire_time;
encryption_manager->last_access_time[i] = time;
memcpy( encryption_manager->send_key + i * NETCODE_KEY_BYTES, send_key, NETCODE_KEY_BYTES );
memcpy( encryption_manager->receive_key + i * NETCODE_KEY_BYTES, receive_key, NETCODE_KEY_BYTES );
return 1;
}
}
for ( i = 0; i < NETCODE_MAX_ENCRYPTION_MAPPINGS; ++i )
{
if ( encryption_manager->last_access_time[i] + NETCODE_TIMEOUT_SECONDS < time || ( encryption_manager->expire_time[i] >= 0.0 && encryption_manager->expire_time[i] < time ) )
{
encryption_manager->address[i] = *address;
encryption_manager->expire_time[i] = expire_time;
encryption_manager->last_access_time[i] = time;
memcpy( encryption_manager->send_key + i * NETCODE_KEY_BYTES, send_key, NETCODE_KEY_BYTES );
memcpy( encryption_manager->receive_key + i * NETCODE_KEY_BYTES, receive_key, NETCODE_KEY_BYTES );
if ( i + 1 > encryption_manager->num_encryption_mappings )
encryption_manager->num_encryption_mappings = i + 1;
return 1;
}
}
return 0;
}
int netcode_encryption_manager_remove_encryption_mapping( struct netcode_encryption_manager_t * encryption_manager, struct netcode_address_t * address, double time )
{
assert( encryption_manager );
assert( address );
int i;
for ( i = 0; i < encryption_manager->num_encryption_mappings; ++i )
{
if ( netcode_address_equal( &encryption_manager->address[i], address ) )
{
encryption_manager->expire_time[i] = -1.0;
encryption_manager->last_access_time[i] = -1000.0;
memset( &encryption_manager->address[i], 0, sizeof( struct netcode_address_t ) );
memset( encryption_manager->send_key + i * NETCODE_KEY_BYTES, 0, NETCODE_KEY_BYTES );
memset( encryption_manager->receive_key + i * NETCODE_KEY_BYTES, 0, NETCODE_KEY_BYTES );
if ( i + 1 == encryption_manager->num_encryption_mappings )
{
int index = i - 1;
while ( index >= 0 )
{
if ( encryption_manager->last_access_time[index] + NETCODE_TIMEOUT_SECONDS >= time && ( encryption_manager->expire_time[index] < 0 || encryption_manager->expire_time[index] > time ) )
break;
index--;
}
encryption_manager->num_encryption_mappings = index + 1;
}
return 1;
}
}
return 0;
}
int netcode_encryption_manager_find_encryption_mapping( struct netcode_encryption_manager_t * encryption_manager, struct netcode_address_t * address, double time )
{
int i;
for ( i = 0; i < encryption_manager->num_encryption_mappings; ++i )
{
if ( netcode_address_equal( &encryption_manager->address[i], address ) && encryption_manager->last_access_time[i] + NETCODE_TIMEOUT_SECONDS >= time && ( encryption_manager->expire_time[i] < 0.0 || encryption_manager->expire_time[i] >= time ) )
{
encryption_manager->last_access_time[i] = time;
return i;
}
}
return -1;
}
int netcode_encryption_manager_touch( struct netcode_encryption_manager_t * encryption_manager, int index, struct netcode_address_t * address, double time )
{
assert( index >= 0 );
assert( index < encryption_manager->num_encryption_mappings );
if ( !netcode_address_equal( &encryption_manager->address[index], address ) )
return 0;
encryption_manager->last_access_time[index] = time;
return 1;
}
void netcode_encryption_manager_set_expire_time( struct netcode_encryption_manager_t * encryption_manager, int index, double expire_time )
{
assert( index >= 0 );
assert( index < encryption_manager->num_encryption_mappings );
encryption_manager->expire_time[index] = expire_time;
}
uint8_t * netcode_encryption_manager_get_send_key( struct netcode_encryption_manager_t * encryption_manager, int index )
{
assert( encryption_manager );
if ( index == -1 )
return NULL;
assert( index >= 0 );
assert( index < encryption_manager->num_encryption_mappings );
return encryption_manager->send_key + index * NETCODE_KEY_BYTES;
}
uint8_t * netcode_encryption_manager_get_receive_key( struct netcode_encryption_manager_t * encryption_manager, int index )
{
assert( encryption_manager );
if ( index == -1 )
return NULL;
assert( index >= 0 );
assert( index < encryption_manager->num_encryption_mappings );
return encryption_manager->receive_key + index * NETCODE_KEY_BYTES;
}
#define NETCODE_MAX_CONNECT_TOKEN_ENTRIES ( NETCODE_MAX_CLIENTS * 8 )
struct netcode_connect_token_entry_t
{
double time;
uint8_t mac[NETCODE_MAC_BYTES];
struct netcode_address_t address;
};
void netcode_connect_token_entries_reset( struct netcode_connect_token_entry_t * connect_token_entries )
{
int i;
for ( i = 0; i < NETCODE_MAX_CONNECT_TOKEN_ENTRIES; ++i )
{
connect_token_entries[i].time = -1000.0;
memset( connect_token_entries[i].mac, 0, NETCODE_MAC_BYTES );
memset( &connect_token_entries[i].address, 0, sizeof( struct netcode_address_t ) );
}
}
int netcode_connect_token_entries_find_or_add( struct netcode_connect_token_entry_t * connect_token_entries, struct netcode_address_t * address, uint8_t * mac, double time )
{
assert( connect_token_entries );
assert( address );
assert( mac );
int matching_token_index = -1;
int oldest_token_index = -1;
double oldest_token_time = 0.0;
int i;
for ( i = 0; i < NETCODE_MAX_CONNECT_TOKEN_ENTRIES; ++i )
{
if ( memcmp( mac, connect_token_entries[i].mac, NETCODE_MAC_BYTES ) == 0 )
matching_token_index = i;
if ( oldest_token_index == -1 || connect_token_entries[i].time < oldest_token_time )
{
oldest_token_time = connect_token_entries[i].time;
oldest_token_index = i;
}
}
assert( oldest_token_index != -1 );
if ( matching_token_index == -1 )
{
connect_token_entries[oldest_token_index].time = time;
connect_token_entries[oldest_token_index].address = *address;
memcpy( connect_token_entries[oldest_token_index].mac, mac, NETCODE_MAC_BYTES );
return 1;
}
assert( matching_token_index >= 0 );
assert( matching_token_index < NETCODE_MAX_CONNECT_TOKEN_ENTRIES );
if ( netcode_address_equal( &connect_token_entries[matching_token_index].address, address ) )
return 1;
return 0;
}
#define NETCODE_SERVER_FLAG_IGNORE_CONNECTION_REQUEST_PACKETS 1
#define NETCODE_SERVER_FLAG_IGNORE_CONNECTION_RESPONSE_PACKETS (1<<1)
struct netcode_server_t
{
struct netcode_socket_t socket;
struct netcode_address_t bind_address;
struct netcode_address_t public_address;
struct netcode_network_simulator_t * network_simulator;
uint32_t flags;
uint64_t protocol_id;
double time;
int running;
int max_clients;
int num_connected_clients;
uint64_t global_sequence;
uint8_t private_key[NETCODE_KEY_BYTES];
uint64_t challenge_sequence;
uint8_t challenge_key[NETCODE_KEY_BYTES];
int client_connected[NETCODE_MAX_CLIENTS];
int client_confirmed[NETCODE_MAX_CLIENTS];
int client_encryption_index[NETCODE_MAX_CLIENTS];
uint64_t client_id[NETCODE_MAX_CLIENTS];
uint64_t client_sequence[NETCODE_MAX_CLIENTS];
double client_last_packet_send_time[NETCODE_MAX_CLIENTS];
double client_last_packet_receive_time[NETCODE_MAX_CLIENTS];
uint8_t client_user_data[NETCODE_MAX_CLIENTS][NETCODE_USER_DATA_BYTES];
struct netcode_replay_protection_t client_replay_protection[NETCODE_MAX_CLIENTS];
struct netcode_packet_queue_t client_packet_queue[NETCODE_MAX_CLIENTS];
struct netcode_address_t client_address[NETCODE_MAX_CLIENTS];
struct netcode_connect_token_entry_t connect_token_entries[NETCODE_MAX_CONNECT_TOKEN_ENTRIES];
struct netcode_encryption_manager_t encryption_manager;
uint8_t * receive_packet_data[NETCODE_SERVER_MAX_RECEIVE_PACKETS];
int receive_packet_bytes[NETCODE_SERVER_MAX_RECEIVE_PACKETS];
struct netcode_address_t receive_from[NETCODE_SERVER_MAX_RECEIVE_PACKETS];
};
struct netcode_server_t * netcode_server_create_internal( char * bind_address_string, char * public_address_string, uint64_t protocol_id, uint8_t * private_key, double time, struct netcode_network_simulator_t * network_simulator )
{
assert( netcode.initialized );
struct netcode_address_t bind_address;
struct netcode_address_t public_address;
if ( !netcode_parse_address( bind_address_string, &bind_address ) )
{
printf( "error: failed to parse server public address\n" );
return NULL;
}
if ( bind_address.port == 0 )
{
printf( "error: server must bind to a specific port\n" );
return NULL;
}
if ( !netcode_parse_address( public_address_string, &public_address ) )
{
printf( "error: failed to parse server public address\n" );
return NULL;
}
struct netcode_socket_t socket;
memset( &socket, 0, sizeof( socket ) );
if ( !network_simulator )
{
if ( netcode_socket_create( &socket, &bind_address, NETCODE_SOCKET_SNDBUF_SIZE, NETCODE_SOCKET_RCVBUF_SIZE ) != NETCODE_SOCKET_ERROR_NONE )
{
return NULL;
}
}
struct netcode_server_t * server = (struct netcode_server_t*) malloc( sizeof( struct netcode_server_t ) );
if ( !server )
{
netcode_socket_destroy( &socket );
return NULL;
}
char server_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH];
if ( !network_simulator )
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server listening on %s\n", netcode_address_to_string( &socket.address, server_address_string ) );
}
else
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server listening on %s (network simulator)\n", netcode_address_to_string( &public_address, server_address_string ) );
}
server->socket = socket;
server->bind_address = bind_address;
server->public_address = public_address;
server->network_simulator = network_simulator;
server->flags = 0;
server->protocol_id = protocol_id;
server->time = time;
server->running = 0;
server->max_clients = 0;
server->num_connected_clients = 0;
server->global_sequence = 1ULL << 63;
memcpy( server->private_key, private_key, NETCODE_KEY_BYTES );
memset( server->client_connected, 0, sizeof( server->client_connected ) );
memset( server->client_confirmed, 0, sizeof( server->client_confirmed ) );
memset( server->client_id, 0, sizeof( server->client_id ) );
memset( server->client_sequence, 0, sizeof( server->client_sequence ) );
memset( server->client_last_packet_send_time, 0, sizeof( server->client_last_packet_send_time ) );
memset( server->client_last_packet_receive_time, 0, sizeof( server->client_last_packet_receive_time ) );
memset( server->client_address, 0, sizeof( server->client_address ) );
memset( server->client_user_data, 0, NETCODE_USER_DATA_BYTES );
int i;
for ( i = 0; i < NETCODE_MAX_CLIENTS; ++i )
server->client_encryption_index[i] = -1;
netcode_connect_token_entries_reset( server->connect_token_entries );
netcode_encryption_manager_reset( &server->encryption_manager );
for ( i = 0; i < NETCODE_MAX_CLIENTS; ++i )
netcode_replay_protection_reset( &server->client_replay_protection[i] );
memset( &server->client_packet_queue, 0, sizeof( server->client_packet_queue ) );
return server;
}
struct netcode_server_t * netcode_server_create( char * bind_address_string, char * public_address_string, uint64_t protocol_id, uint8_t * private_key, double time )
{
return netcode_server_create_internal( bind_address_string, public_address_string, protocol_id, private_key, time, NULL );
}
void netcode_server_stop( struct netcode_server_t * server );
void netcode_server_destroy( struct netcode_server_t * server )
{
assert( server );
netcode_server_stop( server );
netcode_socket_destroy( &server->socket );
free( server );
}
void netcode_server_start( struct netcode_server_t * server, int max_clients )
{
assert( server );
assert( max_clients > 0 );
assert( max_clients <= NETCODE_MAX_CLIENTS );
if ( server->running )
netcode_server_stop( server );
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server started with %d client slots\n", max_clients );
server->running = 1;
server->max_clients = max_clients;
server->num_connected_clients = 0;
server->challenge_sequence = 0;
netcode_generate_key( server->challenge_key );
int i;
for ( i = 0; i < server->max_clients; ++i )
netcode_packet_queue_init( &server->client_packet_queue[i] );
}
void netcode_server_send_global_packet( struct netcode_server_t * server, void * packet, struct netcode_address_t * to, uint8_t * packet_key )
{
assert( server );
assert( packet );
assert( to );
assert( packet_key );
uint8_t packet_data[NETCODE_MAX_PACKET_BYTES];
int packet_bytes = netcode_write_packet( packet, packet_data, NETCODE_MAX_PACKET_BYTES, server->global_sequence, packet_key, server->protocol_id );
assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES );
if ( server->network_simulator )
{
netcode_network_simulator_send_packet( server->network_simulator, &server->public_address, to, packet_data, packet_bytes );
}
else
{
netcode_socket_send_packet( &server->socket, to, packet_data, packet_bytes );
}
server->global_sequence++;
}
void netcode_server_send_client_packet( struct netcode_server_t * server, void * packet, int client_index )
{
assert( server );
assert( packet );
assert( client_index >= 0 );
assert( client_index < server->max_clients );
assert( server->client_connected[client_index] );
uint8_t packet_data[NETCODE_MAX_PACKET_BYTES];
if ( !netcode_encryption_manager_touch( &server->encryption_manager, server->client_encryption_index[client_index], &server->client_address[client_index], server->time ) )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: encryption mapping is out of date for client %d\n", client_index );
return;
}
uint8_t * packet_key = netcode_encryption_manager_get_send_key( &server->encryption_manager, server->client_encryption_index[client_index] );
int packet_bytes = netcode_write_packet( packet, packet_data, NETCODE_MAX_PACKET_BYTES, server->client_sequence[client_index], packet_key, server->protocol_id );
assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES );
if ( server->network_simulator )
{
netcode_network_simulator_send_packet( server->network_simulator, &server->public_address, &server->client_address[client_index], packet_data, packet_bytes );
}
else
{
netcode_socket_send_packet( &server->socket, &server->client_address[client_index], packet_data, packet_bytes );
}
server->client_sequence[client_index]++;
server->client_last_packet_send_time[client_index] = server->time;
}
void netcode_server_disconnect_client_internal( struct netcode_server_t * server, int client_index, int send_disconnect_packets )
{
assert( server );
assert( server->running );
assert( client_index >= 0 );
assert( client_index < server->max_clients );
assert( server->client_connected[client_index] );
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server disconnected client %d\n", client_index );
if ( send_disconnect_packets )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server sent disconnect packets to client %d\n", client_index );
int i;
for ( i = 0; i < NETCODE_NUM_DISCONNECT_PACKETS; ++i )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server sent disconnect packet %d\n", i );
struct netcode_connection_disconnect_packet_t packet;
packet.packet_type = NETCODE_CONNECTION_DISCONNECT_PACKET;
netcode_server_send_client_packet( server, &packet, client_index );
}
}
netcode_replay_protection_reset( &server->client_replay_protection[client_index] );
netcode_encryption_manager_remove_encryption_mapping( &server->encryption_manager, &server->client_address[client_index], server->time );
server->client_connected[client_index] = 0;
server->client_confirmed[client_index] = 0;
server->client_id[client_index] = 0;
server->client_sequence[client_index] = 0;
server->client_last_packet_send_time[client_index] = 0.0;
server->client_last_packet_receive_time[client_index] = 0.0;
memset( &server->client_address[client_index], 0, sizeof( struct netcode_address_t ) );
server->client_encryption_index[client_index] = -1;
memset( server->client_user_data[client_index], 0, NETCODE_USER_DATA_BYTES );
server->num_connected_clients--;
assert( server->num_connected_clients >= 0 );
}
void netcode_server_disconnect_client( struct netcode_server_t * server, int client_index )
{
assert( server );
if ( !server->running )
return;
assert( client_index >= 0 );
assert( client_index < server->max_clients );
if ( !server->client_connected[client_index] )
return;
netcode_server_disconnect_client_internal( server, client_index, 1 );
}
void netcode_server_disconnect_all_clients( struct netcode_server_t * server )
{
assert( server );
if ( !server->running )
return;
int i;
for ( i = 0; i < server->max_clients; ++i )
{
if ( server->client_connected[i] )
netcode_server_disconnect_client_internal( server, i, 1 );
}
}
void netcode_server_stop( struct netcode_server_t * server )
{
assert( server );
if ( !server->running )
return;
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server stopped\n" );
netcode_server_disconnect_all_clients( server );
int i;
for ( i = 0; i < server->max_clients; ++i )
{
while ( 1 )
{
void * packet = netcode_packet_queue_pop( &server->client_packet_queue[i], NULL );
if ( !packet )
break;
free( packet );
}
netcode_packet_queue_clear( &server->client_packet_queue[i] );
netcode_replay_protection_reset( &server->client_replay_protection[i] );
}
server->running = 0;
server->max_clients = 0;
server->num_connected_clients = 0;
server->global_sequence = 0;
server->challenge_sequence = 0;
memset( server->challenge_key, 0, NETCODE_KEY_BYTES );
netcode_connect_token_entries_reset( server->connect_token_entries );
netcode_encryption_manager_reset( &server->encryption_manager );
}
int netcode_server_find_client_index_by_id( struct netcode_server_t * server, uint64_t client_id )
{
assert( server );
int i;
for ( i = 0; i < server->max_clients; ++i )
{
if ( server->client_connected[i] && server->client_id[i] == client_id )
return i;
}
return -1;
}
int netcode_server_find_client_index_by_address( struct netcode_server_t * server, struct netcode_address_t * address )
{
assert( server );
assert( address );
if ( address->type == 0 )
return -1;
int i;
for ( i = 0; i < server->max_clients; ++i )
{
if ( server->client_connected[i] && netcode_address_equal( &server->client_address[i], address ) )
return i;
}
return -1;
}
void netcode_server_process_connection_request_packet( struct netcode_server_t * server, struct netcode_address_t * from, struct netcode_connection_request_packet_t * packet )
{
assert( server );
(void) from;
struct netcode_connect_token_private_t connect_token_private;
if ( !netcode_read_connect_token_private( packet->connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, &connect_token_private ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. failed to read connect token\n" );
return;
}
int found_server_address = 0;
int i;
for ( i = 0; i < connect_token_private.num_server_addresses; ++i )
{
if ( netcode_address_equal( &server->public_address, &connect_token_private.server_addresses[i] ) )
{
found_server_address = 1;
}
}
if ( !found_server_address )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. server address not in connect token whitelist\n" );
return;
}
if ( netcode_server_find_client_index_by_address( server, from ) != -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. a client with this address is already connected\n" );
return;
}
if ( netcode_server_find_client_index_by_id( server, connect_token_private.client_id ) != -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. a client with this id is already connected\n" );
return;
}
if ( !netcode_connect_token_entries_find_or_add( server->connect_token_entries, from, packet->connect_token_data + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES, server->time ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. connect token has already been used\n" );
return;
}
if ( server->num_connected_clients == server->max_clients )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server denied connection request. server is full\n" );
struct netcode_connection_denied_packet_t p;
p.packet_type = NETCODE_CONNECTION_DENIED_PACKET;
netcode_server_send_global_packet( server, &p, from, connect_token_private.server_to_client_key );
return;
}
if ( !netcode_encryption_manager_add_encryption_mapping( &server->encryption_manager, from, connect_token_private.server_to_client_key, connect_token_private.client_to_server_key, server->time, server->time + NETCODE_TIMEOUT_SECONDS ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. failed to add encryption mapping\n" );
return;
}
struct netcode_challenge_token_t challenge_token;
challenge_token.client_id = connect_token_private.client_id;
memcpy( challenge_token.user_data, connect_token_private.user_data, NETCODE_USER_DATA_BYTES );
struct netcode_connection_challenge_packet_t challenge_packet;
challenge_packet.packet_type = NETCODE_CONNECTION_CHALLENGE_PACKET;
challenge_packet.challenge_token_sequence = server->challenge_sequence;
netcode_write_challenge_token( &challenge_token, challenge_packet.challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
if ( !netcode_encrypt_challenge_token( challenge_packet.challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES, server->challenge_sequence, server->challenge_key ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection request. failed to encrypt challenge token\n" );
return;
}
server->challenge_sequence++;
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server sent connection challenge packet\n" );
netcode_server_send_global_packet( server, &challenge_packet, from, connect_token_private.server_to_client_key );
}
int netcode_server_find_free_client_index( struct netcode_server_t * server )
{
assert( server );
int i;
for ( i = 0; i < server->max_clients; ++i )
{
if ( !server->client_connected[i] )
return i;
}
return -1;
}
void netcode_server_connect_client( struct netcode_server_t * server, int client_index, struct netcode_address_t * address, uint64_t client_id, int encryption_index, void * user_data )
{
assert( server );
assert( server->running );
assert( client_index >= 0 );
assert( client_index < server->max_clients );
assert( address );
assert( encryption_index != -1 );
assert( user_data );
server->num_connected_clients++;
assert( server->num_connected_clients <= server->max_clients );
assert( server->client_connected[client_index] == 0 );
netcode_encryption_manager_set_expire_time( &server->encryption_manager, encryption_index, -1.0 );
server->client_connected[client_index] = 1;
server->client_encryption_index[client_index] = encryption_index;
server->client_id[client_index] = client_id;
server->client_sequence[client_index] = 0;
server->client_address[client_index] = *address;
server->client_last_packet_send_time[client_index] = server->time;
server->client_last_packet_receive_time[client_index] = server->time;
memcpy( server->client_user_data[client_index], user_data, NETCODE_USER_DATA_BYTES );
char address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH];
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server accepted client %.16" PRIx64 " %s in slot %d\n", client_id, netcode_address_to_string( address, address_string ), client_index );
struct netcode_connection_keep_alive_packet_t packet;
packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET;
packet.client_index = client_index;
packet.max_clients = server->max_clients;
netcode_server_send_client_packet( server, &packet, client_index );
}
void netcode_server_process_connection_response_packet( struct netcode_server_t * server, struct netcode_address_t * from, struct netcode_connection_response_packet_t * packet, int encryption_index )
{
assert( server );
if ( !netcode_decrypt_challenge_token( packet->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES, packet->challenge_token_sequence, server->challenge_key ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection response. failed to decrypt challenge token\n" );
return;
}
struct netcode_challenge_token_t challenge_token;
if ( !netcode_read_challenge_token( packet->challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES, &challenge_token ) )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection response. failed to read challenge token\n" );
return;
}
uint8_t * packet_send_key = netcode_encryption_manager_get_send_key( &server->encryption_manager, encryption_index );
if ( !packet_send_key )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection response. no packet send key\n" );
return;
}
if ( netcode_server_find_client_index_by_address( server, from ) != -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection response. a client with this address is already connected\n" );
return;
}
if ( netcode_server_find_client_index_by_id( server, challenge_token.client_id ) != -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server ignored connection response. a client with this id is already connected\n" );
return;
}
if ( server->num_connected_clients == server->max_clients )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server denied connection response. server is full\n" );
struct netcode_connection_denied_packet_t p;
p.packet_type = NETCODE_CONNECTION_DENIED_PACKET;
netcode_server_send_global_packet( server, &p, from, packet_send_key );
return;
}
int client_index = netcode_server_find_free_client_index( server );
assert( client_index != -1 );
netcode_server_connect_client( server, client_index, from, challenge_token.client_id, encryption_index, challenge_token.user_data );
}
void netcode_server_process_packet( struct netcode_server_t * server, struct netcode_address_t * from, void * packet, uint64_t sequence, int encryption_index, int client_index )
{
assert( server );
assert( packet );
(void) from;
(void) sequence;
uint8_t packet_type = ( (uint8_t*) packet ) [0];
switch ( packet_type )
{
case NETCODE_CONNECTION_REQUEST_PACKET:
{
if ( ( server->flags & NETCODE_SERVER_FLAG_IGNORE_CONNECTION_REQUEST_PACKETS ) == 0 )
{
char from_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH];
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received connection request from %s\n", netcode_address_to_string( from, from_address_string ) );
netcode_server_process_connection_request_packet( server, from, (struct netcode_connection_request_packet_t*) packet );
}
}
break;
case NETCODE_CONNECTION_RESPONSE_PACKET:
{
if ( ( server->flags & NETCODE_SERVER_FLAG_IGNORE_CONNECTION_RESPONSE_PACKETS ) == 0 )
{
char from_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH];
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received connection response from %s\n", netcode_address_to_string( from, from_address_string ) );
netcode_server_process_connection_response_packet( server, from, (struct netcode_connection_response_packet_t*) packet, encryption_index );
}
}
break;
case NETCODE_CONNECTION_KEEP_ALIVE_PACKET:
{
if ( client_index != -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received connection keep alive packet from client %d\n", client_index );
server->client_last_packet_receive_time[client_index] = server->time;
if ( !server->client_confirmed[client_index] )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server confirmed connection to client %d\n", client_index );
server->client_confirmed[client_index] = 1;
}
}
}
break;
case NETCODE_CONNECTION_PAYLOAD_PACKET:
{
if ( client_index != -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received connection payload packet from client %d\n", client_index );
server->client_last_packet_receive_time[client_index] = server->time;
if ( !server->client_confirmed[client_index] )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server confirmed connection to client %d\n", client_index );
server->client_confirmed[client_index] = 1;
}
netcode_packet_queue_push( &server->client_packet_queue[client_index], packet, sequence );
return;
}
}
break;
case NETCODE_CONNECTION_DISCONNECT_PACKET:
{
if ( client_index != -1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server received disconnect packet from client %d\n", client_index );
netcode_server_disconnect_client_internal( server, client_index, 0 );
return;
}
}
break;
default:
break;
}
free( packet );
}
void netcode_server_read_and_process_packet( struct netcode_server_t * server, struct netcode_address_t * from, uint8_t * packet_data, int packet_bytes, uint64_t current_timestamp, uint8_t * allowed_packets )
{
if ( !server->running )
return;
uint64_t sequence;
int encryption_index = -1;
int client_index = netcode_server_find_client_index_by_address( server, from );
if ( client_index != -1 )
{
assert( client_index >= 0 );
assert( client_index < server->max_clients );
encryption_index = server->client_encryption_index[client_index];
}
else
{
encryption_index = netcode_encryption_manager_find_encryption_mapping( &server->encryption_manager, from, server->time );
}
uint8_t * read_packet_key = netcode_encryption_manager_get_receive_key( &server->encryption_manager, encryption_index );
void * packet = netcode_read_packet( packet_data, packet_bytes, &sequence, read_packet_key, server->protocol_id, current_timestamp, server->private_key, allowed_packets, &server->client_replay_protection[client_index] );
if ( !packet )
return;
netcode_server_process_packet( server, from, packet, sequence, encryption_index, client_index );
}
void netcode_server_receive_packets( struct netcode_server_t * server )
{
assert( server );
uint8_t allowed_packets[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packets, 0, sizeof( allowed_packets ) );
allowed_packets[NETCODE_CONNECTION_REQUEST_PACKET] = 1;
allowed_packets[NETCODE_CONNECTION_RESPONSE_PACKET] = 1;
allowed_packets[NETCODE_CONNECTION_KEEP_ALIVE_PACKET] = 1;
allowed_packets[NETCODE_CONNECTION_PAYLOAD_PACKET] = 1;
allowed_packets[NETCODE_CONNECTION_DISCONNECT_PACKET] = 1;
uint64_t current_timestamp = (uint64_t) time( NULL );
if ( !server->network_simulator )
{
while ( 1 )
{
struct netcode_address_t from;
uint8_t packet_data[NETCODE_MAX_PACKET_BYTES];
int packet_bytes = netcode_socket_receive_packet( &server->socket, &from, packet_data, NETCODE_MAX_PACKET_BYTES );
if ( packet_bytes == 0 )
break;
netcode_server_read_and_process_packet( server, &from, packet_data, packet_bytes, current_timestamp, allowed_packets );
}
}
else
{
int num_packets_received = netcode_network_simulator_receive_packets( server->network_simulator, &server->public_address, NETCODE_SERVER_MAX_RECEIVE_PACKETS, server->receive_packet_data, server->receive_packet_bytes, server->receive_from );
int i;
for ( i = 0; i < num_packets_received; ++i )
{
netcode_server_read_and_process_packet( server, &server->receive_from[i], server->receive_packet_data[i], server->receive_packet_bytes[i], current_timestamp, allowed_packets );
free( server->receive_packet_data[i] );
}
}
}
void netcode_server_send_packets( struct netcode_server_t * server )
{
assert( server );
if ( !server->running )
return;
int i;
for ( i = 0; i < server->max_clients; ++i )
{
if ( server->client_connected[i] && server->client_last_packet_send_time[i] + ( 1.0 / NETCODE_PACKET_SEND_RATE ) <= server->time )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "server sent connection keep alive packet to client %d\n", i );
struct netcode_connection_keep_alive_packet_t packet;
packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET;
packet.client_index = i;
packet.max_clients = server->max_clients;
netcode_server_send_client_packet( server, &packet, i );
}
}
}
void netcode_server_check_for_timeouts( struct netcode_server_t * server )
{
assert( server );
if ( !server->running )
return;
int i;
for ( i = 0; i < server->max_clients; ++i )
{
if ( server->client_connected[i] && ( server->client_last_packet_receive_time[i] + NETCODE_TIMEOUT_SECONDS <= server->time ) )
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server timed out client %d\n", i );
netcode_server_disconnect_client_internal( server, i, 0 );
return;
}
}
}
int netcode_server_client_connected( struct netcode_server_t * server, int client_index )
{
assert( server );
if ( !server->running )
return 0;
if ( client_index < 0 || client_index >= server->max_clients )
return 0;
return server->client_connected[client_index];
}
uint64_t netcode_server_client_id( struct netcode_server_t * server, int client_index )
{
assert( server );
if ( !server->running )
return 0;
if ( client_index < 0 || client_index >= server->max_clients )
return 0;
return server->client_id[client_index];
}
uint64_t netcode_server_next_packet_sequence( struct netcode_server_t * server, int client_index )
{
assert( client_index >= 0 );
assert( client_index < server->max_clients );
if ( !server->client_connected[client_index] )
return 0;
return server->client_sequence[client_index];
}
void netcode_server_send_packet( struct netcode_server_t * server, int client_index, uint8_t * packet_data, int packet_bytes )
{
assert( server );
assert( packet_data );
assert( NETCODE_MAX_PACKET_SIZE == NETCODE_MAX_PAYLOAD_BYTES );
assert( packet_bytes >= 0 );
assert( packet_bytes <= NETCODE_MAX_PACKET_SIZE );
if ( !server->running )
return;
assert( client_index >= 0 );
assert( client_index < server->max_clients );
if ( !server->client_connected[client_index] )
return;
uint8_t buffer[NETCODE_MAX_PAYLOAD_BYTES*2];
struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) buffer;
packet->packet_type = NETCODE_CONNECTION_PAYLOAD_PACKET;
packet->payload_bytes = packet_bytes;
memcpy( packet->payload_data, packet_data, packet_bytes );
if ( !server->client_confirmed[client_index] )
{
struct netcode_connection_keep_alive_packet_t keep_alive_packet;
keep_alive_packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET;
keep_alive_packet.client_index = client_index;
keep_alive_packet.max_clients = server->max_clients;
netcode_server_send_client_packet( server, &keep_alive_packet, client_index );
}
netcode_server_send_client_packet( server, packet, client_index );
}
void * netcode_server_receive_packet( struct netcode_server_t * server, int client_index, int * packet_bytes, uint64_t * packet_sequence )
{
assert( server );
assert( packet_bytes );
if ( !server->running )
return NULL;
assert( client_index >= 0 );
assert( client_index < server->max_clients );
struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*) netcode_packet_queue_pop( &server->client_packet_queue[client_index], packet_sequence );
if ( packet )
{
assert( packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET );
*packet_bytes = packet->payload_bytes;
assert( *packet_bytes >= 0 );
assert( *packet_bytes <= NETCODE_MAX_PACKET_BYTES );
return &packet->payload_data;
}
else
{
return NULL;
}
}
void netcode_server_free_packet( struct netcode_server_t * server, void * packet )
{
assert( server );
assert( packet );
(void) server;
int offset = offsetof( struct netcode_connection_payload_packet_t, payload_data );
free( ( (uint8_t*) packet ) - offset );
}
int netcode_server_num_clients_connected( struct netcode_server_t * server )
{
assert( server );
return server->num_connected_clients;
}
void * netcode_server_client_user_data( struct netcode_server_t * server, int client_index )
{
assert( server );
assert( client_index >= 0 );
assert( client_index < server->max_clients );
return server->client_user_data[client_index];
}
void netcode_server_update( struct netcode_server_t * server, double time )
{
assert( server );
server->time = time;
netcode_server_receive_packets( server );
netcode_server_send_packets( server );
netcode_server_check_for_timeouts( server );
}
int netcode_generate_connect_token( int num_server_addresses, char ** server_addresses, int expire_seconds, uint64_t client_id, uint64_t protocol_id, uint64_t sequence, uint8_t * private_key, uint8_t * output_buffer )
{
assert( num_server_addresses > 0 );
assert( num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT );
assert( server_addresses );
assert( private_key );
assert( output_buffer );
struct netcode_address_t parsed_server_addresses[NETCODE_MAX_SERVERS_PER_CONNECT];
int i;
for ( i = 0; i < num_server_addresses; ++i )
{
if ( !netcode_parse_address( server_addresses[i], &parsed_server_addresses[i] ) )
return 0;
}
uint8_t user_data[NETCODE_USER_DATA_BYTES];
netcode_random_bytes( user_data, NETCODE_USER_DATA_BYTES );
struct netcode_connect_token_private_t connect_token_private;
netcode_generate_connect_token_private( &connect_token_private, client_id, num_server_addresses, parsed_server_addresses, user_data );
uint8_t connect_token_data[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES];
netcode_write_connect_token_private( &connect_token_private, connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
uint64_t create_timestamp = time( NULL );
uint64_t expire_timestamp = create_timestamp + expire_seconds;
if ( !netcode_encrypt_connect_token_private( connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, protocol_id, expire_timestamp, sequence, private_key ) )
return 0;
struct netcode_connect_token_t connect_token;
memcpy( connect_token.version_info, NETCODE_VERSION_INFO, NETCODE_VERSION_INFO_BYTES );
connect_token.protocol_id = protocol_id;
connect_token.create_timestamp = create_timestamp;
connect_token.expire_timestamp = expire_timestamp;
connect_token.sequence = sequence;
memcpy( connect_token.private_data, connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
connect_token.num_server_addresses = num_server_addresses;
for ( i = 0; i < num_server_addresses; ++i )
connect_token.server_addresses[i] = parsed_server_addresses[i];
memcpy( connect_token.client_to_server_key, connect_token_private.client_to_server_key, NETCODE_KEY_BYTES );
memcpy( connect_token.server_to_client_key, connect_token_private.server_to_client_key, NETCODE_KEY_BYTES );
connect_token.timeout_seconds = (int) NETCODE_TIMEOUT_SECONDS;
netcode_write_connect_token( &connect_token, output_buffer, NETCODE_CONNECT_TOKEN_BYTES );
return 1;
}
#if __APPLE__
#include <unistd.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
void netcode_sleep( double time )
{
usleep( (int) ( time * 1000000 ) );
}
static uint64_t start = 0;
static mach_timebase_info_data_t timebase_info;
double netcode_time()
{
if ( start == 0 )
{
mach_timebase_info( &timebase_info );
start = mach_absolute_time();
return 0.0;
}
uint64_t current = mach_absolute_time();
return ( (double) ( current - start ) ) * ( (double) timebase_info.numer ) / ( (double) timebase_info.denom ) / 1000000000.0;
}
#elif __linux
#include <unistd.h>
void netcode_sleep( double time )
{
usleep( (int) ( time * 1000000 ) );
}
double netcode_time()
{
static double start = -1;
if ( start == -1 )
{
struct timespec ts;
clock_gettime( CLOCK_MONOTONIC_RAW, &ts );
start = ts.tv_sec + ( (double) ( ts.tv_nsec ) ) / 1000000000.0;
return 0.0;
}
struct timespec ts;
clock_gettime( CLOCK_MONOTONIC_RAW, &ts );
double current = ts.tv_sec + ( (double) ( ts.tv_nsec ) ) / 1000000000.0;
return current - start;
}
#elif defined( _WIN32 )
#define NOMINMAX
#include <windows.h>
void netcode_sleep( double time )
{
const int milliseconds = (int) ( time * 1000 );
Sleep( milliseconds );
}
static int timer_initialized = 0;
static LARGE_INTEGER timer_frequency;
static LARGE_INTEGER timer_start;
double netcode_time()
{
if ( !timer_initialized )
{
QueryPerformanceFrequency( &timer_frequency );
QueryPerformanceCounter( &timer_start );
timer_initialized = 1;
}
LARGE_INTEGER now;
QueryPerformanceCounter( &now );
return ( (double) ( now.QuadPart - timer_start.QuadPart ) ) / ( (double) ( timer_frequency.QuadPart ) );
}
#else
#error unsupported platform!
#endif
#if NETCODE_ENABLE_TESTS
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <time.h>
static void check_handler( char * condition,
char * function,
char * file,
int line )
{
printf( "check failed: ( %s ), function %s, file %s, line %d\n", condition, function, file, line );
#ifndef NDEBUG
#if defined( __GNUC__ )
__builtin_trap();
#elif defined( _MSC_VER )
__debugbreak();
#endif
#endif
exit( 1 );
}
#define check( condition ) \
do \
{ \
if ( !(condition) ) \
{ \
check_handler( #condition, (char*) __FUNCTION__, (char*) __FILE__, __LINE__ ); \
} \
} while(0)
static void test_queue()
{
struct netcode_packet_queue_t queue;
netcode_packet_queue_init( &queue );
check( queue.num_packets == 0 );
check( queue.start_index == 0 );
check( netcode_packet_queue_pop( &queue, NULL ) == NULL );
{
#define NUM_PACKETS 100
void * packets[NUM_PACKETS];
int i;
for ( i = 0; i < NUM_PACKETS; ++i )
{
packets[i] = malloc( (i+1) * 256 );
check( netcode_packet_queue_push( &queue, packets[i], (uint64_t) i ) == 1 );
}
check( queue.num_packets == NUM_PACKETS );
for ( i = 0; i < NUM_PACKETS; ++i )
{
uint64_t sequence;
void * packet = netcode_packet_queue_pop( &queue, &sequence );
check( sequence == (uint64_t) i ) ;
check( packet == packets[i] );
free( packet );
}
}
check( queue.num_packets == 0 );
check( netcode_packet_queue_pop( &queue, NULL ) == NULL );
void * packets[NETCODE_PACKET_QUEUE_SIZE];
int i;
for ( i = 0; i < NETCODE_PACKET_QUEUE_SIZE; ++i )
{
packets[i] = malloc( i * 256 );
check( netcode_packet_queue_push( &queue, packets[i], (uint64_t) i ) == 1 );
}
check( queue.num_packets == NETCODE_PACKET_QUEUE_SIZE );
check( netcode_packet_queue_push( &queue, malloc( 100 ), 0 ) == 0 );
for ( i = 0; i < NETCODE_PACKET_QUEUE_SIZE; ++i )
{
uint64_t sequence;
void * packet = netcode_packet_queue_pop( &queue, &sequence );
check( sequence == (uint64_t) i );
check( packet == packets[i] );
free( packet );
}
for ( i = 0; i < NETCODE_PACKET_QUEUE_SIZE; ++i )
{
packets[i] = malloc( i * 256 );
check( netcode_packet_queue_push( &queue, packets[i], (uint64_t) i ) == 1 );
}
netcode_packet_queue_clear( &queue );
check( queue.start_index == 0 );
check( queue.num_packets == 0 );
for ( i = 0; i < NETCODE_PACKET_QUEUE_SIZE; ++i )
check( queue.packet_data[i] == NULL );
}
static void test_endian()
{
uint32_t value = 0x11223344;
char * bytes = (char*) &value;
#if NETCODE_LITTLE_ENDIAN
check( bytes[0] == 0x44 );
check( bytes[1] == 0x33 );
check( bytes[2] == 0x22 );
check( bytes[3] == 0x11 );
#else
check( bytes[3] == 0x44 );
check( bytes[2] == 0x33 );
check( bytes[1] == 0x22 );
check( bytes[0] == 0x11 );
#endif }
static void test_sequence()
{
check( netcode_sequence_number_bytes_required( 0 ) == 1 );
check( netcode_sequence_number_bytes_required( 0x11 ) == 1 );
check( netcode_sequence_number_bytes_required( 0x1122 ) == 2 );
check( netcode_sequence_number_bytes_required( 0x112233 ) == 3 );
check( netcode_sequence_number_bytes_required( 0x11223344 ) == 4 );
check( netcode_sequence_number_bytes_required( 0x1122334455 ) == 5 );
check( netcode_sequence_number_bytes_required( 0x112233445566 ) == 6 );
check( netcode_sequence_number_bytes_required( 0x11223344556677 ) == 7 );
check( netcode_sequence_number_bytes_required( 0x1122334455667788 ) == 8 );
}
static void test_address()
{
{
struct netcode_address_t address;
check( netcode_parse_address( "", &address ) == 0 );
check( netcode_parse_address( "[", &address ) == 0 );
check( netcode_parse_address( "[]", &address ) == 0 );
check( netcode_parse_address( "[]:", &address ) == 0 );
check( netcode_parse_address( ":", &address ) == 0 );
check( netcode_parse_address( "1", &address ) == 0 );
check( netcode_parse_address( "12", &address ) == 0 );
check( netcode_parse_address( "123", &address ) == 0 );
check( netcode_parse_address( "1234", &address ) == 0 );
check( netcode_parse_address( "1234.0.12313.0000", &address ) == 0 );
check( netcode_parse_address( "1234.0.12313.0000.0.0.0.0.0", &address ) == 0 );
check( netcode_parse_address( "1312313:123131:1312313:123131:1312313:123131:1312313:123131:1312313:123131:1312313:123131", &address ) == 0 );
check( netcode_parse_address( ".", &address ) == 0 );
check( netcode_parse_address( "..", &address ) == 0 );
check( netcode_parse_address( "...", &address ) == 0 );
check( netcode_parse_address( "....", &address ) == 0 );
check( netcode_parse_address( ".....", &address ) == 0 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "107.77.207.77", &address ) );
check( address.type == NETCODE_ADDRESS_IPV4 );
check( address.port == 0 );
check( address.data.ipv4[0] == 107 );
check( address.data.ipv4[1] == 77 );
check( address.data.ipv4[2] == 207 );
check( address.data.ipv4[3] == 77 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "127.0.0.1", &address ) );
check( address.type == NETCODE_ADDRESS_IPV4 );
check( address.port == 0 );
check( address.data.ipv4[0] == 127 );
check( address.data.ipv4[1] == 0 );
check( address.data.ipv4[2] == 0 );
check( address.data.ipv4[3] == 1 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "107.77.207.77:40000", &address ) );
check( address.type == NETCODE_ADDRESS_IPV4 );
check( address.port == 40000 );
check( address.data.ipv4[0] == 107 );
check( address.data.ipv4[1] == 77 );
check( address.data.ipv4[2] == 207 );
check( address.data.ipv4[3] == 77 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "127.0.0.1:40000", &address ) );
check( address.type == NETCODE_ADDRESS_IPV4 );
check( address.port == 40000 );
check( address.data.ipv4[0] == 127 );
check( address.data.ipv4[1] == 0 );
check( address.data.ipv4[2] == 0 );
check( address.data.ipv4[3] == 1 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "fe80::202:b3ff:fe1e:8329", &address ) );
check( address.type == NETCODE_ADDRESS_IPV6 );
check( address.port == 0 );
check( address.data.ipv6[0] == 0xfe80 );
check( address.data.ipv6[1] == 0x0000 );
check( address.data.ipv6[2] == 0x0000 );
check( address.data.ipv6[3] == 0x0000 );
check( address.data.ipv6[4] == 0x0202 );
check( address.data.ipv6[5] == 0xb3ff );
check( address.data.ipv6[6] == 0xfe1e );
check( address.data.ipv6[7] == 0x8329 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "::", &address ) );
check( address.type == NETCODE_ADDRESS_IPV6 );
check( address.port == 0 );
check( address.data.ipv6[0] == 0x0000 );
check( address.data.ipv6[1] == 0x0000 );
check( address.data.ipv6[2] == 0x0000 );
check( address.data.ipv6[3] == 0x0000 );
check( address.data.ipv6[4] == 0x0000 );
check( address.data.ipv6[5] == 0x0000 );
check( address.data.ipv6[6] == 0x0000 );
check( address.data.ipv6[7] == 0x0000 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "::1", &address ) );
check( address.type == NETCODE_ADDRESS_IPV6 );
check( address.port == 0 );
check( address.data.ipv6[0] == 0x0000 );
check( address.data.ipv6[1] == 0x0000 );
check( address.data.ipv6[2] == 0x0000 );
check( address.data.ipv6[3] == 0x0000 );
check( address.data.ipv6[4] == 0x0000 );
check( address.data.ipv6[5] == 0x0000 );
check( address.data.ipv6[6] == 0x0000 );
check( address.data.ipv6[7] == 0x0001 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "[fe80::202:b3ff:fe1e:8329]:40000", &address ) );
check( address.type == NETCODE_ADDRESS_IPV6 );
check( address.port == 40000 );
check( address.data.ipv6[0] == 0xfe80 );
check( address.data.ipv6[1] == 0x0000 );
check( address.data.ipv6[2] == 0x0000 );
check( address.data.ipv6[3] == 0x0000 );
check( address.data.ipv6[4] == 0x0202 );
check( address.data.ipv6[5] == 0xb3ff );
check( address.data.ipv6[6] == 0xfe1e );
check( address.data.ipv6[7] == 0x8329 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "[::]:40000", &address ) );
check( address.type == NETCODE_ADDRESS_IPV6 );
check( address.port == 40000 );
check( address.data.ipv6[0] == 0x0000 );
check( address.data.ipv6[1] == 0x0000 );
check( address.data.ipv6[2] == 0x0000 );
check( address.data.ipv6[3] == 0x0000 );
check( address.data.ipv6[4] == 0x0000 );
check( address.data.ipv6[5] == 0x0000 );
check( address.data.ipv6[6] == 0x0000 );
check( address.data.ipv6[7] == 0x0000 );
}
{
struct netcode_address_t address;
check( netcode_parse_address( "[::1]:40000", &address ) );
check( address.type == NETCODE_ADDRESS_IPV6 );
check( address.port == 40000 );
check( address.data.ipv6[0] == 0x0000 );
check( address.data.ipv6[1] == 0x0000 );
check( address.data.ipv6[2] == 0x0000 );
check( address.data.ipv6[3] == 0x0000 );
check( address.data.ipv6[4] == 0x0000 );
check( address.data.ipv6[5] == 0x0000 );
check( address.data.ipv6[6] == 0x0000 );
check( address.data.ipv6[7] == 0x0001 );
}
}
#define TEST_PROTOCOL_ID 0x1122334455667788ULL
#define TEST_CLIENT_ID 0x1ULL
#define TEST_SERVER_PORT 40000
#define TEST_CONNECT_TOKEN_EXPIRY 30
static void test_connect_token()
{
struct netcode_address_t server_address;
server_address.type = NETCODE_ADDRESS_IPV4;
server_address.data.ipv4[0] = 127;
server_address.data.ipv4[1] = 0;
server_address.data.ipv4[2] = 0;
server_address.data.ipv4[3] = 1;
server_address.port = TEST_SERVER_PORT;
uint8_t user_data[NETCODE_USER_DATA_BYTES];
netcode_random_bytes( user_data, NETCODE_USER_DATA_BYTES );
struct netcode_connect_token_private_t input_token;
netcode_generate_connect_token_private( &input_token, TEST_CLIENT_ID, 1, &server_address, user_data );
check( input_token.client_id == TEST_CLIENT_ID );
check( input_token.num_server_addresses == 1 );
check( memcmp( input_token.user_data, user_data, NETCODE_USER_DATA_BYTES ) == 0 );
check( netcode_address_equal( &input_token.server_addresses[0], &server_address ) );
uint8_t buffer[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES];
netcode_write_connect_token_private( &input_token, buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
uint64_t sequence = 1000;
uint64_t expire_timestamp = time( NULL ) + 30;
uint8_t key[NETCODE_KEY_BYTES];
netcode_generate_key( key );
check( netcode_encrypt_connect_token_private( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, TEST_PROTOCOL_ID, expire_timestamp, sequence, key ) == 1 );
check( netcode_decrypt_connect_token_private( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, TEST_PROTOCOL_ID, expire_timestamp, sequence, key ) == 1 );
struct netcode_connect_token_private_t output_token;
check( netcode_read_connect_token_private( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, &output_token ) == 1 );
check( output_token.client_id == input_token.client_id );
check( output_token.num_server_addresses == input_token.num_server_addresses );
check( netcode_address_equal( &output_token.server_addresses[0], &input_token.server_addresses[0] ) );
check( memcmp( output_token.client_to_server_key, input_token.client_to_server_key, NETCODE_KEY_BYTES ) == 0 );
check( memcmp( output_token.server_to_client_key, input_token.server_to_client_key, NETCODE_KEY_BYTES ) == 0 );
check( memcmp( output_token.user_data, input_token.user_data, NETCODE_USER_DATA_BYTES ) == 0 );
}
static void test_challenge_token()
{
struct netcode_challenge_token_t input_token;
input_token.client_id = TEST_CLIENT_ID;
netcode_random_bytes( input_token.user_data, NETCODE_USER_DATA_BYTES );
uint8_t buffer[NETCODE_CHALLENGE_TOKEN_BYTES];
netcode_write_challenge_token( &input_token, buffer, NETCODE_CHALLENGE_TOKEN_BYTES );
uint64_t sequence = 1000;
uint8_t key[NETCODE_KEY_BYTES];
netcode_generate_key( key );
check( netcode_encrypt_challenge_token( buffer, NETCODE_CHALLENGE_TOKEN_BYTES, sequence, key ) == 1 );
check( netcode_decrypt_challenge_token( buffer, NETCODE_CHALLENGE_TOKEN_BYTES, sequence, key ) == 1 );
struct netcode_challenge_token_t output_token;
check( netcode_read_challenge_token( buffer, NETCODE_CHALLENGE_TOKEN_BYTES, &output_token ) == 1 );
check( output_token.client_id == input_token.client_id );
check( memcmp( output_token.user_data, input_token.user_data, NETCODE_USER_DATA_BYTES ) == 0 );
}
static void test_connection_request_packet()
{
struct netcode_address_t server_address;
server_address.type = NETCODE_ADDRESS_IPV4;
server_address.data.ipv4[0] = 127;
server_address.data.ipv4[1] = 0;
server_address.data.ipv4[2] = 0;
server_address.data.ipv4[3] = 1;
server_address.port = TEST_SERVER_PORT;
uint8_t user_data[NETCODE_USER_DATA_BYTES];
netcode_random_bytes( user_data, NETCODE_USER_DATA_BYTES );
struct netcode_connect_token_private_t input_token;
netcode_generate_connect_token_private( &input_token, TEST_CLIENT_ID, 1, &server_address, user_data );
check( input_token.client_id == TEST_CLIENT_ID );
check( input_token.num_server_addresses == 1 );
check( memcmp( input_token.user_data, user_data, NETCODE_USER_DATA_BYTES ) == 0 );
check( netcode_address_equal( &input_token.server_addresses[0], &server_address ) );
uint8_t connect_token_data[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES];
netcode_write_connect_token_private( &input_token, connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
uint8_t encrypted_connect_token_data[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES];
memcpy( encrypted_connect_token_data, connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
uint64_t connect_token_sequence = 1000;
uint64_t connect_token_expire_timestamp = time( NULL ) + 30;
uint8_t connect_token_key[NETCODE_KEY_BYTES];
netcode_generate_key( connect_token_key );
check( netcode_encrypt_connect_token_private( encrypted_connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, TEST_PROTOCOL_ID, connect_token_expire_timestamp, connect_token_sequence, connect_token_key ) == 1 );
struct netcode_connection_request_packet_t input_packet;
input_packet.packet_type = NETCODE_CONNECTION_REQUEST_PACKET;
memcpy( input_packet.version_info, NETCODE_VERSION_INFO, NETCODE_VERSION_INFO_BYTES );
input_packet.protocol_id = TEST_PROTOCOL_ID;
input_packet.connect_token_expire_timestamp = connect_token_expire_timestamp;
input_packet.connect_token_sequence = connect_token_sequence;
memcpy( input_packet.connect_token_data, encrypted_connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
uint8_t buffer[2048];
uint8_t packet_key[NETCODE_KEY_BYTES];
netcode_generate_key( packet_key );
int bytes_written = netcode_write_packet( &input_packet, buffer, sizeof( buffer ), 1000, packet_key, TEST_PROTOCOL_ID );
check( bytes_written > 0 );
uint64_t sequence = 1000;
uint8_t allowed_packets[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packets, 1, sizeof( allowed_packets ) );
struct netcode_connection_request_packet_t * output_packet = (struct netcode_connection_request_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), connect_token_key, allowed_packets, NULL );
check( output_packet );
check( output_packet->packet_type == NETCODE_CONNECTION_REQUEST_PACKET );
check( memcmp( output_packet->version_info, input_packet.version_info, NETCODE_VERSION_INFO_BYTES ) == 0 );
check( output_packet->protocol_id == input_packet.protocol_id );
check( output_packet->connect_token_expire_timestamp == input_packet.connect_token_expire_timestamp );
check( output_packet->connect_token_sequence == input_packet.connect_token_sequence );
check( memcmp( output_packet->connect_token_data, connect_token_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES ) == 0 );
free( output_packet );
}
void test_connection_denied_packet()
{
struct netcode_connection_denied_packet_t input_packet;
input_packet.packet_type = NETCODE_CONNECTION_DENIED_PACKET;
uint8_t buffer[NETCODE_MAX_PACKET_BYTES];
uint8_t packet_key[NETCODE_KEY_BYTES];
netcode_generate_key( packet_key );
int bytes_written = netcode_write_packet( &input_packet, buffer, sizeof( buffer ), 1000, packet_key, TEST_PROTOCOL_ID );
check( bytes_written > 0 );
uint64_t sequence;
uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) );
struct netcode_connection_denied_packet_t * output_packet = (struct netcode_connection_denied_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL );
check( output_packet );
check( output_packet->packet_type == NETCODE_CONNECTION_DENIED_PACKET );
free( output_packet );
}
void test_connection_challenge_packet()
{
struct netcode_connection_challenge_packet_t input_packet;
input_packet.packet_type = NETCODE_CONNECTION_CHALLENGE_PACKET;
input_packet.challenge_token_sequence = 0;
netcode_random_bytes( input_packet.challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
uint8_t buffer[NETCODE_MAX_PACKET_BYTES];
uint8_t packet_key[NETCODE_KEY_BYTES];
netcode_generate_key( packet_key );
int bytes_written = netcode_write_packet( &input_packet, buffer, sizeof( buffer ), 1000, packet_key, TEST_PROTOCOL_ID );
check( bytes_written > 0 );
uint64_t sequence;
uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) );
struct netcode_connection_challenge_packet_t * output_packet = (struct netcode_connection_challenge_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL );
check( output_packet );
check( output_packet->packet_type == NETCODE_CONNECTION_CHALLENGE_PACKET );
check( output_packet->challenge_token_sequence == input_packet.challenge_token_sequence );
check( memcmp( output_packet->challenge_token_data, input_packet.challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES ) == 0 );
free( output_packet );
}
void test_connection_response_packet()
{
struct netcode_connection_response_packet_t input_packet;
input_packet.packet_type = NETCODE_CONNECTION_RESPONSE_PACKET;
input_packet.challenge_token_sequence = 0;
netcode_random_bytes( input_packet.challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES );
uint8_t buffer[NETCODE_MAX_PACKET_BYTES];
uint8_t packet_key[NETCODE_KEY_BYTES];
netcode_generate_key( packet_key );
int bytes_written = netcode_write_packet( &input_packet, buffer, sizeof( buffer ), 1000, packet_key, TEST_PROTOCOL_ID );
check( bytes_written > 0 );
uint64_t sequence;
uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) );
struct netcode_connection_response_packet_t * output_packet = (struct netcode_connection_response_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL );
check( output_packet );
check( output_packet->packet_type == NETCODE_CONNECTION_RESPONSE_PACKET );
check( output_packet->challenge_token_sequence == input_packet.challenge_token_sequence );
check( memcmp( output_packet->challenge_token_data, input_packet.challenge_token_data, NETCODE_CHALLENGE_TOKEN_BYTES ) == 0 );
free( output_packet );
}
void test_connection_keep_alive_packet()
{
struct netcode_connection_keep_alive_packet_t input_packet;
input_packet.packet_type = NETCODE_CONNECTION_KEEP_ALIVE_PACKET;
input_packet.client_index = 10;
input_packet.max_clients = 16;
uint8_t buffer[NETCODE_MAX_PACKET_BYTES];
uint8_t packet_key[NETCODE_KEY_BYTES];
netcode_generate_key( packet_key );
int bytes_written = netcode_write_packet( &input_packet, buffer, sizeof( buffer ), 1000, packet_key, TEST_PROTOCOL_ID );
check( bytes_written > 0 );
uint64_t sequence;
uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) );
struct netcode_connection_keep_alive_packet_t * output_packet = (struct netcode_connection_keep_alive_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL );
check( output_packet );
check( output_packet->packet_type == NETCODE_CONNECTION_KEEP_ALIVE_PACKET );
check( output_packet->client_index == input_packet.client_index );
check( output_packet->max_clients == input_packet.max_clients );
free( output_packet );
}
void test_connection_payload_packet()
{
struct netcode_connection_payload_packet_t * input_packet = netcode_create_payload_packet( NETCODE_MAX_PAYLOAD_BYTES );
check( input_packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET );
check( input_packet->payload_bytes == NETCODE_MAX_PAYLOAD_BYTES );
netcode_random_bytes( input_packet->payload_data, NETCODE_MAX_PAYLOAD_BYTES );
uint8_t buffer[NETCODE_MAX_PACKET_BYTES];
uint8_t packet_key[NETCODE_KEY_BYTES];
netcode_generate_key( packet_key );
int bytes_written = netcode_write_packet( input_packet, buffer, sizeof( buffer ), 1000, packet_key, TEST_PROTOCOL_ID );
check( bytes_written > 0 );
uint64_t sequence;
uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) );
struct netcode_connection_payload_packet_t * output_packet = (struct netcode_connection_payload_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL );
check( output_packet );
check( output_packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET );
check( output_packet->payload_bytes == input_packet->payload_bytes );
check( memcmp( output_packet->payload_data, input_packet->payload_data, NETCODE_MAX_PAYLOAD_BYTES ) == 0 );
free( input_packet );
free( output_packet );
}
void test_connection_disconnect_packet()
{
struct netcode_connection_disconnect_packet_t input_packet;
input_packet.packet_type = NETCODE_CONNECTION_DISCONNECT_PACKET;
uint8_t buffer[NETCODE_MAX_PACKET_BYTES];
uint8_t packet_key[NETCODE_KEY_BYTES];
netcode_generate_key( packet_key );
int bytes_written = netcode_write_packet( &input_packet, buffer, sizeof( buffer ), 1000, packet_key, TEST_PROTOCOL_ID );
check( bytes_written > 0 );
uint64_t sequence;
uint8_t allowed_packet_types[NETCODE_CONNECTION_NUM_PACKETS];
memset( allowed_packet_types, 1, sizeof( allowed_packet_types ) );
struct netcode_connection_disconnect_packet_t * output_packet = (struct netcode_connection_disconnect_packet_t*) netcode_read_packet( buffer, bytes_written, &sequence, packet_key, TEST_PROTOCOL_ID, time( NULL ), NULL, allowed_packet_types, NULL );
check( output_packet );
check( output_packet->packet_type == NETCODE_CONNECTION_DISCONNECT_PACKET );
free( output_packet );
}
void test_connect_token_public()
{
struct netcode_address_t server_address;
server_address.type = NETCODE_ADDRESS_IPV4;
server_address.data.ipv4[0] = 127;
server_address.data.ipv4[1] = 0;
server_address.data.ipv4[2] = 0;
server_address.data.ipv4[3] = 1;
server_address.port = TEST_SERVER_PORT;
uint8_t user_data[NETCODE_USER_DATA_BYTES];
netcode_random_bytes( user_data, NETCODE_USER_DATA_BYTES );
struct netcode_connect_token_private_t connect_token_private;
netcode_generate_connect_token_private( &connect_token_private, TEST_CLIENT_ID, 1, &server_address, user_data );
check( connect_token_private.client_id == TEST_CLIENT_ID );
check( connect_token_private.num_server_addresses == 1 );
check( memcmp( connect_token_private.user_data, user_data, NETCODE_USER_DATA_BYTES ) == 0 );
check( netcode_address_equal( &connect_token_private.server_addresses[0], &server_address ) );
uint8_t connect_token_private_data[NETCODE_CONNECT_TOKEN_PRIVATE_BYTES];
netcode_write_connect_token_private( &connect_token_private, connect_token_private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
uint64_t sequence = 1000;
uint64_t create_timestamp = time( NULL );
uint64_t expire_timestamp = create_timestamp + 30;
uint8_t key[NETCODE_KEY_BYTES];
netcode_generate_key( key );
check( netcode_encrypt_connect_token_private( connect_token_private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, NETCODE_VERSION_INFO, TEST_PROTOCOL_ID, expire_timestamp, sequence, key ) == 1 );
struct netcode_connect_token_t input_connect_token;
memcpy( input_connect_token.version_info, NETCODE_VERSION_INFO, NETCODE_VERSION_INFO_BYTES );
input_connect_token.protocol_id = TEST_PROTOCOL_ID;
input_connect_token.create_timestamp = create_timestamp;
input_connect_token.expire_timestamp = expire_timestamp;
input_connect_token.sequence = sequence;
memcpy( input_connect_token.private_data, connect_token_private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
input_connect_token.num_server_addresses = 1;
input_connect_token.server_addresses[0] = server_address;
memcpy( input_connect_token.client_to_server_key, input_connect_token.client_to_server_key, NETCODE_KEY_BYTES );
memcpy( input_connect_token.server_to_client_key, input_connect_token.server_to_client_key, NETCODE_KEY_BYTES );
input_connect_token.timeout_seconds = (int) NETCODE_TIMEOUT_SECONDS;
uint8_t buffer[NETCODE_CONNECT_TOKEN_BYTES];
netcode_write_connect_token( &input_connect_token, buffer, NETCODE_CONNECT_TOKEN_BYTES );
struct netcode_connect_token_t output_connect_token;
check( netcode_read_connect_token( buffer, NETCODE_CONNECT_TOKEN_BYTES, &output_connect_token ) == 1 );
check( memcmp( output_connect_token.version_info, input_connect_token.version_info, NETCODE_VERSION_INFO_BYTES ) == 0 );
check( output_connect_token.protocol_id == input_connect_token.protocol_id );
check( output_connect_token.create_timestamp == input_connect_token.create_timestamp );
check( output_connect_token.expire_timestamp == input_connect_token.expire_timestamp );
check( output_connect_token.sequence == input_connect_token.sequence );
check( memcmp( output_connect_token.private_data, input_connect_token.private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES ) == 0 );
check( output_connect_token.num_server_addresses == input_connect_token.num_server_addresses );
check( netcode_address_equal( &output_connect_token.server_addresses[0], &input_connect_token.server_addresses[0] ) );
check( memcmp( output_connect_token.client_to_server_key, input_connect_token.client_to_server_key, NETCODE_KEY_BYTES ) == 0 );
check( memcmp( output_connect_token.server_to_client_key, input_connect_token.server_to_client_key, NETCODE_KEY_BYTES ) == 0 );
check( output_connect_token.timeout_seconds == input_connect_token.timeout_seconds );
}
void test_encryption_manager()
{
struct netcode_encryption_manager_t encryption_manager;
double time = 100.0;
struct encryption_mapping_t
{
struct netcode_address_t address;
uint8_t send_key[NETCODE_KEY_BYTES];
uint8_t receive_key[NETCODE_KEY_BYTES];
};
#define NUM_ENCRYPTION_MAPPINGS 5
struct encryption_mapping_t encryption_mapping[NUM_ENCRYPTION_MAPPINGS];
int i;
for ( i = 0; i < NUM_ENCRYPTION_MAPPINGS; ++i )
{
encryption_mapping[i].address.type = NETCODE_ADDRESS_IPV6;
encryption_mapping[i].address.data.ipv6[7] = 1;
encryption_mapping[i].address.port = ( uint16_t) ( 20000 + i );
netcode_generate_key( encryption_mapping[i].send_key );
netcode_generate_key( encryption_mapping[i].receive_key );
}
for ( i = 0; i < NUM_ENCRYPTION_MAPPINGS; ++i )
{
int encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time );
check( encryption_index == -1 );
check( netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index ) == NULL );
check( netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index ) == NULL );
check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, encryption_mapping[i].send_key, encryption_mapping[i].receive_key, time, -1.0 ) );
encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time );
uint8_t * send_key = netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index );
uint8_t * receive_key = netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index );
check( send_key );
check( receive_key );
check( memcmp( send_key, encryption_mapping[i].send_key, NETCODE_KEY_BYTES ) == 0 );
check( memcmp( receive_key, encryption_mapping[i].receive_key, NETCODE_KEY_BYTES ) == 0 );
}
{
struct netcode_address_t address;
address.type = NETCODE_ADDRESS_IPV6;
address.data.ipv6[7] = 1;
address.port = 50000;
check( netcode_encryption_manager_remove_encryption_mapping( &encryption_manager, &address, time ) == 0 );
}
check( netcode_encryption_manager_remove_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, time ) == 1 );
check( netcode_encryption_manager_remove_encryption_mapping( &encryption_manager, &encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].address, time ) == 1 );
for ( i = 0; i < NUM_ENCRYPTION_MAPPINGS; ++i )
{
int encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time );
uint8_t * send_key = netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index );
uint8_t * receive_key = netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index );
if ( i != 0 && i != NUM_ENCRYPTION_MAPPINGS - 1 )
{
check( send_key );
check( receive_key );
check( memcmp( send_key, encryption_mapping[i].send_key, NETCODE_KEY_BYTES ) == 0 );
check( memcmp( receive_key, encryption_mapping[i].receive_key, NETCODE_KEY_BYTES ) == 0 );
}
else
{
check( !send_key );
check( !receive_key );
}
}
check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, encryption_mapping[0].send_key, encryption_mapping[0].receive_key, time, -1.0 ) );
check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].address, encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].send_key, encryption_mapping[NUM_ENCRYPTION_MAPPINGS-1].receive_key, time, -1.0 ) );
for ( i = 0; i < NUM_ENCRYPTION_MAPPINGS; ++i )
{
int encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time );
uint8_t * send_key = netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index );
uint8_t * receive_key = netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index );
check( send_key );
check( receive_key );
check( memcmp( send_key, encryption_mapping[i].send_key, NETCODE_KEY_BYTES ) == 0 );
check( memcmp( receive_key, encryption_mapping[i].receive_key, NETCODE_KEY_BYTES ) == 0 );
}
time += NETCODE_TIMEOUT_SECONDS * 2;
for ( i = 0; i < NUM_ENCRYPTION_MAPPINGS; ++i )
{
int encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time );
uint8_t * send_key = netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index );
uint8_t * receive_key = netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index );
check( !send_key );
check( !receive_key );
}
for ( i = 0; i < NUM_ENCRYPTION_MAPPINGS; ++i )
{
int encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time );
check( encryption_index == -1 );
check( netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index ) == NULL );
check( netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index ) == NULL );
check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, encryption_mapping[i].send_key, encryption_mapping[i].receive_key, time, -1.0 ) );
encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time );
uint8_t * send_key = netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index );
uint8_t * receive_key = netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index );
check( send_key );
check( receive_key );
check( memcmp( send_key, encryption_mapping[i].send_key, NETCODE_KEY_BYTES ) == 0 );
check( memcmp( receive_key, encryption_mapping[i].receive_key, NETCODE_KEY_BYTES ) == 0 );
}
netcode_encryption_manager_reset( &encryption_manager );
for ( i = 0; i < NUM_ENCRYPTION_MAPPINGS; ++i )
{
int encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[i].address, time );
uint8_t * send_key = netcode_encryption_manager_get_send_key( &encryption_manager, encryption_index );
uint8_t * receive_key = netcode_encryption_manager_get_receive_key( &encryption_manager, encryption_index );
check( !send_key );
check( !receive_key );
}
check( netcode_encryption_manager_add_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, encryption_mapping[0].send_key, encryption_mapping[0].receive_key, time, time + 1.0 ) );
int encryption_index = netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, time );
check( encryption_index != -1 );
check( netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, time + 1.1f ) == -1 );
netcode_encryption_manager_set_expire_time( &encryption_manager, encryption_index, -1.0 );
check( netcode_encryption_manager_find_encryption_mapping( &encryption_manager, &encryption_mapping[0].address, time ) == encryption_index );
}
void test_replay_protection()
{
struct netcode_replay_protection_t replay_protection;
int i;
for ( i = 0; i < 2; ++i )
{
netcode_replay_protection_reset( &replay_protection );
check( replay_protection.most_recent_sequence == 0 );
check( netcode_replay_protection_packet_already_received( &replay_protection, 1ULL<<63 ) == 0 );
check( replay_protection.most_recent_sequence == 0 );
#define MAX_SEQUENCE ( NETCODE_REPLAY_PROTECTION_BUFFER_SIZE * 4 )
uint64_t sequence;
for ( sequence = 0; sequence < MAX_SEQUENCE; ++sequence )
{
check( netcode_replay_protection_packet_already_received( &replay_protection, sequence ) == 0 );
}
check( netcode_replay_protection_packet_already_received( &replay_protection, 0 ) == 1 );
for ( sequence = MAX_SEQUENCE - 10; sequence < MAX_SEQUENCE; ++sequence )
{
check( netcode_replay_protection_packet_already_received( &replay_protection, sequence ) == 1 );
}
check( netcode_replay_protection_packet_already_received( &replay_protection, MAX_SEQUENCE + NETCODE_REPLAY_PROTECTION_BUFFER_SIZE ) == 0 );
for ( sequence = 0; sequence < MAX_SEQUENCE; ++sequence )
{
check( netcode_replay_protection_packet_already_received( &replay_protection, sequence ) == 1 );
}
}
}
static uint8_t private_key[NETCODE_KEY_BYTES] = { 0x60, 0x6a, 0xbe, 0x6e, 0xc9, 0x19, 0x10, 0xea,
0x9a, 0x65, 0x62, 0xf6, 0x6f, 0x2b, 0x30, 0xe4,
0x43, 0x71, 0xd6, 0x2c, 0xd1, 0x99, 0x27, 0x26,
0x6b, 0x3c, 0x60, 0xf4, 0xb7, 0x15, 0xab, 0xa1 };
void test_client_server_connect()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
int server_num_packets_received = 0;
int client_num_packets_received = 0;
uint8_t packet_data[NETCODE_MAX_PACKET_SIZE];
int i;
for ( i = 0; i < NETCODE_MAX_PACKET_SIZE; ++i )
packet_data[i] = (uint8_t) i;
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
netcode_client_send_packet( client, packet_data, NETCODE_MAX_PACKET_SIZE );
netcode_server_send_packet( server, 0, packet_data, NETCODE_MAX_PACKET_SIZE );
while ( 1 )
{
int packet_bytes;
uint64_t packet_sequence;
void * packet = netcode_client_receive_packet( client, &packet_bytes, &packet_sequence );
if ( !packet )
break;
(void) packet_sequence;
assert( packet_bytes == NETCODE_MAX_PACKET_SIZE );
assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 );
client_num_packets_received++;
netcode_client_free_packet( client, packet );
}
while ( 1 )
{
int packet_bytes;
uint64_t packet_sequence;
void * packet = netcode_server_receive_packet( server, 0, &packet_bytes, &packet_sequence );
if ( !packet )
break;
(void) packet_sequence;
assert( packet_bytes == NETCODE_MAX_PACKET_SIZE );
assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 );
server_num_packets_received++;
netcode_server_free_packet( server, packet );
}
if ( client_num_packets_received >= 10 && server_num_packets_received >= 10 )
{
if ( netcode_server_client_connected( server, 0 ) )
{
netcode_server_disconnect_client( server, 0 );
}
}
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
time += delta_time;
}
check( client_num_packets_received >= 10 && server_num_packets_received >= 10 );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_server_keep_alive()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
int num_iterations = (int) ceil( 1.25f * NETCODE_TIMEOUT_SECONDS / delta_time );
int i;
for ( i = 0; i < num_iterations; ++i )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_server_multiple_clients()
{
#define NUM_START_STOP_ITERATIONS 4
int max_clients[NUM_START_STOP_ITERATIONS] = { 2, 32, 5, 256 };
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
uint64_t token_sequence = 0;
int i;
for ( i = 0; i < NUM_START_STOP_ITERATIONS; ++i )
{
netcode_server_start( server, max_clients[i] );
struct netcode_client_t ** client = malloc( sizeof( struct netcode_client_t* ) * max_clients[i] );
check( client );
int j;
for ( j = 0; j < max_clients[i]; ++j )
{
char client_address[NETCODE_MAX_ADDRESS_STRING_LENGTH];
sprintf( client_address, "[::]:%d", 50000 + j );
client[j] = netcode_client_create_internal( client_address, time, network_simulator );
check( client[j] );
uint64_t client_id = j;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, token_sequence++, private_key, connect_token ) );
netcode_client_connect( client[j], connect_token );
}
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
for ( j = 0; j < max_clients[i]; ++j )
{
netcode_client_update( client[j], time );
}
netcode_server_update( server, time );
int num_connected_clients = 0;
for ( j = 0; j < max_clients[i]; ++j )
{
if ( netcode_client_state( client[j] ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client[j] ) == NETCODE_CLIENT_STATE_CONNECTED )
num_connected_clients++;
}
if ( num_connected_clients == max_clients[i] )
break;
time += delta_time;
}
check( netcode_server_num_clients_connected( server ) == max_clients[i] );
for ( j = 0; j < max_clients[i]; ++j )
{
check( netcode_client_state( client[j] ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_server_client_connected( server, j ) == 1 );
}
int * server_num_packets_received = malloc( sizeof(int) * max_clients[i] );
int * client_num_packets_received = malloc( sizeof(int) * max_clients[i] );
memset( server_num_packets_received, 0, sizeof(int) * max_clients[i] );
memset( client_num_packets_received, 0, sizeof(int) * max_clients[i] );
uint8_t packet_data[NETCODE_MAX_PACKET_SIZE];
for ( j = 0; j < NETCODE_MAX_PACKET_SIZE; ++j )
packet_data[j] = (uint8_t) j;
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
for ( j = 0; j < max_clients[i]; ++j )
{
netcode_client_update( client[j], time );
}
netcode_server_update( server, time );
for ( j = 0; j < max_clients[i]; ++j )
{
netcode_client_send_packet( client[j], packet_data, NETCODE_MAX_PACKET_SIZE );
}
for ( j = 0; j < max_clients[i]; ++j )
{
netcode_server_send_packet( server, j, packet_data, NETCODE_MAX_PACKET_SIZE );
}
for ( j = 0; j < max_clients[i]; ++j )
{
while ( 1 )
{
int packet_bytes;
uint64_t packet_sequence;
void * packet = netcode_client_receive_packet( client[j], &packet_bytes, &packet_sequence );
if ( !packet )
break;
(void) packet_sequence;
assert( packet_bytes == NETCODE_MAX_PACKET_SIZE );
assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 );
client_num_packets_received[j]++;
netcode_client_free_packet( client[j], packet );
}
}
for ( j = 0; j < max_clients[i]; ++j )
{
while ( 1 )
{
int packet_bytes;
uint64_t packet_sequence;
void * packet = netcode_server_receive_packet( server, j, &packet_bytes, &packet_sequence );
if ( !packet )
break;
(void) packet_sequence;
assert( packet_bytes == NETCODE_MAX_PACKET_SIZE );
assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 );
server_num_packets_received[j]++;
netcode_server_free_packet( server, packet );
}
}
int num_clients_ready = 0;
for ( j = 0; j < max_clients[i]; ++j )
{
if ( client_num_packets_received[j] >= 1 && server_num_packets_received[j] >= 1 )
{
num_clients_ready++;
}
}
if ( num_clients_ready == max_clients[i] )
break;
for ( j = 0; j < max_clients[i]; ++j )
{
if ( netcode_client_state( client[j] ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
}
time += delta_time;
}
int num_clients_ready = 0;
for ( j = 0; j < max_clients[i]; ++j )
{
if ( client_num_packets_received[j] >= 1 && server_num_packets_received[j] >= 1 )
{
num_clients_ready++;
}
}
check( num_clients_ready == max_clients[i] );
free( server_num_packets_received );
free( client_num_packets_received );
netcode_network_simulator_discard_packets( network_simulator );
for ( j = 0; j < max_clients[i]; ++j )
{
netcode_client_destroy( client[j] );
}
free( client );
netcode_server_stop( server );
}
netcode_server_destroy( server );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_server_multiple_servers()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
netcode_server_start( server, 1 );
char * server_address[] = { "10.10.10.10:1000", "100.100.100.100:50000", "[::1]:40000" };
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 3, server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
int server_num_packets_received = 0;
int client_num_packets_received = 0;
uint8_t packet_data[NETCODE_MAX_PACKET_SIZE];
int i;
for ( i = 0; i < NETCODE_MAX_PACKET_SIZE; ++i )
packet_data[i] = (uint8_t) i;
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
netcode_client_send_packet( client, packet_data, NETCODE_MAX_PACKET_SIZE );
netcode_server_send_packet( server, 0, packet_data, NETCODE_MAX_PACKET_SIZE );
while ( 1 )
{
int packet_bytes;
uint64_t packet_sequence;
void * packet = netcode_client_receive_packet( client, &packet_bytes, &packet_sequence );
if ( !packet )
break;
(void) packet_sequence;
assert( packet_bytes == NETCODE_MAX_PACKET_SIZE );
assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 );
client_num_packets_received++;
netcode_client_free_packet( client, packet );
}
while ( 1 )
{
int packet_bytes;
uint64_t packet_sequence;
void * packet = netcode_server_receive_packet( server, 0, &packet_bytes, &packet_sequence );
if ( !packet )
break;
assert( packet_bytes == NETCODE_MAX_PACKET_SIZE );
assert( memcmp( packet, packet_data, NETCODE_MAX_PACKET_SIZE ) == 0 );
server_num_packets_received++;
netcode_server_free_packet( server, packet );
}
if ( client_num_packets_received >= 10 && server_num_packets_received >= 10 )
{
if ( netcode_server_client_connected( server, 0 ) )
{
netcode_server_disconnect_client( server, 0 );
}
}
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
time += delta_time;
}
check( client_num_packets_received >= 10 && server_num_packets_received >= 10 );
netcode_server_destroy( server );
netcode_client_destroy( client );
}
void test_client_error_connect_token_expired()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, 0, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
netcode_client_update( client, time );
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECT_TOKEN_EXPIRED );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_error_invalid_connect_token()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
netcode_random_bytes( connect_token, NETCODE_CONNECT_TOKEN_BYTES );
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
netcode_client_connect( client, connect_token );
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_INVALID_CONNECT_TOKEN );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_error_connection_timed_out()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTION_TIMED_OUT );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_error_connection_response_timeout()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
server->flags = NETCODE_SERVER_FLAG_IGNORE_CONNECTION_RESPONSE_PACKETS;
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTION_RESPONSE_TIMED_OUT );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_error_connection_request_timeout()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 60.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
server->flags = NETCODE_SERVER_FLAG_IGNORE_CONNECTION_REQUEST_PACKETS;
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTION_REQUEST_TIMED_OUT );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_error_connection_denied()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
struct netcode_client_t * client2 = netcode_client_create_internal( "[::]:50001", time, network_simulator );
check( client2 );
uint8_t connect_token2[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id2 = 0;
netcode_random_bytes( (uint8_t*) &client_id2, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id2, TEST_PROTOCOL_ID, 0, private_key, connect_token2 ) );
netcode_client_connect( client2, connect_token2 );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_client_update( client2, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client2 ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_state( client2 ) == NETCODE_CLIENT_STATE_CONNECTION_DENIED );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_client_destroy( client2 );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_side_disconnect()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
netcode_client_disconnect( client );
int i;
for ( i = 0; i < 10; ++i )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_server_client_connected( server, 0 ) == 0 )
break;
time += delta_time;
}
check( netcode_server_client_connected( server, 0 ) == 0 );
check( netcode_server_num_clients_connected( server ) == 0 );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_server_side_disconnect()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
netcode_server_disconnect_client( server, 0 );
int i;
for ( i = 0; i < 10; ++i )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_DISCONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_DISCONNECTED );
check( netcode_server_client_connected( server, 0 ) == 0 );
check( netcode_server_num_clients_connected( server ) == 0 );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
void test_client_reconnect()
{
struct netcode_network_simulator_t * network_simulator = netcode_network_simulator_create();
network_simulator->latency_milliseconds = 250;
network_simulator->jitter_milliseconds = 250;
network_simulator->packet_loss_percent = 5;
network_simulator->duplicate_packet_percent = 10;
double time = 0.0;
double delta_time = 1.0 / 10.0;
struct netcode_client_t * client = netcode_client_create_internal( "[::]:50000", time, network_simulator );
check( client );
struct netcode_server_t * server = netcode_server_create_internal( "[::]:40000", "[::1]:40000", TEST_PROTOCOL_ID, private_key, time, network_simulator );
check( server );
netcode_server_start( server, 1 );
char * server_address = "[::1]:40000";
uint8_t connect_token[NETCODE_CONNECT_TOKEN_BYTES];
uint64_t client_id = 0;
netcode_random_bytes( (uint8_t*) &client_id, 8 );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
netcode_network_simulator_discard_packets( network_simulator );
netcode_server_disconnect_client( server, 0 );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_DISCONNECTED );
check( netcode_server_client_connected( server, 0 ) == 0 );
check( netcode_server_num_clients_connected( server ) == 0 );
netcode_network_simulator_discard_packets( network_simulator );
check( netcode_generate_connect_token( 1, &server_address, TEST_CONNECT_TOKEN_EXPIRY, client_id, TEST_PROTOCOL_ID, 0, private_key, connect_token ) );
netcode_client_connect( client, connect_token );
while ( 1 )
{
netcode_network_simulator_update( network_simulator, time );
netcode_client_update( client, time );
netcode_server_update( server, time );
if ( netcode_client_state( client ) <= NETCODE_CLIENT_STATE_DISCONNECTED )
break;
if ( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED )
break;
time += delta_time;
}
check( netcode_client_state( client ) == NETCODE_CLIENT_STATE_CONNECTED );
check( netcode_client_index( client ) == 0 );
check( netcode_server_client_connected( server, 0 ) == 1 );
check( netcode_server_num_clients_connected( server ) == 1 );
netcode_server_destroy( server );
netcode_client_destroy( client );
netcode_network_simulator_destroy( network_simulator );
}
#define RUN_TEST( test_function ) \
do \
{ \
printf( #test_function "\n" ); \
test_function(); \
} \
while (0)
void netcode_test()
{
printf( "\n" );
{
RUN_TEST( test_queue );
RUN_TEST( test_endian );
RUN_TEST( test_address );
RUN_TEST( test_sequence );
RUN_TEST( test_connect_token );
RUN_TEST( test_challenge_token );
RUN_TEST( test_connection_request_packet );
RUN_TEST( test_connection_denied_packet );
RUN_TEST( test_connection_challenge_packet );
RUN_TEST( test_connection_response_packet );
RUN_TEST( test_connection_payload_packet );
RUN_TEST( test_connection_disconnect_packet );
RUN_TEST( test_connect_token_public );
RUN_TEST( test_encryption_manager );
RUN_TEST( test_replay_protection );
RUN_TEST( test_client_server_connect );
RUN_TEST( test_client_server_keep_alive );
RUN_TEST( test_client_server_multiple_clients );
RUN_TEST( test_client_server_multiple_servers );
RUN_TEST( test_client_error_connect_token_expired );
RUN_TEST( test_client_error_invalid_connect_token );
RUN_TEST( test_client_error_connection_timed_out );
RUN_TEST( test_client_error_connection_response_timeout );
RUN_TEST( test_client_error_connection_request_timeout );
RUN_TEST( test_client_error_connection_denied );
RUN_TEST( test_client_side_disconnect );
RUN_TEST( test_server_side_disconnect );
RUN_TEST( test_client_reconnect );
}
printf( "\n*** ALL TESTS PASSED ***\n\n" );
}
#endif