blender/extern/verse/dist/v_connection.c

485 lines
17 KiB
C

/*
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "v_cmd_buf.h"
#include "v_network_in_que.h"
#include "v_network_out_que.h"
#include "v_cmd_gen.h"
#include "v_connection.h"
#include "v_encryption.h"
#include "v_util.h"
#if !defined(V_GENERATE_FUNC_MODE)
#include "verse.h"
#define CONNECTION_CHUNK_SIZE 16
#define V_MAX_CONNECT_PACKET_SIZE 1500
#define V_CON_MAX_MICROSECOND_BETWEEN_SENDS 100
#define V_RE_CONNECTON_TIME_OUT 4
#define V_CONNECTON_TIME_OUT 30
typedef struct {
VNetOutQueue *out_queue;
VNetInQueue in_queue;
VNetworkAddress network_address;
boolean connected;
unsigned int avatar;
/* unsigned int packet_id;*/
int32 timedelta_s;
uint32 timedelta_f;
boolean destroy_flag;
void *ordered_storage;
char name[V_ENCRYPTION_LOGIN_KEY_SIZE / 2];
char pass[V_ENCRYPTION_LOGIN_KEY_SIZE / 2];
VConnectStage connect_stage;
unsigned int stage_atempts;
uint8 key_my[V_ENCRYPTION_LOGIN_KEY_FULL_SIZE];
uint8 key_other[V_ENCRYPTION_LOGIN_KEY_FULL_SIZE];
uint8 key_data[V_ENCRYPTION_DATA_KEY_SIZE];
uint8 *expected_key;
} VConnection;
static struct {
VConnection *con;
unsigned int con_count;
unsigned int current_connection;
VNetworkAddress *connect_address;
void *unified_func_storage;
uint16 connect_port;
unsigned int pending_packets;
uint8 host_id[V_ENCRYPTION_LOGIN_KEY_FULL_SIZE];
} VConData;
extern void cmd_buf_init(void);
void v_con_init(void) /* since verse doesnt have an init function this function is runned over an ove ard starts whit a check it it has run before */
{
static boolean v_con_initialized = FALSE;
if(v_con_initialized)
return;
cmd_buf_init();
v_con_initialized = TRUE;
VConData.con = malloc(CONNECTION_CHUNK_SIZE * sizeof *VConData.con);
memset(VConData.con, 0, CONNECTION_CHUNK_SIZE * sizeof *VConData.con); /* Clear the memory. */
VConData.con_count = 0;
VConData.pending_packets = 0;
/* v_e_connect_create_key(&VConData.host_id[V_ENCRYPTION_LOGIN_PRIVATE_START],
&VConData.host_id[V_ENCRYPTION_LOGIN_PUBLIC_START],
&VConData.host_id[V_ENCRYPTION_LOGIN_N_START]);*/ /* default host id if none is set by user */
}
void verse_set_port(uint16 port)
{
v_n_set_port(port);
}
void verse_host_id_create(uint8 *id)
{
v_e_connect_create_key(&id[V_ENCRYPTION_LOGIN_PRIVATE_START],
&id[V_ENCRYPTION_LOGIN_PUBLIC_START], &id[V_ENCRYPTION_LOGIN_N_START]);
}
void verse_host_id_set(uint8 *id)
{
memcpy(VConData.host_id, id, V_ENCRYPTION_LOGIN_KEY_FULL_SIZE);
}
extern void *v_fs_create_func_storage(void);
extern void *v_create_ordered_storage(void);
extern void v_destroy_ordered_storage(void *data);
void *v_con_connect(uint32 ip, uint16 port, VConnectStage stage) /* creates a new connection slot */
{
v_con_init(); /* init connections, if not done yet */
if((VConData.con_count - 1) % CONNECTION_CHUNK_SIZE == 0) /* do we need more slots for connections, then reallocate more space */
VConData.con = realloc(VConData.con, (sizeof *VConData.con) * (VConData.con_count + CONNECTION_CHUNK_SIZE));
VConData.con[VConData.con_count].out_queue = v_noq_create_network_queue(); /* create a out queue fo all out going commands */
v_niq_clear(&VConData.con[VConData.con_count].in_queue); /* clear and init the que of incomming packets.*/
VConData.con[VConData.con_count].connected = FALSE; /* not yet propperly connected and should not accept commands yet */
VConData.con[VConData.con_count].network_address.ip = ip; /* ip address of other side */
VConData.con[VConData.con_count].network_address.port = port; /* port used by other side */
VConData.con[VConData.con_count].avatar = 0; /* no avatar set yet*/
/* VConData.con[VConData.con_count].packet_id = 2;*/
VConData.con[VConData.con_count].destroy_flag = FALSE; /* this is a flag that is set once the connection is about to be destroyed.*/
VConData.con[VConData.con_count].ordered_storage = v_create_ordered_storage();
VConData.con[VConData.con_count].name[0] = 0; /* nouser name set yet */
VConData.con[VConData.con_count].pass[0] = 0; /* no password set yet */
VConData.con[VConData.con_count].connect_stage = stage; /* this is the stage of the connection, it show if the connection is ready, the init state depends if this is a client or host */
VConData.con[VConData.con_count].stage_atempts = 0; /* each stage in the connection prosess is atempted multiple times to avoid failiure if packets get lost*/
VConData.con[VConData.con_count].timedelta_s = 0; /* number of seconds since last incomming packet to the connection*/
VConData.con[VConData.con_count].timedelta_f = 0; /* number of fractions of a second since last incomming packet to the connection*/
VConData.con[VConData.con_count].expected_key = NULL; /* expected hist id if this is a client */
VConData.current_connection = VConData.con_count; /* set the new connection to the current*/
++VConData.con_count; /* add one to the number of connections*/
return VConData.con[VConData.current_connection].out_queue;
}
void verse_session_destroy(VSession session) /* a session can not be destroyed right away, because this function might be called inside a call back from the session it tryes tpo destroy, therfor it only markes it*/
{
unsigned int i;
for(i = 0; i < VConData.con_count && VConData.con[i].out_queue != session; i++);
if(i < VConData.con_count)
{
VConData.con[i].destroy_flag = TRUE;
}
}
void verse_session_set(VSession session) /* find a session and make it the current*/
{
unsigned int i;
for(i = 0; i < VConData.con_count && session != VConData.con[i].out_queue; i++);
if(i < VConData.con_count)
VConData.current_connection = i;
}
VSession verse_session_get(void)
{
if(VConData.current_connection < VConData.con_count)
return VConData.con[VConData.current_connection].out_queue;
return NULL;
}
uint32 v_co_find_connection(uint32 ip, uint16 port) /* if a packet comes form a ip address what connection does it belong to? */
{
unsigned int i;
for(i = 0; i < VConData.con_count; i++)
{
if(ip == VConData.con[i].network_address.ip &&
port == VConData.con[i].network_address.port &&
VConData.con[i].destroy_flag == 0)
{
return i;
}
}
return -1;
}
boolean v_co_switch_connection(uint32 ip, uint16 port) /* switches to the current connection to one ip address if it exists */
{
unsigned int i;
for(i = 0; i < VConData.con_count; i++)
{
if(ip == VConData.con[i].network_address.ip && port == VConData.con[i].network_address.port)
{
VConData.current_connection = i;
return TRUE;
}
}
return FALSE;
}
void v_con_inqueue_timer_update(void)
{
if(VConData.current_connection < VConData.con_count)
{
v_niq_timer_update(&VConData.con[VConData.current_connection].in_queue);
}
}
/*
extern void v_fs_buf_unpack(const uint8 *data, unsigned int length);
extern void v_fs_buf_store_pack(uint8 *data, unsigned int length);
extern boolean v_fs_buf_unpack_stored(void);
*/
extern void v_unpack_connection(const char *buf, unsigned int buffer_length);
extern void verse_send_packet_nak(uint32 packet_id);
extern void v_callback_connect_terminate(const char *bye);
extern boolean v_connect_unpack_ping(const char *buf, size_t buffer_length, uint32 ip, uint16 port);
extern void v_ping_update(void);
void v_fs_unpack_beginning(uint8 *data, unsigned int length);
/* Main function that receives and distributes all incoming packets. */
boolean v_con_network_listen(void)
{
VNetworkAddress address;
uint8 buf[V_MAX_CONNECT_PACKET_SIZE], *store;
int size = 0;
unsigned int connection;
uint32 packet_id;
boolean ret = FALSE;
v_con_init(); /* Init if needed. */
connection = VConData.current_connection; /* Store current connection in a local variable so that we can restore it later. */
size = v_n_receive_data(&address, buf, sizeof buf); /* Ask for incoming data from the network. */
while(size != -1 && size != 0) /* Did we get any data? */
{
VConData.current_connection = v_co_find_connection(address.ip, address.port); /* Is there a connection matching the IP and port? */
vnp_raw_unpack_uint32(buf, &packet_id); /* Unpack the ID of the packet. */
/* printf("got packet ID %u, %d bytes, connection %u\n", packet_id, size, VConData.current_connection);*/
if(VConData.current_connection < VConData.con_count &&
!(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED && packet_id == 0)) /* If this isn't a packet from an existing connection, disregard it. */
{
if(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED) /* Is this connection initialized? */
{
store = v_niq_store(&VConData.con[VConData.current_connection].in_queue, size, packet_id); /* Store the packet. */
if(store != NULL)
{
VConData.pending_packets++; /* We now have one more packet pending unpack. */
v_e_data_decrypt_packet(store, buf, size, VConData.con[VConData.current_connection].key_data); /* Decrypt the packet. */
v_fs_unpack_beginning(store, size);
}
}
else
{
v_unpack_connection(buf, size); /* This is an ongoing connecton-attempt. */
v_niq_timer_update(&VConData.con[VConData.current_connection].in_queue);
}
}
else if(v_connect_unpack_ping(buf, size, address.ip, address.port)) /* Ping handled. */
;
else if(v_fs_func_accept_connections()) /* Do we accept connection-attempts? */
{
if(VConData.current_connection >= VConData.con_count ||
V_RE_CONNECTON_TIME_OUT < v_niq_time_out(&VConData.con[VConData.current_connection].in_queue)) /* Is it a new client, or an old client that we haven't heard form in some time? */
{
if(VConData.current_connection < VConData.con_count)
{
VConData.con[VConData.current_connection].network_address.ip = 0;
VConData.con[VConData.current_connection].destroy_flag = TRUE; /* Destroy old connection if there is one. */
}
v_con_connect(address.ip, address.port, V_CS_IDLE); /* Create a new connection. */
v_unpack_connection(buf, size); /* Unpack the connection-attempt. */
}
}
else
{
fprintf(stderr, __FILE__ ": Unhandled packet--dropping\n");
fprintf(stderr, __FILE__ ": State: current=%u count=%u stage=%d id=%u\n",
VConData.current_connection,
VConData.con_count,
VConData.con[VConData.current_connection].connect_stage,
packet_id);
}
size = v_n_receive_data(&address, buf, sizeof buf); /* See if there are more incoming packets. */
ret = TRUE;
}
VConData.current_connection = connection; /* Reset the current connection. */
return ret;
}
extern void v_update_connection_pending(boolean resend);
boolean v_con_callback_update(void)
{
static unsigned int seconds;
boolean output = FALSE;
unsigned int size, connection, s;
VNetInPacked *p;
v_n_get_current_time(&s, NULL);
connection = VConData.current_connection;
for(VConData.current_connection = 0; VConData.current_connection < VConData.con_count; VConData.current_connection++)
if(VConData.con[VConData.current_connection].connect_stage != V_CS_CONNECTED)
v_update_connection_pending(s != seconds);
seconds = s;
VConData.current_connection = connection;
if(VConData.pending_packets == 0)
return FALSE;
if(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED)
{
while((p = v_niq_get(&VConData.con[VConData.current_connection].in_queue, &size)) != NULL)
{
VConData.pending_packets--;
v_fs_unpack(p->data, size);
v_niq_release(&VConData.con[VConData.current_connection].in_queue, p);
output = TRUE;
}
v_con_network_listen();
}
return output;
}
void verse_callback_update(unsigned int microseconds)
{
unsigned int connection, passed;
v_ping_update(); /* Deliver any pending pings. */
connection = VConData.current_connection;
for(VConData.current_connection = 0; VConData.current_connection < VConData.con_count; VConData.current_connection++)
{
if(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED)
v_noq_send_queue(VConData.con[VConData.current_connection].out_queue, &VConData.con[VConData.current_connection].network_address);
if(VConData.con[VConData.current_connection].destroy_flag == TRUE)
{
v_noq_destroy_network_queue(VConData.con[VConData.current_connection].out_queue);
VConData.pending_packets -= v_niq_free(&VConData.con[VConData.current_connection].in_queue);
v_destroy_ordered_storage(VConData.con[VConData.current_connection].ordered_storage);
if(VConData.con[VConData.current_connection].expected_key != NULL)
free(VConData.con[VConData.current_connection].expected_key);
VConData.con[VConData.current_connection] = VConData.con[--VConData.con_count];
if(connection >= VConData.con_count)
VConData.current_connection = 0;
return;
}
}
VConData.current_connection = connection;
if(VConData.con_count > 0)
{
/* printf("checking timeout of stage %d connection %u\n",
VConData.con[VConData.current_connection].connect_stage, VConData.current_connection);
*/ if(V_CONNECTON_TIME_OUT < v_niq_time_out(&VConData.con[VConData.current_connection].in_queue))
{
if(VConData.con[VConData.current_connection].connect_stage != V_CS_CONNECTED)
{
VConData.con[VConData.current_connection].destroy_flag = TRUE;
}
else
v_callback_connect_terminate("connection timed out");
}
}
v_con_network_listen();
if(VConData.con_count > 0)
if(v_con_callback_update())
return;
for(passed = 0; passed < microseconds && VConData.pending_packets == 0;)
{
boolean update;
if(microseconds - passed > V_CON_MAX_MICROSECOND_BETWEEN_SENDS) /* Still a long way to go? */
passed += v_n_wait_for_incoming(V_CON_MAX_MICROSECOND_BETWEEN_SENDS);
else
passed += v_n_wait_for_incoming(microseconds - passed);
do
{
update = v_con_network_listen();
connection = VConData.current_connection;
for(VConData.current_connection = 0; VConData.current_connection < VConData.con_count; VConData.current_connection++)
{
if(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED)
{
if(v_noq_send_queue(VConData.con[VConData.current_connection].out_queue, &VConData.con[VConData.current_connection].network_address))
update = TRUE;
}
}
VConData.current_connection = connection;
} while(update);
}
if(VConData.con_count > 0)
v_con_callback_update();
}
void v_con_set_name_pass(const char *name, const char *pass)
{
v_strlcpy(VConData.con[VConData.current_connection].name, name, sizeof VConData.con[VConData.current_connection].name);
v_strlcpy(VConData.con[VConData.current_connection].pass, pass, sizeof VConData.con[VConData.current_connection].pass);
}
const char * v_con_get_name(void)
{
return VConData.con[VConData.current_connection].name;
}
const char * v_con_get_pass(void)
{
return VConData.con[VConData.current_connection].pass;
}
void v_con_set_connect_stage(VConnectStage stage)
{
VConData.con[VConData.current_connection].connect_stage = stage;
VConData.con[VConData.current_connection].stage_atempts = 0;
}
VConnectStage v_con_get_connect_stage(void)
{
return VConData.con[VConData.current_connection].connect_stage;
}
uint8 *v_con_get_my_key(void)
{
return VConData.con[VConData.current_connection].key_my;
}
uint8 *v_con_get_other_key(void)
{
return VConData.con[VConData.current_connection].key_other;
}
uint8 **v_con_get_expected_key(void)
{
return &VConData.con[VConData.current_connection].expected_key;
}
uint8 * v_con_get_host_id(void)
{
return VConData.host_id;
}
void v_con_set_data_key(const uint8 *key)
{
memcpy(VConData.con[VConData.current_connection].key_data, key, V_ENCRYPTION_DATA_KEY_SIZE);
}
const uint8 * v_con_get_data_key(void)
{
return VConData.con[VConData.current_connection].key_data;
}
void * v_con_get_network_queue(void)
{
return VConData.con[VConData.current_connection].out_queue;
}
VNetworkAddress * v_con_get_network_address(void)
{
return &VConData.con[VConData.current_connection].network_address;
}
void * v_con_get_ordered_storage(void)
{
return VConData.con[VConData.current_connection].ordered_storage;
}
void v_con_set_avatar(uint32 avatar)
{
VConData.con[VConData.current_connection].avatar = avatar;
}
uint32 verse_session_get_avatar(void)
{
return VConData.con[VConData.current_connection].avatar;
}
void verse_session_get_time(uint32 *seconds, uint32 *fractions)
{
uint32 s, f;
v_n_get_current_time(&s, &f);
if((uint32)~0 - f < VConData.con[VConData.current_connection].timedelta_f)
s++;
if(seconds != NULL)
{
if(VConData.con[VConData.current_connection].timedelta_s < 0)
*seconds = s - (uint32)(-VConData.con[VConData.current_connection].timedelta_s);
else
*seconds = s + VConData.con[VConData.current_connection].timedelta_s;
}
if(fractions != NULL)
*fractions = f + VConData.con[VConData.current_connection].timedelta_f;
}
void v_con_set_time(uint32 seconds, uint32 fractions)
{
uint32 s, f;
v_n_get_current_time(&s, &f);
if(f < fractions)
s--;
if (s < seconds)
VConData.con[VConData.current_connection].timedelta_s = -(int)(seconds - s);
else
VConData.con[VConData.current_connection].timedelta_s = (int)(s - seconds);
VConData.con[VConData.current_connection].timedelta_f = f - fractions;
}
#endif