summaryrefslogtreecommitdiff
path: root/toxcore/friend_connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxcore/friend_connection.c')
-rw-r--r--toxcore/friend_connection.c565
1 files changed, 565 insertions, 0 deletions
diff --git a/toxcore/friend_connection.c b/toxcore/friend_connection.c
new file mode 100644
index 00000000..09dea4c3
--- /dev/null
+++ b/toxcore/friend_connection.c
@@ -0,0 +1,565 @@
1/* friend_connection.c
2 *
3 * Connection to friends.
4 *
5 * Copyright (C) 2014 Tox project All Rights Reserved.
6 *
7 * This file is part of Tox.
8 *
9 * Tox is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * Tox is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "friend_connection.h"
29#include "util.h"
30
31/* return 1 if the friendcon_id is not valid.
32 * return 0 if the friendcon_id is valid.
33 */
34static uint8_t friendconn_id_not_valid(const Friend_Connections *fr_c, int friendcon_id)
35{
36 if ((unsigned int)friendcon_id >= fr_c->num_cons)
37 return 1;
38
39 if (fr_c->conns == NULL)
40 return 1;
41
42 if (fr_c->conns[friendcon_id].status == FRIENDCONN_STATUS_NONE)
43 return 1;
44
45 return 0;
46}
47
48
49/* Set the size of the friend connections list to num.
50 *
51 * return -1 if realloc fails.
52 * return 0 if it succeeds.
53 */
54static int realloc_friendconns(Friend_Connections *fr_c, uint32_t num)
55{
56 if (num == 0) {
57 free(fr_c->conns);
58 fr_c->conns = NULL;
59 return 0;
60 }
61
62 Friend_Conn *newgroup_cons = realloc(fr_c->conns, num * sizeof(Friend_Conn));
63
64 if (newgroup_cons == NULL)
65 return -1;
66
67 fr_c->conns = newgroup_cons;
68 return 0;
69}
70
71/* Create a new empty friend connection.
72 *
73 * return -1 on failure.
74 * return friendcon_id on success.
75 */
76static int create_friend_conn(Friend_Connections *fr_c)
77{
78 uint32_t i;
79
80 for (i = 0; i < fr_c->num_cons; ++i) {
81 if (fr_c->conns[i].status == FRIENDCONN_STATUS_NONE)
82 return i;
83 }
84
85 int id = -1;
86
87 if (realloc_friendconns(fr_c, fr_c->num_cons + 1) == 0) {
88 id = fr_c->num_cons;
89 ++fr_c->num_cons;
90 memset(&(fr_c->conns[id]), 0, sizeof(Friend_Conn));
91 }
92
93 return id;
94}
95
96/* Wipe a friend connection.
97 *
98 * return -1 on failure.
99 * return 0 on success.
100 */
101static int wipe_friend_conn(Friend_Connections *fr_c, int friendcon_id)
102{
103 if (friendconn_id_not_valid(fr_c, friendcon_id))
104 return -1;
105
106 uint32_t i;
107 memset(&(fr_c->conns[friendcon_id]), 0 , sizeof(Friend_Conn));
108
109 for (i = fr_c->num_cons; i != 0; --i) {
110 if (fr_c->conns[i - 1].status != FRIENDCONN_STATUS_NONE)
111 break;
112 }
113
114 if (fr_c->num_cons != i) {
115 fr_c->num_cons = i;
116 realloc_friendconns(fr_c, fr_c->num_cons);
117 }
118
119 return 0;
120}
121
122static Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id)
123{
124 if (friendconn_id_not_valid(fr_c, friendcon_id))
125 return 0;
126
127 return &fr_c->conns[friendcon_id];
128}
129
130/* return friendcon_id corresponding to the real public key on success.
131 * return -1 on failure.
132 */
133int getfriend_conn_id_pk(Friend_Connections *fr_c, const uint8_t *real_pk)
134{
135 uint32_t i;
136
137 for (i = 0; i < fr_c->num_cons; ++i) {
138 Friend_Conn *friend_con = get_conn(fr_c, i);
139
140 if (friend_con) {
141 if (memcmp(friend_con->real_public_key, real_pk, crypto_box_PUBLICKEYBYTES) == 0)
142 return i;
143 }
144 }
145
146 return -1;
147}
148
149/* callback for recv TCP relay nodes. */
150static int tcp_relay_node_callback(void *object, uint32_t number, IP_Port ip_port, const uint8_t *public_key)
151{
152 Friend_Connections *fr_c = object;
153 Friend_Conn *friend_con = get_conn(fr_c, number);
154
155 if (!friend_con)
156 return -1;
157
158 if (friend_con->crypt_connection_id != -1) {
159 return add_tcp_relay_peer(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, public_key);
160 } else {
161 return add_tcp_relay(fr_c->net_crypto, ip_port, public_key);
162 }
163}
164
165static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id);
166/* Callback for DHT ip_port changes. */
167static void dht_ip_callback(void *object, int32_t number, IP_Port ip_port)
168{
169 Friend_Connections *fr_c = object;
170 Friend_Conn *friend_con = get_conn(fr_c, number);
171
172 if (!friend_con)
173 return;
174
175 if (friend_con->crypt_connection_id == -1) {
176 friend_new_connection(fr_c, number);
177 }
178
179 set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port);
180 friend_con->dht_ip_port = ip_port;
181 friend_con->dht_ip_port_lastrecv = unix_time();
182}
183
184/* Callback for dht public key changes. */
185static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key)
186{
187 Friend_Connections *fr_c = object;
188 Friend_Conn *friend_con = get_conn(fr_c, number);
189
190 if (!friend_con)
191 return;
192
193 friend_con->dht_ping_lastrecv = unix_time();
194
195 if (memcmp(friend_con->dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES) == 0)
196 return;
197
198 if (friend_con->dht_lock) {
199 if (DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock) != 0) {
200 printf("a. Could not delete dht peer. Please report this.\n");
201 return;
202 }
203
204 friend_con->dht_lock = 0;
205 }
206
207 DHT_addfriend(fr_c->dht, dht_public_key, dht_ip_callback, object, number, &friend_con->dht_lock);
208
209 if (friend_con->crypt_connection_id == -1) {
210 friend_new_connection(fr_c, number);
211 }
212
213 set_connection_dht_public_key(fr_c->net_crypto, friend_con->crypt_connection_id, dht_public_key);
214 onion_set_friend_DHT_pubkey(fr_c->onion_c, friend_con->onion_friendnum, dht_public_key);
215
216 memcpy(friend_con->dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES);
217}
218
219static int handle_status(void *object, int number, uint8_t status)
220{
221 Friend_Connections *fr_c = object;
222 Friend_Conn *friend_con = get_conn(fr_c, number);
223
224 if (!friend_con)
225 return -1;
226
227 if (status) { /* Went online. */
228 friend_con->status = FRIENDCONN_STATUS_CONNECTED;
229 friend_con->ping_lastrecv = unix_time();
230 onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
231 } else { /* Went offline. */
232 friend_con->status = FRIENDCONN_STATUS_CONNECTING;
233 friend_con->crypt_connection_id = -1;
234 friend_con->dht_ping_lastrecv = unix_time();
235 }
236
237 unsigned int i;
238
239 for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
240 if (friend_con->callbacks[i].status_callback)
241 friend_con->callbacks[i].status_callback(friend_con->callbacks[i].status_callback_object,
242 friend_con->callbacks[i].status_callback_id, status);
243 }
244
245 return 0;
246}
247
248static int handle_packet(void *object, int number, uint8_t *data, uint16_t length)
249{
250 if (length == 0)
251 return -1;
252
253 Friend_Connections *fr_c = object;
254 Friend_Conn *friend_con = get_conn(fr_c, number);
255
256 if (!friend_con)
257 return -1;
258
259 if (data[0] == PACKET_ID_ALIVE) {
260 friend_con->ping_lastrecv = unix_time();
261 return 0;
262 }
263
264 unsigned int i;
265
266 for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
267 if (friend_con->callbacks[i].data_callback)
268 friend_con->callbacks[i].data_callback(friend_con->callbacks[i].data_callback_object,
269 friend_con->callbacks[i].data_callback_id, data, length);
270 }
271
272 return 0;
273}
274
275static int handle_lossy_packet(void *object, int number, const uint8_t *data, uint16_t length)
276{
277 if (length == 0)
278 return -1;
279
280 Friend_Connections *fr_c = object;
281 Friend_Conn *friend_con = get_conn(fr_c, number);
282
283 if (!friend_con)
284 return -1;
285
286 unsigned int i;
287
288 for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
289 if (friend_con->callbacks[i].lossy_data_callback)
290 friend_con->callbacks[i].lossy_data_callback(friend_con->callbacks[i].lossy_data_callback_object,
291 friend_con->callbacks[i].lossy_data_callback_id, data, length);
292 }
293
294 return 0;
295}
296
297static int handle_new_connections(void *object, New_Connection *n_c)
298{
299 Friend_Connections *fr_c = object;
300 int friendcon_id = getfriend_conn_id_pk(fr_c, n_c->public_key);
301 Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
302
303 if (friend_con) {
304
305 if (friend_con->crypt_connection_id != -1)
306 return -1;
307
308 int id = accept_crypto_connection(fr_c->net_crypto, n_c);
309 connection_status_handler(fr_c->net_crypto, id, &handle_status, fr_c, friendcon_id);
310 connection_data_handler(fr_c->net_crypto, id, &handle_packet, fr_c, friendcon_id);
311 connection_lossy_data_handler(fr_c->net_crypto, id, &handle_lossy_packet, fr_c, friendcon_id);
312 friend_con->crypt_connection_id = id;
313
314 if (n_c->source.ip.family != AF_INET && n_c->source.ip.family != AF_INET6) {
315 set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port);
316 } else {
317 friend_con->dht_ip_port = n_c->source;
318 friend_con->dht_ip_port_lastrecv = unix_time();
319 }
320
321 dht_pk_callback(fr_c, friendcon_id, n_c->dht_public_key);
322
323 nc_dht_pk_callback(fr_c->net_crypto, id, &dht_pk_callback, fr_c, friendcon_id);
324 return 0;
325 }
326
327 return -1;
328}
329
330static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id)
331{
332 Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
333
334 if (!friend_con)
335 return -1;
336
337 if (friend_con->crypt_connection_id != -1) {
338 return -1;
339 }
340
341 int id = new_crypto_connection(fr_c->net_crypto, friend_con->real_public_key);
342
343 if (id == -1)
344 return -1;
345
346 friend_con->crypt_connection_id = id;
347 connection_status_handler(fr_c->net_crypto, id, &handle_status, fr_c, friendcon_id);
348 connection_data_handler(fr_c->net_crypto, id, &handle_packet, fr_c, friendcon_id);
349 connection_lossy_data_handler(fr_c->net_crypto, id, &handle_lossy_packet, fr_c, friendcon_id);
350 nc_dht_pk_callback(fr_c->net_crypto, id, &dht_pk_callback, fr_c, friendcon_id);
351
352 return 0;
353}
354
355static int send_ping(const Friend_Connections *fr_c, int friendcon_id)
356{
357 Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
358
359 if (!friend_con)
360 return -1;
361
362 uint8_t ping = PACKET_ID_ALIVE;
363 int64_t ret = write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, &ping, sizeof(ping), 0);
364
365 if (ret != -1) {
366 friend_con->ping_lastsent = unix_time();
367 return 0;
368 }
369
370 return -1;
371}
372
373/* Set the callbacks for the friend connection.
374 * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array.
375 *
376 * return 0 on success.
377 * return -1 on failure
378 */
379int friend_connection_callbacks(Friend_Connections *fr_c, int friendcon_id, unsigned int index,
380 int (*status_callback)(void *object, int id, uint8_t status), int (*data_callback)(void *object, int id, uint8_t *data,
381 uint16_t length), int (*lossy_data_callback)(void *object, int id, const uint8_t *data, uint16_t length), void *object,
382 int number)
383{
384 Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
385
386 if (!friend_con)
387 return -1;
388
389 if (index >= MAX_FRIEND_CONNECTION_CALLBACKS)
390 return -1;
391
392 friend_con->callbacks[index].status_callback = status_callback;
393 friend_con->callbacks[index].data_callback = data_callback;
394 friend_con->callbacks[index].lossy_data_callback = lossy_data_callback;
395
396 friend_con->callbacks[index].status_callback_object =
397 friend_con->callbacks[index].data_callback_object =
398 friend_con->callbacks[index].lossy_data_callback_object = object;
399
400 friend_con->callbacks[index].status_callback_id =
401 friend_con->callbacks[index].data_callback_id =
402 friend_con->callbacks[index].lossy_data_callback_id = number;
403 return 0;
404}
405
406/* return the crypt_connection_id for the connection.
407 *
408 * return crypt_connection_id on success.
409 * return -1 on failure.
410 */
411int friend_connection_crypt_connection_id(Friend_Connections *fr_c, int friendcon_id)
412{
413 Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
414
415 if (!friend_con)
416 return -1;
417
418 return friend_con->crypt_connection_id;
419}
420
421/* Create a new friend connection.
422 * If one to that real public key already exists, increase lock count and return it.
423 *
424 * return -1 on failure.
425 * return connection id on success.
426 */
427int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key)
428{
429 int friendcon_id = getfriend_conn_id_pk(fr_c, real_public_key);
430
431 if (friendcon_id != -1) {
432 ++fr_c->conns[friendcon_id].lock_count;
433 return friendcon_id;
434 }
435
436 friendcon_id = create_friend_conn(fr_c);
437
438 if (friendcon_id == -1)
439 return -1;
440
441 int32_t onion_friendnum = onion_addfriend(fr_c->onion_c, real_public_key);
442
443 if (onion_friendnum == -1)
444 return -1;
445
446 Friend_Conn *friend_con = &fr_c->conns[friendcon_id];
447
448 friend_con->crypt_connection_id = -1;
449 friend_con->status = FRIENDCONN_STATUS_CONNECTING;
450 memcpy(friend_con->real_public_key, real_public_key, crypto_box_PUBLICKEYBYTES);
451 friend_con->onion_friendnum = onion_friendnum;
452
453 recv_tcp_relay_handler(fr_c->onion_c, onion_friendnum, &tcp_relay_node_callback, fr_c, friendcon_id);
454 onion_dht_pk_callback(fr_c->onion_c, onion_friendnum, &dht_pk_callback, fr_c, friendcon_id);
455
456 return friendcon_id;
457}
458
459/* Kill a friend connection.
460 *
461 * return -1 on failure.
462 * return 0 on success.
463 */
464int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id)
465{
466 Friend_Conn *friend_con = get_conn(fr_c, friendcon_id);
467
468 if (!friend_con)
469 return -1;
470
471 if (friend_con->lock_count) {
472 --friend_con->lock_count;
473 return 0;
474 }
475
476 onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum);
477 crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
478
479 if (friend_con->dht_lock) {
480 DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
481 }
482
483 return wipe_friend_conn(fr_c, friendcon_id);
484}
485
486
487/* Create new friend_connections instance. */
488Friend_Connections *new_friend_connections(Onion_Client *onion_c)
489{
490 if (!onion_c)
491 return NULL;
492
493 Friend_Connections *temp = calloc(1, sizeof(Friend_Connections));
494
495 if (temp == NULL)
496 return NULL;
497
498 temp->dht = onion_c->dht;
499 temp->net_crypto = onion_c->c;
500 temp->onion_c = onion_c;
501
502 new_connection_handler(temp->net_crypto, &handle_new_connections, temp);
503
504 return temp;
505}
506
507/* main friend_connections loop. */
508void do_friend_connections(Friend_Connections *fr_c)
509{
510 uint32_t i;
511 uint64_t temp_time = unix_time();
512
513 for (i = 0; i < fr_c->num_cons; ++i) {
514 Friend_Conn *friend_con = get_conn(fr_c, i);
515
516 if (friend_con) {
517 if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) {
518 if (friend_con->dht_ping_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
519 if (friend_con->dht_lock) {
520 DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
521 friend_con->dht_lock = 0;
522 }
523 }
524
525 if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
526 friend_con->dht_ip_port.ip.family = 0;
527 }
528
529 if (friend_con->dht_lock) {
530 if (friend_new_connection(fr_c, i) == 0) {
531 set_connection_dht_public_key(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_temp_pk);
532 set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port);
533 }
534 }
535
536 } else if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) {
537 if (friend_con->ping_lastsent + FRIEND_PING_INTERVAL < temp_time) {
538 send_ping(fr_c, i);
539 }
540
541 if (friend_con->ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) {
542 /* If we stopped receiving ping packets, kill it. */
543 crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
544 friend_con->crypt_connection_id = -1;
545 handle_status(fr_c, i, 0); /* Going offline. */
546 }
547 }
548 }
549 }
550}
551
552/* Free everything related with friend_connections. */
553void kill_friend_connections(Friend_Connections *fr_c)
554{
555 if (!fr_c)
556 return;
557
558 uint32_t i;
559
560 for (i = 0; i < fr_c->num_cons; ++i) {
561 kill_friend_connection(fr_c, i);
562 }
563
564 free(fr_c);
565}