* remove -Wextra from makefile to make older compilers happy
[blender.git] / extern / verse / dist / v_connection.c
1 /*
2 **
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "v_cmd_buf.h"
10 #include "v_network_in_que.h"
11 #include "v_network_out_que.h"
12 #include "v_cmd_gen.h"
13 #include "v_connection.h"
14 #include "v_encryption.h"
15 #include "v_util.h"
16
17 #if !defined(V_GENERATE_FUNC_MODE)
18 #include "verse.h"
19
20 #define CONNECTION_CHUNK_SIZE   16
21 #define V_MAX_CONNECT_PACKET_SIZE       1500
22 #define V_CON_MAX_MICROSECOND_BETWEEN_SENDS     100
23 #define V_RE_CONNECTON_TIME_OUT 4
24 #define V_CONNECTON_TIME_OUT 30
25
26 typedef struct {
27         VNetOutQueue    *out_queue;
28         VNetInQueue             in_queue;
29         VNetworkAddress network_address;
30         boolean                 connected;
31         unsigned int    avatar;
32 /*      unsigned int    packet_id;*/
33         int32                   timedelta_s;
34         uint32                  timedelta_f;
35         boolean                 destroy_flag;
36         void                    *ordered_storage;
37         char                    name[V_ENCRYPTION_LOGIN_KEY_SIZE / 2];
38         char                    pass[V_ENCRYPTION_LOGIN_KEY_SIZE / 2];
39         VConnectStage   connect_stage;
40         unsigned int    stage_atempts;
41         uint8                   key_my[V_ENCRYPTION_LOGIN_KEY_FULL_SIZE];
42         uint8                   key_other[V_ENCRYPTION_LOGIN_KEY_FULL_SIZE];
43         uint8                   key_data[V_ENCRYPTION_DATA_KEY_SIZE];
44         uint8                   *expected_key;
45 } VConnection;
46
47 static struct {
48         VConnection             *con;
49         unsigned int    con_count;
50         unsigned int    current_connection;
51         VNetworkAddress *connect_address;
52         void                    *unified_func_storage;
53         uint16                  connect_port;
54         unsigned int    pending_packets;
55         uint8                   host_id[V_ENCRYPTION_LOGIN_KEY_FULL_SIZE];
56 } VConData;
57
58 extern void cmd_buf_init(void);
59
60 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 */
61 {
62         static boolean v_con_initialized = FALSE;
63
64         if(v_con_initialized)
65                 return;
66         cmd_buf_init();
67         v_con_initialized = TRUE;
68         VConData.con = malloc(CONNECTION_CHUNK_SIZE * sizeof *VConData.con);
69         memset(VConData.con, 0, CONNECTION_CHUNK_SIZE * sizeof *VConData.con);  /* Clear the memory. */
70         VConData.con_count = 0;
71         VConData.pending_packets = 0;
72 /*      v_e_connect_create_key(&VConData.host_id[V_ENCRYPTION_LOGIN_PRIVATE_START],
73                        &VConData.host_id[V_ENCRYPTION_LOGIN_PUBLIC_START],
74                        &VConData.host_id[V_ENCRYPTION_LOGIN_N_START]);*/ /* default host id if none is set by user */
75 }
76
77 void verse_set_port(uint16 port)
78 {
79         v_n_set_port(port);
80 }
81
82 void verse_host_id_create(uint8 *id)
83 {
84         v_e_connect_create_key(&id[V_ENCRYPTION_LOGIN_PRIVATE_START],
85                                &id[V_ENCRYPTION_LOGIN_PUBLIC_START], &id[V_ENCRYPTION_LOGIN_N_START]);
86 }
87
88 void verse_host_id_set(uint8 *id)
89 {
90         memcpy(VConData.host_id, id, V_ENCRYPTION_LOGIN_KEY_FULL_SIZE);
91 }
92
93 extern void *v_fs_create_func_storage(void);
94 extern void *v_create_ordered_storage(void);
95 extern void v_destroy_ordered_storage(void *data);
96
97 void *v_con_connect(uint32 ip, uint16 port, VConnectStage stage) /* creates a new connection slot */
98 {
99         v_con_init(); /* init connections, if not done yet */
100         if((VConData.con_count - 1) % CONNECTION_CHUNK_SIZE == 0) /* do we need more slots for connections, then reallocate more space */
101                 VConData.con = realloc(VConData.con, (sizeof *VConData.con) * (VConData.con_count + CONNECTION_CHUNK_SIZE));
102         VConData.con[VConData.con_count].out_queue = v_noq_create_network_queue(); /* create a out queue fo all out going commands */
103         v_niq_clear(&VConData.con[VConData.con_count].in_queue); /* clear and init the que of incomming packets.*/
104         VConData.con[VConData.con_count].connected = FALSE; /* not yet propperly connected and should not accept commands yet */
105         VConData.con[VConData.con_count].network_address.ip = ip; /* ip address of other side */
106         VConData.con[VConData.con_count].network_address.port = port; /* port used by other side */
107         VConData.con[VConData.con_count].avatar = 0; /* no avatar set yet*/
108 /*      VConData.con[VConData.con_count].packet_id = 2;*/
109         VConData.con[VConData.con_count].destroy_flag = FALSE; /* this is a flag that is set once the connection is about to be destroyed.*/
110         VConData.con[VConData.con_count].ordered_storage = v_create_ordered_storage();
111         VConData.con[VConData.con_count].name[0] = 0; /* nouser name set yet */
112         VConData.con[VConData.con_count].pass[0] = 0; /* no password set yet */
113         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 */
114         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*/
115         VConData.con[VConData.con_count].timedelta_s = 0; /* number of seconds since last incomming packet to the connection*/
116         VConData.con[VConData.con_count].timedelta_f = 0; /* number of fractions of a second since last incomming packet to the connection*/
117         VConData.con[VConData.con_count].expected_key = NULL; /* expected hist id if this is a client */
118         VConData.current_connection = VConData.con_count; /* set the new connection to the current*/
119         ++VConData.con_count; /* add one to the number of connections*/
120         return VConData.con[VConData.current_connection].out_queue;
121 }
122
123 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*/
124 {
125         unsigned int i;
126         for(i = 0; i < VConData.con_count && VConData.con[i].out_queue != session; i++);
127         if(i < VConData.con_count)
128         {
129                 VConData.con[i].destroy_flag = TRUE;
130         }
131 }
132
133 void verse_session_set(VSession session) /* find a session and make it the current*/
134 {
135         unsigned int i;
136         for(i = 0; i < VConData.con_count && session != VConData.con[i].out_queue; i++);
137         if(i < VConData.con_count)
138                 VConData.current_connection = i;
139 }
140
141 VSession verse_session_get(void)
142 {
143         if(VConData.current_connection < VConData.con_count)
144                 return VConData.con[VConData.current_connection].out_queue;
145         return NULL;
146 }
147
148 uint32 v_co_find_connection(uint32 ip, uint16 port) /* if a packet comes form a ip address what connection does it belong to? */
149 {
150         unsigned int i;
151
152         for(i = 0; i < VConData.con_count; i++)
153         {
154                 if(ip == VConData.con[i].network_address.ip &&
155                    port == VConData.con[i].network_address.port &&
156                    VConData.con[i].destroy_flag == 0)
157                 {
158                         return i;
159                 }
160         }
161         return -1;
162 }
163
164 boolean v_co_switch_connection(uint32 ip, uint16 port) /* switches to the current connection to one ip address if it exists */
165 {
166         unsigned int i;
167         for(i = 0; i < VConData.con_count; i++)
168         {
169                 if(ip == VConData.con[i].network_address.ip && port == VConData.con[i].network_address.port)
170                 {
171                         VConData.current_connection = i;
172                         return TRUE;
173                 }
174         }
175         return FALSE;
176 }
177
178 void v_con_inqueue_timer_update(void)
179 {
180         if(VConData.current_connection < VConData.con_count)
181         {
182                 v_niq_timer_update(&VConData.con[VConData.current_connection].in_queue);
183         }
184 }
185
186 /*
187 extern void     v_fs_buf_unpack(const uint8 *data, unsigned int length);
188 extern void     v_fs_buf_store_pack(uint8 *data, unsigned int length);
189 extern boolean  v_fs_buf_unpack_stored(void);
190 */
191 extern void v_unpack_connection(const char *buf, unsigned int buffer_length);
192
193 extern void     verse_send_packet_nak(uint32 packet_id);
194 extern void     v_callback_connect_terminate(const char *bye);
195 extern boolean  v_connect_unpack_ping(const char *buf, size_t buffer_length, uint32 ip, uint16 port);
196 extern void     v_ping_update(void);
197 void v_fs_unpack_beginning(uint8 *data, unsigned int length);
198
199 /* Main function that receives and distributes all incoming packets. */
200 boolean v_con_network_listen(void)
201 {
202         VNetworkAddress address;
203         uint8 buf[V_MAX_CONNECT_PACKET_SIZE], *store;
204         int size = 0;
205         unsigned int connection;
206         uint32 packet_id;
207         boolean ret = FALSE;
208
209         v_con_init(); /* Init if needed. */
210         connection = VConData.current_connection; /* Store current connection in a local variable so that we can restore it later. */
211         size = v_n_receive_data(&address, buf, sizeof buf); /* Ask for incoming data from the network. */
212         while(size != -1 && size != 0) /* Did we get any data? */
213         {
214                 VConData.current_connection = v_co_find_connection(address.ip, address.port); /* Is there a connection matching the IP and port? */
215                 vnp_raw_unpack_uint32(buf, &packet_id); /* Unpack the ID of the packet. */
216 /*              printf("got packet ID %u, %d bytes, connection %u\n", packet_id, size, VConData.current_connection);*/
217                 if(VConData.current_connection < VConData.con_count &&
218                    !(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. */
219                 {
220                         if(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED) /* Is this connection initialized? */
221                         {
222                                 store = v_niq_store(&VConData.con[VConData.current_connection].in_queue, size, packet_id); /* Store the packet. */
223                                 if(store != NULL)
224                                 {
225                                         VConData.pending_packets++; /* We now have one more packet pending unpack. */
226                                         v_e_data_decrypt_packet(store, buf, size, VConData.con[VConData.current_connection].key_data); /* Decrypt the packet. */
227                                         v_fs_unpack_beginning(store, size);
228                                 }
229                         }
230                         else
231                         {
232                                 v_unpack_connection(buf, size); /* This is an ongoing connecton-attempt. */
233                                 v_niq_timer_update(&VConData.con[VConData.current_connection].in_queue);
234                         }
235                 }
236                 else if(v_connect_unpack_ping(buf, size, address.ip, address.port))     /* Ping handled. */
237                         ;
238                 else if(v_fs_func_accept_connections()) /* Do we accept connection-attempts? */
239                 {
240                         if(VConData.current_connection >= VConData.con_count ||
241                            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? */
242                         {
243                                 if(VConData.current_connection < VConData.con_count)
244                                 {
245                                         VConData.con[VConData.current_connection].network_address.ip = 0;
246                                         VConData.con[VConData.current_connection].destroy_flag = TRUE; /* Destroy old connection if there is one. */
247                                 }
248                                 v_con_connect(address.ip, address.port, V_CS_IDLE); /* Create a new connection. */
249                                 v_unpack_connection(buf, size); /* Unpack the connection-attempt. */
250                         }
251                 }
252                 else
253                 {
254                         fprintf(stderr, __FILE__ ": Unhandled packet--dropping\n");
255                         if(VConData.con_count > 0)
256                         {
257                                 fprintf(stderr, __FILE__ ": State: connections=%u, current=%u (stage %u), packet_id=%u\n",
258                                         VConData.con_count,
259                                         VConData.current_connection,
260                                         (VConData.current_connection < VConData.con_count) ? VConData.con[VConData.current_connection].connect_stage : 0,
261                                         packet_id);
262                         }
263                 }
264                 size = v_n_receive_data(&address, buf, sizeof buf); /* See if there are more incoming packets. */
265                 ret = TRUE;
266         }
267         VConData.current_connection = connection; /* Reset the current connection. */
268
269         return ret;
270 }
271
272 extern void     v_update_connection_pending(boolean resend);
273
274 boolean v_con_callback_update(void)
275 {
276         static unsigned int seconds;
277         boolean output = FALSE;
278         unsigned int    size, connection, s;
279         VNetInPacked    *p;
280
281         v_n_get_current_time(&s, NULL);
282         connection = VConData.current_connection;
283         for(VConData.current_connection = 0; VConData.current_connection < VConData.con_count; VConData.current_connection++)
284                 if(VConData.con[VConData.current_connection].connect_stage != V_CS_CONNECTED)
285                         v_update_connection_pending(s != seconds);
286         seconds = s;
287         VConData.current_connection = connection;
288         if(VConData.pending_packets == 0)
289                 return FALSE;
290         if(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED)
291         {
292                 while((p = v_niq_get(&VConData.con[VConData.current_connection].in_queue, &size)) != NULL)
293                 {
294                         VConData.pending_packets--;
295                         v_fs_unpack(p->data, size);
296                         v_niq_release(&VConData.con[VConData.current_connection].in_queue, p);
297                         output = TRUE;
298                 }
299                 v_con_network_listen();
300         }
301         return output;
302 }
303
304 void verse_callback_update(unsigned int microseconds)
305 {
306         unsigned int connection, passed;
307
308         v_ping_update();        /* Deliver any pending pings. */
309         connection = VConData.current_connection;
310         for(VConData.current_connection = 0; VConData.current_connection < VConData.con_count; VConData.current_connection++)
311         {
312                 if(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED)
313                         v_noq_send_queue(VConData.con[VConData.current_connection].out_queue, &VConData.con[VConData.current_connection].network_address);
314                 if(VConData.con[VConData.current_connection].destroy_flag == TRUE)
315                 {
316                         v_noq_destroy_network_queue(VConData.con[VConData.current_connection].out_queue);
317                         VConData.pending_packets -= v_niq_free(&VConData.con[VConData.current_connection].in_queue);
318                         v_destroy_ordered_storage(VConData.con[VConData.current_connection].ordered_storage);
319                         if(VConData.con[VConData.current_connection].expected_key != NULL)
320                                 free(VConData.con[VConData.current_connection].expected_key);
321                         if(VConData.con_count - 1 != VConData.current_connection)
322                                 VConData.con[VConData.current_connection] = VConData.con[VConData.con_count - 1];
323                         VConData.con_count--;
324                         if(connection >= VConData.con_count)
325                                 VConData.current_connection = 0;
326                         return;
327                 }
328         }
329         VConData.current_connection = connection;
330
331         if(VConData.con_count > 0)
332         {
333 /*              printf("checking timeout of stage %d connection %u\n",
334                        VConData.con[VConData.current_connection].connect_stage, VConData.current_connection);
335 */              if(V_CONNECTON_TIME_OUT < v_niq_time_out(&VConData.con[VConData.current_connection].in_queue))
336                 {
337                         if(VConData.con[VConData.current_connection].connect_stage != V_CS_CONNECTED)
338                         {
339                                 VConData.con[VConData.current_connection].destroy_flag = TRUE;
340                         }
341                         else
342                                 v_callback_connect_terminate("connection timed out");
343                 }
344         }
345
346         v_con_network_listen();
347         if(VConData.con_count > 0)
348                 if(v_con_callback_update())
349                         return;
350         for(passed = 0; passed < microseconds && VConData.pending_packets == 0;)
351         {
352                 boolean update;
353                 if(microseconds - passed > V_CON_MAX_MICROSECOND_BETWEEN_SENDS) /* Still a long way to go? */
354                         passed += v_n_wait_for_incoming(V_CON_MAX_MICROSECOND_BETWEEN_SENDS);
355                 else
356                         passed += v_n_wait_for_incoming(microseconds - passed);
357                 do
358                 {
359                         update = v_con_network_listen();
360                         connection = VConData.current_connection;
361                         for(VConData.current_connection = 0; VConData.current_connection < VConData.con_count; VConData.current_connection++)
362                         {
363                                 if(VConData.con[VConData.current_connection].connect_stage == V_CS_CONNECTED)
364                                 {
365                                         if(v_noq_send_queue(VConData.con[VConData.current_connection].out_queue, &VConData.con[VConData.current_connection].network_address))
366                                                 update = TRUE;
367                                 }
368                         }
369                         VConData.current_connection = connection;
370                 } while(update);
371         }
372         if(VConData.con_count > 0)
373                 v_con_callback_update();
374 }
375
376 void v_con_set_name_pass(const char *name, const char *pass)
377 {
378         v_strlcpy(VConData.con[VConData.current_connection].name, name, sizeof VConData.con[VConData.current_connection].name);
379         v_strlcpy(VConData.con[VConData.current_connection].pass, pass, sizeof VConData.con[VConData.current_connection].pass);
380 }
381
382 const char * v_con_get_name(void)
383 {
384         return VConData.con[VConData.current_connection].name;
385 }
386
387 const char * v_con_get_pass(void)
388 {
389         return VConData.con[VConData.current_connection].pass;
390 }
391
392 void v_con_set_connect_stage(VConnectStage stage)
393 {
394         VConData.con[VConData.current_connection].connect_stage = stage;
395         VConData.con[VConData.current_connection].stage_atempts = 0;
396 }
397
398 VConnectStage v_con_get_connect_stage(void)
399 {
400         return VConData.con[VConData.current_connection].connect_stage;
401 }
402
403 uint8 *v_con_get_my_key(void)
404 {
405         return VConData.con[VConData.current_connection].key_my;
406 }
407
408 uint8 *v_con_get_other_key(void)
409 {
410         return VConData.con[VConData.current_connection].key_other;
411 }
412
413 uint8 **v_con_get_expected_key(void)
414 {
415         return &VConData.con[VConData.current_connection].expected_key;
416 }
417
418 uint8 * v_con_get_host_id(void)
419 {
420         return VConData.host_id;
421 }
422
423 void v_con_set_data_key(const uint8 *key)
424 {
425         memcpy(VConData.con[VConData.current_connection].key_data, key, V_ENCRYPTION_DATA_KEY_SIZE);
426 }
427
428 const uint8 * v_con_get_data_key(void)
429 {
430         return VConData.con[VConData.current_connection].key_data;
431 }
432
433 void * v_con_get_network_queue(void)
434 {
435         return VConData.con[VConData.current_connection].out_queue;
436 }
437
438 VNetworkAddress * v_con_get_network_address(void)
439 {
440         return &VConData.con[VConData.current_connection].network_address;
441 }
442
443 void * v_con_get_ordered_storage(void)
444 {
445         return VConData.con[VConData.current_connection].ordered_storage;
446 }
447
448 void v_con_set_avatar(uint32 avatar)
449 {
450         VConData.con[VConData.current_connection].avatar = avatar;
451 }
452
453 uint32 verse_session_get_avatar(void)
454 {
455         return VConData.con[VConData.current_connection].avatar;
456 }
457
458 void verse_session_get_time(uint32 *seconds, uint32 *fractions)
459 {
460         uint32 s, f;
461         v_n_get_current_time(&s, &f);
462         if((uint32)~0 - f < VConData.con[VConData.current_connection].timedelta_f)
463                 s++;
464         if(seconds != NULL) 
465         {
466                 if(VConData.con[VConData.current_connection].timedelta_s < 0)
467                         *seconds = s - (uint32)(-VConData.con[VConData.current_connection].timedelta_s);
468                 else
469                         *seconds = s + VConData.con[VConData.current_connection].timedelta_s;
470         }
471         if(fractions != NULL)
472                 *fractions = f + VConData.con[VConData.current_connection].timedelta_f;
473 }
474
475 void v_con_set_time(uint32 seconds, uint32 fractions)
476 {
477         uint32 s, f;
478         v_n_get_current_time(&s, &f);
479
480         if(f < fractions)
481                 s--;
482         if (s < seconds)
483                 VConData.con[VConData.current_connection].timedelta_s = -(int)(seconds - s);
484         else
485                 VConData.con[VConData.current_connection].timedelta_s = (int)(s - seconds);
486         VConData.con[VConData.current_connection].timedelta_f = f - fractions;
487 }
488
489 #endif