Permalink
Switch branches/tags
Nothing to show
Find file Copy path
efc59b0 Jul 19, 2018
2 contributors

Users who have contributed to this file

@gafferongames @armandgray
executable file 8672 lines (6647 sloc) 318 KB
/*
netcode.io reference implementation
Copyright © 2017, The Network Protocol Company, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "netcode.h"
#include <stdlib.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 // #ifdef _MSC_VER
#include <sodium.h>
#define NETCODE_SOCKET_IPV6 1
#define NETCODE_SOCKET_IPV4 2
#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 1200
#define NETCODE_MAX_PAYLOAD_BYTES 1100
#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_CLIENT_SOCKET_SNDBUF_SIZE ( 256 * 1024 )
#define NETCODE_CLIENT_SOCKET_RCVBUF_SIZE ( 256 * 1024 )
#define NETCODE_SERVER_SOCKET_SNDBUF_SIZE ( 4 * 1024 * 1024 )
#define NETCODE_SERVER_SOCKET_RCVBUF_SIZE ( 4 * 1024 * 1024 )
#define NETCODE_VERSION_INFO ( (uint8_t*) "NETCODE 1.01" )
#define NETCODE_PACKET_SEND_RATE 10.0
#define NETCODE_NUM_DISCONNECT_PACKETS 10
#ifndef NETCODE_ENABLE_TESTS
#define NETCODE_ENABLE_TESTS 0
#endif // #ifndef NETCODE_ENABLE_TESTS
#ifndef NETCODE_ENABLE_LOGGING
#define NETCODE_ENABLE_LOGGING 1
#endif // #ifndef NETCODE_ENABLE_LOGGING
// ------------------------------------------------------------------
static void netcode_default_assert_handler( NETCODE_CONST char * condition, NETCODE_CONST char * function, NETCODE_CONST char * file, int line )
{
printf( "assert failed: ( %s ), function %s, file %s, line %d\n", condition, function, file, line );
#if defined( __GNUC__ )
__builtin_trap();
#elif defined( _MSC_VER )
__debugbreak();
#endif
exit( 1 );
}
static int log_level = 0;
static int (*printf_function)( NETCODE_CONST char *, ... ) = ( int (*)( NETCODE_CONST char *, ... ) ) printf;
void (*netcode_assert_function)( NETCODE_CONST char *, NETCODE_CONST char *, NETCODE_CONST char * file, int line ) = netcode_default_assert_handler;
void netcode_log_level( int level )
{
log_level = level;
}
void netcode_set_printf_function( int (*function)( NETCODE_CONST char *, ... ) )
{
netcode_assert( function );
printf_function = function;
}
void netcode_set_assert_function( void (*function)( NETCODE_CONST char *, NETCODE_CONST char *, NETCODE_CONST char * file, int line ) )
{
netcode_assert_function = function;
}
#if NETCODE_ENABLE_LOGGING
void netcode_printf( int level, NETCODE_CONST char * format, ... )
{
if ( level > log_level )
return;
va_list args;
va_start( args, format );
char buffer[4*1024];
vsprintf( buffer, format, args );
printf_function( "%s", buffer );
va_end( args );
}
#else // #if NETCODE_ENABLE_LOGGING
void netcode_printf( int level, NETCODE_CONST char * format, ... )
{
(void) level;
(void) format;
}
#endif // #if NETCODE_ENABLE_LOGGING
void * netcode_default_allocate_function( void * context, uint64_t bytes )
{
(void) context;
return malloc( bytes );
}
void netcode_default_free_function( void * context, void * pointer )
{
(void) context;
free( pointer );
}
// ------------------------------------------------------------------
#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 // #ifdef SetPort
#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
// ----------------------------------------------------------------
int netcode_parse_address( NETCODE_CONST char * address_string_in, struct netcode_address_t * address )
{
netcode_assert( address_string_in );
netcode_assert( address );
memset( address, 0, sizeof( struct netcode_address_t ) );
// first try to parse the string as an IPv6 address:
// 1. if the first character is '[' then it's probably an ipv6 in form "[addr6]:portnum"
// 2. otherwise try to parse as a raw IPv6 address using inet_pton
#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] == '[' )
{
int base_index = address_string_length - 1;
int i;
for ( i = 0; i < 6; ++i ) // note: no need to search past 6 characters as ":65535" is longest possible port value
{
int index = base_index - i;
if ( index < 3 )
return NETCODE_ERROR;
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 NETCODE_OK;
}
// otherwise it's probably an IPv4 address:
// 1. look for ":portnum", if found save the portnum and strip it out
// 2. parse remaining ipv4 address via inet_pton
address_string_length = (int) strlen( address_string );
int base_index = address_string_length - 1;
int i;
for ( i = 0; i < 6; ++i )
{
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 NETCODE_OK;
}
return NETCODE_ERROR;
}
char * netcode_address_to_string( struct netcode_address_t * address, char * buffer )
{
netcode_assert( address );
netcode_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 )
{
netcode_assert( a );
netcode_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()
{
netcode_assert( !netcode.initialized );
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
WSADATA WsaData;
if ( WSAStartup( MAKEWORD(2,2), &WsaData ) != NO_ERROR )
return NETCODE_ERROR;
#endif // #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
if ( sodium_init() == -1 )
return NETCODE_ERROR;
netcode.initialized = 1;
return NETCODE_OK;
}
void netcode_term()
{
netcode_assert( netcode.initialized );
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
WSACleanup();
#endif // #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
netcode.initialized = 0;
}
// ----------------------------------------------------------------
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
typedef uint64_t netcode_socket_handle_t;
#else // #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
typedef int netcode_socket_handle_t;
#endif // #if NETCODE_PLATFORM == NETCODe_PLATFORM_WINDOWS
struct netcode_socket_t
{
struct netcode_address_t address;
netcode_socket_handle_t handle;
};
struct netcode_socket_holder_t
{
struct netcode_socket_t ipv4;
struct netcode_socket_t ipv6;
};
#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 )
{
netcode_assert( socket );
netcode_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 )
{
netcode_assert( s );
netcode_assert( address );
netcode_assert( netcode.initialized );
netcode_assert( address->type != NETCODE_ADDRESS_NONE );
s->address = *address;
// create socket
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 NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
if ( s->handle <= 0 )
#endif // #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to create socket\n" );
return NETCODE_SOCKET_ERROR_CREATE_FAILED;
}
// force IPv6 only if necessary
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;
}
}
// increase socket send and receive buffer sizes
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;
}
// bind to port
if ( address->type == NETCODE_ADDRESS_IPV6 )
{
struct sockaddr_in6 socket_address;
memset( &socket_address, 0, sizeof( struct sockaddr_in6 ) );
socket_address.sin6_family = AF_INET6;
int i;
for ( i = 0; i < 8; ++i )
{
( (uint16_t*) &socket_address.sin6_addr ) [i] = htons( address->data.ipv6[i] );
}
socket_address.sin6_port = htons( address->port );
if ( bind( s->handle, (struct sockaddr*) &socket_address, sizeof( socket_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 socket_address;
memset( &socket_address, 0, sizeof( socket_address ) );
socket_address.sin_family = AF_INET;
socket_address.sin_addr.s_addr = ( ( (uint32_t) address->data.ipv4[0] ) ) |
( ( (uint32_t) address->data.ipv4[1] ) << 8 ) |
( ( (uint32_t) address->data.ipv4[2] ) << 16 ) |
( ( (uint32_t) address->data.ipv4[3] ) << 24 );
socket_address.sin_port = htons( address->port );
if ( bind( s->handle, (struct sockaddr*) &socket_address, sizeof( socket_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 bound to port 0 find the actual port we got
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 );
}
}
// set non-blocking io
#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 )
{
netcode_assert( socket );
netcode_assert( socket->handle != 0 );
netcode_assert( to );
netcode_assert( to->type == NETCODE_ADDRESS_IPV6 || to->type == NETCODE_ADDRESS_IPV4 );
netcode_assert( packet_data );
netcode_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 );
int result = sendto( socket->handle, (char*) packet_data, packet_bytes, 0, (struct sockaddr*) &socket_address, sizeof( struct sockaddr_in6 ) );
(void) result;
}
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] ) ) |
( ( (uint32_t) to->data.ipv4[1] ) << 8 ) |
( ( (uint32_t) to->data.ipv4[2] ) << 16 ) |
( ( (uint32_t) to->data.ipv4[3] ) << 24 );
socket_address.sin_port = htons( to->port );
int result = sendto( socket->handle, (NETCODE_CONST char*) packet_data, packet_bytes, 0, (struct sockaddr*) &socket_address, sizeof( struct sockaddr_in ) );
(void) result;
}
}
int netcode_socket_receive_packet( struct netcode_socket_t * socket, struct netcode_address_t * from, void * packet_data, int max_packet_size )
{
netcode_assert( socket );
netcode_assert( socket->handle != 0 );
netcode_assert( from );
netcode_assert( packet_data );
netcode_assert( max_packet_size > 0 );
#if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
typedef int socklen_t;
#endif // #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
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 || error == WSAECONNRESET )
return 0;
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: recvfrom failed with error %d\n", error );
return 0;
}
#else // #if NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
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 NETCODE_PLATFORM == NETCODE_PLATFORM_WINDOWS
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 & 0x000000FF ) );
from->data.ipv4[1] = (uint8_t) ( ( addr_ipv4->sin_addr.s_addr & 0x0000FF00 ) >> 8 );
from->data.ipv4[2] = (uint8_t) ( ( addr_ipv4->sin_addr.s_addr & 0x00FF0000 ) >> 16 );
from->data.ipv4[3] = (uint8_t) ( ( addr_ipv4->sin_addr.s_addr & 0xFF000000 ) >> 24 );
from->port = ntohs( addr_ipv4->sin_port );
}
else
{
netcode_assert( 0 );
return 0;
}
netcode_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 )
{
netcode_assert( key );
randombytes_buf( key, NETCODE_KEY_BYTES );
}
void netcode_random_bytes( uint8_t * data, int bytes )
{
netcode_assert( data );
netcode_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,
NETCODE_CONST uint8_t * key )
{
unsigned long long encrypted_length;
#if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS
int result = crypto_aead_chacha20poly1305_ietf_encrypt( message, &encrypted_length,
message, (unsigned long long) message_length,
additional, (unsigned long long) additional_length,
NULL, nonce, key );
if ( result != 0 )
return NETCODE_ERROR;
#else // #if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS
uint8_t * temp = alloca( message_length + NETCODE_MAC_BYTES );
int result = crypto_aead_chacha20poly1305_ietf_encrypt( temp, &encrypted_length,
message, (unsigned long long) message_length,
additional, (unsigned long long) additional_length,
NULL, nonce, key );
if ( result != 0 )
return NETCODE_ERROR;
memcpy( message, temp, message_length + NETCODE_MAC_BYTES );
#endif // #if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS
netcode_assert( encrypted_length == message_length + NETCODE_MAC_BYTES );
return NETCODE_OK;
}
int netcode_decrypt_aead( uint8_t * message, uint64_t message_length,
uint8_t * additional, uint64_t additional_length,
uint8_t * nonce,
uint8_t * key )
{
unsigned long long decrypted_length;
#if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS
int result = crypto_aead_chacha20poly1305_ietf_decrypt( message, &decrypted_length,
NULL,
message, (unsigned long long) message_length,
additional, (unsigned long long) additional_length,
nonce, key );
if ( result != 0 )
return NETCODE_ERROR;
#else // #if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS
uint8_t * temp = alloca( message_length );
int result = crypto_aead_chacha20poly1305_ietf_decrypt( temp, &decrypted_length,
NULL,
message, (unsigned long long) message_length,
additional, (unsigned long long) additional_length,
nonce, key );
if ( result != 0 )
return NETCODE_ERROR;
memcpy( message, temp, decrypted_length );
#endif // #if SODIUM_SUPPORTS_OVERLAPPING_BUFFERS
netcode_assert( decrypted_length == message_length - NETCODE_MAC_BYTES );
return NETCODE_OK;
}
// ----------------------------------------------------------------
struct netcode_connect_token_private_t
{
uint64_t client_id;
int timeout_seconds;
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 timeout_seconds,
int num_server_addresses,
struct netcode_address_t * server_addresses,
uint8_t * user_data )
{
netcode_assert( connect_token );
netcode_assert( num_server_addresses > 0 );
netcode_assert( num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT );
netcode_assert( server_addresses );
netcode_assert( user_data );
connect_token->client_id = client_id;
connect_token->timeout_seconds = timeout_seconds;
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;
netcode_assert( connect_token );
netcode_assert( connect_token->num_server_addresses > 0 );
netcode_assert( connect_token->num_server_addresses <= NETCODE_MAX_SERVERS_PER_CONNECT );
netcode_assert( buffer );
netcode_assert( buffer_length >= 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->timeout_seconds );
netcode_write_uint32( &buffer, connect_token->num_server_addresses );
int i,j;
for ( i = 0; i < connect_token->num_server_addresses; ++i )
{
// todo: should really have a function to write an address
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
{
netcode_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 );
netcode_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,
NETCODE_CONST uint8_t * key )
{
netcode_assert( buffer );
netcode_assert( buffer_length == NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
netcode_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[12];
{
uint8_t * p = nonce;
netcode_write_uint32( &p, 0 );
netcode_write_uint64( &p, sequence );
}
return netcode_encrypt_aead( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - NETCODE_MAC_BYTES, additional_data, sizeof( additional_data ), nonce, key );
}
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 )
{
netcode_assert( buffer );
netcode_assert( buffer_length == NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
netcode_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[12];
{
uint8_t * p = nonce;
netcode_write_uint32( &p, 0 );
netcode_write_uint64( &p, sequence );
}
return netcode_decrypt_aead( buffer, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, additional_data, sizeof( additional_data ), nonce, key );
}
int netcode_read_connect_token_private( uint8_t * buffer, int buffer_length, struct netcode_connect_token_private_t * connect_token )
{
netcode_assert( buffer );
netcode_assert( connect_token );
if ( buffer_length < NETCODE_CONNECT_TOKEN_PRIVATE_BYTES )
return NETCODE_ERROR;
connect_token->client_id = netcode_read_uint64( &buffer );
connect_token->timeout_seconds = (int) netcode_read_uint32( &buffer );
connect_token->num_server_addresses = netcode_read_uint32( &buffer );
if ( connect_token->num_server_addresses <= 0 )
return NETCODE_ERROR;
if ( connect_token->num_server_addresses > NETCODE_MAX_SERVERS_PER_CONNECT )
return NETCODE_ERROR;
int i,j;
for ( i = 0; i < connect_token->num_server_addresses; ++i )
{
// todo: should really have a function to read an address
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 NETCODE_ERROR;
}
}
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 NETCODE_OK;
}
// -----------------------------------------------
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;
netcode_assert( challenge_token );
netcode_assert( buffer );
netcode_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 );
netcode_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 )
{
netcode_assert( buffer );
netcode_assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES );
netcode_assert( key );
(void) buffer_length;
uint8_t nonce[12];
{
uint8_t * p = nonce;
netcode_write_uint32( &p, 0 );
netcode_write_uint64( &p, sequence );
}
return netcode_encrypt_aead( buffer, NETCODE_CHALLENGE_TOKEN_BYTES - NETCODE_MAC_BYTES, NULL, 0, nonce, key );
}
int netcode_decrypt_challenge_token( uint8_t * buffer, int buffer_length, uint64_t sequence, uint8_t * key )
{
netcode_assert( buffer );
netcode_assert( buffer_length >= NETCODE_CHALLENGE_TOKEN_BYTES );
netcode_assert( key );
(void) buffer_length;
uint8_t nonce[12];
{
uint8_t * p = nonce;
netcode_write_uint32( &p, 0 );
netcode_write_uint64( &p, sequence );
}
return netcode_decrypt_aead( buffer, NETCODE_CHALLENGE_TOKEN_BYTES, NULL, 0, nonce, key );
}
int netcode_read_challenge_token( uint8_t * buffer, int buffer_length, struct netcode_challenge_token_t * challenge_token )
{
netcode_assert( buffer );
netcode_assert( challenge_token );
if ( buffer_length < NETCODE_CHALLENGE_TOKEN_BYTES )
return NETCODE_ERROR;
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 );
netcode_assert( buffer - start == 8 + NETCODE_USER_DATA_BYTES );
return NETCODE_OK;
}
// ----------------------------------------------------------------
#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, void * allocator_context, void* (*allocate_function)(void*,uint64_t) )
{
netcode_assert( payload_bytes >= 0 );
netcode_assert( payload_bytes <= NETCODE_MAX_PAYLOAD_BYTES );
if ( allocate_function == NULL )
{
allocate_function = netcode_default_allocate_function;
}
struct netcode_connection_payload_packet_t * packet = (struct netcode_connection_payload_packet_t*)
allocate_function( allocator_context, 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 )
{
netcode_assert( packet );
netcode_assert( buffer );
netcode_assert( write_packet_key );
(void) buffer_length;
uint8_t packet_type = ((uint8_t*)packet)[0];
if ( packet_type == NETCODE_CONNECTION_REQUEST_PACKET )
{
// connection request packet: first byte is zero
netcode_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 );
netcode_assert( buffer - start == 1 + 13 + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
return (int) ( buffer - start );
}
else
{
// *** encrypted packets ***
// write the prefix byte (this is a combination of the packet type and number of sequence bytes)
uint8_t * start = buffer;
uint8_t sequence_bytes = (uint8_t) netcode_sequence_number_bytes_required( sequence );
netcode_assert( sequence_bytes >= 1 );
netcode_assert( sequence_bytes <= 8 );
netcode_assert( packet_type <= 0xF );
uint8_t prefix_byte = packet_type | ( sequence_bytes << 4 );
netcode_write_uint8( &buffer, prefix_byte );
// write the variable length sequence number [1,8] bytes.
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;
}
// write packet data according to type. this data will be encrypted.
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;
netcode_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:
netcode_assert( 0 );
}
netcode_assert( buffer - start <= buffer_length - NETCODE_MAC_BYTES );
uint8_t * encrypted_finish = buffer;
// encrypt the per-packet packet written with the prefix byte, protocol id and version as the associated data. this must match to decrypt.
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[12];
{
uint8_t * p = nonce;
netcode_write_uint32( &p, 0 );
netcode_write_uint64( &p, sequence );
}
if ( netcode_encrypt_aead( encrypted_start,
encrypted_finish - encrypted_start,
additional_data, sizeof( additional_data ),
nonce, write_packet_key ) != NETCODE_OK )
{
return NETCODE_ERROR;
}
buffer += NETCODE_MAC_BYTES;
netcode_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 )
{
netcode_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 )
{
netcode_assert( replay_protection );
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;
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,
void * allocator_context,
void* (*allocate_function)(void*,uint64_t) )
{
netcode_assert( sequence );
netcode_assert( allowed_packets );
// todo: is this still necessary? probably not.
if ( allocate_function == NULL )
{
allocate_function = netcode_default_allocate_function;
}
*sequence = 0;
if ( buffer_length < 1 )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored packet. buffer length is less than 1\n" );
return NULL;
}
uint8_t * start = buffer;
uint8_t prefix_byte = netcode_read_uint8( &buffer );
if ( prefix_byte == NETCODE_CONNECTION_REQUEST_PACKET )
{
// connection request packet: first byte is zero
if ( !allowed_packets[NETCODE_CONNECTION_REQUEST_PACKET] )
{
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 (expected %d, got %d)\n", 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES, buffer_length );
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] != '1' ||
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. expected %.16" PRIx64 ", got %.16" PRIx64 "\n",
protocol_id, packet_protocol_id );
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 );
netcode_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_OK )
{
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*)
allocate_function( allocator_context, 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 );
netcode_assert( buffer - start == 1 + NETCODE_VERSION_INFO_BYTES + 8 + 8 + 8 + NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
return packet;
}
else
{
// *** encrypted packets ***
if ( !read_packet_key )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. no read packet key for this address\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 (%d bytes)\n", buffer_length );
return NULL;
}
// extract the packet type and number of sequence bytes from the prefix byte
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;
}
// read variable length sequence number [1,8]
int i;
for ( i = 0; i < sequence_bytes; ++i )
{
uint8_t value = netcode_read_uint8( &buffer );
(*sequence) |= ( uint64_t) ( value ) << ( 8 * i );
}
// replay protection (optional)
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;
}
}
// decrypt the per-packet type data
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[12];
{
uint8_t * p = nonce;
netcode_write_uint32( &p, 0 );
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_OK )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. failed to decrypt\n" );
return NULL;
}
int decrypted_bytes = encrypted_bytes - NETCODE_MAC_BYTES;
// process the per-packet type data that was just decrypted
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*)
allocate_function( allocator_context, 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*)
allocate_function( allocator_context, 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*)
allocate_function( allocator_context, 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*)
allocate_function( allocator_context, 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, allocator_context, allocate_function );
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*)
allocate_function( allocator_context, 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 timeout_seconds;
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];
};
void netcode_write_connect_token( struct netcode_connect_token_t * connect_token, uint8_t * buffer, int buffer_length )
{
netcode_assert( connect_token );
netcode_assert( buffer );
netcode_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->timeout_seconds );
netcode_write_uint32( &buffer, connect_token->num_server_addresses );
for ( i = 0; i < connect_token->num_server_addresses; ++i )
{
// todo: really just need a function to write an address. too much cut & paste here
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
{
netcode_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_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 )
{
netcode_assert( buffer );
netcode_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 NETCODE_ERROR;
}
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] != '1' ||
connect_token->version_info[12] != '\0' )
{
connect_token->version_info[12] = '\0';
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: read connect data has bad version info (got %s, expected %s)\n", connect_token->version_info, NETCODE_VERSION_INFO );
return NETCODE_ERROR;
}
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 NETCODE_ERROR;
connect_token->sequence = netcode_read_uint64( &buffer );
netcode_read_bytes( &buffer, connect_token->private_data, NETCODE_CONNECT_TOKEN_PRIVATE_BYTES );
connect_token->timeout_seconds = (int) netcode_read_uint32( &buffer );
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 number of server addresses (%d)\n", connect_token->num_server_addresses );
return NETCODE_ERROR;
}
int i,j;
for ( i = 0; i < connect_token->num_server_addresses; ++i )
{
// todo: really need a function to read an address
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 NETCODE_ERROR;
}
}
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 );
return NETCODE_OK;
}
// ----------------------------------------------------------------
struct netcode_packet_queue_t
{
void * allocator_context;
void * (*allocate_function)(void*,uint64_t);
void (*free_function)(void*,void*);
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,
void * allocator_context,
void * (*allocate_function)(void*,uint64_t),
void (*free_function)(void*,void*) )
{
if ( allocate_function == NULL )
{
allocate_function = netcode_default_allocate_function;
}
if ( free_function == NULL )
{
free_function = netcode_default_free_function;
}
netcode_assert( queue );
queue->allocator_context = allocator_context;
queue->allocate_function = allocate_function;
queue->free_function = free_function;
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 )
{
int i;
for ( i = 0; i < queue->num_packets; ++i )
{
queue->free_function( queue->allocator_context, queue->packet_data[i] );
}
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 )
{
netcode_assert( queue );
netcode_assert( packet_data );
if ( queue->num_packets == NETCODE_PACKET_QUEUE_SIZE )
{
queue->free_function( queue->allocator_context, 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
{
void * allocator_context;
void * (*allocate_function)(void*,uint64_t);
void (*free_function)(void*,void*);
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( void * allocator_context,
void * (*allocate_function)(void*,uint64_t),
void (*free_function)(void*,void*) )
{
if ( allocate_function == NULL )
{
allocate_function = netcode_default_allocate_function;
}
if ( free_function == NULL )
{
free_function = netcode_default_free_function;
}
struct netcode_network_simulator_t * network_simulator = (struct netcode_network_simulator_t*)
allocate_function( allocator_context, sizeof( struct netcode_network_simulator_t ) );
netcode_assert( network_simulator );
memset( network_simulator, 0, sizeof( struct netcode_network_simulator_t ) );
network_simulator->allocator_context = allocator_context;
network_simulator->allocate_function = allocate_function;
network_simulator->free_function = free_function;
return network_simulator;
}
void netcode_network_simulator_reset( struct netcode_network_simulator_t * network_simulator )
{
netcode_assert( network_simulator );
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "network simulator reset\n" );
int i;
for ( i = 0; i < NETCODE_NETWORK_SIMULATOR_NUM_PACKET_ENTRIES; ++i )
{
network_simulator->free_function( network_simulator->allocator_context, 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 )
{
network_simulator->free_function( network_simulator->allocator_context, 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;
}
void netcode_network_simulator_destroy( struct netcode_network_simulator_t * network_simulator )
{
netcode_assert( network_simulator );
netcode_network_simulator_reset( network_simulator );
network_simulator->free_function( network_simulator->allocator_context, network_simulator );
}
float netcode_random_float( float a, float b )
{
netcode_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*) network_simulator->allocate_function( network_simulator->allocator_context, 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 )
{
netcode_assert( network_simulator );
netcode_assert( from );
netcode_assert( from->type != 0 );
netcode_assert( to );
netcode_assert( to->type != 0 );
netcode_assert( packet_data );
netcode_assert( packet_bytes > 0 );
netcode_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 )
{
network_simulator->free_function( network_simulator->allocator_context, 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 )
{
netcode_assert( network_simulator );
netcode_assert( max_packets >= 0 );
netcode_assert( packet_data );
netcode_assert( packet_bytes );
netcode_assert( from );
netcode_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++;
}
netcode_assert( num_packets <= max_packets );
return num_packets;
}
void netcode_network_simulator_update( struct netcode_network_simulator_t * network_simulator, double time )
{
netcode_assert( network_simulator );
network_simulator->time = time;
// discard any pending receive packets that are still in the buffer
int i;
for ( i = 0; i < network_simulator->num_pending_receive_packets; ++i )
{
if ( network_simulator->pending_receive_packets[i].packet_data )
{
network_simulator->free_function( network_simulator->allocator_context, network_simulator->pending_receive_packets[i].packet_data );
network_simulator->pending_receive_packets[i].packet_data = NULL;
}
}
network_simulator->num_pending_receive_packets = 0;
// walk across packet entries and move any that are ready to be received into the pending receive buffer
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;
}
}
}
// ----------------------------------------------------------------
NETCODE_CONST 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:
netcode_assert( 0 );
return "???";
}
}
void netcode_default_client_config( struct netcode_client_config_t * config )
{
netcode_assert( config );
config->allocator_context = NULL;
config->allocate_function = netcode_default_allocate_function;
config->free_function = netcode_default_free_function;
config->network_simulator = NULL;
config->callback_context = NULL;
config->state_change_callback = NULL;
config->send_loopback_packet_callback = NULL;
config->override_send_and_receive = 0;
config->send_packet_override = NULL;
config->receive_packet_override = NULL;
};
struct netcode_client_t
{
struct netcode_client_config_t config;
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_socket_holder_t socket_holder;
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];
int loopback;
};
int netcode_client_socket_create( struct netcode_socket_t * socket,
struct netcode_address_t * address,
int send_buffer_size,
int receive_buffer_size,
NETCODE_CONST struct netcode_client_config_t * config )
{
netcode_assert( socket );
netcode_assert( address );
netcode_assert( config );
if ( !config->network_simulator )
{
if ( !config->override_send_and_receive )
{
if ( netcode_socket_create( socket, address, send_buffer_size, receive_buffer_size ) != NETCODE_SOCKET_ERROR_NONE )
{
return 0;
}
}
}
else
{
if ( address->port == 0 )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: must bind to a specific port when using network simulator\n" );
return 0;
}
}
return 1;
}
struct netcode_client_t * netcode_client_create_overload( NETCODE_CONST char * address1_string,
NETCODE_CONST char * address2_string,
NETCODE_CONST struct netcode_client_config_t * config,
double time )
{
netcode_assert( config );
netcode_assert( netcode.initialized );
struct netcode_address_t address1;
struct netcode_address_t address2;
memset( &address1, 0, sizeof( address1 ) );
memset( &address2, 0, sizeof( address2 ) );
if ( netcode_parse_address( address1_string, &address1 ) != NETCODE_OK )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to parse client address\n" );
return NULL;
}
if ( address2_string != NULL && netcode_parse_address( address2_string, &address2 ) != NETCODE_OK )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to parse client address2\n" );
return NULL;
}
struct netcode_socket_t socket_ipv4;
struct netcode_socket_t socket_ipv6;
memset( &socket_ipv4, 0, sizeof( socket_ipv4 ) );
memset( &socket_ipv6, 0, sizeof( socket_ipv6 ) );
if ( address1.type == NETCODE_ADDRESS_IPV4 || address2.type == NETCODE_ADDRESS_IPV4 )
{
if ( !netcode_client_socket_create( &socket_ipv4, address1.type == NETCODE_ADDRESS_IPV4 ? &address1 : &address2, NETCODE_CLIENT_SOCKET_SNDBUF_SIZE, NETCODE_CLIENT_SOCKET_RCVBUF_SIZE, config ) )
{
return NULL;
}
}
if ( address1.type == NETCODE_ADDRESS_IPV6 || address2.type == NETCODE_ADDRESS_IPV6 )
{
if ( !netcode_client_socket_create( &socket_ipv6, address1.type == NETCODE_ADDRESS_IPV6 ? &address1 : &address2, NETCODE_CLIENT_SOCKET_SNDBUF_SIZE, NETCODE_CLIENT_SOCKET_RCVBUF_SIZE, config ) )
{
return NULL;
}
}
struct netcode_client_t * client = (struct netcode_client_t*) config->allocate_function( config->allocator_context, sizeof( struct netcode_client_t ) );
if ( !client )
{
netcode_socket_destroy( &socket_ipv4 );
netcode_socket_destroy( &socket_ipv6 );
return NULL;
}
struct netcode_address_t socket_address = address1.type == NETCODE_ADDRESS_IPV4 ? socket_ipv4.address : socket_ipv6.address;
if ( !config->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", socket_address.port );
}
client->config = *config;
client->socket_holder.ipv4 = socket_ipv4;
client->socket_holder.ipv6 = socket_ipv6;
client->address = config->network_simulator ? address1 : socket_address;
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;
client->loopback = 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, config->allocator_context, config->allocate_function, config->free_function );
netcode_replay_protection_reset( &client->replay_protection );
return client;
}
struct netcode_client_t * netcode_client_create( NETCODE_CONST char * address,
NETCODE_CONST struct netcode_client_config_t * config,
double time )
{
return netcode_client_create_overload( address, NULL, config, time );
}
void netcode_client_destroy( struct netcode_client_t * client )
{
netcode_assert( client );
if ( !client->loopback )
netcode_client_disconnect( client );
else
netcode_client_disconnect_loopback( client );
netcode_socket_destroy( &client->socket_holder.ipv4 );
netcode_socket_destroy( &client->socket_holder.ipv6 );
netcode_packet_queue_clear( &client->packet_receive_queue );
client->config.free_function( client->config.allocator_context, 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 ) );
if ( client->config.state_change_callback )
{
client->config.state_change_callback( client->config.callback_context, client->state, client_state );
}
client->state = client_state;
}
void netcode_client_reset_before_next_connect( struct netcode_client_t * client )
{
client->connect_start_time = client->time;
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 )
{
netcode_assert( client );
client->sequence = 0;
client->loopback = 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;
client->config.free_function( client->config.allocator_context, 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 )
{
netcode_assert( client );
netcode_assert( connect_token );
netcode_client_disconnect( client );
if ( netcode_read_connect_token( connect_token, NETCODE_CONNECT_TOKEN_BYTES, &client->connect_token ) != NETCODE_OK )
{
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];
char server_address_string[NETCODE_MAX_ADDRESS_STRING_LENGTH];
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connecting to server %s [%d/%d]\n",
netcode_address_to_string( &client->server_address, server_address_string ), client->server_address_index + 1, client->connect_token.num_server_addresses );
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_internal( struct netcode_client_t * client, struct netcode_address_t * from, uint8_t * packet, uint64_t sequence )
{
netcode_assert( client );
netcode_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;
}
client->config.free_function( client->config.allocator_context, packet );
}
void netcode_client_process_packet( struct netcode_client_t * client, struct netcode_address_t * from, uint8_t * packet_data, int packet_bytes )
{
(void) client;
(void) from;
(void) packet_data;
(void) packet_bytes;
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 );
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,
client->config.allocator_context,
client->config.allocate_function );
if ( !packet )
return;
netcode_client_process_packet_internal( client, from, (uint8_t*)packet, sequence );
}
void netcode_client_receive_packets( struct netcode_client_t * client )
{
netcode_assert( client );
netcode_assert( !client->loopback );
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->config.network_simulator )
{
// process packets received from socket
while ( 1 )
{
struct netcode_address_t from;
uint8_t packet_data[NETCODE_MAX_PACKET_BYTES];
int packet_bytes = 0;
if ( client->config.override_send_and_receive )
{
packet_bytes = client->config.receive_packet_override( client->config.callback_context, &from, packet_data, NETCODE_MAX_PACKET_BYTES );
}
else if ( client->server_address.type == NETCODE_ADDRESS_IPV4 )
{
packet_bytes = netcode_socket_receive_packet( &client->socket_holder.ipv4, &from, packet_data, NETCODE_MAX_PACKET_BYTES );
}
else if ( client->server_address.type == NETCODE_ADDRESS_IPV6 )
{
packet_bytes = netcode_socket_receive_packet( &client->socket_holder.ipv6, &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,
client->config.allocator_context,
client->config.allocate_function );
if ( !packet )
continue;
netcode_client_process_packet_internal( client, &from, (uint8_t*)packet, sequence );
}
}
else
{
// process packets received from network simulator
int num_packets_received = netcode_network_simulator_receive_packets( client->config.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,
client->config.allocator_context,
client->config.allocate_function );
client->config.free_function( client->config.allocator_context, client->receive_packet_data[i] );
if ( !packet )
continue;
netcode_client_process_packet_internal( client, &client->receive_from[i], (uint8_t*)packet, sequence );
}
}
}
void netcode_client_send_packet_to_server_internal( struct netcode_client_t * client, void * packet )
{
netcode_assert( client );
netcode_assert( !client->loopback );
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 );
netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES );
if ( client->config.network_simulator )
{
netcode_network_simulator_send_packet( client->config.network_simulator, &client->address, &client->server_address, packet_data, packet_bytes );
}
else
{
if ( client->config.override_send_and_receive )
{
client->config.send_packet_override( client->config.callback_context, &client->server_address, packet_data, packet_bytes );
}
else if ( client->server_address.type == NETCODE_ADDRESS_IPV4 )
{
netcode_socket_send_packet( &client->socket_holder.ipv4, &client->server_address, packet_data, packet_bytes );
}
else if ( client->server_address.type == NETCODE_ADDRESS_IPV6 )
{
netcode_socket_send_packet( &client->socket_holder.ipv6, &client->server_address, packet_data, packet_bytes );
}
}
client->last_packet_send_time = client->time;
}
void netcode_client_send_packets( struct netcode_client_t * client )
{
netcode_assert( client );
netcode_assert( !client->loopback );
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 )
{
netcode_assert( client );
if ( client->server_address_index + 1 >= client->connect_token.num_server_addresses )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client has no more servers to connect to\n" );
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_INFO, "client connecting to next server %s [%d/%d]\n",
netcode_address_to_string( &client->server_address, server_address_string ),
client->server_address_index + 1,
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 )
{
netcode_assert( client );
client->time = time;
if ( client->loopback )
return;
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->time - client->connect_start_time >= connect_token_expire_seconds )
{
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->connect_token.timeout_seconds > 0 && 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->connect_token.timeout_seconds > 0 && 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->connect_token.timeout_seconds > 0 && 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_packet_sequence( struct netcode_client_t * client )
{
netcode_assert( client );
return client->sequence;
}
void netcode_client_send_packet( struct netcode_client_t * client, NETCODE_CONST uint8_t * packet_data, int packet_bytes )
{
netcode_assert( client );
netcode_assert( packet_data );
netcode_assert( packet_bytes >= 0 );
netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_SIZE );
if ( client->state != NETCODE_CLIENT_STATE_CONNECTED )
return;
if ( !client->loopback )
{
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 );
}
else
{
client->config.send_loopback_packet_callback( client->config.callback_context,
client->client_index,
packet_data,
packet_bytes,
client->sequence++ );
}
}
uint8_t * netcode_client_receive_packet( struct netcode_client_t * client, int * packet_bytes, uint64_t * packet_sequence )
{
netcode_assert( client );
netcode_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 )
{
netcode_assert( packet->packet_type == NETCODE_CONNECTION_PAYLOAD_PACKET );
*packet_bytes = packet->payload_bytes;
netcode_assert( *packet_bytes >= 0 );
netcode_assert( *packet_bytes <= NETCODE_MAX_PACKET_BYTES );
return (uint8_t*) &packet->payload_data;
}
else
{
return NULL;
}
}
void netcode_client_free_packet( struct netcode_client_t * client, void * packet )
{
netcode_assert( client );
netcode_assert( packet );
uint8_t * packet_data = (uint8_t*) packet;
int offset = offsetof( struct netcode_connection_payload_packet_t, payload_data );
client->config.free_function( client->config.allocator_context, packet_data - offset );
}
void netcode_client_disconnect( struct netcode_client_t * client )
{
netcode_assert( client );
netcode_assert( !client->loopback );
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 )
{
netcode_assert( !client->loopback );
netcode_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 ( !client->loopback && 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 )
{
netcode_assert( client );
return client->state;
}
int netcode_client_index( struct netcode_client_t * client )
{
netcode_assert( client );
return client->client_index;
}
int netcode_client_max_clients( struct netcode_client_t * client )
{
netcode_assert( client );
return client->max_clients;
}
void netcode_client_connect_loopback( struct netcode_client_t * client, int client_index, int max_clients )
{
netcode_assert( client );
netcode_assert( client->state <= NETCODE_CLIENT_STATE_DISCONNECTED );
netcode_printf( NETCODE_LOG_LEVEL_INFO, "client connected to server via loopback as client %d\n", client_index );
client->state = NETCODE_CLIENT_STATE_CONNECTED;
client->client_index = client_index;
client->max_clients = max_clients;
client->loopback = 1;
}
void netcode_client_disconnect_loopback( struct netcode_client_t * client )
{
netcode_assert( client );
netcode_assert( client->loopback );
netcode_client_reset_connection_data( client, NETCODE_CLIENT_STATE_DISCONNECTED );
}
int netcode_client_loopback( struct netcode_client_t * client )
{
netcode_assert( client );
return client->loopback;
}
void netcode_client_process_loopback_packet( struct netcode_client_t * client, NETCODE_CONST uint8_t * packet_data, int packet_bytes, uint64_t packet_sequence )
{
netcode_assert( client );
netcode_assert( client->loopback );
struct netcode_connection_payload_packet_t * packet = netcode_create_payload_packet( packet_bytes, client->config.allocator_context, client->config.allocate_function );
if ( !packet )
return;
memcpy( packet->payload_data, packet_data, packet_bytes );
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "client processing loopback packet from server\n" );
netcode_packet_queue_push( &client->packet_receive_queue, packet, packet_sequence );
}
uint16_t netcode_client_get_port( struct netcode_client_t * client )
{
netcode_assert( client );
return client->address.type == NETCODE_ADDRESS_IPV4 ? client->socket_holder.ipv4.address.port : client->socket_holder.ipv6.address.port;
}
struct netcode_address_t * netcode_client_server_address( struct netcode_client_t * client )
{
netcode_assert( client );
return &client->server_address;
}
// ----------------------------------------------------------------
#define NETCODE_MAX_ENCRYPTION_MAPPINGS ( NETCODE_MAX_CLIENTS * 4 )
struct netcode_encryption_manager_t
{
int num_encryption_mappings;
int timeout[NETCODE_MAX_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" );
netcode_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->timeout, 0, sizeof( encryption_manager->timeout ) );
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_entry_expired( struct netcode_encryption_manager_t * encryption_manager, int index, double time )
{
return ( encryption_manager->timeout[index] > 0 && ( encryption_manager->last_access_time[index] + encryption_manager->timeout[index] ) < time ) ||
( encryption_manager->expire_time[index] >= 0.0 && encryption_manager->expire_time[index] < time );
}
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 timeout )
{
int i;
for ( i = 0; i < encryption_manager->num_encryption_mappings; ++i )
{
if ( netcode_address_equal( &encryption_manager->address[i], address ) && !netcode_encryption_manager_entry_expired( encryption_manager, i, time ) )
{
encryption_manager->timeout[i] = timeout;
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->address[i].type == NETCODE_ADDRESS_NONE || netcode_encryption_manager_entry_expired( encryption_manager, i, time ) )
{
encryption_manager->timeout[i] = timeout;
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 )
{
netcode_assert( encryption_manager );
netcode_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 ( !netcode_encryption_manager_entry_expired( encryption_manager, index, time ) )
{
break;
}
encryption_manager->address[index].type = NETCODE_ADDRESS_NONE;
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 ) && !netcode_encryption_manager_entry_expired( encryption_manager, 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 )
{
netcode_assert( index >= 0 );
netcode_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 )
{
netcode_assert( index >= 0 );
netcode_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 )
{
netcode_assert( encryption_manager );
if ( index == -1 )
return NULL;
netcode_assert( index >= 0 );
netcode_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 )
{
netcode_assert( encryption_manager );
if ( index == -1 )
return NULL;
netcode_assert( index >= 0 );
netcode_assert( index < encryption_manager->num_encryption_mappings );
return encryption_manager->receive_key + index * NETCODE_KEY_BYTES;
}
int netcode_encryption_manager_get_timeout( struct netcode_encryption_manager_t * encryption_manager, int index )
{
netcode_assert( encryption_manager );
if ( index == -1 )
return 0;
netcode_assert( index >= 0 );
netcode_assert( index < encryption_manager->num_encryption_mappings );
return encryption_manager->timeout[index];
}
// ----------------------------------------------------------------
#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 )
{
netcode_assert( connect_token_entries );
netcode_assert( address );
netcode_assert( mac );
// find the matching entry for the token mac and the oldest token entry. constant time worst case. This is intentional!
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;
}
}
// if no entry is found with the mac, this is a new connect token. replace the oldest token entry.
netcode_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;
}
// allow connect tokens we have already seen from the same address
netcode_assert( matching_token_index >= 0 );
netcode_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)
void netcode_default_server_config( struct netcode_server_config_t * config )
{
netcode_assert( config );
config->allocator_context = NULL;
config->allocate_function = netcode_default_allocate_function;
config->free_function = netcode_default_free_function;
config->network_simulator = NULL;
config->callback_context = NULL;
config->connect_disconnect_callback = NULL;
config->send_loopback_packet_callback = NULL;
config->override_send_and_receive = 0;
config->send_packet_override = NULL;
config->receive_packet_override = NULL;
};
struct netcode_server_t
{
struct netcode_server_config_t config;
struct netcode_socket_holder_t socket_holder;
struct netcode_address_t address;
uint32_t flags;
double time;
int running;
int max_clients;
int num_connected_clients;
uint64_t global_sequence;
uint64_t challenge_sequence;
uint8_t challenge_key[NETCODE_KEY_BYTES];
int client_connected[NETCODE_MAX_CLIENTS];
int client_timeout[NETCODE_MAX_CLIENTS];
int client_loopback[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];
};
int netcode_server_socket_create( struct netcode_socket_t * socket,
struct netcode_address_t * address,
int send_buffer_size,
int receive_buffer_size,
NETCODE_CONST struct netcode_server_config_t * config )
{
netcode_assert( socket );
netcode_assert( address );
netcode_assert( config );
if ( !config->network_simulator )
{
if ( !config->override_send_and_receive )
{
if ( netcode_socket_create( socket, address, send_buffer_size, receive_buffer_size ) != NETCODE_SOCKET_ERROR_NONE )
{
return 0;
}
}
}
return 1;
}
struct netcode_server_t * netcode_server_create_overload( NETCODE_CONST char * server_address1_string, NETCODE_CONST char * server_address2_string, NETCODE_CONST struct netcode_server_config_t * config, double time )
{
netcode_assert( config );
netcode_assert( netcode.initialized );
struct netcode_address_t server_address1;
struct netcode_address_t server_address2;
memset( &server_address1, 0, sizeof( server_address1 ) );
memset( &server_address2, 0, sizeof( server_address2 ) );
if ( netcode_parse_address( server_address1_string, &server_address1 ) != NETCODE_OK )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to parse server public address\n" );
return NULL;
}
if ( server_address2_string != NULL && netcode_parse_address( server_address2_string, &server_address2 ) != NETCODE_OK )
{
netcode_printf( NETCODE_LOG_LEVEL_ERROR, "error: failed to parse server public address2\n" );
return NULL;
}
struct netcode_address_t bind_address_ipv4;
struct netcode_address_t bind_address_ipv6;
memset( &bind_address_ipv4, 0, sizeof( bind_address_ipv4 ) );
memset( &bind_address_ipv6, 0, sizeof( bind_address_ipv6 ) );
struct netcode_socket_t socket_ipv4;
struct netcode_socket_t socket_ipv6;
memset( &socket_ipv4, 0, sizeof( socket_ipv4 ) );
memset( &socket_ipv6, 0, sizeof( socket_ipv6 ) );
if ( server_address1.type == NETCODE_ADDRESS_IPV4 || server_address2.type == NETCODE_ADDRESS_IPV4 )
{
bind_address_ipv4.type = NETCODE_ADDRESS_IPV4;
bind_address_ipv4.port = server_address1.type == NETCODE_ADDRESS_IPV4 ? server_address1.port : server_address2.port;
if ( !netcode_server_socket_create( &socket_ipv4, &bind_address_ipv4, NETCODE_SERVER_SOCKET_SNDBUF_SIZE, NETCODE_SERVER_SOCKET_RCVBUF_SIZE, config ) )
{
return NULL;
}
}
if ( server_address1.type == NETCODE_ADDRESS_IPV6 || server_address2.type == NETCODE_ADDRESS_IPV6 )
{
bind_address_ipv6.type = NETCODE_ADDRESS_IPV6;
bind_address_ipv6.port = server_address1.type == NETCODE_ADDRESS_IPV6 ? server_address1.port : server_address2.port;
if ( !netcode_server_socket_create( &socket_ipv6, &bind_address_ipv6, NETCODE_SERVER_SOCKET_SNDBUF_SIZE, NETCODE_SERVER_SOCKET_RCVBUF_SIZE, config ) )
{
return NULL;
}
}
struct netcode_server_t * server = (struct netcode_server_t*) config->allocate_function( config->allocator_context, sizeof( struct netcode_server_t ) );
if ( !server )
{
netcode_socket_destroy( &socket_ipv4 );
netcode_socket_destroy( &socket_ipv6 );
return NULL;
}
if ( !config->network_simulator )
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server listening on %s\n", server_address1_string );
}
else
{
netcode_printf( NETCODE_LOG_LEVEL_INFO, "server listening on %s (network simulator)\n", server_address1_string );
}
server->config = *config;
server->socket_holder.ipv4 = socket_ipv4;
server->socket_holder.ipv6 = socket_ipv6;
server->address = server_address1;
server->flags = 0;
server->time = time;
server->running = 0;
server->max_clients = 0;
server->num_connected_clients = 0;
server->global_sequence = 1ULL << 63;
memset( server->client_connected, 0, sizeof( server->client_connected ) );
memset( server->client_loopback, 0, sizeof( server->client_loopback ) );
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, sizeof( server->client_user_data ) );
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( NETCODE_CONST char * server_address_string, NETCODE_CONST struct netcode_server_config_t * config, double time )
{
return netcode_server_create_overload( server_address_string, NULL, config, time );
}
void netcode_server_stop( struct netcode_server_t * server );
void netcode_server_destroy( struct netcode_server_t * server )
{
netcode_assert( server );
netcode_server_stop( server );
netcode_socket_destroy( &server->socket_holder.ipv4 );
netcode_socket_destroy( &server->socket_holder.ipv6 );
server->config.free_function( server->config.allocator_context, server );
}
void netcode_server_start( struct netcode_server_t * server, int max_clients )
{
netcode_assert( server );
netcode_assert( max_clients > 0 );
netcode_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], server->config.allocator_context, server->config.allocate_function, server->config.free_function );
}
}
void netcode_server_send_global_packet( struct netcode_server_t * server, void * packet, struct netcode_address_t * to, uint8_t * packet_key )
{
netcode_assert( server );
netcode_assert( packet );
netcode_assert( to );
netcode_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->config.protocol_id );
netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES );
if ( server->config.network_simulator )
{
netcode_network_simulator_send_packet( server->config.network_simulator, &server->address, to, packet_data, packet_bytes );
}
else
{
if ( server->config.override_send_and_receive )
{
server->config.send_packet_override( server->config.callback_context, to, packet_data, packet_bytes );
}
else if ( to->type == NETCODE_ADDRESS_IPV4 )
{
netcode_socket_send_packet( &server->socket_holder.ipv4, to, packet_data, packet_bytes );
}
else if ( to->type == NETCODE_ADDRESS_IPV6 )
{
netcode_socket_send_packet( &server->socket_holder.ipv6, to, packet_data, packet_bytes );
}
}
server->global_sequence++;
}
void netcode_server_send_client_packet( struct netcode_server_t * server, void * packet, int client_index )
{
netcode_assert( server );
netcode_assert( packet );
netcode_assert( client_index >= 0 );
netcode_assert( client_index < server->max_clients );
netcode_assert( server->client_connected[client_index] );
netcode_assert( !server->client_loopback[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->config.protocol_id );
netcode_assert( packet_bytes <= NETCODE_MAX_PACKET_BYTES );
if ( server->config.network_simulator )
{
netcode_network_simulator_send_packet( server->config.network_simulator, &server->address, &server->client_address[client_index], packet_data, packet_bytes );
}
else
{
if ( server->config.override_send_and_receive )
{
server->config.send_packet_override( server->config.callback_context, &server->client_address[client_index], packet_data, packet_bytes );
}
else
{
if ( server->client_address[client_index].type == NETCODE_ADDRESS_IPV4 )
{
netcode_socket_send_packet( &server->socket_holder.ipv4, &server->client_address[client_index], packet_data, packet_bytes );
}
else if ( server->client_address[client_index].type == NETCODE_ADDRESS_IPV6 )
{
netcode_socket_send_packet( &server->socket_holder.ipv6, &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 )