diff options
author | jin-eld <jin at mediatomb dot cc> | 2013-08-04 15:10:37 +0300 |
---|---|---|
committer | jin-eld <jin at mediatomb dot cc> | 2013-08-24 03:25:07 +0300 |
commit | e658892793c42b2d058eed0937025ef2ddaaa372 (patch) | |
tree | 2a022cab057f2c16ca95860ed980092880052f6e /toxcore | |
parent | e2aa8161adc85795fe4d63d4642f47e90937ddc2 (diff) |
Rename core directory because of autoconf name clash
While doing the checks configure might generate "core" files and will
then try to remove them. Having a "core" directory generates an error
while runing the configure script.
There's no workaround but to rename the core directory.
Diffstat (limited to 'toxcore')
-rw-r--r-- | toxcore/CMakeLists.txt | 45 | ||||
-rw-r--r-- | toxcore/DHT.c | 1264 | ||||
-rw-r--r-- | toxcore/DHT.h | 193 | ||||
-rw-r--r-- | toxcore/LAN_discovery.c | 152 | ||||
-rw-r--r-- | toxcore/LAN_discovery.h | 55 | ||||
-rw-r--r-- | toxcore/Lossless_UDP.c | 842 | ||||
-rw-r--r-- | toxcore/Lossless_UDP.h | 222 | ||||
-rw-r--r-- | toxcore/Messenger.c | 1020 | ||||
-rw-r--r-- | toxcore/Messenger.h | 351 | ||||
-rw-r--r-- | toxcore/friend_requests.c | 141 | ||||
-rw-r--r-- | toxcore/friend_requests.h | 70 | ||||
-rw-r--r-- | toxcore/net_crypto.c | 771 | ||||
-rw-r--r-- | toxcore/net_crypto.h | 200 | ||||
-rw-r--r-- | toxcore/network.c | 218 | ||||
-rw-r--r-- | toxcore/network.h | 159 | ||||
-rw-r--r-- | toxcore/packets.h | 31 | ||||
-rw-r--r-- | toxcore/ping.c | 229 | ||||
-rw-r--r-- | toxcore/ping.h | 17 | ||||
-rw-r--r-- | toxcore/tox.c | 374 | ||||
-rw-r--r-- | toxcore/tox.h | 289 | ||||
-rw-r--r-- | toxcore/util.c | 45 | ||||
-rw-r--r-- | toxcore/util.h | 12 |
22 files changed, 6700 insertions, 0 deletions
diff --git a/toxcore/CMakeLists.txt b/toxcore/CMakeLists.txt new file mode 100644 index 00000000..ecbb65c2 --- /dev/null +++ b/toxcore/CMakeLists.txt | |||
@@ -0,0 +1,45 @@ | |||
1 | cmake_minimum_required(VERSION 2.6.0) | ||
2 | project(toxcore C) | ||
3 | |||
4 | set(core_sources | ||
5 | DHT.c | ||
6 | network.c | ||
7 | Lossless_UDP.c | ||
8 | net_crypto.c | ||
9 | friend_requests.c | ||
10 | LAN_discovery.c | ||
11 | Messenger.c | ||
12 | util.c | ||
13 | ping.c | ||
14 | tox.c) | ||
15 | |||
16 | set(core_headers | ||
17 | DHT.h | ||
18 | network.h | ||
19 | Lossless_UDP.h | ||
20 | net_crypto.h | ||
21 | friend_requests.h | ||
22 | LAN_discovery.h | ||
23 | Messenger.h | ||
24 | util.h | ||
25 | ping.h) | ||
26 | |||
27 | add_library(toxcore SHARED ${core_sources}) | ||
28 | add_library(toxcore_static ${core_sources}) | ||
29 | set_target_properties(toxcore_static PROPERTIES OUTPUT_NAME toxcore) | ||
30 | |||
31 | target_link_libraries(toxcore ${LINK_CRYPTO_LIBRARY}) | ||
32 | |||
33 | install(TARGETS toxcore toxcore_static DESTINATION lib) | ||
34 | install(FILES ${core_headers} DESTINATION include) | ||
35 | |||
36 | if(WIN32) | ||
37 | target_link_libraries(toxcore ws2_32) | ||
38 | endif() | ||
39 | |||
40 | execute_process(COMMAND git rev-list HEAD --count OUTPUT_VARIABLE COMMIT) | ||
41 | |||
42 | # Write pkgconfig-file: | ||
43 | include(InstallPkgConfigFile) | ||
44 | install_pkg_config_file(toxcore CFLAGS LIBS -ltoxcore REQUIRES VERSION 0.1.1_r${COMMIT}) | ||
45 | |||
diff --git a/toxcore/DHT.c b/toxcore/DHT.c new file mode 100644 index 00000000..533425c4 --- /dev/null +++ b/toxcore/DHT.c | |||
@@ -0,0 +1,1264 @@ | |||
1 | /* DHT.c | ||
2 | * | ||
3 | * An implementation of the DHT as seen in http://wiki.tox.im/index.php/DHT | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | /*----------------------------------------------------------------------------------*/ | ||
25 | |||
26 | #include "DHT.h" | ||
27 | #include "packets.h" | ||
28 | #include "ping.h" | ||
29 | |||
30 | /* the number of seconds for a non responsive node to become bad. */ | ||
31 | #define BAD_NODE_TIMEOUT 70 | ||
32 | |||
33 | /* the max number of nodes to send with send nodes. */ | ||
34 | #define MAX_SENT_NODES 8 | ||
35 | |||
36 | /* ping timeout in seconds */ | ||
37 | #define PING_TIMEOUT 5 | ||
38 | |||
39 | /* The timeout after which a node is discarded completely. */ | ||
40 | #define Kill_NODE_TIMEOUT 300 | ||
41 | |||
42 | /* ping interval in seconds for each node in our lists. */ | ||
43 | #define PING_INTERVAL 60 | ||
44 | |||
45 | /* ping interval in seconds for each random sending of a get nodes request. */ | ||
46 | #define GET_NODE_INTERVAL 10 | ||
47 | |||
48 | #define MAX_PUNCHING_PORTS 32 | ||
49 | |||
50 | /*Interval in seconds between punching attempts*/ | ||
51 | #define PUNCH_INTERVAL 10 | ||
52 | |||
53 | /*Ping newly announced nodes to ping per TIME_TOPING seconds*/ | ||
54 | #define TIME_TOPING 5 | ||
55 | |||
56 | #define NAT_PING_REQUEST 0 | ||
57 | #define NAT_PING_RESPONSE 1 | ||
58 | |||
59 | |||
60 | Client_data *DHT_get_close_list(DHT *dht) | ||
61 | { | ||
62 | return dht->close_clientlist; | ||
63 | } | ||
64 | |||
65 | /* Compares client_id1 and client_id2 with client_id | ||
66 | * return 0 if both are same distance | ||
67 | * return 1 if client_id1 is closer | ||
68 | * return 2 if client_id2 is closer | ||
69 | */ | ||
70 | static int id_closest(uint8_t *id, uint8_t *id1, uint8_t *id2) | ||
71 | { | ||
72 | size_t i; | ||
73 | uint8_t distance1, distance2; | ||
74 | |||
75 | for (i = 0; i < CLIENT_ID_SIZE; ++i) { | ||
76 | |||
77 | distance1 = abs(id[i] ^ id1[i]); | ||
78 | distance2 = abs(id[i] ^ id2[i]); | ||
79 | |||
80 | if (distance1 < distance2) | ||
81 | return 1; | ||
82 | |||
83 | if (distance1 > distance2) | ||
84 | return 2; | ||
85 | } | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static int ipport_equal(IP_Port a, IP_Port b) | ||
91 | { | ||
92 | return (a.ip.i == b.ip.i) && (a.port == b.port); | ||
93 | } | ||
94 | |||
95 | static int id_equal(uint8_t *a, uint8_t *b) | ||
96 | { | ||
97 | return memcmp(a, b, CLIENT_ID_SIZE) == 0; | ||
98 | } | ||
99 | |||
100 | static int is_timeout(uint64_t time_now, uint64_t timestamp, uint64_t timeout) | ||
101 | { | ||
102 | return timestamp + timeout <= time_now; | ||
103 | } | ||
104 | |||
105 | /* check if client with client_id is already in list of length length. | ||
106 | * if it is then set its corresponding timestamp to current time. | ||
107 | * if the id is already in the list with a different ip_port, update it. | ||
108 | * return True(1) or False(0) | ||
109 | * | ||
110 | * TODO: maybe optimize this. | ||
111 | */ | ||
112 | static int client_in_list(Client_data *list, uint32_t length, uint8_t *client_id, IP_Port ip_port) | ||
113 | { | ||
114 | uint32_t i; | ||
115 | uint64_t temp_time = unix_time(); | ||
116 | |||
117 | for (i = 0; i < length; ++i) { | ||
118 | /*If ip_port is assigned to a different client_id replace it*/ | ||
119 | if (ipport_equal(list[i].ip_port, ip_port)) { | ||
120 | memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); | ||
121 | } | ||
122 | |||
123 | if (id_equal(list[i].client_id, client_id)) { | ||
124 | /* Refresh the client timestamp. */ | ||
125 | list[i].timestamp = temp_time; | ||
126 | list[i].ip_port.ip.i = ip_port.ip.i; | ||
127 | list[i].ip_port.port = ip_port.port; | ||
128 | return 1; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | /* check if client with client_id is already in node format list of length length. | ||
136 | * return True(1) or False(0) | ||
137 | */ | ||
138 | static int client_in_nodelist(Node_format *list, uint32_t length, uint8_t *client_id) | ||
139 | { | ||
140 | uint32_t i; | ||
141 | |||
142 | for (i = 0; i < length; ++i) { | ||
143 | if (id_equal(list[i].client_id, client_id)) | ||
144 | return 1; | ||
145 | } | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | /* Returns the friend number from the client_id, or -1 if a failure occurs | ||
151 | */ | ||
152 | static int friend_number(DHT *dht, uint8_t *client_id) | ||
153 | { | ||
154 | uint32_t i; | ||
155 | |||
156 | for (i = 0; i < dht->num_friends; ++i) { | ||
157 | if (id_equal(dht->friends_list[i].client_id, client_id)) | ||
158 | return i; | ||
159 | } | ||
160 | |||
161 | return -1; | ||
162 | } | ||
163 | |||
164 | /* Find MAX_SENT_NODES nodes closest to the client_id for the send nodes request: | ||
165 | * put them in the nodes_list and return how many were found. | ||
166 | * | ||
167 | * TODO: For the love of based Allah make this function cleaner and much more efficient. | ||
168 | */ | ||
169 | static int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list) | ||
170 | { | ||
171 | uint32_t i, j, k; | ||
172 | uint64_t temp_time = unix_time(); | ||
173 | int num_nodes = 0, closest, tout, inlist; | ||
174 | |||
175 | for (i = 0; i < LCLIENT_LIST; ++i) { | ||
176 | tout = is_timeout(temp_time, dht->close_clientlist[i].timestamp, BAD_NODE_TIMEOUT); | ||
177 | inlist = client_in_nodelist(nodes_list, MAX_SENT_NODES, dht->close_clientlist[i].client_id); | ||
178 | |||
179 | /* if node isn't good or is already in list. */ | ||
180 | if (tout || inlist) | ||
181 | continue; | ||
182 | |||
183 | if (num_nodes < MAX_SENT_NODES) { | ||
184 | |||
185 | memcpy( nodes_list[num_nodes].client_id, | ||
186 | dht->close_clientlist[i].client_id, | ||
187 | CLIENT_ID_SIZE ); | ||
188 | |||
189 | nodes_list[num_nodes].ip_port = dht->close_clientlist[i].ip_port; | ||
190 | num_nodes++; | ||
191 | |||
192 | } else { | ||
193 | |||
194 | for (j = 0; j < MAX_SENT_NODES; ++j) { | ||
195 | closest = id_closest( client_id, | ||
196 | nodes_list[j].client_id, | ||
197 | dht->close_clientlist[i].client_id ); | ||
198 | |||
199 | if (closest == 2) { | ||
200 | memcpy( nodes_list[j].client_id, | ||
201 | dht->close_clientlist[i].client_id, | ||
202 | CLIENT_ID_SIZE); | ||
203 | |||
204 | nodes_list[j].ip_port = dht->close_clientlist[i].ip_port; | ||
205 | break; | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | for (i = 0; i < dht->num_friends; ++i) { | ||
212 | for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { | ||
213 | |||
214 | tout = is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT); | ||
215 | inlist = client_in_nodelist( nodes_list, | ||
216 | MAX_SENT_NODES, | ||
217 | dht->friends_list[i].client_list[j].client_id); | ||
218 | |||
219 | /* if node isn't good or is already in list. */ | ||
220 | if (tout || inlist) | ||
221 | continue; | ||
222 | |||
223 | if (num_nodes < MAX_SENT_NODES) { | ||
224 | |||
225 | memcpy( nodes_list[num_nodes].client_id, | ||
226 | dht->friends_list[i].client_list[j].client_id, | ||
227 | CLIENT_ID_SIZE); | ||
228 | |||
229 | nodes_list[num_nodes].ip_port = dht->friends_list[i].client_list[j].ip_port; | ||
230 | num_nodes++; | ||
231 | } else { | ||
232 | for (k = 0; k < MAX_SENT_NODES; ++k) { | ||
233 | |||
234 | closest = id_closest( client_id, | ||
235 | nodes_list[k].client_id, | ||
236 | dht->friends_list[i].client_list[j].client_id ); | ||
237 | |||
238 | if (closest == 2) { | ||
239 | memcpy( nodes_list[k].client_id, | ||
240 | dht->friends_list[i].client_list[j].client_id, | ||
241 | CLIENT_ID_SIZE ); | ||
242 | |||
243 | nodes_list[k].ip_port = dht->friends_list[i].client_list[j].ip_port; | ||
244 | break; | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | } | ||
250 | |||
251 | return num_nodes; | ||
252 | } | ||
253 | |||
254 | /* replace first bad (or empty) node with this one | ||
255 | * return 0 if successful | ||
256 | * return 1 if not (list contains no bad nodes) | ||
257 | */ | ||
258 | static int replace_bad( Client_data *list, | ||
259 | uint32_t length, | ||
260 | uint8_t *client_id, | ||
261 | IP_Port ip_port ) | ||
262 | { | ||
263 | uint32_t i; | ||
264 | uint64_t temp_time = unix_time(); | ||
265 | |||
266 | for (i = 0; i < length; ++i) { | ||
267 | /* if node is bad */ | ||
268 | if (is_timeout(temp_time, list[i].timestamp, BAD_NODE_TIMEOUT)) { | ||
269 | memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); | ||
270 | list[i].ip_port = ip_port; | ||
271 | list[i].timestamp = temp_time; | ||
272 | list[i].ret_ip_port.ip.i = 0; | ||
273 | list[i].ret_ip_port.port = 0; | ||
274 | list[i].ret_timestamp = 0; | ||
275 | return 0; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | return 1; | ||
280 | } | ||
281 | /*Sort the list. It will be sorted from furthest to closest. | ||
282 | TODO: this is innefficient and needs to be optimized.*/ | ||
283 | static void sort_list(Client_data *list, uint32_t length, uint8_t *comp_client_id) | ||
284 | { | ||
285 | if (length == 0) | ||
286 | return; | ||
287 | |||
288 | uint32_t i, count; | ||
289 | |||
290 | while (1) { | ||
291 | count = 0; | ||
292 | |||
293 | for (i = 0; i < (length - 1); ++i) { | ||
294 | if (id_closest(comp_client_id, list[i].client_id, list[i + 1].client_id) == 1) { | ||
295 | Client_data temp = list[i + 1]; | ||
296 | list[i + 1] = list[i]; | ||
297 | list[i] = temp; | ||
298 | ++count; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | if (count == 0) | ||
303 | return; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | |||
308 | /* replace the first good node that is further to the comp_client_id than that of the client_id in the list */ | ||
309 | static int replace_good( Client_data *list, | ||
310 | uint32_t length, | ||
311 | uint8_t *client_id, | ||
312 | IP_Port ip_port, | ||
313 | uint8_t *comp_client_id ) | ||
314 | { | ||
315 | uint32_t i; | ||
316 | uint64_t temp_time = unix_time(); | ||
317 | sort_list(list, length, comp_client_id); | ||
318 | |||
319 | for (i = 0; i < length; ++i) | ||
320 | if (id_closest(comp_client_id, list[i].client_id, client_id) == 2) { | ||
321 | memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE); | ||
322 | list[i].ip_port = ip_port; | ||
323 | list[i].timestamp = temp_time; | ||
324 | list[i].ret_ip_port.ip.i = 0; | ||
325 | list[i].ret_ip_port.port = 0; | ||
326 | list[i].ret_timestamp = 0; | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | return 1; | ||
331 | } | ||
332 | |||
333 | /* Attempt to add client with ip_port and client_id to the friends client list | ||
334 | * and close_clientlist | ||
335 | */ | ||
336 | void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id) | ||
337 | { | ||
338 | uint32_t i; | ||
339 | |||
340 | /* NOTE: current behavior if there are two clients with the same id is | ||
341 | * to replace the first ip by the second. | ||
342 | */ | ||
343 | if (!client_in_list(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) { | ||
344 | if (replace_bad(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) { | ||
345 | /* if we can't replace bad nodes we try replacing good ones */ | ||
346 | replace_good( dht->close_clientlist, | ||
347 | LCLIENT_LIST, | ||
348 | client_id, | ||
349 | ip_port, | ||
350 | dht->c->self_public_key ); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | for (i = 0; i < dht->num_friends; ++i) { | ||
355 | if (!client_in_list( dht->friends_list[i].client_list, | ||
356 | MAX_FRIEND_CLIENTS, | ||
357 | client_id, | ||
358 | ip_port )) { | ||
359 | |||
360 | if (replace_bad( dht->friends_list[i].client_list, | ||
361 | MAX_FRIEND_CLIENTS, | ||
362 | client_id, | ||
363 | ip_port )) { | ||
364 | /* if we can't replace bad nodes we try replacing good ones. */ | ||
365 | replace_good( dht->friends_list[i].client_list, | ||
366 | MAX_FRIEND_CLIENTS, | ||
367 | client_id, | ||
368 | ip_port, | ||
369 | dht->friends_list[i].client_id ); | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | |||
375 | /* If client_id is a friend or us, update ret_ip_port | ||
376 | * nodeclient_id is the id of the node that sent us this info | ||
377 | */ | ||
378 | static void returnedip_ports(DHT *dht, IP_Port ip_port, uint8_t *client_id, uint8_t *nodeclient_id) | ||
379 | { | ||
380 | uint32_t i, j; | ||
381 | uint64_t temp_time = unix_time(); | ||
382 | |||
383 | if (id_equal(client_id, dht->c->self_public_key)) { | ||
384 | |||
385 | for (i = 0; i < LCLIENT_LIST; ++i) { | ||
386 | if (id_equal(nodeclient_id, dht->close_clientlist[i].client_id)) { | ||
387 | dht->close_clientlist[i].ret_ip_port = ip_port; | ||
388 | dht->close_clientlist[i].ret_timestamp = temp_time; | ||
389 | return; | ||
390 | } | ||
391 | } | ||
392 | |||
393 | } else { | ||
394 | |||
395 | for (i = 0; i < dht->num_friends; ++i) { | ||
396 | if (id_equal(client_id, dht->friends_list[i].client_id)) { | ||
397 | |||
398 | for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { | ||
399 | if (id_equal(nodeclient_id, dht->friends_list[i].client_list[j].client_id)) { | ||
400 | dht->friends_list[i].client_list[j].ret_ip_port = ip_port; | ||
401 | dht->friends_list[i].client_list[j].ret_timestamp = temp_time; | ||
402 | return; | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | |||
408 | } | ||
409 | } | ||
410 | |||
411 | /* Same as last function but for get_node requests. */ | ||
412 | static int is_gettingnodes(DHT *dht, IP_Port ip_port, uint64_t ping_id) | ||
413 | { | ||
414 | uint32_t i; | ||
415 | uint8_t pinging; | ||
416 | uint64_t temp_time = unix_time(); | ||
417 | |||
418 | for (i = 0; i < LSEND_NODES_ARRAY; ++i ) { | ||
419 | if (!is_timeout(temp_time, dht->send_nodes[i].timestamp, PING_TIMEOUT)) { | ||
420 | pinging = 0; | ||
421 | |||
422 | if (ip_port.ip.i != 0 && ipport_equal(dht->send_nodes[i].ip_port, ip_port)) | ||
423 | ++pinging; | ||
424 | |||
425 | if (ping_id != 0 && dht->send_nodes[i].ping_id == ping_id) | ||
426 | ++pinging; | ||
427 | |||
428 | if (pinging == (ping_id != 0) + (ip_port.ip.i != 0)) | ||
429 | return 1; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /* Same but for get node requests */ | ||
437 | static uint64_t add_gettingnodes(DHT *dht, IP_Port ip_port) | ||
438 | { | ||
439 | uint32_t i, j; | ||
440 | uint64_t ping_id = ((uint64_t)random_int() << 32) + random_int(); | ||
441 | uint64_t temp_time = unix_time(); | ||
442 | |||
443 | for (i = 0; i < PING_TIMEOUT; ++i ) { | ||
444 | for (j = 0; j < LSEND_NODES_ARRAY; ++j ) { | ||
445 | if (is_timeout(temp_time, dht->send_nodes[j].timestamp, PING_TIMEOUT - i)) { | ||
446 | dht->send_nodes[j].timestamp = temp_time; | ||
447 | dht->send_nodes[j].ip_port = ip_port; | ||
448 | dht->send_nodes[j].ping_id = ping_id; | ||
449 | return ping_id; | ||
450 | } | ||
451 | } | ||
452 | } | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | /* send a getnodes request */ | ||
458 | static int getnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *client_id) | ||
459 | { | ||
460 | /* check if packet is gonna be sent to ourself */ | ||
461 | if (id_equal(public_key, dht->c->self_public_key) || is_gettingnodes(dht, ip_port, 0)) | ||
462 | return 1; | ||
463 | |||
464 | uint64_t ping_id = add_gettingnodes(dht, ip_port); | ||
465 | |||
466 | if (ping_id == 0) | ||
467 | return 1; | ||
468 | |||
469 | uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING]; | ||
470 | uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE]; | ||
471 | uint8_t encrypt[sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING]; | ||
472 | uint8_t nonce[crypto_box_NONCEBYTES]; | ||
473 | random_nonce(nonce); | ||
474 | |||
475 | memcpy(plain, &ping_id, sizeof(ping_id)); | ||
476 | memcpy(plain + sizeof(ping_id), client_id, CLIENT_ID_SIZE); | ||
477 | |||
478 | int len = encrypt_data( public_key, | ||
479 | dht->c->self_secret_key, | ||
480 | nonce, | ||
481 | plain, | ||
482 | sizeof(ping_id) + CLIENT_ID_SIZE, | ||
483 | encrypt ); | ||
484 | |||
485 | if (len != sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING) | ||
486 | return -1; | ||
487 | |||
488 | data[0] = NET_PACKET_GET_NODES; | ||
489 | memcpy(data + 1, dht->c->self_public_key, CLIENT_ID_SIZE); | ||
490 | memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES); | ||
491 | memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len); | ||
492 | |||
493 | return sendpacket(dht->c->lossless_udp->net->sock, ip_port, data, sizeof(data)); | ||
494 | } | ||
495 | |||
496 | /* send a send nodes response */ | ||
497 | static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *client_id, uint64_t ping_id) | ||
498 | { | ||
499 | /* check if packet is gonna be sent to ourself */ | ||
500 | if (id_equal(public_key, dht->c->self_public_key)) | ||
501 | return 1; | ||
502 | |||
503 | uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) | ||
504 | + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING]; | ||
505 | |||
506 | Node_format nodes_list[MAX_SENT_NODES]; | ||
507 | int num_nodes = get_close_nodes(dht, client_id, nodes_list); | ||
508 | |||
509 | if (num_nodes == 0) | ||
510 | return 0; | ||
511 | |||
512 | uint8_t plain[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES]; | ||
513 | uint8_t encrypt[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING]; | ||
514 | uint8_t nonce[crypto_box_NONCEBYTES]; | ||
515 | random_nonce(nonce); | ||
516 | |||
517 | memcpy(plain, &ping_id, sizeof(ping_id)); | ||
518 | memcpy(plain + sizeof(ping_id), nodes_list, num_nodes * sizeof(Node_format)); | ||
519 | |||
520 | int len = encrypt_data( public_key, | ||
521 | dht->c->self_secret_key, | ||
522 | nonce, | ||
523 | plain, | ||
524 | sizeof(ping_id) + num_nodes * sizeof(Node_format), | ||
525 | encrypt ); | ||
526 | |||
527 | if (len != sizeof(ping_id) + num_nodes * sizeof(Node_format) + ENCRYPTION_PADDING) | ||
528 | return -1; | ||
529 | |||
530 | data[0] = NET_PACKET_SEND_NODES; | ||
531 | memcpy(data + 1, dht->c->self_public_key, CLIENT_ID_SIZE); | ||
532 | memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES); | ||
533 | memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len); | ||
534 | |||
535 | return sendpacket(dht->c->lossless_udp->net->sock, ip_port, data, 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + len); | ||
536 | } | ||
537 | |||
538 | static int handle_getnodes(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
539 | { | ||
540 | DHT *dht = object; | ||
541 | uint64_t ping_id; | ||
542 | |||
543 | if (length != ( 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES | ||
544 | + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING )) | ||
545 | return 1; | ||
546 | |||
547 | /* check if packet is from ourself. */ | ||
548 | if (id_equal(packet + 1, dht->c->self_public_key)) | ||
549 | return 1; | ||
550 | |||
551 | uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE]; | ||
552 | |||
553 | int len = decrypt_data( packet + 1, | ||
554 | dht->c->self_secret_key, | ||
555 | packet + 1 + CLIENT_ID_SIZE, | ||
556 | packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, | ||
557 | sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING, | ||
558 | plain ); | ||
559 | |||
560 | if (len != sizeof(ping_id) + CLIENT_ID_SIZE) | ||
561 | return 1; | ||
562 | |||
563 | memcpy(&ping_id, plain, sizeof(ping_id)); | ||
564 | sendnodes(dht, source, packet + 1, plain + sizeof(ping_id), ping_id); | ||
565 | |||
566 | //send_ping_request(dht, source, (clientid_t*) (packet + 1)); /* TODO: make this smarter? */ | ||
567 | |||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
572 | { | ||
573 | DHT *dht = object; | ||
574 | uint64_t ping_id; | ||
575 | uint32_t cid_size = 1 + CLIENT_ID_SIZE; | ||
576 | cid_size += crypto_box_NONCEBYTES + sizeof(ping_id) + ENCRYPTION_PADDING; | ||
577 | |||
578 | if (length > (cid_size + sizeof(Node_format) * MAX_SENT_NODES) || | ||
579 | ((length - cid_size) % sizeof(Node_format)) != 0 || | ||
580 | (length < cid_size + sizeof(Node_format))) | ||
581 | return 1; | ||
582 | |||
583 | uint32_t num_nodes = (length - cid_size) / sizeof(Node_format); | ||
584 | uint8_t plain[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES]; | ||
585 | |||
586 | int len = decrypt_data( | ||
587 | packet + 1, | ||
588 | dht->c->self_secret_key, | ||
589 | packet + 1 + CLIENT_ID_SIZE, | ||
590 | packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, | ||
591 | sizeof(ping_id) + num_nodes * sizeof(Node_format) + ENCRYPTION_PADDING, plain ); | ||
592 | |||
593 | if (len != sizeof(ping_id) + num_nodes * sizeof(Node_format)) | ||
594 | return 1; | ||
595 | |||
596 | memcpy(&ping_id, plain, sizeof(ping_id)); | ||
597 | |||
598 | if (!is_gettingnodes(dht, source, ping_id)) | ||
599 | return 1; | ||
600 | |||
601 | Node_format nodes_list[MAX_SENT_NODES]; | ||
602 | memcpy(nodes_list, plain + sizeof(ping_id), num_nodes * sizeof(Node_format)); | ||
603 | |||
604 | addto_lists(dht, source, packet + 1); | ||
605 | |||
606 | uint32_t i; | ||
607 | |||
608 | for (i = 0; i < num_nodes; ++i) { | ||
609 | send_ping_request(dht->ping, dht->c, nodes_list[i].ip_port, (clientid_t *) &nodes_list[i].client_id); | ||
610 | returnedip_ports(dht, nodes_list[i].ip_port, nodes_list[i].client_id, packet + 1); | ||
611 | } | ||
612 | |||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | /*----------------------------------------------------------------------------------*/ | ||
617 | /*------------------------END of packet handling functions--------------------------*/ | ||
618 | |||
619 | int DHT_addfriend(DHT *dht, uint8_t *client_id) | ||
620 | { | ||
621 | if (friend_number(dht, client_id) != -1) /*Is friend already in DHT?*/ | ||
622 | return 1; | ||
623 | |||
624 | DHT_Friend *temp; | ||
625 | temp = realloc(dht->friends_list, sizeof(DHT_Friend) * (dht->num_friends + 1)); | ||
626 | |||
627 | if (temp == NULL) | ||
628 | return 1; | ||
629 | |||
630 | dht->friends_list = temp; | ||
631 | memset(&dht->friends_list[dht->num_friends], 0, sizeof(DHT_Friend)); | ||
632 | memcpy(dht->friends_list[dht->num_friends].client_id, client_id, CLIENT_ID_SIZE); | ||
633 | |||
634 | dht->friends_list[dht->num_friends].NATping_id = ((uint64_t)random_int() << 32) + random_int(); | ||
635 | ++dht->num_friends; | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | int DHT_delfriend(DHT *dht, uint8_t *client_id) | ||
640 | { | ||
641 | uint32_t i; | ||
642 | DHT_Friend *temp; | ||
643 | |||
644 | for (i = 0; i < dht->num_friends; ++i) { | ||
645 | /* Equal */ | ||
646 | if (id_equal(dht->friends_list[i].client_id, client_id)) { | ||
647 | --dht->num_friends; | ||
648 | |||
649 | if (dht->num_friends != i) { | ||
650 | memcpy( dht->friends_list[i].client_id, | ||
651 | dht->friends_list[dht->num_friends].client_id, | ||
652 | CLIENT_ID_SIZE ); | ||
653 | } | ||
654 | |||
655 | if (dht->num_friends == 0) { | ||
656 | free(dht->friends_list); | ||
657 | dht->friends_list = NULL; | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | temp = realloc(dht->friends_list, sizeof(DHT_Friend) * (dht->num_friends)); | ||
662 | |||
663 | if (temp == NULL) | ||
664 | return 1; | ||
665 | |||
666 | dht->friends_list = temp; | ||
667 | return 0; | ||
668 | } | ||
669 | } | ||
670 | |||
671 | return 1; | ||
672 | } | ||
673 | |||
674 | /* TODO: Optimize this. */ | ||
675 | IP_Port DHT_getfriendip(DHT *dht, uint8_t *client_id) | ||
676 | { | ||
677 | uint32_t i, j; | ||
678 | uint64_t temp_time = unix_time(); | ||
679 | IP_Port empty = {{{0}}, 0}; | ||
680 | |||
681 | for (i = 0; i < dht->num_friends; ++i) { | ||
682 | /* Equal */ | ||
683 | if (id_equal(dht->friends_list[i].client_id, client_id)) { | ||
684 | for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { | ||
685 | if (id_equal(dht->friends_list[i].client_list[j].client_id, client_id) | ||
686 | && !is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT)) | ||
687 | return dht->friends_list[i].client_list[j].ip_port; | ||
688 | } | ||
689 | |||
690 | return empty; | ||
691 | } | ||
692 | } | ||
693 | |||
694 | empty.ip.i = 1; | ||
695 | return empty; | ||
696 | } | ||
697 | |||
698 | /* Ping each client in the "friends" list every PING_INTERVAL seconds. Send a get nodes request | ||
699 | * every GET_NODE_INTERVAL seconds to a random good node for each "friend" in our "friends" list. | ||
700 | */ | ||
701 | static void do_DHT_friends(DHT *dht) | ||
702 | { | ||
703 | uint32_t i, j; | ||
704 | uint64_t temp_time = unix_time(); | ||
705 | uint32_t rand_node; | ||
706 | uint32_t index[MAX_FRIEND_CLIENTS]; | ||
707 | |||
708 | for (i = 0; i < dht->num_friends; ++i) { | ||
709 | uint32_t num_nodes = 0; | ||
710 | |||
711 | for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { | ||
712 | /* if node is not dead. */ | ||
713 | if (!is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, Kill_NODE_TIMEOUT)) { | ||
714 | if ((dht->friends_list[i].client_list[j].last_pinged + PING_INTERVAL) <= temp_time) { | ||
715 | send_ping_request(dht->ping, dht->c, dht->friends_list[i].client_list[j].ip_port, | ||
716 | (clientid_t *) &dht->friends_list[i].client_list[j].client_id ); | ||
717 | dht->friends_list[i].client_list[j].last_pinged = temp_time; | ||
718 | } | ||
719 | |||
720 | /* if node is good. */ | ||
721 | if (!is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT)) { | ||
722 | index[num_nodes] = j; | ||
723 | ++num_nodes; | ||
724 | } | ||
725 | } | ||
726 | } | ||
727 | |||
728 | if (dht->friends_list[i].lastgetnode + GET_NODE_INTERVAL <= temp_time && num_nodes != 0) { | ||
729 | rand_node = rand() % num_nodes; | ||
730 | getnodes(dht, dht->friends_list[i].client_list[index[rand_node]].ip_port, | ||
731 | dht->friends_list[i].client_list[index[rand_node]].client_id, | ||
732 | dht->friends_list[i].client_id ); | ||
733 | dht->friends_list[i].lastgetnode = temp_time; | ||
734 | } | ||
735 | } | ||
736 | } | ||
737 | |||
738 | /* Ping each client in the close nodes list every PING_INTERVAL seconds. | ||
739 | * Send a get nodes request every GET_NODE_INTERVAL seconds to a random good node in the list. | ||
740 | */ | ||
741 | static void do_Close(DHT *dht) | ||
742 | { | ||
743 | uint32_t i; | ||
744 | uint64_t temp_time = unix_time(); | ||
745 | uint32_t num_nodes = 0; | ||
746 | uint32_t rand_node; | ||
747 | uint32_t index[LCLIENT_LIST]; | ||
748 | |||
749 | for (i = 0; i < LCLIENT_LIST; ++i) { | ||
750 | /* if node is not dead. */ | ||
751 | if (!is_timeout(temp_time, dht->close_clientlist[i].timestamp, Kill_NODE_TIMEOUT)) { | ||
752 | if ((dht->close_clientlist[i].last_pinged + PING_INTERVAL) <= temp_time) { | ||
753 | send_ping_request(dht->ping, dht->c, dht->close_clientlist[i].ip_port, | ||
754 | (clientid_t *) &dht->close_clientlist[i].client_id ); | ||
755 | dht->close_clientlist[i].last_pinged = temp_time; | ||
756 | } | ||
757 | |||
758 | /* if node is good. */ | ||
759 | if (!is_timeout(temp_time, dht->close_clientlist[i].timestamp, BAD_NODE_TIMEOUT)) { | ||
760 | index[num_nodes] = i; | ||
761 | ++num_nodes; | ||
762 | } | ||
763 | } | ||
764 | } | ||
765 | |||
766 | if (dht->close_lastgetnodes + GET_NODE_INTERVAL <= temp_time && num_nodes != 0) { | ||
767 | rand_node = rand() % num_nodes; | ||
768 | getnodes(dht, dht->close_clientlist[index[rand_node]].ip_port, | ||
769 | dht->close_clientlist[index[rand_node]].client_id, | ||
770 | dht->c->self_public_key ); | ||
771 | dht->close_lastgetnodes = temp_time; | ||
772 | } | ||
773 | } | ||
774 | |||
775 | void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key) | ||
776 | { | ||
777 | getnodes(dht, ip_port, public_key, dht->c->self_public_key); | ||
778 | send_ping_request(dht->ping, dht->c, ip_port, (clientid_t *) public_key); | ||
779 | } | ||
780 | |||
781 | /* send the given packet to node with client_id | ||
782 | * returns -1 if failure | ||
783 | */ | ||
784 | int route_packet(DHT *dht, uint8_t *client_id, uint8_t *packet, uint32_t length) | ||
785 | { | ||
786 | uint32_t i; | ||
787 | |||
788 | for (i = 0; i < LCLIENT_LIST; ++i) { | ||
789 | if (id_equal(client_id, dht->close_clientlist[i].client_id)) | ||
790 | return sendpacket(dht->c->lossless_udp->net->sock, dht->close_clientlist[i].ip_port, packet, length); | ||
791 | } | ||
792 | |||
793 | return -1; | ||
794 | } | ||
795 | |||
796 | /* Puts all the different ips returned by the nodes for a friend_num into array ip_portlist | ||
797 | * ip_portlist must be at least MAX_FRIEND_CLIENTS big | ||
798 | * returns the number of ips returned | ||
799 | * return 0 if we are connected to friend or if no ips were found. | ||
800 | * returns -1 if no such friend | ||
801 | */ | ||
802 | static int friend_iplist(DHT *dht, IP_Port *ip_portlist, uint16_t friend_num) | ||
803 | { | ||
804 | int num_ips = 0; | ||
805 | uint32_t i; | ||
806 | uint64_t temp_time = unix_time(); | ||
807 | |||
808 | if (friend_num >= dht->num_friends) | ||
809 | return -1; | ||
810 | |||
811 | DHT_Friend *friend = &dht->friends_list[friend_num]; | ||
812 | Client_data *client; | ||
813 | |||
814 | for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) { | ||
815 | client = &friend->client_list[i]; | ||
816 | |||
817 | /*If ip is not zero and node is good */ | ||
818 | if (client->ret_ip_port.ip.i != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { | ||
819 | |||
820 | if (id_equal(client->client_id, friend->client_id)) | ||
821 | return 0; | ||
822 | |||
823 | ip_portlist[num_ips] = client->ret_ip_port; | ||
824 | ++num_ips; | ||
825 | } | ||
826 | } | ||
827 | |||
828 | return num_ips; | ||
829 | } | ||
830 | |||
831 | |||
832 | /* Send the following packet to everyone who tells us they are connected to friend_id | ||
833 | * returns the number of nodes it sent the packet to | ||
834 | * | ||
835 | * Only works if more than (MAX_FRIEND_CLIENTS / 2) return an ip for friend. | ||
836 | */ | ||
837 | int route_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t length) | ||
838 | { | ||
839 | int num = friend_number(dht, friend_id); | ||
840 | |||
841 | if (num == -1) | ||
842 | return 0; | ||
843 | |||
844 | uint32_t i, sent = 0; | ||
845 | |||
846 | IP_Port ip_list[MAX_FRIEND_CLIENTS]; | ||
847 | int ip_num = friend_iplist(dht, ip_list, num); | ||
848 | |||
849 | if (ip_num < (MAX_FRIEND_CLIENTS / 2)) | ||
850 | return 0; | ||
851 | |||
852 | uint64_t temp_time = unix_time(); | ||
853 | DHT_Friend *friend = &dht->friends_list[num]; | ||
854 | Client_data *client; | ||
855 | |||
856 | for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) { | ||
857 | client = &friend->client_list[i]; | ||
858 | |||
859 | /*If ip is not zero and node is good */ | ||
860 | if (client->ret_ip_port.ip.i != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { | ||
861 | if (sendpacket(dht->c->lossless_udp->net->sock, client->ip_port, packet, length) == length) | ||
862 | ++sent; | ||
863 | } | ||
864 | } | ||
865 | |||
866 | return sent; | ||
867 | } | ||
868 | |||
869 | /* Send the following packet to one random person who tells us they are connected to friend_id | ||
870 | * returns the number of nodes it sent the packet to | ||
871 | */ | ||
872 | static int routeone_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t length) | ||
873 | { | ||
874 | int num = friend_number(dht, friend_id); | ||
875 | |||
876 | if (num == -1) | ||
877 | return 0; | ||
878 | |||
879 | DHT_Friend *friend = &dht->friends_list[num]; | ||
880 | Client_data *client; | ||
881 | |||
882 | IP_Port ip_list[MAX_FRIEND_CLIENTS]; | ||
883 | int n = 0; | ||
884 | uint32_t i; | ||
885 | uint64_t temp_time = unix_time(); | ||
886 | |||
887 | for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) { | ||
888 | client = &friend->client_list[i]; | ||
889 | |||
890 | /*If ip is not zero and node is good */ | ||
891 | if (client->ret_ip_port.ip.i != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) { | ||
892 | ip_list[n] = client->ip_port; | ||
893 | ++n; | ||
894 | } | ||
895 | } | ||
896 | |||
897 | if (n < 1) | ||
898 | return 0; | ||
899 | |||
900 | if (sendpacket(dht->c->lossless_udp->net->sock, ip_list[rand() % n], packet, length) == length) | ||
901 | return 1; | ||
902 | |||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | /* Puts all the different ips returned by the nodes for a friend_id into array ip_portlist | ||
907 | * ip_portlist must be at least MAX_FRIEND_CLIENTS big | ||
908 | * returns the number of ips returned | ||
909 | * return 0 if we are connected to friend or if no ips were found. | ||
910 | * returns -1 if no such friend | ||
911 | */ | ||
912 | int friend_ips(DHT *dht, IP_Port *ip_portlist, uint8_t *friend_id) | ||
913 | { | ||
914 | uint32_t i; | ||
915 | |||
916 | for (i = 0; i < dht->num_friends; ++i) { | ||
917 | /* Equal */ | ||
918 | if (id_equal(dht->friends_list[i].client_id, friend_id)) | ||
919 | return friend_iplist(dht, ip_portlist, i); | ||
920 | } | ||
921 | |||
922 | return -1; | ||
923 | } | ||
924 | |||
925 | /*----------------------------------------------------------------------------------*/ | ||
926 | /*---------------------BEGINNING OF NAT PUNCHING FUNCTIONS--------------------------*/ | ||
927 | |||
928 | static int send_NATping(DHT *dht, uint8_t *public_key, uint64_t ping_id, uint8_t type) | ||
929 | { | ||
930 | uint8_t data[sizeof(uint64_t) + 1]; | ||
931 | uint8_t packet[MAX_DATA_SIZE]; | ||
932 | |||
933 | int num = 0; | ||
934 | |||
935 | data[0] = type; | ||
936 | memcpy(data + 1, &ping_id, sizeof(uint64_t)); | ||
937 | /* 254 is NAT ping request packet id */ | ||
938 | int len = create_request(dht->c->self_public_key, dht->c->self_secret_key, packet, public_key, data, | ||
939 | sizeof(uint64_t) + 1, CRYPTO_PACKET_NAT_PING); | ||
940 | |||
941 | if (len == -1) | ||
942 | return -1; | ||
943 | |||
944 | if (type == 0) /*If packet is request use many people to route it*/ | ||
945 | num = route_tofriend(dht, public_key, packet, len); | ||
946 | else if (type == 1) /*If packet is response use only one person to route it*/ | ||
947 | num = routeone_tofriend(dht, public_key, packet, len); | ||
948 | |||
949 | if (num == 0) | ||
950 | return -1; | ||
951 | |||
952 | return num; | ||
953 | } | ||
954 | |||
955 | /* Handle a received ping request for */ | ||
956 | static int handle_NATping(void *object, IP_Port source, uint8_t *source_pubkey, uint8_t *packet, uint32_t length) | ||
957 | { | ||
958 | DHT *dht = object; | ||
959 | uint64_t ping_id; | ||
960 | memcpy(&ping_id, packet + 1, sizeof(uint64_t)); | ||
961 | |||
962 | int friendnumber = friend_number(dht, source_pubkey); | ||
963 | |||
964 | if (friendnumber == -1) | ||
965 | return 1; | ||
966 | |||
967 | DHT_Friend *friend = &dht->friends_list[friendnumber]; | ||
968 | |||
969 | if (packet[0] == NAT_PING_REQUEST) { | ||
970 | /* 1 is reply */ | ||
971 | send_NATping(dht, source_pubkey, ping_id, NAT_PING_RESPONSE); | ||
972 | friend->recvNATping_timestamp = unix_time(); | ||
973 | return 0; | ||
974 | } else if (packet[0] == NAT_PING_RESPONSE) { | ||
975 | if (friend->NATping_id == ping_id) { | ||
976 | friend->NATping_id = ((uint64_t)random_int() << 32) + random_int(); | ||
977 | friend->hole_punching = 1; | ||
978 | return 0; | ||
979 | } | ||
980 | } | ||
981 | |||
982 | return 1; | ||
983 | } | ||
984 | |||
985 | /* Get the most common ip in the ip_portlist | ||
986 | * Only return ip if it appears in list min_num or more | ||
987 | * len must not be bigger than MAX_FRIEND_CLIENTS | ||
988 | * return ip of 0 if failure | ||
989 | */ | ||
990 | static IP NAT_commonip(IP_Port *ip_portlist, uint16_t len, uint16_t min_num) | ||
991 | { | ||
992 | IP zero = {{0}}; | ||
993 | |||
994 | if (len > MAX_FRIEND_CLIENTS) | ||
995 | return zero; | ||
996 | |||
997 | uint32_t i, j; | ||
998 | uint16_t numbers[MAX_FRIEND_CLIENTS] = {0}; | ||
999 | |||
1000 | for (i = 0; i < len; ++i) { | ||
1001 | for (j = 0; j < len; ++j) { | ||
1002 | if (ip_portlist[i].ip.i == ip_portlist[j].ip.i) | ||
1003 | ++numbers[i]; | ||
1004 | } | ||
1005 | |||
1006 | if (numbers[i] >= min_num) | ||
1007 | return ip_portlist[i].ip; | ||
1008 | } | ||
1009 | |||
1010 | return zero; | ||
1011 | } | ||
1012 | |||
1013 | /* Return all the ports for one ip in a list | ||
1014 | * portlist must be at least len long | ||
1015 | * where len is the length of ip_portlist | ||
1016 | * returns the number of ports and puts the list of ports in portlist | ||
1017 | */ | ||
1018 | static uint16_t NAT_getports(uint16_t *portlist, IP_Port *ip_portlist, uint16_t len, IP ip) | ||
1019 | { | ||
1020 | uint32_t i; | ||
1021 | uint16_t num = 0; | ||
1022 | |||
1023 | for (i = 0; i < len; ++i) { | ||
1024 | if (ip_portlist[i].ip.i == ip.i) { | ||
1025 | portlist[num] = ntohs(ip_portlist[i].port); | ||
1026 | ++num; | ||
1027 | } | ||
1028 | } | ||
1029 | |||
1030 | return num; | ||
1031 | } | ||
1032 | |||
1033 | static void punch_holes(DHT *dht, IP ip, uint16_t *port_list, uint16_t numports, uint16_t friend_num) | ||
1034 | { | ||
1035 | if (numports > MAX_FRIEND_CLIENTS || numports == 0) | ||
1036 | return; | ||
1037 | |||
1038 | uint32_t i; | ||
1039 | uint32_t top = dht->friends_list[friend_num].punching_index + MAX_PUNCHING_PORTS; | ||
1040 | |||
1041 | for (i = dht->friends_list[friend_num].punching_index; i != top; i++) { | ||
1042 | /*TODO: improve port guessing algorithm*/ | ||
1043 | uint16_t port = port_list[(i / 2) % numports] + (i / (2 * numports)) * ((i % 2) ? -1 : 1); | ||
1044 | IP_Port pinging = {ip, htons(port)}; | ||
1045 | send_ping_request(dht->ping, dht->c, pinging, (clientid_t *) &dht->friends_list[friend_num].client_id); | ||
1046 | } | ||
1047 | |||
1048 | dht->friends_list[friend_num].punching_index = i; | ||
1049 | } | ||
1050 | |||
1051 | static void do_NAT(DHT *dht) | ||
1052 | { | ||
1053 | uint32_t i; | ||
1054 | uint64_t temp_time = unix_time(); | ||
1055 | |||
1056 | for (i = 0; i < dht->num_friends; ++i) { | ||
1057 | IP_Port ip_list[MAX_FRIEND_CLIENTS]; | ||
1058 | int num = friend_iplist(dht, ip_list, i); | ||
1059 | |||
1060 | /*If already connected or friend is not online don't try to hole punch*/ | ||
1061 | if (num < MAX_FRIEND_CLIENTS / 2) | ||
1062 | continue; | ||
1063 | |||
1064 | if (dht->friends_list[i].NATping_timestamp + PUNCH_INTERVAL < temp_time) { | ||
1065 | send_NATping(dht, dht->friends_list[i].client_id, dht->friends_list[i].NATping_id, NAT_PING_REQUEST); | ||
1066 | dht->friends_list[i].NATping_timestamp = temp_time; | ||
1067 | } | ||
1068 | |||
1069 | if (dht->friends_list[i].hole_punching == 1 && | ||
1070 | dht->friends_list[i].punching_timestamp + PUNCH_INTERVAL < temp_time && | ||
1071 | dht->friends_list[i].recvNATping_timestamp + PUNCH_INTERVAL * 2 >= temp_time) { | ||
1072 | |||
1073 | IP ip = NAT_commonip(ip_list, num, MAX_FRIEND_CLIENTS / 2); | ||
1074 | |||
1075 | if (ip.i == 0) | ||
1076 | continue; | ||
1077 | |||
1078 | uint16_t port_list[MAX_FRIEND_CLIENTS]; | ||
1079 | uint16_t numports = NAT_getports(port_list, ip_list, num, ip); | ||
1080 | punch_holes(dht, ip, port_list, numports, i); | ||
1081 | |||
1082 | dht->friends_list[i].punching_timestamp = temp_time; | ||
1083 | dht->friends_list[i].hole_punching = 0; | ||
1084 | } | ||
1085 | } | ||
1086 | } | ||
1087 | |||
1088 | /*----------------------------------------------------------------------------------*/ | ||
1089 | /*-----------------------END OF NAT PUNCHING FUNCTIONS------------------------------*/ | ||
1090 | |||
1091 | |||
1092 | /* Add nodes to the toping list | ||
1093 | all nodes in this list are pinged every TIME_TOPING seconds | ||
1094 | and are then removed from the list. | ||
1095 | if the list is full the nodes farthest from our client_id are replaced | ||
1096 | the purpose of this list is to enable quick integration of new nodes into the | ||
1097 | network while preventing amplification attacks. | ||
1098 | return 0 if node was added | ||
1099 | return -1 if node was not added */ | ||
1100 | int add_toping(DHT *dht, uint8_t *client_id, IP_Port ip_port) | ||
1101 | { | ||
1102 | if (ip_port.ip.i == 0) | ||
1103 | return -1; | ||
1104 | |||
1105 | uint32_t i; | ||
1106 | |||
1107 | for (i = 0; i < MAX_TOPING; ++i) { | ||
1108 | if (dht->toping[i].ip_port.ip.i == 0) { | ||
1109 | memcpy(dht->toping[i].client_id, client_id, CLIENT_ID_SIZE); | ||
1110 | dht->toping[i].ip_port.ip.i = ip_port.ip.i; | ||
1111 | dht->toping[i].ip_port.port = ip_port.port; | ||
1112 | return 0; | ||
1113 | } | ||
1114 | } | ||
1115 | |||
1116 | for (i = 0; i < MAX_TOPING; ++i) { | ||
1117 | if (id_closest(dht->c->self_public_key, dht->toping[i].client_id, client_id) == 2) { | ||
1118 | memcpy(dht->toping[i].client_id, client_id, CLIENT_ID_SIZE); | ||
1119 | dht->toping[i].ip_port.ip.i = ip_port.ip.i; | ||
1120 | dht->toping[i].ip_port.port = ip_port.port; | ||
1121 | return 0; | ||
1122 | } | ||
1123 | } | ||
1124 | |||
1125 | return -1; | ||
1126 | } | ||
1127 | |||
1128 | /*Ping all the valid nodes in the toping list every TIME_TOPING seconds | ||
1129 | this function must be run at least once every TIME_TOPING seconds*/ | ||
1130 | static void do_toping(DHT *dht) | ||
1131 | { | ||
1132 | uint64_t temp_time = unix_time(); | ||
1133 | |||
1134 | if (!is_timeout(temp_time, dht->last_toping, TIME_TOPING)) | ||
1135 | return; | ||
1136 | |||
1137 | dht->last_toping = temp_time; | ||
1138 | uint32_t i; | ||
1139 | |||
1140 | for (i = 0; i < MAX_TOPING; ++i) { | ||
1141 | if (dht->toping[i].ip_port.ip.i == 0) | ||
1142 | return; | ||
1143 | |||
1144 | send_ping_request(dht->ping, dht->c, dht->toping[i].ip_port, (clientid_t *) dht->toping[i].client_id); | ||
1145 | dht->toping[i].ip_port.ip.i = 0; | ||
1146 | } | ||
1147 | } | ||
1148 | |||
1149 | |||
1150 | DHT *new_DHT(Net_Crypto *c) | ||
1151 | { | ||
1152 | if (c == NULL) | ||
1153 | return NULL; | ||
1154 | |||
1155 | DHT *temp = calloc(1, sizeof(DHT)); | ||
1156 | |||
1157 | if (temp == NULL) | ||
1158 | return NULL; | ||
1159 | |||
1160 | temp->ping = new_ping(); | ||
1161 | |||
1162 | if (temp->ping == NULL) { | ||
1163 | kill_DHT(temp); | ||
1164 | return NULL; | ||
1165 | } | ||
1166 | |||
1167 | temp->c = c; | ||
1168 | networking_registerhandler(c->lossless_udp->net, NET_PACKET_PING_REQUEST, &handle_ping_request, temp); | ||
1169 | networking_registerhandler(c->lossless_udp->net, NET_PACKET_PING_RESPONSE, &handle_ping_response, temp); | ||
1170 | networking_registerhandler(c->lossless_udp->net, NET_PACKET_GET_NODES, &handle_getnodes, temp); | ||
1171 | networking_registerhandler(c->lossless_udp->net, NET_PACKET_SEND_NODES, &handle_sendnodes, temp); | ||
1172 | init_cryptopackets(temp); | ||
1173 | cryptopacket_registerhandler(c, CRYPTO_PACKET_NAT_PING, &handle_NATping, temp); | ||
1174 | return temp; | ||
1175 | } | ||
1176 | |||
1177 | void do_DHT(DHT *dht) | ||
1178 | { | ||
1179 | do_Close(dht); | ||
1180 | do_DHT_friends(dht); | ||
1181 | do_NAT(dht); | ||
1182 | do_toping(dht); | ||
1183 | } | ||
1184 | void kill_DHT(DHT *dht) | ||
1185 | { | ||
1186 | kill_ping(dht->ping); | ||
1187 | free(dht->friends_list); | ||
1188 | free(dht); | ||
1189 | } | ||
1190 | |||
1191 | /* get the size of the DHT (for saving) */ | ||
1192 | uint32_t DHT_size(DHT *dht) | ||
1193 | { | ||
1194 | return sizeof(dht->close_clientlist) + sizeof(DHT_Friend) * dht->num_friends; | ||
1195 | } | ||
1196 | |||
1197 | /* save the DHT in data where data is an array of size DHT_size() */ | ||
1198 | void DHT_save(DHT *dht, uint8_t *data) | ||
1199 | { | ||
1200 | memcpy(data, dht->close_clientlist, sizeof(dht->close_clientlist)); | ||
1201 | memcpy(data + sizeof(dht->close_clientlist), dht->friends_list, sizeof(DHT_Friend) * dht->num_friends); | ||
1202 | } | ||
1203 | |||
1204 | /* load the DHT from data of size size; | ||
1205 | * return -1 if failure | ||
1206 | * return 0 if success | ||
1207 | */ | ||
1208 | int DHT_load(DHT *dht, uint8_t *data, uint32_t size) | ||
1209 | { | ||
1210 | if (size < sizeof(dht->close_clientlist)) | ||
1211 | return -1; | ||
1212 | |||
1213 | if ((size - sizeof(dht->close_clientlist)) % sizeof(DHT_Friend) != 0) | ||
1214 | return -1; | ||
1215 | |||
1216 | uint32_t i, j; | ||
1217 | uint16_t temp; | ||
1218 | /* uint64_t temp_time = unix_time(); */ | ||
1219 | |||
1220 | Client_data *client; | ||
1221 | |||
1222 | temp = (size - sizeof(dht->close_clientlist)) / sizeof(DHT_Friend); | ||
1223 | |||
1224 | if (temp != 0) { | ||
1225 | DHT_Friend *tempfriends_list = (DHT_Friend *)(data + sizeof(dht->close_clientlist)); | ||
1226 | |||
1227 | for (i = 0; i < temp; ++i) { | ||
1228 | DHT_addfriend(dht, tempfriends_list[i].client_id); | ||
1229 | |||
1230 | for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { | ||
1231 | client = &tempfriends_list[i].client_list[j]; | ||
1232 | |||
1233 | if (client->timestamp != 0) | ||
1234 | getnodes(dht, client->ip_port, client->client_id, tempfriends_list[i].client_id); | ||
1235 | } | ||
1236 | } | ||
1237 | } | ||
1238 | |||
1239 | Client_data *tempclose_clientlist = (Client_data *)data; | ||
1240 | |||
1241 | for (i = 0; i < LCLIENT_LIST; ++i) { | ||
1242 | if (tempclose_clientlist[i].timestamp != 0) | ||
1243 | DHT_bootstrap(dht, tempclose_clientlist[i].ip_port, | ||
1244 | tempclose_clientlist[i].client_id ); | ||
1245 | } | ||
1246 | |||
1247 | return 0; | ||
1248 | } | ||
1249 | |||
1250 | /* returns 0 if we are not connected to the DHT | ||
1251 | * returns 1 if we are | ||
1252 | */ | ||
1253 | int DHT_isconnected(DHT *dht) | ||
1254 | { | ||
1255 | uint32_t i; | ||
1256 | uint64_t temp_time = unix_time(); | ||
1257 | |||
1258 | for (i = 0; i < LCLIENT_LIST; ++i) { | ||
1259 | if (!is_timeout(temp_time, dht->close_clientlist[i].timestamp, BAD_NODE_TIMEOUT)) | ||
1260 | return 1; | ||
1261 | } | ||
1262 | |||
1263 | return 0; | ||
1264 | } | ||
diff --git a/toxcore/DHT.h b/toxcore/DHT.h new file mode 100644 index 00000000..6295581b --- /dev/null +++ b/toxcore/DHT.h | |||
@@ -0,0 +1,193 @@ | |||
1 | /* DHT.h | ||
2 | * | ||
3 | * An implementation of the DHT as seen in http://wiki.tox.im/index.php/DHT | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #ifndef DHT_H | ||
25 | #define DHT_H | ||
26 | |||
27 | #include "net_crypto.h" | ||
28 | |||
29 | #ifdef __cplusplus | ||
30 | extern "C" { | ||
31 | #endif | ||
32 | |||
33 | |||
34 | /* size of the client_id in bytes */ | ||
35 | #define CLIENT_ID_SIZE crypto_box_PUBLICKEYBYTES | ||
36 | |||
37 | /* maximum number of clients stored per friend. */ | ||
38 | #define MAX_FRIEND_CLIENTS 8 | ||
39 | |||
40 | /* A list of the clients mathematically closest to ours. */ | ||
41 | #define LCLIENT_LIST 32 | ||
42 | |||
43 | /* The list of ip ports along with the ping_id of what we sent them and a timestamp */ | ||
44 | #define LPING_ARRAY 256 //NOTE Deprecated (doesn't do anything) | ||
45 | |||
46 | #define LSEND_NODES_ARRAY LPING_ARRAY/2 | ||
47 | |||
48 | /*Maximum newly announced nodes to ping per TIME_TOPING seconds*/ | ||
49 | #define MAX_TOPING 16 | ||
50 | |||
51 | typedef struct { | ||
52 | uint8_t client_id[CLIENT_ID_SIZE]; | ||
53 | IP_Port ip_port; | ||
54 | uint64_t timestamp; | ||
55 | uint64_t last_pinged; | ||
56 | |||
57 | /* Returned by this node. Either our friend or us */ | ||
58 | IP_Port ret_ip_port; | ||
59 | uint64_t ret_timestamp; | ||
60 | } Client_data; | ||
61 | |||
62 | /*----------------------------------------------------------------------------------*/ | ||
63 | |||
64 | typedef struct { | ||
65 | uint8_t client_id[CLIENT_ID_SIZE]; | ||
66 | Client_data client_list[MAX_FRIEND_CLIENTS]; | ||
67 | |||
68 | /* time at which the last get_nodes request was sent. */ | ||
69 | uint64_t lastgetnode; | ||
70 | |||
71 | /* Symetric NAT hole punching stuff */ | ||
72 | |||
73 | /* 1 if currently hole punching, otherwise 0 */ | ||
74 | uint8_t hole_punching; | ||
75 | uint32_t punching_index; | ||
76 | uint64_t punching_timestamp; | ||
77 | uint64_t recvNATping_timestamp; | ||
78 | uint64_t NATping_id; | ||
79 | uint64_t NATping_timestamp; | ||
80 | } DHT_Friend; | ||
81 | |||
82 | typedef struct { | ||
83 | uint8_t client_id[CLIENT_ID_SIZE]; | ||
84 | IP_Port ip_port; | ||
85 | } Node_format; | ||
86 | |||
87 | typedef struct { | ||
88 | IP_Port ip_port; | ||
89 | uint64_t ping_id; | ||
90 | uint64_t timestamp; | ||
91 | } Pinged; | ||
92 | |||
93 | /*----------------------------------------------------------------------------------*/ | ||
94 | typedef struct { | ||
95 | Net_Crypto *c; | ||
96 | Client_data close_clientlist[LCLIENT_LIST]; | ||
97 | DHT_Friend *friends_list; | ||
98 | uint16_t num_friends; | ||
99 | Pinged send_nodes[LSEND_NODES_ARRAY]; | ||
100 | Node_format toping[MAX_TOPING]; | ||
101 | uint64_t last_toping; | ||
102 | uint64_t close_lastgetnodes; | ||
103 | void *ping; | ||
104 | } DHT; | ||
105 | /*----------------------------------------------------------------------------------*/ | ||
106 | |||
107 | Client_data *DHT_get_close_list(DHT *dht); | ||
108 | |||
109 | /* Add a new friend to the friends list | ||
110 | client_id must be CLIENT_ID_SIZE bytes long. | ||
111 | returns 0 if success | ||
112 | returns 1 if failure (friends list is full) */ | ||
113 | int DHT_addfriend(DHT *dht, uint8_t *client_id); | ||
114 | |||
115 | /* Delete a friend from the friends list | ||
116 | client_id must be CLIENT_ID_SIZE bytes long. | ||
117 | returns 0 if success | ||
118 | returns 1 if failure (client_id not in friends list) */ | ||
119 | int DHT_delfriend(DHT *dht, uint8_t *client_id); | ||
120 | |||
121 | /* Get ip of friend | ||
122 | client_id must be CLIENT_ID_SIZE bytes long. | ||
123 | ip must be 4 bytes long. | ||
124 | port must be 2 bytes long. | ||
125 | returns ip if success | ||
126 | returns ip of 0 if failure (This means the friend is either offline or we have not found him yet.) | ||
127 | returns ip of 1 if friend is not in list. */ | ||
128 | IP_Port DHT_getfriendip(DHT *dht, uint8_t *client_id); | ||
129 | |||
130 | /* Run this function at least a couple times per second (It's the main loop) */ | ||
131 | void do_DHT(DHT *dht); | ||
132 | |||
133 | /* Use this function to bootstrap the client | ||
134 | Sends a get nodes request to the given node with ip port and public_key */ | ||
135 | void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key); | ||
136 | |||
137 | /* Add nodes to the toping list | ||
138 | all nodes in this list are pinged every TIME_TOPING seconds | ||
139 | and are then removed from the list. | ||
140 | if the list is full the nodes farthest from our client_id are replaced | ||
141 | the purpose of this list is to enable quick integration of new nodes into the | ||
142 | network while preventing amplification attacks. | ||
143 | return 0 if node was added | ||
144 | return -1 if node was not added */ | ||
145 | int add_toping(DHT *dht, uint8_t *client_id, IP_Port ip_port); | ||
146 | |||
147 | /* ROUTING FUNCTIONS */ | ||
148 | |||
149 | /* send the given packet to node with client_id | ||
150 | returns -1 if failure */ | ||
151 | int route_packet(DHT *dht, uint8_t *client_id, uint8_t *packet, uint32_t length); | ||
152 | |||
153 | /* Send the following packet to everyone who tells us they are connected to friend_id | ||
154 | returns the number of nodes it sent the packet to */ | ||
155 | int route_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t length); | ||
156 | |||
157 | /* NAT PUNCHING FUNCTIONS */ | ||
158 | |||
159 | /* Puts all the different ips returned by the nodes for a friend_id into array ip_portlist | ||
160 | ip_portlist must be at least MAX_FRIEND_CLIENTS big | ||
161 | returns the number of ips returned | ||
162 | returns -1 if no such friend*/ | ||
163 | int friend_ips(DHT *dht, IP_Port *ip_portlist, uint8_t *friend_id); | ||
164 | |||
165 | /* SAVE/LOAD functions */ | ||
166 | |||
167 | /* get the size of the DHT (for saving) */ | ||
168 | uint32_t DHT_size(DHT *dht); | ||
169 | |||
170 | /* save the DHT in data where data is an array of size DHT_size() */ | ||
171 | void DHT_save(DHT *dht, uint8_t *data); | ||
172 | |||
173 | /* init DHT */ | ||
174 | DHT *new_DHT(Net_Crypto *c); | ||
175 | |||
176 | void kill_DHT(DHT *dht); | ||
177 | |||
178 | /* load the DHT from data of size size; | ||
179 | return -1 if failure | ||
180 | return 0 if success */ | ||
181 | int DHT_load(DHT *dht, uint8_t *data, uint32_t size); | ||
182 | |||
183 | /* returns 0 if we are not connected to the DHT | ||
184 | returns 1 if we are */ | ||
185 | int DHT_isconnected(DHT *dht); | ||
186 | |||
187 | void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id); | ||
188 | |||
189 | #ifdef __cplusplus | ||
190 | } | ||
191 | #endif | ||
192 | |||
193 | #endif | ||
diff --git a/toxcore/LAN_discovery.c b/toxcore/LAN_discovery.c new file mode 100644 index 00000000..49f52ce7 --- /dev/null +++ b/toxcore/LAN_discovery.c | |||
@@ -0,0 +1,152 @@ | |||
1 | /* LAN_discovery.c | ||
2 | * | ||
3 | * LAN discovery implementation. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #include "LAN_discovery.h" | ||
25 | |||
26 | #define MAX_INTERFACES 16 | ||
27 | |||
28 | #ifdef __linux | ||
29 | /* get the first working broadcast address that's not from "lo" | ||
30 | * returns higher than 0 on success | ||
31 | * returns 0 on error */ | ||
32 | static uint32_t get_broadcast(void) | ||
33 | { | ||
34 | /* not sure how many platforms this will | ||
35 | * run on, so it's wrapped in __linux for now */ | ||
36 | struct sockaddr_in *sock_holder = NULL; | ||
37 | struct ifreq i_faces[MAX_INTERFACES]; | ||
38 | struct ifconf ifconf; | ||
39 | int count = 0; | ||
40 | int sock = 0; | ||
41 | int i = 0; | ||
42 | |||
43 | /* configure ifconf for the ioctl call */ | ||
44 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | ||
45 | perror("[!] get_broadcast: socket() error"); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | memset(i_faces, 0, sizeof(struct ifreq) * MAX_INTERFACES); | ||
50 | |||
51 | ifconf.ifc_buf = (char *)i_faces; | ||
52 | ifconf.ifc_len = sizeof(i_faces); | ||
53 | count = ifconf.ifc_len / sizeof(struct ifreq); | ||
54 | |||
55 | if (ioctl(sock, SIOCGIFCONF, &ifconf) < 0) { | ||
56 | perror("get_broadcast: ioctl() error"); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | for (i = 0; i < count; i++) { | ||
61 | /* skip the loopback interface, as it's useless */ | ||
62 | if (strcmp(i_faces[i].ifr_name, "lo") != 0) { | ||
63 | if (ioctl(sock, SIOCGIFBRDADDR, &i_faces[i]) < 0) { | ||
64 | perror("[!] get_broadcast: ioctl error"); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | /* just to clarify where we're getting the values from */ | ||
69 | sock_holder = (struct sockaddr_in *)&i_faces[i].ifr_broadaddr; | ||
70 | break; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | close(sock); | ||
75 | |||
76 | if (sock_holder == NULL) { | ||
77 | perror("[!] no broadcast device found"); | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | return sock_holder->sin_addr.s_addr; | ||
82 | } | ||
83 | #endif | ||
84 | |||
85 | /* Return the broadcast ip */ | ||
86 | static IP broadcast_ip(void) | ||
87 | { | ||
88 | IP ip; | ||
89 | #ifdef __linux | ||
90 | ip.i = get_broadcast(); | ||
91 | |||
92 | if (ip.i == 0) | ||
93 | /* error errored, but try anyway? */ | ||
94 | ip.i = ~0; | ||
95 | |||
96 | #else | ||
97 | ip.i = ~0; | ||
98 | #endif | ||
99 | return ip; | ||
100 | } | ||
101 | |||
102 | /*return 0 if ip is a LAN ip | ||
103 | return -1 if it is not */ | ||
104 | static int LAN_ip(IP ip) | ||
105 | { | ||
106 | if (ip.c[0] == 127)/* Loopback */ | ||
107 | return 0; | ||
108 | |||
109 | if (ip.c[0] == 10)/* 10.0.0.0 to 10.255.255.255 range */ | ||
110 | return 0; | ||
111 | |||
112 | if (ip.c[0] == 172 && ip.c[1] >= 16 && ip.c[1] <= 31)/* 172.16.0.0 to 172.31.255.255 range */ | ||
113 | return 0; | ||
114 | |||
115 | if (ip.c[0] == 192 && ip.c[1] == 168) /* 192.168.0.0 to 192.168.255.255 range */ | ||
116 | return 0; | ||
117 | |||
118 | if (ip.c[0] == 169 && ip.c[1] == 254 && ip.c[2] != 0 && ip.c[2] != 255)/* 169.254.1.0 to 169.254.254.255 range */ | ||
119 | return 0; | ||
120 | |||
121 | return -1; | ||
122 | } | ||
123 | |||
124 | static int handle_LANdiscovery(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
125 | { | ||
126 | DHT *dht = object; | ||
127 | |||
128 | if (LAN_ip(source.ip) == -1) | ||
129 | return 1; | ||
130 | |||
131 | if (length != crypto_box_PUBLICKEYBYTES + 1) | ||
132 | return 1; | ||
133 | |||
134 | DHT_bootstrap(dht, source, packet + 1); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | |||
139 | int send_LANdiscovery(uint16_t port, Net_Crypto *c) | ||
140 | { | ||
141 | uint8_t data[crypto_box_PUBLICKEYBYTES + 1]; | ||
142 | data[0] = NET_PACKET_LAN_DISCOVERY; | ||
143 | memcpy(data + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES); | ||
144 | IP_Port ip_port = {broadcast_ip(), port}; | ||
145 | return sendpacket(c->lossless_udp->net->sock, ip_port, data, 1 + crypto_box_PUBLICKEYBYTES); | ||
146 | } | ||
147 | |||
148 | |||
149 | void LANdiscovery_init(DHT *dht) | ||
150 | { | ||
151 | networking_registerhandler(dht->c->lossless_udp->net, NET_PACKET_LAN_DISCOVERY, &handle_LANdiscovery, dht); | ||
152 | } | ||
diff --git a/toxcore/LAN_discovery.h b/toxcore/LAN_discovery.h new file mode 100644 index 00000000..5a790331 --- /dev/null +++ b/toxcore/LAN_discovery.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* LAN_discovery.h | ||
2 | * | ||
3 | * LAN discovery implementation. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | |||
25 | #ifndef LAN_DISCOVERY_H | ||
26 | #define LAN_DISCOVERY_H | ||
27 | |||
28 | |||
29 | #include "DHT.h" | ||
30 | |||
31 | /* used for get_broadcast() */ | ||
32 | #ifdef __linux | ||
33 | #include <sys/ioctl.h> | ||
34 | #include <arpa/inet.h> | ||
35 | #include <linux/netdevice.h> | ||
36 | #endif | ||
37 | |||
38 | #ifdef __cplusplus | ||
39 | extern "C" { | ||
40 | #endif | ||
41 | |||
42 | /*Send a LAN discovery pcaket to the broadcast address with port port*/ | ||
43 | int send_LANdiscovery(uint16_t port, Net_Crypto *c); | ||
44 | |||
45 | |||
46 | /* sets up packet handlers */ | ||
47 | void LANdiscovery_init(DHT *dht); | ||
48 | |||
49 | |||
50 | |||
51 | #ifdef __cplusplus | ||
52 | } | ||
53 | #endif | ||
54 | |||
55 | #endif | ||
diff --git a/toxcore/Lossless_UDP.c b/toxcore/Lossless_UDP.c new file mode 100644 index 00000000..c30eb903 --- /dev/null +++ b/toxcore/Lossless_UDP.c | |||
@@ -0,0 +1,842 @@ | |||
1 | /* Lossless_UDP.c | ||
2 | * | ||
3 | * An implementation of the Lossless_UDP protocol as seen in http://wiki.tox.im/index.php/Lossless_UDP | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | /* | ||
25 | * TODO: clean this file a bit. | ||
26 | * There are a couple of useless variables to get rid of. | ||
27 | */ | ||
28 | |||
29 | #include "Lossless_UDP.h" | ||
30 | |||
31 | |||
32 | /* Functions */ | ||
33 | |||
34 | /* | ||
35 | * Get connection id from IP_Port | ||
36 | * Return -1 if there are no connections like we are looking for | ||
37 | * Return id if it found it | ||
38 | */ | ||
39 | int getconnection_id(Lossless_UDP *ludp, IP_Port ip_port) | ||
40 | { | ||
41 | uint32_t i; | ||
42 | |||
43 | for (i = 0; i < ludp->connections_length; ++i) { | ||
44 | if (ludp->connections[i].ip_port.ip.i == ip_port.ip.i && | ||
45 | ludp->connections[i].ip_port.port == ip_port.port && | ||
46 | ludp->connections[i].status > 0) | ||
47 | return i; | ||
48 | } | ||
49 | |||
50 | return -1; | ||
51 | } | ||
52 | |||
53 | |||
54 | /* | ||
55 | * Generate a handshake_id which depends on the ip_port. | ||
56 | * This function will always give one unique handshake_id per ip_port. | ||
57 | * | ||
58 | * TODO: make this better | ||
59 | */ | ||
60 | static uint32_t handshake_id(Lossless_UDP *ludp, IP_Port source) | ||
61 | { | ||
62 | uint32_t id = 0, i; | ||
63 | |||
64 | for (i = 0; i < 6; ++i) { | ||
65 | if (ludp->randtable[i][((uint8_t *)&source)[i]] == 0) | ||
66 | ludp->randtable[i][((uint8_t *)&source)[i]] = random_int(); | ||
67 | |||
68 | id ^= ludp->randtable[i][((uint8_t *)&source)[i]]; | ||
69 | } | ||
70 | |||
71 | if (id == 0) /* id can't be zero */ | ||
72 | id = 1; | ||
73 | |||
74 | return id; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Change the hanshake id associated with that ip_port | ||
79 | * | ||
80 | * TODO: make this better | ||
81 | */ | ||
82 | static void change_handshake(Lossless_UDP *ludp, IP_Port source) | ||
83 | { | ||
84 | uint8_t rand = random_int() % 4; | ||
85 | ludp->randtable[rand][((uint8_t *)&source)[rand]] = random_int(); | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * Initialize a new connection to ip_port | ||
90 | * Returns an integer corresponding to the connection idt | ||
91 | * Return -1 if it could not initialize the connectiont | ||
92 | * If there already was an existing connection to that ip_port return its number. | ||
93 | */ | ||
94 | int new_connection(Lossless_UDP *ludp, IP_Port ip_port) | ||
95 | { | ||
96 | int connect = getconnection_id(ludp, ip_port); | ||
97 | |||
98 | if (connect != -1) | ||
99 | return connect; | ||
100 | |||
101 | if (ludp->connections_number == ludp->connections_length) { | ||
102 | Connection *temp; | ||
103 | temp = realloc(ludp->connections, sizeof(Connection) * (ludp->connections_length + 1)); | ||
104 | |||
105 | if (temp == NULL) | ||
106 | return -1; | ||
107 | |||
108 | memset(&temp[ludp->connections_length], 0, sizeof(Connection)); | ||
109 | ++ludp->connections_length; | ||
110 | ludp->connections = temp; | ||
111 | } | ||
112 | |||
113 | uint32_t i; | ||
114 | |||
115 | for (i = 0; i < ludp->connections_length; ++i) { | ||
116 | if (ludp->connections[i].status == 0) { | ||
117 | memset(&ludp->connections[i], 0, sizeof(Connection)); | ||
118 | uint32_t handshake_id1 = handshake_id(ludp, ip_port); | ||
119 | |||
120 | ludp->connections[i] = (Connection) { | ||
121 | .ip_port = ip_port, | ||
122 | .status = 1, | ||
123 | .inbound = 0, | ||
124 | .handshake_id1 = handshake_id1, | ||
125 | .sent_packetnum = handshake_id1, | ||
126 | .sendbuff_packetnum = handshake_id1, | ||
127 | .successful_sent = handshake_id1, | ||
128 | .SYNC_rate = SYNC_RATE, | ||
129 | .data_rate = DATA_SYNC_RATE, | ||
130 | .last_recvSYNC = current_time(), | ||
131 | .last_sent = current_time(), | ||
132 | .killat = ~0, | ||
133 | .send_counter = 0, | ||
134 | /* add randomness to timeout to prevent connections getting stuck in a loop. */ | ||
135 | .timeout = CONNEXION_TIMEOUT + rand() % CONNEXION_TIMEOUT | ||
136 | }; | ||
137 | ++ludp->connections_number; | ||
138 | |||
139 | return i; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | return -1; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Initialize a new inbound connection from ip_port | ||
148 | * Returns an integer corresponding to the connection id. | ||
149 | * Return -1 if it could not initialize the connection. | ||
150 | */ | ||
151 | static int new_inconnection(Lossless_UDP *ludp, IP_Port ip_port) | ||
152 | { | ||
153 | if (getconnection_id(ludp, ip_port) != -1) | ||
154 | return -1; | ||
155 | |||
156 | if (ludp->connections_number == ludp->connections_length) { | ||
157 | Connection *temp; | ||
158 | temp = realloc(ludp->connections, sizeof(Connection) * (ludp->connections_length + 1)); | ||
159 | |||
160 | if (temp == NULL) | ||
161 | return -1; | ||
162 | |||
163 | memset(&temp[ludp->connections_length], 0, sizeof(Connection)); | ||
164 | ++ludp->connections_length; | ||
165 | ludp->connections = temp; | ||
166 | } | ||
167 | |||
168 | uint32_t i; | ||
169 | |||
170 | for (i = 0; i < ludp->connections_length; ++i) { | ||
171 | if (ludp->connections[i].status == 0) { | ||
172 | memset(&ludp->connections[i], 0, sizeof(Connection)); | ||
173 | uint64_t timeout = CONNEXION_TIMEOUT + rand() % CONNEXION_TIMEOUT; | ||
174 | |||
175 | ludp->connections[i] = (Connection) { | ||
176 | .ip_port = ip_port, | ||
177 | .status = 2, | ||
178 | .inbound = 2, | ||
179 | .SYNC_rate = SYNC_RATE, | ||
180 | .data_rate = DATA_SYNC_RATE, | ||
181 | .last_recvSYNC = current_time(), | ||
182 | .last_sent = current_time(), | ||
183 | .send_counter = 127, | ||
184 | |||
185 | /* add randomness to timeout to prevent connections getting stuck in a loop. */ | ||
186 | .timeout = timeout, | ||
187 | |||
188 | /* if this connection isn't handled within the timeout kill it. */ | ||
189 | .killat = current_time() + 1000000UL * timeout | ||
190 | }; | ||
191 | ++ludp->connections_number; | ||
192 | return i; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | return -1; | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * Returns an integer corresponding to the next connection in our incoming connection list. | ||
201 | * Return -1 if there are no new incoming connections in the list. | ||
202 | */ | ||
203 | int incoming_connection(Lossless_UDP *ludp) | ||
204 | { | ||
205 | uint32_t i; | ||
206 | |||
207 | for (i = 0; i < ludp->connections_length; ++i) { | ||
208 | if (ludp->connections[i].inbound == 2) { | ||
209 | ludp->connections[i].inbound = 1; | ||
210 | return i; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | return -1; | ||
215 | } | ||
216 | |||
217 | /* Try to free some memory from the connections array. */ | ||
218 | static void free_connections(Lossless_UDP *ludp) | ||
219 | { | ||
220 | uint32_t i; | ||
221 | |||
222 | for (i = ludp->connections_length; i != 0; --i) | ||
223 | if (ludp->connections[i - 1].status != 0) | ||
224 | break; | ||
225 | |||
226 | if (ludp->connections_length == i) | ||
227 | return; | ||
228 | |||
229 | if (i == 0) { | ||
230 | free(ludp->connections); | ||
231 | ludp->connections = NULL; | ||
232 | ludp->connections_length = i; | ||
233 | return; | ||
234 | } | ||
235 | |||
236 | Connection *temp; | ||
237 | temp = realloc(ludp->connections, sizeof(Connection) * i); | ||
238 | |||
239 | if (temp == NULL && i != 0) | ||
240 | return; | ||
241 | |||
242 | ludp->connections = temp; | ||
243 | ludp->connections_length = i; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * Return -1 if it could not kill the connection. | ||
248 | * Return 0 if killed successfully | ||
249 | */ | ||
250 | int kill_connection(Lossless_UDP *ludp, int connection_id) | ||
251 | { | ||
252 | if (connection_id >= 0 && connection_id < ludp->connections_length) { | ||
253 | if (ludp->connections[connection_id].status > 0) { | ||
254 | ludp->connections[connection_id].status = 0; | ||
255 | change_handshake(ludp, ludp->connections[connection_id].ip_port); | ||
256 | --ludp->connections_number; | ||
257 | free_connections(ludp); | ||
258 | return 0; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | return -1; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Kill connection in seconds. | ||
267 | * Return -1 if it can not kill the connection. | ||
268 | * Return 0 if it will kill it. | ||
269 | */ | ||
270 | int kill_connection_in(Lossless_UDP *ludp, int connection_id, uint32_t seconds) | ||
271 | { | ||
272 | if (connection_id >= 0 && connection_id < ludp->connections_length) { | ||
273 | if (ludp->connections[connection_id].status > 0) { | ||
274 | ludp->connections[connection_id].killat = current_time() + 1000000UL * seconds; | ||
275 | return 0; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | return -1; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * Check if connection is connected: | ||
284 | * Return 0 no. | ||
285 | * Return 1 if attempting handshake. | ||
286 | * Return 2 if handshake is done. | ||
287 | * Return 3 if fully connected. | ||
288 | * Return 4 if timed out and waiting to be killed. | ||
289 | */ | ||
290 | int is_connected(Lossless_UDP *ludp, int connection_id) | ||
291 | { | ||
292 | if (connection_id >= 0 && connection_id < ludp->connections_length) | ||
293 | return ludp->connections[connection_id].status; | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | /* returns the ip_port of the corresponding connection. */ | ||
299 | IP_Port connection_ip(Lossless_UDP *ludp, int connection_id) | ||
300 | { | ||
301 | if (connection_id >= 0 && connection_id < ludp->connections_length) | ||
302 | return ludp->connections[connection_id].ip_port; | ||
303 | |||
304 | IP_Port zero = {{{0}}, 0}; | ||
305 | return zero; | ||
306 | } | ||
307 | |||
308 | /* returns the number of packets in the queue waiting to be successfully sent. */ | ||
309 | uint32_t sendqueue(Lossless_UDP *ludp, int connection_id) | ||
310 | { | ||
311 | if (connection_id < 0 || connection_id >= ludp->connections_length) | ||
312 | return 0; | ||
313 | |||
314 | return ludp->connections[connection_id].sendbuff_packetnum - ludp->connections[connection_id].successful_sent; | ||
315 | } | ||
316 | |||
317 | /* returns the number of packets in the queue waiting to be successfully read with read_packet(...) */ | ||
318 | uint32_t recvqueue(Lossless_UDP *ludp, int connection_id) | ||
319 | { | ||
320 | if (connection_id < 0 || connection_id >= ludp->connections_length) | ||
321 | return 0; | ||
322 | |||
323 | return ludp->connections[connection_id].recv_packetnum - ludp->connections[connection_id].successful_read; | ||
324 | } | ||
325 | |||
326 | /* returns the id of the next packet in the queue | ||
327 | return -1 if no packet in queue */ | ||
328 | char id_packet(Lossless_UDP *ludp, int connection_id) | ||
329 | { | ||
330 | if (connection_id < 0 || connection_id >= ludp->connections_length) | ||
331 | return -1; | ||
332 | |||
333 | if (recvqueue(ludp, connection_id) != 0 && ludp->connections[connection_id].status != 0) | ||
334 | return ludp->connections[connection_id].recvbuffer[ludp->connections[connection_id].successful_read % | ||
335 | MAX_QUEUE_NUM].data[0]; | ||
336 | |||
337 | return -1; | ||
338 | } | ||
339 | |||
340 | /* return 0 if there is no received data in the buffer. | ||
341 | return length of received packet if successful */ | ||
342 | int read_packet(Lossless_UDP *ludp, int connection_id, uint8_t *data) | ||
343 | { | ||
344 | if (recvqueue(ludp, connection_id) != 0) { | ||
345 | uint16_t index = ludp->connections[connection_id].successful_read % MAX_QUEUE_NUM; | ||
346 | uint16_t size = ludp->connections[connection_id].recvbuffer[index].size; | ||
347 | memcpy(data, ludp->connections[connection_id].recvbuffer[index].data, size); | ||
348 | ++ludp->connections[connection_id].successful_read; | ||
349 | ludp->connections[connection_id].recvbuffer[index].size = 0; | ||
350 | return size; | ||
351 | } | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | /* | ||
357 | * Return 0 if data could not be put in packet queue | ||
358 | * Return 1 if data was put into the queue | ||
359 | */ | ||
360 | int write_packet(Lossless_UDP *ludp, int connection_id, uint8_t *data, uint32_t length) | ||
361 | { | ||
362 | if (length > MAX_DATA_SIZE || length == 0) | ||
363 | return 0; | ||
364 | |||
365 | if (sendqueue(ludp, connection_id) < BUFFER_PACKET_NUM) { | ||
366 | uint32_t index = ludp->connections[connection_id].sendbuff_packetnum % MAX_QUEUE_NUM; | ||
367 | memcpy(ludp->connections[connection_id].sendbuffer[index].data, data, length); | ||
368 | ludp->connections[connection_id].sendbuffer[index].size = length; | ||
369 | ludp->connections[connection_id].sendbuff_packetnum++; | ||
370 | return 1; | ||
371 | } | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | /* put the packet numbers the we are missing in requested and return the number */ | ||
377 | uint32_t missing_packets(Lossless_UDP *ludp, int connection_id, uint32_t *requested) | ||
378 | { | ||
379 | uint32_t number = 0; | ||
380 | uint32_t i; | ||
381 | uint32_t temp; | ||
382 | |||
383 | /* don't request packets if the buffer is full. */ | ||
384 | if (recvqueue(ludp, connection_id) >= (BUFFER_PACKET_NUM - 1)) | ||
385 | return 0; | ||
386 | |||
387 | for (i = ludp->connections[connection_id].recv_packetnum; i != ludp->connections[connection_id].osent_packetnum; i++) { | ||
388 | if (ludp->connections[connection_id].recvbuffer[i % MAX_QUEUE_NUM].size == 0) { | ||
389 | temp = htonl(i); | ||
390 | memcpy(requested + number, &temp, 4); | ||
391 | ++number; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | if (number == 0) | ||
396 | ludp->connections[connection_id].recv_packetnum = ludp->connections[connection_id].osent_packetnum; | ||
397 | |||
398 | return number; | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * BEGIN Packet sending functions | ||
403 | * One per packet type. | ||
404 | * see http://wiki.tox.im/index.php/Lossless_UDP for more information. | ||
405 | */ | ||
406 | |||
407 | static int send_handshake(Lossless_UDP *ludp, IP_Port ip_port, uint32_t handshake_id1, uint32_t handshake_id2) | ||
408 | { | ||
409 | uint8_t packet[1 + 4 + 4]; | ||
410 | uint32_t temp; | ||
411 | |||
412 | packet[0] = NET_PACKET_HANDSHAKE; | ||
413 | temp = htonl(handshake_id1); | ||
414 | memcpy(packet + 1, &temp, 4); | ||
415 | temp = htonl(handshake_id2); | ||
416 | memcpy(packet + 5, &temp, 4); | ||
417 | |||
418 | return sendpacket(ludp->net->sock, ip_port, packet, sizeof(packet)); | ||
419 | } | ||
420 | |||
421 | static int send_SYNC(Lossless_UDP *ludp, uint32_t connection_id) | ||
422 | { | ||
423 | uint8_t packet[(BUFFER_PACKET_NUM * 4 + 4 + 4 + 2)]; | ||
424 | uint16_t index = 0; | ||
425 | |||
426 | IP_Port ip_port = ludp->connections[connection_id].ip_port; | ||
427 | uint8_t counter = ludp->connections[connection_id].send_counter; | ||
428 | uint32_t recv_packetnum = htonl(ludp->connections[connection_id].recv_packetnum); | ||
429 | uint32_t sent_packetnum = htonl(ludp->connections[connection_id].sent_packetnum); | ||
430 | |||
431 | uint32_t requested[BUFFER_PACKET_NUM]; | ||
432 | uint32_t number = missing_packets(ludp, connection_id, requested); | ||
433 | |||
434 | packet[0] = NET_PACKET_SYNC; | ||
435 | index += 1; | ||
436 | memcpy(packet + index, &counter, 1); | ||
437 | index += 1; | ||
438 | memcpy(packet + index, &recv_packetnum, 4); | ||
439 | index += 4; | ||
440 | memcpy(packet + index, &sent_packetnum, 4); | ||
441 | index += 4; | ||
442 | memcpy(packet + index, requested, 4 * number); | ||
443 | |||
444 | return sendpacket(ludp->net->sock, ip_port, packet, (number * 4 + 4 + 4 + 2)); | ||
445 | |||
446 | } | ||
447 | |||
448 | static int send_data_packet(Lossless_UDP *ludp, uint32_t connection_id, uint32_t packet_num) | ||
449 | { | ||
450 | uint32_t index = packet_num % MAX_QUEUE_NUM; | ||
451 | uint32_t temp; | ||
452 | uint8_t packet[1 + 4 + MAX_DATA_SIZE]; | ||
453 | packet[0] = NET_PACKET_DATA; | ||
454 | temp = htonl(packet_num); | ||
455 | memcpy(packet + 1, &temp, 4); | ||
456 | memcpy(packet + 5, ludp->connections[connection_id].sendbuffer[index].data, | ||
457 | ludp->connections[connection_id].sendbuffer[index].size); | ||
458 | return sendpacket(ludp->net->sock, ludp->connections[connection_id].ip_port, packet, | ||
459 | 1 + 4 + ludp->connections[connection_id].sendbuffer[index].size); | ||
460 | } | ||
461 | |||
462 | /* sends 1 data packet */ | ||
463 | static int send_DATA(Lossless_UDP *ludp, uint32_t connection_id) | ||
464 | { | ||
465 | int ret; | ||
466 | uint32_t buffer[BUFFER_PACKET_NUM]; | ||
467 | |||
468 | if (ludp->connections[connection_id].num_req_paquets > 0) { | ||
469 | ret = send_data_packet(ludp, connection_id, ludp->connections[connection_id].req_packets[0]); | ||
470 | ludp->connections[connection_id].num_req_paquets--; | ||
471 | memcpy(buffer, ludp->connections[connection_id].req_packets + 1, ludp->connections[connection_id].num_req_paquets * 4); | ||
472 | memcpy(ludp->connections[connection_id].req_packets, buffer, ludp->connections[connection_id].num_req_paquets * 4); | ||
473 | return ret; | ||
474 | } | ||
475 | |||
476 | if (ludp->connections[connection_id].sendbuff_packetnum != ludp->connections[connection_id].sent_packetnum) { | ||
477 | ret = send_data_packet(ludp, connection_id, ludp->connections[connection_id].sent_packetnum); | ||
478 | ludp->connections[connection_id].sent_packetnum++; | ||
479 | return ret; | ||
480 | } | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | /* | ||
486 | * END of packet sending functions | ||
487 | * | ||
488 | * | ||
489 | * BEGIN Packet handling functions | ||
490 | * One to handle each type of packets we receive | ||
491 | */ | ||
492 | |||
493 | |||
494 | /* Return 0 if handled correctly, 1 if packet is bad. */ | ||
495 | static int handle_handshake(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
496 | { | ||
497 | Lossless_UDP *ludp = object; | ||
498 | |||
499 | if (length != (1 + 4 + 4)) | ||
500 | return 1; | ||
501 | |||
502 | uint32_t temp; | ||
503 | uint32_t handshake_id1, handshake_id2; | ||
504 | |||
505 | int connection = getconnection_id(ludp, source); | ||
506 | memcpy(&temp, packet + 1, 4); | ||
507 | handshake_id1 = ntohl(temp); | ||
508 | memcpy(&temp, packet + 5, 4); | ||
509 | handshake_id2 = ntohl(temp); | ||
510 | |||
511 | if (handshake_id2 == 0 && is_connected(ludp, connection) < 3) { | ||
512 | send_handshake(ludp, source, handshake_id(ludp, source), handshake_id1); | ||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | if (is_connected(ludp, connection) != 1) | ||
517 | return 1; | ||
518 | |||
519 | /* if handshake_id2 is what we sent previously as handshake_id1 */ | ||
520 | if (handshake_id2 == ludp->connections[connection].handshake_id1) { | ||
521 | ludp->connections[connection].status = 2; | ||
522 | /* NOTE: is this necessary? | ||
523 | ludp->connections[connection].handshake_id2 = handshake_id1; */ | ||
524 | ludp->connections[connection].orecv_packetnum = handshake_id2; | ||
525 | ludp->connections[connection].osent_packetnum = handshake_id1; | ||
526 | ludp->connections[connection].recv_packetnum = handshake_id1; | ||
527 | ludp->connections[connection].successful_read = handshake_id1; | ||
528 | } | ||
529 | |||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | /* returns 1 if sync packet is valid 0 if not. */ | ||
534 | static int SYNC_valid(uint32_t length) | ||
535 | { | ||
536 | if (length < 4 + 4 + 2) | ||
537 | return 0; | ||
538 | |||
539 | if (length > (BUFFER_PACKET_NUM * 4 + 4 + 4 + 2) || | ||
540 | ((length - 4 - 4 - 2) % 4) != 0) | ||
541 | return 0; | ||
542 | |||
543 | return 1; | ||
544 | } | ||
545 | |||
546 | /* case 1 in handle_SYNC: */ | ||
547 | static int handle_SYNC1(Lossless_UDP *ludp, IP_Port source, uint32_t recv_packetnum, uint32_t sent_packetnum) | ||
548 | { | ||
549 | if (handshake_id(ludp, source) == recv_packetnum) { | ||
550 | int x = new_inconnection(ludp, source); | ||
551 | |||
552 | if (x != -1) { | ||
553 | ludp->connections[x].orecv_packetnum = recv_packetnum; | ||
554 | ludp->connections[x].sent_packetnum = recv_packetnum; | ||
555 | ludp->connections[x].sendbuff_packetnum = recv_packetnum; | ||
556 | ludp->connections[x].successful_sent = recv_packetnum; | ||
557 | ludp->connections[x].osent_packetnum = sent_packetnum; | ||
558 | ludp->connections[x].recv_packetnum = sent_packetnum; | ||
559 | ludp->connections[x].successful_read = sent_packetnum; | ||
560 | |||
561 | return x; | ||
562 | } | ||
563 | } | ||
564 | |||
565 | return -1; | ||
566 | } | ||
567 | |||
568 | /* case 2 in handle_SYNC: */ | ||
569 | static int handle_SYNC2(Lossless_UDP *ludp, int connection_id, uint8_t counter, uint32_t recv_packetnum, | ||
570 | uint32_t sent_packetnum) | ||
571 | { | ||
572 | if (recv_packetnum == ludp->connections[connection_id].orecv_packetnum) { | ||
573 | /* && sent_packetnum == ludp->connections[connection_id].osent_packetnum) */ | ||
574 | ludp->connections[connection_id].status = 3; | ||
575 | ludp->connections[connection_id].recv_counter = counter; | ||
576 | ++ludp->connections[connection_id].send_counter; | ||
577 | send_SYNC(ludp, connection_id); | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | return 1; | ||
582 | } | ||
583 | /* case 3 in handle_SYNC: */ | ||
584 | static int handle_SYNC3(Lossless_UDP *ludp, int connection_id, uint8_t counter, uint32_t recv_packetnum, | ||
585 | uint32_t sent_packetnum, | ||
586 | uint32_t *req_packets, | ||
587 | uint16_t number) | ||
588 | { | ||
589 | uint8_t comp_counter = (counter - ludp->connections[connection_id].recv_counter ); | ||
590 | uint32_t i, temp; | ||
591 | /* uint32_t comp_1 = (recv_packetnum - ludp->connections[connection_id].successful_sent); | ||
592 | uint32_t comp_2 = (sent_packetnum - ludp->connections[connection_id].successful_read); */ | ||
593 | uint32_t comp_1 = (recv_packetnum - ludp->connections[connection_id].orecv_packetnum); | ||
594 | uint32_t comp_2 = (sent_packetnum - ludp->connections[connection_id].osent_packetnum); | ||
595 | |||
596 | /* packet valid */ | ||
597 | if (comp_1 <= BUFFER_PACKET_NUM && | ||
598 | comp_2 <= BUFFER_PACKET_NUM && | ||
599 | comp_counter < 10 && comp_counter != 0) { | ||
600 | |||
601 | ludp->connections[connection_id].orecv_packetnum = recv_packetnum; | ||
602 | ludp->connections[connection_id].osent_packetnum = sent_packetnum; | ||
603 | ludp->connections[connection_id].successful_sent = recv_packetnum; | ||
604 | ludp->connections[connection_id].last_recvSYNC = current_time(); | ||
605 | ludp->connections[connection_id].recv_counter = counter; | ||
606 | |||
607 | ++ludp->connections[connection_id].send_counter; | ||
608 | |||
609 | for (i = 0; i < number; ++i) { | ||
610 | temp = ntohl(req_packets[i]); | ||
611 | memcpy(ludp->connections[connection_id].req_packets + i, &temp, 4 * number); | ||
612 | } | ||
613 | |||
614 | ludp->connections[connection_id].num_req_paquets = number; | ||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | return 1; | ||
619 | } | ||
620 | |||
621 | static int handle_SYNC(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
622 | { | ||
623 | Lossless_UDP *ludp = object; | ||
624 | |||
625 | if (!SYNC_valid(length)) | ||
626 | return 1; | ||
627 | |||
628 | int connection = getconnection_id(ludp, source); | ||
629 | uint8_t counter; | ||
630 | uint32_t temp; | ||
631 | uint32_t recv_packetnum, sent_packetnum; | ||
632 | uint32_t req_packets[BUFFER_PACKET_NUM]; | ||
633 | uint16_t number = (length - 4 - 4 - 2) / 4; | ||
634 | |||
635 | memcpy(&counter, packet + 1, 1); | ||
636 | memcpy(&temp, packet + 2, 4); | ||
637 | recv_packetnum = ntohl(temp); | ||
638 | memcpy(&temp, packet + 6, 4); | ||
639 | sent_packetnum = ntohl(temp); | ||
640 | |||
641 | if (number != 0) | ||
642 | memcpy(req_packets, packet + 10, 4 * number); | ||
643 | |||
644 | if (connection == -1) | ||
645 | return handle_SYNC1(ludp, source, recv_packetnum, sent_packetnum); | ||
646 | |||
647 | if (ludp->connections[connection].status == 2) | ||
648 | return handle_SYNC2(ludp, connection, counter, | ||
649 | recv_packetnum, sent_packetnum); | ||
650 | |||
651 | if (ludp->connections[connection].status == 3) | ||
652 | return handle_SYNC3(ludp, connection, counter, recv_packetnum, | ||
653 | sent_packetnum, req_packets, number); | ||
654 | |||
655 | return 0; | ||
656 | } | ||
657 | |||
658 | /* | ||
659 | * Add a packet to the received buffer and set the recv_packetnum of the | ||
660 | * connection to its proper value. Return 1 if data was too big, 0 if not. | ||
661 | */ | ||
662 | static int add_recv(Lossless_UDP *ludp, int connection_id, uint32_t data_num, uint8_t *data, uint16_t size) | ||
663 | { | ||
664 | if (size > MAX_DATA_SIZE) | ||
665 | return 1; | ||
666 | |||
667 | uint32_t i; | ||
668 | uint32_t maxnum = ludp->connections[connection_id].successful_read + BUFFER_PACKET_NUM; | ||
669 | uint32_t sent_packet = data_num - ludp->connections[connection_id].osent_packetnum; | ||
670 | |||
671 | for (i = ludp->connections[connection_id].recv_packetnum; i != maxnum; ++i) { | ||
672 | if (i == data_num) { | ||
673 | memcpy(ludp->connections[connection_id].recvbuffer[i % MAX_QUEUE_NUM].data, data, size); | ||
674 | |||
675 | ludp->connections[connection_id].recvbuffer[i % MAX_QUEUE_NUM].size = size; | ||
676 | ludp->connections[connection_id].last_recvdata = current_time(); | ||
677 | |||
678 | if (sent_packet < BUFFER_PACKET_NUM) { | ||
679 | ludp->connections[connection_id].osent_packetnum = data_num; | ||
680 | } | ||
681 | |||
682 | break; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | for (i = ludp->connections[connection_id].recv_packetnum; i != maxnum; ++i) { | ||
687 | if (ludp->connections[connection_id].recvbuffer[i % MAX_QUEUE_NUM].size != 0) | ||
688 | ludp->connections[connection_id].recv_packetnum = i; | ||
689 | else | ||
690 | break; | ||
691 | } | ||
692 | |||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | static int handle_data(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
697 | { | ||
698 | Lossless_UDP *ludp = object; | ||
699 | int connection = getconnection_id(ludp, source); | ||
700 | |||
701 | if (connection == -1) | ||
702 | return 1; | ||
703 | |||
704 | /* Drop the data packet if connection is not connected. */ | ||
705 | if (ludp->connections[connection].status != 3) | ||
706 | return 1; | ||
707 | |||
708 | if (length > 1 + 4 + MAX_DATA_SIZE || length < 1 + 4 + 1) | ||
709 | return 1; | ||
710 | |||
711 | uint32_t temp; | ||
712 | uint32_t number; | ||
713 | uint16_t size = length - 1 - 4; | ||
714 | |||
715 | memcpy(&temp, packet + 1, 4); | ||
716 | number = ntohl(temp); | ||
717 | |||
718 | return add_recv(ludp, connection, number, packet + 5, size); | ||
719 | } | ||
720 | |||
721 | /* | ||
722 | * END of packet handling functions | ||
723 | */ | ||
724 | |||
725 | Lossless_UDP *new_lossless_udp(Networking_Core *net) | ||
726 | { | ||
727 | if (net == NULL) | ||
728 | return NULL; | ||
729 | |||
730 | Lossless_UDP *temp = calloc(1, sizeof(Lossless_UDP)); | ||
731 | |||
732 | if (temp == NULL) | ||
733 | return NULL; | ||
734 | |||
735 | temp->net = net; | ||
736 | networking_registerhandler(net, NET_PACKET_HANDSHAKE, &handle_handshake, temp); | ||
737 | networking_registerhandler(net, NET_PACKET_SYNC, &handle_SYNC, temp); | ||
738 | networking_registerhandler(net, NET_PACKET_DATA, &handle_data, temp); | ||
739 | return temp; | ||
740 | } | ||
741 | |||
742 | /* | ||
743 | * Send handshake requests | ||
744 | * handshake packets are sent at the same rate as SYNC packets | ||
745 | */ | ||
746 | static void do_new(Lossless_UDP *ludp) | ||
747 | { | ||
748 | uint32_t i; | ||
749 | uint64_t temp_time = current_time(); | ||
750 | |||
751 | for (i = 0; i < ludp->connections_length; ++i) { | ||
752 | if (ludp->connections[i].status == 1) | ||
753 | if ((ludp->connections[i].last_sent + (1000000UL / ludp->connections[i].SYNC_rate)) <= temp_time) { | ||
754 | send_handshake(ludp, ludp->connections[i].ip_port, ludp->connections[i].handshake_id1, 0); | ||
755 | ludp->connections[i].last_sent = temp_time; | ||
756 | } | ||
757 | |||
758 | /* kill all timed out connections */ | ||
759 | if (ludp->connections[i].status > 0 && | ||
760 | (ludp->connections[i].last_recvSYNC + ludp->connections[i].timeout * 1000000UL) < temp_time && | ||
761 | ludp->connections[i].status != 4) { | ||
762 | ludp->connections[i].status = 4; | ||
763 | /* kill_connection(i); */ | ||
764 | } | ||
765 | |||
766 | if (ludp->connections[i].status > 0 && ludp->connections[i].killat < temp_time) | ||
767 | kill_connection(ludp, i); | ||
768 | } | ||
769 | } | ||
770 | |||
771 | static void do_SYNC(Lossless_UDP *ludp) | ||
772 | { | ||
773 | uint32_t i; | ||
774 | uint64_t temp_time = current_time(); | ||
775 | |||
776 | for (i = 0; i < ludp->connections_length; ++i) { | ||
777 | if (ludp->connections[i].status == 2 || ludp->connections[i].status == 3) | ||
778 | if ((ludp->connections[i].last_SYNC + (1000000UL / ludp->connections[i].SYNC_rate)) <= temp_time) { | ||
779 | send_SYNC(ludp, i); | ||
780 | ludp->connections[i].last_SYNC = temp_time; | ||
781 | } | ||
782 | } | ||
783 | } | ||
784 | |||
785 | static void do_data(Lossless_UDP *ludp) | ||
786 | { | ||
787 | uint32_t i; | ||
788 | uint64_t j; | ||
789 | uint64_t temp_time = current_time(); | ||
790 | |||
791 | for (i = 0; i < ludp->connections_length; ++i) | ||
792 | if (ludp->connections[i].status == 3 && sendqueue(ludp, i) != 0) | ||
793 | if ((ludp->connections[i].last_sent + (1000000UL / ludp->connections[i].data_rate)) <= temp_time) { | ||
794 | for (j = ludp->connections[i].last_sent; j < temp_time; j += (1000000UL / ludp->connections[i].data_rate)) | ||
795 | send_DATA(ludp, i); | ||
796 | |||
797 | ludp->connections[i].last_sent = temp_time; | ||
798 | } | ||
799 | } | ||
800 | |||
801 | #define MAX_SYNC_RATE 10 | ||
802 | |||
803 | /* | ||
804 | * Automatically adjusts send rates of packets for optimal transmission. | ||
805 | * | ||
806 | * TODO: flow control. | ||
807 | */ | ||
808 | static void adjust_rates(Lossless_UDP *ludp) | ||
809 | { | ||
810 | uint32_t i; | ||
811 | uint64_t temp_time = current_time(); | ||
812 | |||
813 | for (i = 0; i < ludp->connections_length; ++i) { | ||
814 | if (ludp->connections[i].status == 1 || ludp->connections[i].status == 2) | ||
815 | ludp->connections[i].SYNC_rate = MAX_SYNC_RATE; | ||
816 | |||
817 | if (ludp->connections[i].status == 3) { | ||
818 | if (sendqueue(ludp, i) != 0) { | ||
819 | ludp->connections[i].data_rate = (BUFFER_PACKET_NUM - ludp->connections[i].num_req_paquets) * MAX_SYNC_RATE; | ||
820 | ludp->connections[i].SYNC_rate = MAX_SYNC_RATE; | ||
821 | } else if (ludp->connections[i].last_recvdata + 1000000UL > temp_time) | ||
822 | ludp->connections[i].SYNC_rate = MAX_SYNC_RATE; | ||
823 | else | ||
824 | ludp->connections[i].SYNC_rate = SYNC_RATE; | ||
825 | } | ||
826 | } | ||
827 | } | ||
828 | |||
829 | /* Call this function a couple times per second It's the main loop. */ | ||
830 | void do_lossless_udp(Lossless_UDP *ludp) | ||
831 | { | ||
832 | do_new(ludp); | ||
833 | do_SYNC(ludp); | ||
834 | do_data(ludp); | ||
835 | adjust_rates(ludp); | ||
836 | } | ||
837 | |||
838 | void kill_lossless_udp(Lossless_UDP *ludp) | ||
839 | { | ||
840 | free(ludp->connections); | ||
841 | free(ludp); | ||
842 | } | ||
diff --git a/toxcore/Lossless_UDP.h b/toxcore/Lossless_UDP.h new file mode 100644 index 00000000..176e86ce --- /dev/null +++ b/toxcore/Lossless_UDP.h | |||
@@ -0,0 +1,222 @@ | |||
1 | /* Lossless_UDP.h | ||
2 | * | ||
3 | * An implementation of the Lossless_UDP protocol as seen in http://wiki.tox.im/index.php/Lossless_UDP | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #ifndef LOSSLESS_UDP_H | ||
25 | #define LOSSLESS_UDP_H | ||
26 | |||
27 | #include "network.h" | ||
28 | |||
29 | #ifdef __cplusplus | ||
30 | extern "C" { | ||
31 | #endif | ||
32 | |||
33 | /* maximum length of the data in the data packets */ | ||
34 | #define MAX_DATA_SIZE 1024 | ||
35 | |||
36 | /* maximum data packets in sent and receive queues. */ | ||
37 | #define MAX_QUEUE_NUM 16 | ||
38 | |||
39 | /* maximum number of data packets in the buffer */ | ||
40 | #define BUFFER_PACKET_NUM (16-1) | ||
41 | |||
42 | /* timeout per connection is randomly set between CONNEXION_TIMEOUT and 2*CONNEXION_TIMEOUT */ | ||
43 | #define CONNEXION_TIMEOUT 5 | ||
44 | |||
45 | /* initial amount of sync/hanshake packets to send per second. */ | ||
46 | #define SYNC_RATE 2 | ||
47 | |||
48 | /* initial send rate of data. */ | ||
49 | #define DATA_SYNC_RATE 30 | ||
50 | |||
51 | typedef struct { | ||
52 | uint8_t data[MAX_DATA_SIZE]; | ||
53 | uint16_t size; | ||
54 | } Data; | ||
55 | |||
56 | typedef struct { | ||
57 | IP_Port ip_port; | ||
58 | |||
59 | /* | ||
60 | * 0 if connection is dead, 1 if attempting handshake, | ||
61 | * 2 if handshake is done (we start sending SYNC packets) | ||
62 | * 3 if we are sending SYNC packets and can send data | ||
63 | * 4 if the connection has timed out. | ||
64 | */ | ||
65 | uint8_t status; | ||
66 | |||
67 | /* | ||
68 | * 1 or 2 if connection was initiated by someone else, 0 if not. | ||
69 | * 2 if incoming_connection() has not returned it yet, 1 if it has. | ||
70 | */ | ||
71 | uint8_t inbound; | ||
72 | |||
73 | uint16_t SYNC_rate; /* current SYNC packet send rate packets per second. */ | ||
74 | uint16_t data_rate; /* current data packet send rate packets per second. */ | ||
75 | |||
76 | uint64_t last_SYNC; /* time our last SYNC packet was sent. */ | ||
77 | uint64_t last_sent; /* time our last data or handshake packet was sent. */ | ||
78 | uint64_t last_recvSYNC; /* time we last received a SYNC packet from the other */ | ||
79 | uint64_t last_recvdata; /* time we last received a DATA packet from the other */ | ||
80 | uint64_t killat; /* time to kill the connection */ | ||
81 | |||
82 | Data sendbuffer[MAX_QUEUE_NUM]; /* packet send buffer. */ | ||
83 | Data recvbuffer[MAX_QUEUE_NUM]; /* packet receive buffer. */ | ||
84 | |||
85 | uint32_t handshake_id1; | ||
86 | uint32_t handshake_id2; | ||
87 | |||
88 | /* number of data packets received (also used as handshake_id1) */ | ||
89 | uint32_t recv_packetnum; | ||
90 | |||
91 | /* number of packets received by the other peer */ | ||
92 | uint32_t orecv_packetnum; | ||
93 | |||
94 | /* number of data packets sent */ | ||
95 | uint32_t sent_packetnum; | ||
96 | |||
97 | /* number of packets sent by the other peer. */ | ||
98 | uint32_t osent_packetnum; | ||
99 | |||
100 | /* number of latest packet written onto the sendbuffer */ | ||
101 | uint32_t sendbuff_packetnum; | ||
102 | |||
103 | /* we know all packets before that number were successfully sent */ | ||
104 | uint32_t successful_sent; | ||
105 | |||
106 | /* packet number of last packet read with the read_packet function */ | ||
107 | uint32_t successful_read; | ||
108 | |||
109 | /* list of currently requested packet numbers(by the other person) */ | ||
110 | uint32_t req_packets[BUFFER_PACKET_NUM]; | ||
111 | |||
112 | /* total number of currently requested packets(by the other person) */ | ||
113 | uint16_t num_req_paquets; | ||
114 | |||
115 | uint8_t recv_counter; | ||
116 | uint8_t send_counter; | ||
117 | uint8_t timeout; /* connection timeout in seconds. */ | ||
118 | } Connection; | ||
119 | |||
120 | typedef struct { | ||
121 | Networking_Core *net; | ||
122 | Connection *connections; | ||
123 | |||
124 | uint32_t connections_length; /* Length of connections array */ | ||
125 | uint32_t connections_number; /* Number of connections in connections array */ | ||
126 | |||
127 | /* table of random numbers used in handshake_id. */ | ||
128 | uint32_t randtable[6][256]; | ||
129 | |||
130 | } Lossless_UDP; | ||
131 | |||
132 | /* | ||
133 | * Initialize a new connection to ip_port | ||
134 | * Returns an integer corresponding to the connection id. | ||
135 | * Return -1 if it could not initialize the connection. | ||
136 | * Return number if there already was an existing connection to that ip_port. | ||
137 | */ | ||
138 | int new_connection(Lossless_UDP *ludp, IP_Port ip_port); | ||
139 | |||
140 | /* | ||
141 | * Get connection id from IP_Port. | ||
142 | * Return -1 if there are no connections like we are looking for. | ||
143 | * Return id if it found it . | ||
144 | */ | ||
145 | int getconnection_id(Lossless_UDP *ludp, IP_Port ip_port); | ||
146 | |||
147 | /* | ||
148 | * Returns an int corresponding to the next connection in our imcoming connection list | ||
149 | * Return -1 if there are no new incoming connections in the list. | ||
150 | */ | ||
151 | int incoming_connection(Lossless_UDP *ludp); | ||
152 | |||
153 | /* | ||
154 | * Return -1 if it could not kill the connection. | ||
155 | * Return 0 if killed successfully | ||
156 | */ | ||
157 | int kill_connection(Lossless_UDP *ludp, int connection_id); | ||
158 | |||
159 | /* | ||
160 | * Kill connection in seconds seconds. | ||
161 | * Return -1 if it can not kill the connection. | ||
162 | * Return 0 if it will kill it | ||
163 | */ | ||
164 | int kill_connection_in(Lossless_UDP *ludp, int connection_id, uint32_t seconds); | ||
165 | |||
166 | /* | ||
167 | * Returns the ip_port of the corresponding connection. | ||
168 | * Return 0 if there is no such connection. | ||
169 | */ | ||
170 | IP_Port connection_ip(Lossless_UDP *ludp, int connection_id); | ||
171 | |||
172 | /* | ||
173 | * Returns the id of the next packet in the queue | ||
174 | * Return -1 if no packet in queue | ||
175 | */ | ||
176 | char id_packet(Lossless_UDP *ludp, int connection_id); | ||
177 | |||
178 | /* | ||
179 | * Return 0 if there is no received data in the buffer. | ||
180 | * Return length of received packet if successful | ||
181 | */ | ||
182 | int read_packet(Lossless_UDP *ludp, int connection_id, uint8_t *data); | ||
183 | |||
184 | /* | ||
185 | * Return 0 if data could not be put in packet queue | ||
186 | * Return 1 if data was put into the queue | ||
187 | */ | ||
188 | int write_packet(Lossless_UDP *ludp, int connection_id, uint8_t *data, uint32_t length); | ||
189 | |||
190 | /* Returns the number of packets in the queue waiting to be successfully sent. */ | ||
191 | uint32_t sendqueue(Lossless_UDP *ludp, int connection_id); | ||
192 | |||
193 | /* | ||
194 | * returns the number of packets in the queue waiting to be successfully | ||
195 | * read with read_packet(...) | ||
196 | */ | ||
197 | uint32_t recvqueue(Lossless_UDP *ludp, int connection_id); | ||
198 | |||
199 | /* Check if connection is connected: | ||
200 | * Return 0 no. | ||
201 | * Return 1 if attempting handshake. | ||
202 | * Return 2 if handshake is done. | ||
203 | * Return 3 if fully connected. | ||
204 | * Return 4 if timed out and wating to be killed. | ||
205 | */ | ||
206 | int is_connected(Lossless_UDP *ludp, int connection_id); | ||
207 | |||
208 | /* Call this function a couple times per second It's the main loop. */ | ||
209 | void do_lossless_udp(Lossless_UDP *ludp); | ||
210 | |||
211 | /* | ||
212 | * This function sets up LosslessUDP packet handling. | ||
213 | */ | ||
214 | Lossless_UDP *new_lossless_udp(Networking_Core *net); | ||
215 | |||
216 | void kill_lossless_udp(Lossless_UDP *ludp); | ||
217 | |||
218 | #ifdef __cplusplus | ||
219 | } | ||
220 | #endif | ||
221 | |||
222 | #endif | ||
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c new file mode 100644 index 00000000..c5dd8a40 --- /dev/null +++ b/toxcore/Messenger.c | |||
@@ -0,0 +1,1020 @@ | |||
1 | /* Messenger.c | ||
2 | * | ||
3 | * An implementation of a simple text chat only messenger on the tox network core. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #include "Messenger.h" | ||
25 | |||
26 | #define MIN(a,b) (((a)<(b))?(a):(b)) | ||
27 | |||
28 | static void set_friend_status(Messenger *m, int friendnumber, uint8_t status); | ||
29 | static int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint8_t *data, uint32_t length); | ||
30 | |||
31 | /* 1 if we are online | ||
32 | 0 if we are offline | ||
33 | static uint8_t online; */ | ||
34 | |||
35 | /* set the size of the friend list to numfriends | ||
36 | return -1 if realloc fails */ | ||
37 | int realloc_friendlist(Messenger *m, uint32_t num) | ||
38 | { | ||
39 | if (num == 0) { | ||
40 | free(m->friendlist); | ||
41 | m->friendlist = NULL; | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | Friend *newfriendlist = realloc(m->friendlist, num * sizeof(Friend)); | ||
46 | |||
47 | if (newfriendlist == NULL) | ||
48 | return -1; | ||
49 | |||
50 | m->friendlist = newfriendlist; | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | /* return the friend id associated to that public key. | ||
55 | return -1 if no such friend */ | ||
56 | int getfriend_id(Messenger *m, uint8_t *client_id) | ||
57 | { | ||
58 | uint32_t i; | ||
59 | |||
60 | for (i = 0; i < m->numfriends; ++i) { | ||
61 | if (m->friendlist[i].status > 0) | ||
62 | if (memcmp(client_id, m->friendlist[i].client_id, crypto_box_PUBLICKEYBYTES) == 0) | ||
63 | return i; | ||
64 | } | ||
65 | |||
66 | return -1; | ||
67 | } | ||
68 | |||
69 | /* copies the public key associated to that friend id into client_id buffer. | ||
70 | make sure that client_id is of size CLIENT_ID_SIZE. | ||
71 | return 0 if success | ||
72 | return -1 if failure. */ | ||
73 | int getclient_id(Messenger *m, int friend_id, uint8_t *client_id) | ||
74 | { | ||
75 | if (friend_id >= m->numfriends || friend_id < 0) | ||
76 | return -1; | ||
77 | |||
78 | if (m->friendlist[friend_id].status > 0) { | ||
79 | memcpy(client_id, m->friendlist[friend_id].client_id, CLIENT_ID_SIZE); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | return -1; | ||
84 | } | ||
85 | /* | ||
86 | * returns a uint16_t that represents the checksum of address of length len | ||
87 | * | ||
88 | * TODO: Another checksum algorithm might be better. | ||
89 | */ | ||
90 | static uint16_t address_checksum(uint8_t *address, uint32_t len) | ||
91 | { | ||
92 | uint8_t checksum[2] = {0}; | ||
93 | uint16_t check; | ||
94 | uint32_t i; | ||
95 | |||
96 | for (i = 0; i < len; ++i) | ||
97 | checksum[i % 2] ^= address[i]; | ||
98 | |||
99 | memcpy(&check, checksum, sizeof(check)); | ||
100 | return check; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * returns a FRIEND_ADDRESS_SIZE byte address to give to others. | ||
105 | * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)] | ||
106 | * | ||
107 | */ | ||
108 | void getaddress(Messenger *m, uint8_t *address) | ||
109 | { | ||
110 | memcpy(address, m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES); | ||
111 | uint32_t nospam = get_nospam(&(m->fr)); | ||
112 | memcpy(address + crypto_box_PUBLICKEYBYTES, &nospam, sizeof(nospam)); | ||
113 | uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); | ||
114 | memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(nospam), &checksum, sizeof(checksum)); | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * add a friend | ||
119 | * set the data that will be sent along with friend request | ||
120 | * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum. | ||
121 | * data is the data and length is the length | ||
122 | * returns the friend number if success | ||
123 | * return FA_TOOLONG if message length is too long | ||
124 | * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte) | ||
125 | * return FAERR_OWNKEY if user's own key | ||
126 | * return FAERR_ALREADYSENT if friend request already sent or already a friend | ||
127 | * return FAERR_UNKNOWN for unknown error | ||
128 | * return FAERR_BADCHECKSUM if bad checksum in address | ||
129 | * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different | ||
130 | * (the nospam for that friend was set to the new one) | ||
131 | * return FAERR_NOMEM if increasing the friend list size fails | ||
132 | */ | ||
133 | int m_addfriend(Messenger *m, uint8_t *address, uint8_t *data, uint16_t length) | ||
134 | { | ||
135 | if (length >= (MAX_DATA_SIZE - crypto_box_PUBLICKEYBYTES | ||
136 | - crypto_box_NONCEBYTES - crypto_box_BOXZEROBYTES | ||
137 | + crypto_box_ZEROBYTES)) | ||
138 | return FAERR_TOOLONG; | ||
139 | |||
140 | uint8_t client_id[crypto_box_PUBLICKEYBYTES]; | ||
141 | memcpy(client_id, address, crypto_box_PUBLICKEYBYTES); | ||
142 | uint16_t check, checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); | ||
143 | memcpy(&check, address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), sizeof(check)); | ||
144 | |||
145 | if (check != checksum) | ||
146 | return FAERR_BADCHECKSUM; | ||
147 | |||
148 | if (length < 1) | ||
149 | return FAERR_NOMESSAGE; | ||
150 | |||
151 | if (memcmp(client_id, m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) | ||
152 | return FAERR_OWNKEY; | ||
153 | |||
154 | int friend_id = getfriend_id(m, client_id); | ||
155 | |||
156 | if (friend_id != -1) { | ||
157 | uint32_t nospam; | ||
158 | memcpy(&nospam, address + crypto_box_PUBLICKEYBYTES, sizeof(nospam)); | ||
159 | |||
160 | if (m->friendlist[friend_id].friendrequest_nospam == nospam) | ||
161 | return FAERR_ALREADYSENT; | ||
162 | |||
163 | m->friendlist[friend_id].friendrequest_nospam = nospam; | ||
164 | return FAERR_SETNEWNOSPAM; | ||
165 | } | ||
166 | |||
167 | /* resize the friend list if necessary */ | ||
168 | if (realloc_friendlist(m, m->numfriends + 1) != 0) | ||
169 | return FAERR_NOMEM; | ||
170 | |||
171 | memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend)); | ||
172 | |||
173 | uint32_t i; | ||
174 | |||
175 | for (i = 0; i <= m->numfriends; ++i) { | ||
176 | if (m->friendlist[i].status == NOFRIEND) { | ||
177 | DHT_addfriend(m->dht, client_id); | ||
178 | m->friendlist[i].status = FRIEND_ADDED; | ||
179 | m->friendlist[i].crypt_connection_id = -1; | ||
180 | m->friendlist[i].friendrequest_lastsent = 0; | ||
181 | m->friendlist[i].friendrequest_timeout = FRIENDREQUEST_TIMEOUT; | ||
182 | memcpy(m->friendlist[i].client_id, client_id, CLIENT_ID_SIZE); | ||
183 | m->friendlist[i].statusmessage = calloc(1, 1); | ||
184 | m->friendlist[i].statusmessage_length = 1; | ||
185 | m->friendlist[i].userstatus = USERSTATUS_NONE; | ||
186 | memcpy(m->friendlist[i].info, data, length); | ||
187 | m->friendlist[i].info_size = length; | ||
188 | m->friendlist[i].message_id = 0; | ||
189 | m->friendlist[i].receives_read_receipts = 1; /* default: YES */ | ||
190 | memcpy(&(m->friendlist[i].friendrequest_nospam), address + crypto_box_PUBLICKEYBYTES, sizeof(uint32_t)); | ||
191 | |||
192 | if (m->numfriends == i) | ||
193 | ++ m->numfriends; | ||
194 | |||
195 | return i; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | return FAERR_UNKNOWN; | ||
200 | } | ||
201 | |||
202 | int m_addfriend_norequest(Messenger *m, uint8_t *client_id) | ||
203 | { | ||
204 | if (getfriend_id(m, client_id) != -1) | ||
205 | return -1; | ||
206 | |||
207 | /* resize the friend list if necessary */ | ||
208 | if (realloc_friendlist(m, m->numfriends + 1) != 0) | ||
209 | return FAERR_NOMEM; | ||
210 | |||
211 | memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend)); | ||
212 | |||
213 | uint32_t i; | ||
214 | |||
215 | for (i = 0; i <= m->numfriends; ++i) { | ||
216 | if (m->friendlist[i].status == NOFRIEND) { | ||
217 | DHT_addfriend(m->dht, client_id); | ||
218 | m->friendlist[i].status = FRIEND_CONFIRMED; | ||
219 | m->friendlist[i].crypt_connection_id = -1; | ||
220 | m->friendlist[i].friendrequest_lastsent = 0; | ||
221 | memcpy(m->friendlist[i].client_id, client_id, CLIENT_ID_SIZE); | ||
222 | m->friendlist[i].statusmessage = calloc(1, 1); | ||
223 | m->friendlist[i].statusmessage_length = 1; | ||
224 | m->friendlist[i].userstatus = USERSTATUS_NONE; | ||
225 | m->friendlist[i].message_id = 0; | ||
226 | m->friendlist[i].receives_read_receipts = 1; /* default: YES */ | ||
227 | |||
228 | if (m->numfriends == i) | ||
229 | ++ m->numfriends; | ||
230 | |||
231 | return i; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | return -1; | ||
236 | } | ||
237 | |||
238 | /* remove a friend | ||
239 | return 0 if success | ||
240 | return -1 if failure */ | ||
241 | int m_delfriend(Messenger *m, int friendnumber) | ||
242 | { | ||
243 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
244 | return -1; | ||
245 | |||
246 | DHT_delfriend(m->dht, m->friendlist[friendnumber].client_id); | ||
247 | crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id); | ||
248 | free(m->friendlist[friendnumber].statusmessage); | ||
249 | memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend)); | ||
250 | uint32_t i; | ||
251 | |||
252 | for (i = m->numfriends; i != 0; --i) { | ||
253 | if (m->friendlist[i - 1].status != NOFRIEND) | ||
254 | break; | ||
255 | } | ||
256 | |||
257 | m->numfriends = i; | ||
258 | |||
259 | if (realloc_friendlist(m, m->numfriends) != 0) | ||
260 | return FAERR_NOMEM; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* return FRIEND_ONLINE if friend is online | ||
266 | return FRIEND_CONFIRMED if friend is confirmed | ||
267 | return FRIEND_REQUESTED if the friend request was sent | ||
268 | return FRIEND_ADDED if the friend was added | ||
269 | return NOFRIEND if there is no friend with that number */ | ||
270 | int m_friendstatus(Messenger *m, int friendnumber) | ||
271 | { | ||
272 | if (friendnumber < 0 || friendnumber >= m->numfriends) | ||
273 | return NOFRIEND; | ||
274 | |||
275 | return m->friendlist[friendnumber].status; | ||
276 | } | ||
277 | |||
278 | /* send a text chat message to an online friend | ||
279 | return the message id if packet was successfully put into the send queue | ||
280 | return 0 if it was not */ | ||
281 | uint32_t m_sendmessage(Messenger *m, int friendnumber, uint8_t *message, uint32_t length) | ||
282 | { | ||
283 | if (friendnumber < 0 || friendnumber >= m->numfriends) | ||
284 | return 0; | ||
285 | |||
286 | uint32_t msgid = ++m->friendlist[friendnumber].message_id; | ||
287 | |||
288 | if (msgid == 0) | ||
289 | msgid = 1; /* otherwise, false error */ | ||
290 | |||
291 | if (m_sendmessage_withid(m, friendnumber, msgid, message, length)) { | ||
292 | return msgid; | ||
293 | } | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | uint32_t m_sendmessage_withid(Messenger *m, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length) | ||
299 | { | ||
300 | if (length >= (MAX_DATA_SIZE - sizeof(theid))) | ||
301 | return 0; | ||
302 | |||
303 | uint8_t temp[MAX_DATA_SIZE]; | ||
304 | theid = htonl(theid); | ||
305 | memcpy(temp, &theid, sizeof(theid)); | ||
306 | memcpy(temp + sizeof(theid), message, length); | ||
307 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_MESSAGE, temp, length + sizeof(theid)); | ||
308 | } | ||
309 | |||
310 | /* send an action to an online friend | ||
311 | return 1 if packet was successfully put into the send queue | ||
312 | return 0 if it was not */ | ||
313 | int m_sendaction(Messenger *m, int friendnumber, uint8_t *action, uint32_t length) | ||
314 | { | ||
315 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_ACTION, action, length); | ||
316 | } | ||
317 | |||
318 | /* send a name packet to friendnumber | ||
319 | length is the length with the NULL terminator*/ | ||
320 | static int m_sendname(Messenger *m, int friendnumber, uint8_t *name, uint16_t length) | ||
321 | { | ||
322 | if (length > MAX_NAME_LENGTH || length == 0) | ||
323 | return 0; | ||
324 | |||
325 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_NICKNAME, name, length); | ||
326 | } | ||
327 | |||
328 | /* set the name of a friend | ||
329 | return 0 if success | ||
330 | return -1 if failure */ | ||
331 | static int setfriendname(Messenger *m, int friendnumber, uint8_t *name) | ||
332 | { | ||
333 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
334 | return -1; | ||
335 | |||
336 | memcpy(m->friendlist[friendnumber].name, name, MAX_NAME_LENGTH); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /* Set our nickname | ||
341 | name must be a string of maximum MAX_NAME_LENGTH length. | ||
342 | length must be at least 1 byte | ||
343 | length is the length of name with the NULL terminator | ||
344 | return 0 if success | ||
345 | return -1 if failure */ | ||
346 | int setname(Messenger *m, uint8_t *name, uint16_t length) | ||
347 | { | ||
348 | if (length > MAX_NAME_LENGTH || length == 0) | ||
349 | return -1; | ||
350 | |||
351 | memcpy(m->name, name, length); | ||
352 | m->name_length = length; | ||
353 | uint32_t i; | ||
354 | |||
355 | for (i = 0; i < m->numfriends; ++i) | ||
356 | m->friendlist[i].name_sent = 0; | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | /* get our nickname | ||
362 | put it in name | ||
363 | name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes. | ||
364 | return the length of the name */ | ||
365 | uint16_t getself_name(Messenger *m, uint8_t *name, uint16_t nlen) | ||
366 | { | ||
367 | uint16_t len; | ||
368 | |||
369 | if (name == NULL || nlen == 0) { | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | len = MIN(nlen, m->name_length); | ||
374 | memcpy(name, m->name, m->name_length); | ||
375 | |||
376 | return len; | ||
377 | } | ||
378 | |||
379 | /* get name of friendnumber | ||
380 | put it in name | ||
381 | name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes. | ||
382 | return 0 if success | ||
383 | return -1 if failure */ | ||
384 | int getname(Messenger *m, int friendnumber, uint8_t *name) | ||
385 | { | ||
386 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
387 | return -1; | ||
388 | |||
389 | memcpy(name, m->friendlist[friendnumber].name, MAX_NAME_LENGTH); | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | int m_set_statusmessage(Messenger *m, uint8_t *status, uint16_t length) | ||
394 | { | ||
395 | if (length > MAX_STATUSMESSAGE_LENGTH) | ||
396 | return -1; | ||
397 | |||
398 | memcpy(m->statusmessage, status, length); | ||
399 | m->statusmessage_length = length; | ||
400 | |||
401 | uint32_t i; | ||
402 | |||
403 | for (i = 0; i < m->numfriends; ++i) | ||
404 | m->friendlist[i].statusmessage_sent = 0; | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | int m_set_userstatus(Messenger *m, USERSTATUS status) | ||
410 | { | ||
411 | if (status >= USERSTATUS_INVALID) { | ||
412 | return -1; | ||
413 | } | ||
414 | |||
415 | m->userstatus = status; | ||
416 | uint32_t i; | ||
417 | |||
418 | for (i = 0; i < m->numfriends; ++i) | ||
419 | m->friendlist[i].userstatus_sent = 0; | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | /* return the size of friendnumber's user status | ||
425 | guaranteed to be at most MAX_STATUSMESSAGE_LENGTH */ | ||
426 | int m_get_statusmessage_size(Messenger *m, int friendnumber) | ||
427 | { | ||
428 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
429 | return -1; | ||
430 | |||
431 | return m->friendlist[friendnumber].statusmessage_length; | ||
432 | } | ||
433 | |||
434 | /* copy the user status of friendnumber into buf, truncating if needed to maxlen | ||
435 | bytes, use m_get_statusmessage_size to find out how much you need to allocate */ | ||
436 | int m_copy_statusmessage(Messenger *m, int friendnumber, uint8_t *buf, uint32_t maxlen) | ||
437 | { | ||
438 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
439 | return -1; | ||
440 | |||
441 | memset(buf, 0, maxlen); | ||
442 | memcpy(buf, m->friendlist[friendnumber].statusmessage, MIN(maxlen, MAX_STATUSMESSAGE_LENGTH) - 1); | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | int m_copy_self_statusmessage(Messenger *m, uint8_t *buf, uint32_t maxlen) | ||
447 | { | ||
448 | memset(buf, 0, maxlen); | ||
449 | memcpy(buf, m->statusmessage, MIN(maxlen, MAX_STATUSMESSAGE_LENGTH) - 1); | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | USERSTATUS m_get_userstatus(Messenger *m, int friendnumber) | ||
454 | { | ||
455 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
456 | return USERSTATUS_INVALID; | ||
457 | |||
458 | USERSTATUS status = m->friendlist[friendnumber].userstatus; | ||
459 | |||
460 | if (status >= USERSTATUS_INVALID) { | ||
461 | status = USERSTATUS_NONE; | ||
462 | } | ||
463 | |||
464 | return status; | ||
465 | } | ||
466 | |||
467 | USERSTATUS m_get_self_userstatus(Messenger *m) | ||
468 | { | ||
469 | return m->userstatus; | ||
470 | } | ||
471 | |||
472 | static int send_statusmessage(Messenger *m, int friendnumber, uint8_t *status, uint16_t length) | ||
473 | { | ||
474 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_STATUSMESSAGE, status, length); | ||
475 | } | ||
476 | |||
477 | static int send_userstatus(Messenger *m, int friendnumber, USERSTATUS status) | ||
478 | { | ||
479 | uint8_t stat = status; | ||
480 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_USERSTATUS, &stat, sizeof(stat)); | ||
481 | } | ||
482 | |||
483 | static int send_ping(Messenger *m, int friendnumber) | ||
484 | { | ||
485 | m->friendlist[friendnumber].ping_lastsent = unix_time(); | ||
486 | return write_cryptpacket_id(m, friendnumber, PACKET_ID_PING, 0, 0); | ||
487 | } | ||
488 | |||
489 | static int set_friend_statusmessage(Messenger *m, int friendnumber, uint8_t *status, uint16_t length) | ||
490 | { | ||
491 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
492 | return -1; | ||
493 | |||
494 | uint8_t *newstatus = calloc(length, 1); | ||
495 | memcpy(newstatus, status, length); | ||
496 | free(m->friendlist[friendnumber].statusmessage); | ||
497 | m->friendlist[friendnumber].statusmessage = newstatus; | ||
498 | m->friendlist[friendnumber].statusmessage_length = length; | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static void set_friend_userstatus(Messenger *m, int friendnumber, USERSTATUS status) | ||
503 | { | ||
504 | m->friendlist[friendnumber].userstatus = status; | ||
505 | } | ||
506 | |||
507 | /* Sets whether we send read receipts for friendnumber. */ | ||
508 | void m_set_sends_receipts(Messenger *m, int friendnumber, int yesno) | ||
509 | { | ||
510 | if (yesno != 0 || yesno != 1) | ||
511 | return; | ||
512 | |||
513 | if (friendnumber >= m->numfriends || friendnumber < 0) | ||
514 | return; | ||
515 | |||
516 | m->friendlist[friendnumber].receives_read_receipts = yesno; | ||
517 | } | ||
518 | |||
519 | /* static void (*friend_request)(uint8_t *, uint8_t *, uint16_t); */ | ||
520 | /* set the function that will be executed when a friend request is received. */ | ||
521 | void m_callback_friendrequest(Messenger *m, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata) | ||
522 | { | ||
523 | callback_friendrequest(&(m->fr), function, userdata); | ||
524 | } | ||
525 | |||
526 | /* set the function that will be executed when a message from a friend is received. */ | ||
527 | void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
528 | void *userdata) | ||
529 | { | ||
530 | m->friend_message = function; | ||
531 | m->friend_message_userdata = userdata; | ||
532 | } | ||
533 | |||
534 | void m_callback_action(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), void *userdata) | ||
535 | { | ||
536 | m->friend_action = function; | ||
537 | m->friend_action_userdata = userdata; | ||
538 | } | ||
539 | |||
540 | void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
541 | void *userdata) | ||
542 | { | ||
543 | m->friend_namechange = function; | ||
544 | m->friend_namechange_userdata = userdata; | ||
545 | } | ||
546 | |||
547 | void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
548 | void *userdata) | ||
549 | { | ||
550 | m->friend_statusmessagechange = function; | ||
551 | m->friend_statuschange_userdata = userdata; | ||
552 | } | ||
553 | |||
554 | void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, int, USERSTATUS, void *), void *userdata) | ||
555 | { | ||
556 | m->friend_userstatuschange = function; | ||
557 | m->friend_userstatuschange_userdata = userdata; | ||
558 | } | ||
559 | |||
560 | void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int, uint32_t, void *), void *userdata) | ||
561 | { | ||
562 | m->read_receipt = function; | ||
563 | m->read_receipt_userdata = userdata; | ||
564 | } | ||
565 | |||
566 | void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), void *userdata) | ||
567 | { | ||
568 | m->friend_connectionstatuschange = function; | ||
569 | m->friend_connectionstatuschange_userdata = userdata; | ||
570 | } | ||
571 | |||
572 | static void check_friend_connectionstatus(Messenger *m, int friendnumber, uint8_t status) | ||
573 | { | ||
574 | if (!m->friend_connectionstatuschange) | ||
575 | return; | ||
576 | |||
577 | if (status == NOFRIEND) | ||
578 | return; | ||
579 | |||
580 | const uint8_t was_connected = m->friendlist[friendnumber].status == FRIEND_ONLINE; | ||
581 | const uint8_t is_connected = status == FRIEND_ONLINE; | ||
582 | |||
583 | if (is_connected != was_connected) | ||
584 | m->friend_connectionstatuschange(m, friendnumber, is_connected, m->friend_connectionstatuschange_userdata); | ||
585 | } | ||
586 | |||
587 | void set_friend_status(Messenger *m, int friendnumber, uint8_t status) | ||
588 | { | ||
589 | check_friend_connectionstatus(m, friendnumber, status); | ||
590 | m->friendlist[friendnumber].status = status; | ||
591 | } | ||
592 | |||
593 | int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint8_t *data, uint32_t length) | ||
594 | { | ||
595 | if (friendnumber < 0 || friendnumber >= m->numfriends) | ||
596 | return 0; | ||
597 | |||
598 | if (length >= MAX_DATA_SIZE || m->friendlist[friendnumber].status != FRIEND_ONLINE) | ||
599 | return 0; | ||
600 | |||
601 | uint8_t packet[length + 1]; | ||
602 | packet[0] = packet_id; | ||
603 | |||
604 | if (length != 0) | ||
605 | memcpy(packet + 1, data, length); | ||
606 | |||
607 | return write_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, packet, length + 1); | ||
608 | } | ||
609 | |||
610 | /*Interval in seconds between LAN discovery packet sending*/ | ||
611 | #define LAN_DISCOVERY_INTERVAL 60 | ||
612 | #define PORT 33445 | ||
613 | |||
614 | /*Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds*/ | ||
615 | static void LANdiscovery(Messenger *m) | ||
616 | { | ||
617 | if (m->last_LANdiscovery + LAN_DISCOVERY_INTERVAL < unix_time()) { | ||
618 | send_LANdiscovery(htons(PORT), m->net_crypto); | ||
619 | m->last_LANdiscovery = unix_time(); | ||
620 | } | ||
621 | } | ||
622 | |||
623 | /* run this at startup */ | ||
624 | Messenger *initMessenger(void) | ||
625 | { | ||
626 | Messenger *m = calloc(1, sizeof(Messenger)); | ||
627 | |||
628 | if ( ! m ) | ||
629 | return NULL; | ||
630 | |||
631 | IP ip; | ||
632 | ip.i = 0; | ||
633 | m->net = new_networking(ip, PORT); | ||
634 | |||
635 | if (m->net == NULL) { | ||
636 | free(m); | ||
637 | return NULL; | ||
638 | } | ||
639 | |||
640 | m->net_crypto = new_net_crypto(m->net); | ||
641 | |||
642 | if (m->net_crypto == NULL) { | ||
643 | kill_networking(m->net); | ||
644 | free(m); | ||
645 | return NULL; | ||
646 | } | ||
647 | |||
648 | m->dht = new_DHT(m->net_crypto); | ||
649 | |||
650 | if (m->dht == NULL) { | ||
651 | kill_net_crypto(m->net_crypto); | ||
652 | kill_networking(m->net); | ||
653 | free(m); | ||
654 | return NULL; | ||
655 | } | ||
656 | |||
657 | new_keys(m->net_crypto); | ||
658 | m_set_statusmessage(m, (uint8_t *)"Online", sizeof("Online")); | ||
659 | |||
660 | friendreq_init(&(m->fr), m->net_crypto); | ||
661 | LANdiscovery_init(m->dht); | ||
662 | set_nospam(&(m->fr), random_int()); | ||
663 | |||
664 | return m; | ||
665 | } | ||
666 | |||
667 | /* run this before closing shop */ | ||
668 | void cleanupMessenger(Messenger *m) | ||
669 | { | ||
670 | /* FIXME TODO ideally cleanupMessenger will mirror initMessenger | ||
671 | * this requires the other modules to expose cleanup functions | ||
672 | */ | ||
673 | kill_DHT(m->dht); | ||
674 | kill_net_crypto(m->net_crypto); | ||
675 | kill_networking(m->net); | ||
676 | free(m->friendlist); | ||
677 | free(m); | ||
678 | } | ||
679 | |||
680 | //TODO: make this function not suck. | ||
681 | void doFriends(Messenger *m) | ||
682 | { | ||
683 | /* TODO: add incoming connections and some other stuff. */ | ||
684 | uint32_t i; | ||
685 | int len; | ||
686 | uint8_t temp[MAX_DATA_SIZE]; | ||
687 | uint64_t temp_time = unix_time(); | ||
688 | |||
689 | for (i = 0; i < m->numfriends; ++i) { | ||
690 | if (m->friendlist[i].status == FRIEND_ADDED) { | ||
691 | int fr = send_friendrequest(m->dht, m->friendlist[i].client_id, m->friendlist[i].friendrequest_nospam, | ||
692 | m->friendlist[i].info, | ||
693 | m->friendlist[i].info_size); | ||
694 | |||
695 | if (fr >= 0) { | ||
696 | set_friend_status(m, i, FRIEND_REQUESTED); | ||
697 | m->friendlist[i].friendrequest_lastsent = temp_time; | ||
698 | } | ||
699 | } | ||
700 | |||
701 | if (m->friendlist[i].status == FRIEND_REQUESTED | ||
702 | || m->friendlist[i].status == FRIEND_CONFIRMED) { /* friend is not online */ | ||
703 | if (m->friendlist[i].status == FRIEND_REQUESTED) { | ||
704 | /* If we didn't connect to friend after successfully sending him a friend request the request is deemed | ||
705 | unsuccessful so we set the status back to FRIEND_ADDED and try again.*/ | ||
706 | if (m->friendlist[i].friendrequest_lastsent + m->friendlist[i].friendrequest_timeout < temp_time) { | ||
707 | set_friend_status(m, i, FRIEND_ADDED); | ||
708 | /* Double the default timeout everytime if friendrequest is assumed to have been | ||
709 | sent unsuccessfully. */ | ||
710 | m->friendlist[i].friendrequest_timeout *= 2; | ||
711 | } | ||
712 | } | ||
713 | |||
714 | IP_Port friendip = DHT_getfriendip(m->dht, m->friendlist[i].client_id); | ||
715 | |||
716 | switch (is_cryptoconnected(m->net_crypto, m->friendlist[i].crypt_connection_id)) { | ||
717 | case 0: | ||
718 | if (friendip.ip.i > 1) | ||
719 | m->friendlist[i].crypt_connection_id = crypto_connect(m->net_crypto, m->friendlist[i].client_id, friendip); | ||
720 | |||
721 | break; | ||
722 | |||
723 | case 3: /* Connection is established */ | ||
724 | set_friend_status(m, i, FRIEND_ONLINE); | ||
725 | m->friendlist[i].name_sent = 0; | ||
726 | m->friendlist[i].userstatus_sent = 0; | ||
727 | m->friendlist[i].statusmessage_sent = 0; | ||
728 | m->friendlist[i].ping_lastrecv = temp_time; | ||
729 | break; | ||
730 | |||
731 | case 4: | ||
732 | crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id); | ||
733 | m->friendlist[i].crypt_connection_id = -1; | ||
734 | break; | ||
735 | |||
736 | default: | ||
737 | break; | ||
738 | } | ||
739 | } | ||
740 | |||
741 | while (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online */ | ||
742 | if (m->friendlist[i].name_sent == 0) { | ||
743 | if (m_sendname(m, i, m->name, m->name_length)) | ||
744 | m->friendlist[i].name_sent = 1; | ||
745 | } | ||
746 | |||
747 | if (m->friendlist[i].statusmessage_sent == 0) { | ||
748 | if (send_statusmessage(m, i, m->statusmessage, m->statusmessage_length)) | ||
749 | m->friendlist[i].statusmessage_sent = 1; | ||
750 | } | ||
751 | |||
752 | if (m->friendlist[i].userstatus_sent == 0) { | ||
753 | if (send_userstatus(m, i, m->userstatus)) | ||
754 | m->friendlist[i].userstatus_sent = 1; | ||
755 | } | ||
756 | |||
757 | if (m->friendlist[i].ping_lastsent + FRIEND_PING_INTERVAL < temp_time) { | ||
758 | send_ping(m, i); | ||
759 | } | ||
760 | |||
761 | len = read_cryptpacket(m->net_crypto, m->friendlist[i].crypt_connection_id, temp); | ||
762 | uint8_t packet_id = temp[0]; | ||
763 | uint8_t *data = temp + 1; | ||
764 | int data_length = len - 1; | ||
765 | |||
766 | if (len > 0) { | ||
767 | switch (packet_id) { | ||
768 | case PACKET_ID_PING: { | ||
769 | m->friendlist[i].ping_lastrecv = temp_time; | ||
770 | break; | ||
771 | } | ||
772 | |||
773 | case PACKET_ID_NICKNAME: { | ||
774 | if (data_length >= MAX_NAME_LENGTH || data_length == 0) | ||
775 | break; | ||
776 | |||
777 | if (m->friend_namechange) | ||
778 | m->friend_namechange(m, i, data, data_length, m->friend_namechange_userdata); | ||
779 | |||
780 | memcpy(m->friendlist[i].name, data, data_length); | ||
781 | m->friendlist[i].name[data_length - 1] = 0; /* make sure the NULL terminator is present. */ | ||
782 | break; | ||
783 | } | ||
784 | |||
785 | case PACKET_ID_STATUSMESSAGE: { | ||
786 | if (data_length == 0) | ||
787 | break; | ||
788 | |||
789 | uint8_t *status = calloc(MIN(data_length, MAX_STATUSMESSAGE_LENGTH), 1); | ||
790 | memcpy(status, data, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); | ||
791 | |||
792 | if (m->friend_statusmessagechange) | ||
793 | m->friend_statusmessagechange(m, i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH), | ||
794 | m->friend_statuschange_userdata); | ||
795 | |||
796 | set_friend_statusmessage(m, i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); | ||
797 | free(status); | ||
798 | break; | ||
799 | } | ||
800 | |||
801 | case PACKET_ID_USERSTATUS: { | ||
802 | if (data_length != 1) | ||
803 | break; | ||
804 | |||
805 | USERSTATUS status = data[0]; | ||
806 | |||
807 | if (m->friend_userstatuschange) | ||
808 | m->friend_userstatuschange(m, i, status, m->friend_userstatuschange_userdata); | ||
809 | |||
810 | set_friend_userstatus(m, i, status); | ||
811 | break; | ||
812 | } | ||
813 | |||
814 | case PACKET_ID_MESSAGE: { | ||
815 | uint8_t *message_id = data; | ||
816 | uint8_t message_id_length = 4; | ||
817 | uint8_t *message = data + message_id_length; | ||
818 | uint16_t message_length = data_length - message_id_length; | ||
819 | |||
820 | if (m->friendlist[i].receives_read_receipts) { | ||
821 | write_cryptpacket_id(m, i, PACKET_ID_RECEIPT, message_id, message_id_length); | ||
822 | } | ||
823 | |||
824 | if (m->friend_message) | ||
825 | (*m->friend_message)(m, i, message, message_length, m->friend_message_userdata); | ||
826 | |||
827 | break; | ||
828 | } | ||
829 | |||
830 | case PACKET_ID_ACTION: { | ||
831 | if (m->friend_action) | ||
832 | (*m->friend_action)(m, i, data, data_length, m->friend_action_userdata); | ||
833 | |||
834 | break; | ||
835 | } | ||
836 | |||
837 | case PACKET_ID_RECEIPT: { | ||
838 | uint32_t msgid; | ||
839 | |||
840 | if (data_length < sizeof(msgid)) | ||
841 | break; | ||
842 | |||
843 | memcpy(&msgid, data, sizeof(msgid)); | ||
844 | msgid = ntohl(msgid); | ||
845 | |||
846 | if (m->read_receipt) | ||
847 | (*m->read_receipt)(m, i, msgid, m->read_receipt_userdata); | ||
848 | |||
849 | break; | ||
850 | } | ||
851 | } | ||
852 | } else { | ||
853 | if (is_cryptoconnected(m->net_crypto, | ||
854 | m->friendlist[i].crypt_connection_id) == 4) { /* if the connection timed out, kill it */ | ||
855 | crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id); | ||
856 | m->friendlist[i].crypt_connection_id = -1; | ||
857 | set_friend_status(m, i, FRIEND_CONFIRMED); | ||
858 | } | ||
859 | |||
860 | break; | ||
861 | } | ||
862 | |||
863 | if (m->friendlist[i].ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) { | ||
864 | /* if we stopped recieving ping packets kill it */ | ||
865 | crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id); | ||
866 | m->friendlist[i].crypt_connection_id = -1; | ||
867 | set_friend_status(m, i, FRIEND_CONFIRMED); | ||
868 | } | ||
869 | } | ||
870 | } | ||
871 | } | ||
872 | |||
873 | void doInbound(Messenger *m) | ||
874 | { | ||
875 | uint8_t secret_nonce[crypto_box_NONCEBYTES]; | ||
876 | uint8_t public_key[crypto_box_PUBLICKEYBYTES]; | ||
877 | uint8_t session_key[crypto_box_PUBLICKEYBYTES]; | ||
878 | int inconnection = crypto_inbound(m->net_crypto, public_key, secret_nonce, session_key); | ||
879 | |||
880 | if (inconnection != -1) { | ||
881 | int friend_id = getfriend_id(m, public_key); | ||
882 | |||
883 | if (friend_id != -1) { | ||
884 | crypto_kill(m->net_crypto, m->friendlist[friend_id].crypt_connection_id); | ||
885 | m->friendlist[friend_id].crypt_connection_id = | ||
886 | accept_crypto_inbound(m->net_crypto, inconnection, public_key, secret_nonce, session_key); | ||
887 | |||
888 | set_friend_status(m, friend_id, FRIEND_CONFIRMED); | ||
889 | } | ||
890 | } | ||
891 | } | ||
892 | |||
893 | /* the main loop that needs to be run at least 20 times per second. */ | ||
894 | void doMessenger(Messenger *m) | ||
895 | { | ||
896 | networking_poll(m->net); | ||
897 | |||
898 | do_DHT(m->dht); | ||
899 | do_net_crypto(m->net_crypto); | ||
900 | doInbound(m); | ||
901 | doFriends(m); | ||
902 | LANdiscovery(m); | ||
903 | } | ||
904 | |||
905 | /* returns the size of the messenger data (for saving) */ | ||
906 | uint32_t Messenger_size(Messenger *m) | ||
907 | { | ||
908 | return crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES | ||
909 | + sizeof(uint32_t) // nospam | ||
910 | + sizeof(uint32_t) // DHT size | ||
911 | + DHT_size(m->dht) // DHT itself | ||
912 | + sizeof(uint32_t) // Friendlist size | ||
913 | + sizeof(Friend) * m->numfriends // Friendlist itself | ||
914 | + sizeof(uint16_t) // Own nickname length | ||
915 | + m->name_length // Own nickname | ||
916 | ; | ||
917 | } | ||
918 | |||
919 | /* save the messenger in data of size Messenger_size() */ | ||
920 | void Messenger_save(Messenger *m, uint8_t *data) | ||
921 | { | ||
922 | save_keys(m->net_crypto, data); | ||
923 | data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; | ||
924 | uint32_t nospam = get_nospam(&(m->fr)); | ||
925 | memcpy(data, &nospam, sizeof(nospam)); | ||
926 | data += sizeof(nospam); | ||
927 | uint32_t size = DHT_size(m->dht); | ||
928 | memcpy(data, &size, sizeof(size)); | ||
929 | data += sizeof(size); | ||
930 | DHT_save(m->dht, data); | ||
931 | data += size; | ||
932 | size = sizeof(Friend) * m->numfriends; | ||
933 | memcpy(data, &size, sizeof(size)); | ||
934 | data += sizeof(size); | ||
935 | memcpy(data, m->friendlist, sizeof(Friend) * m->numfriends); | ||
936 | data += size; | ||
937 | uint16_t small_size = m->name_length; | ||
938 | memcpy(data, &small_size, sizeof(small_size)); | ||
939 | data += sizeof(small_size); | ||
940 | memcpy(data, m->name, small_size); | ||
941 | } | ||
942 | |||
943 | /* load the messenger from data of size length. */ | ||
944 | int Messenger_load(Messenger *m, uint8_t *data, uint32_t length) | ||
945 | { | ||
946 | if (length == ~0) | ||
947 | return -1; | ||
948 | |||
949 | if (length < crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 3) | ||
950 | return -1; | ||
951 | |||
952 | length -= crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 3; | ||
953 | load_keys(m->net_crypto, data); | ||
954 | data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; | ||
955 | uint32_t nospam; | ||
956 | memcpy(&nospam, data, sizeof(nospam)); | ||
957 | set_nospam(&(m->fr), nospam); | ||
958 | data += sizeof(nospam); | ||
959 | uint32_t size; | ||
960 | memcpy(&size, data, sizeof(size)); | ||
961 | data += sizeof(size); | ||
962 | |||
963 | if (length < size) | ||
964 | return -1; | ||
965 | |||
966 | length -= size; | ||
967 | |||
968 | if (DHT_load(m->dht, data, size) == -1) | ||
969 | return -1; | ||
970 | |||
971 | data += size; | ||
972 | memcpy(&size, data, sizeof(size)); | ||
973 | data += sizeof(size); | ||
974 | |||
975 | if (length < size || size % sizeof(Friend) != 0) | ||
976 | return -1; | ||
977 | |||
978 | Friend *temp = malloc(size); | ||
979 | memcpy(temp, data, size); | ||
980 | |||
981 | uint16_t num = size / sizeof(Friend); | ||
982 | |||
983 | uint32_t i; | ||
984 | |||
985 | for (i = 0; i < num; ++i) { | ||
986 | if (temp[i].status >= 3) { | ||
987 | int fnum = m_addfriend_norequest(m, temp[i].client_id); | ||
988 | setfriendname(m, fnum, temp[i].name); | ||
989 | /* set_friend_statusmessage(fnum, temp[i].statusmessage, temp[i].statusmessage_length); */ | ||
990 | } else if (temp[i].status != 0) { | ||
991 | /* TODO: this is not a good way to do this. */ | ||
992 | uint8_t address[FRIEND_ADDRESS_SIZE]; | ||
993 | memcpy(address, temp[i].client_id, crypto_box_PUBLICKEYBYTES); | ||
994 | memcpy(address + crypto_box_PUBLICKEYBYTES, &(temp[i].friendrequest_nospam), sizeof(uint32_t)); | ||
995 | uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); | ||
996 | memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), &checksum, sizeof(checksum)); | ||
997 | m_addfriend(m, address, temp[i].info, temp[i].info_size); | ||
998 | } | ||
999 | } | ||
1000 | |||
1001 | free(temp); | ||
1002 | data += size; | ||
1003 | length -= size; | ||
1004 | |||
1005 | uint16_t small_size; | ||
1006 | |||
1007 | if (length < sizeof(small_size)) | ||
1008 | return -1; | ||
1009 | |||
1010 | memcpy(&small_size, data, sizeof(small_size)); | ||
1011 | data += sizeof(small_size); | ||
1012 | length -= sizeof(small_size); | ||
1013 | |||
1014 | if (length != small_size) | ||
1015 | return -1; | ||
1016 | |||
1017 | setname(m, data, small_size); | ||
1018 | |||
1019 | return 0; | ||
1020 | } | ||
diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h new file mode 100644 index 00000000..e808529f --- /dev/null +++ b/toxcore/Messenger.h | |||
@@ -0,0 +1,351 @@ | |||
1 | /* Messenger.h | ||
2 | * | ||
3 | * An implementation of a simple text chat only messenger on the tox network core. | ||
4 | * | ||
5 | * NOTE: All the text in the messages must be encoded using UTF-8 | ||
6 | * | ||
7 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
8 | * | ||
9 | * This file is part of Tox. | ||
10 | * | ||
11 | * Tox is free software: you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation, either version 3 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * Tox is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #ifndef MESSENGER_H | ||
27 | #define MESSENGER_H | ||
28 | |||
29 | #include "net_crypto.h" | ||
30 | #include "DHT.h" | ||
31 | #include "friend_requests.h" | ||
32 | #include "LAN_discovery.h" | ||
33 | |||
34 | #ifdef __cplusplus | ||
35 | extern "C" { | ||
36 | #endif | ||
37 | |||
38 | #define MAX_NAME_LENGTH 128 | ||
39 | #define MAX_STATUSMESSAGE_LENGTH 128 | ||
40 | |||
41 | #define FRIEND_ADDRESS_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + sizeof(uint16_t)) | ||
42 | |||
43 | #define PACKET_ID_PING 0 | ||
44 | #define PACKET_ID_NICKNAME 48 | ||
45 | #define PACKET_ID_STATUSMESSAGE 49 | ||
46 | #define PACKET_ID_USERSTATUS 50 | ||
47 | #define PACKET_ID_RECEIPT 65 | ||
48 | #define PACKET_ID_MESSAGE 64 | ||
49 | #define PACKET_ID_ACTION 63 | ||
50 | |||
51 | |||
52 | /* status definitions */ | ||
53 | enum { | ||
54 | NOFRIEND, | ||
55 | FRIEND_ADDED, | ||
56 | FRIEND_REQUESTED, | ||
57 | FRIEND_CONFIRMED, | ||
58 | FRIEND_ONLINE, | ||
59 | }; | ||
60 | |||
61 | /* errors for m_addfriend | ||
62 | * FAERR - Friend Add Error */ | ||
63 | enum { | ||
64 | FAERR_TOOLONG = -1, | ||
65 | FAERR_NOMESSAGE = -2, | ||
66 | FAERR_OWNKEY = -3, | ||
67 | FAERR_ALREADYSENT = -4, | ||
68 | FAERR_UNKNOWN = -5, | ||
69 | FAERR_BADCHECKSUM = -6, | ||
70 | FAERR_SETNEWNOSPAM = -7, | ||
71 | FAERR_NOMEM = -8 | ||
72 | }; | ||
73 | |||
74 | /* don't assume MAX_STATUSMESSAGE_LENGTH will stay at 128, it may be increased | ||
75 | to an absurdly large number later */ | ||
76 | |||
77 | /* Default start timeout in seconds between friend requests */ | ||
78 | #define FRIENDREQUEST_TIMEOUT 5; | ||
79 | |||
80 | /* interval between the sending of ping packets.*/ | ||
81 | #define FRIEND_PING_INTERVAL 5 | ||
82 | |||
83 | /* If no packets are recieved from friend in this time interval, kill the connection.*/ | ||
84 | #define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 2) | ||
85 | |||
86 | /* USERSTATUS | ||
87 | * Represents userstatuses someone can have. */ | ||
88 | |||
89 | typedef enum { | ||
90 | USERSTATUS_NONE, | ||
91 | USERSTATUS_AWAY, | ||
92 | USERSTATUS_BUSY, | ||
93 | USERSTATUS_INVALID | ||
94 | } | ||
95 | USERSTATUS; | ||
96 | |||
97 | typedef struct { | ||
98 | uint8_t client_id[CLIENT_ID_SIZE]; | ||
99 | int crypt_connection_id; | ||
100 | uint64_t friendrequest_lastsent; /* time at which the last friend request was sent. */ | ||
101 | uint32_t friendrequest_timeout; /* The timeout between successful friendrequest sending attempts */ | ||
102 | uint8_t status; /* 0 if no friend, 1 if added, 2 if friend request sent, 3 if confirmed friend, 4 if online. */ | ||
103 | uint8_t info[MAX_DATA_SIZE]; /* the data that is sent during the friend requests we do */ | ||
104 | uint8_t name[MAX_NAME_LENGTH]; | ||
105 | uint8_t name_sent; /* 0 if we didn't send our name to this friend 1 if we have. */ | ||
106 | uint8_t *statusmessage; | ||
107 | uint16_t statusmessage_length; | ||
108 | uint8_t statusmessage_sent; | ||
109 | USERSTATUS userstatus; | ||
110 | uint8_t userstatus_sent; | ||
111 | uint16_t info_size; /* length of the info */ | ||
112 | uint32_t message_id; /* a semi-unique id used in read receipts */ | ||
113 | uint8_t receives_read_receipts; /* shall we send read receipts to this person? */ | ||
114 | uint32_t friendrequest_nospam; /*The nospam number used in the friend request*/ | ||
115 | uint64_t ping_lastrecv; | ||
116 | uint64_t ping_lastsent; | ||
117 | } Friend; | ||
118 | |||
119 | typedef struct Messenger { | ||
120 | |||
121 | Networking_Core *net; | ||
122 | Net_Crypto *net_crypto; | ||
123 | DHT *dht; | ||
124 | Friend_Requests fr; | ||
125 | uint8_t name[MAX_NAME_LENGTH]; | ||
126 | uint16_t name_length; | ||
127 | |||
128 | uint8_t statusmessage[MAX_STATUSMESSAGE_LENGTH]; | ||
129 | uint16_t statusmessage_length; | ||
130 | |||
131 | USERSTATUS userstatus; | ||
132 | |||
133 | Friend *friendlist; | ||
134 | uint32_t numfriends; | ||
135 | |||
136 | uint64_t last_LANdiscovery; | ||
137 | |||
138 | void (*friend_message)(struct Messenger *m, int, uint8_t *, uint16_t, void *); | ||
139 | void *friend_message_userdata; | ||
140 | void (*friend_action)(struct Messenger *m, int, uint8_t *, uint16_t, void *); | ||
141 | void *friend_action_userdata; | ||
142 | void (*friend_namechange)(struct Messenger *m, int, uint8_t *, uint16_t, void *); | ||
143 | void *friend_namechange_userdata; | ||
144 | void (*friend_statusmessagechange)(struct Messenger *m, int, uint8_t *, uint16_t, void *); | ||
145 | void *friend_statusmessagechange_userdata; | ||
146 | void (*friend_userstatuschange)(struct Messenger *m, int, USERSTATUS, void *); | ||
147 | void *friend_userstatuschange_userdata; | ||
148 | void (*read_receipt)(struct Messenger *m, int, uint32_t, void *); | ||
149 | void *read_receipt_userdata; | ||
150 | void (*friend_statuschange)(struct Messenger *m, int, uint8_t, void *); | ||
151 | void *friend_statuschange_userdata; | ||
152 | void (*friend_connectionstatuschange)(struct Messenger *m, int, uint8_t, void *); | ||
153 | void *friend_connectionstatuschange_userdata; | ||
154 | |||
155 | |||
156 | } Messenger; | ||
157 | |||
158 | /* | ||
159 | * returns a FRIEND_ADDRESS_SIZE byte address to give to others. | ||
160 | * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)] | ||
161 | * | ||
162 | */ | ||
163 | void getaddress(Messenger *m, uint8_t *address); | ||
164 | |||
165 | /* | ||
166 | * add a friend | ||
167 | * set the data that will be sent along with friend request | ||
168 | * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum. | ||
169 | * data is the data and length is the length | ||
170 | * returns the friend number if success | ||
171 | * return -1 if message length is too long | ||
172 | * return -2 if no message (message length must be >= 1 byte) | ||
173 | * return -3 if user's own key | ||
174 | * return -4 if friend request already sent or already a friend | ||
175 | * return -5 for unknown error | ||
176 | * return -6 if bad checksum in address | ||
177 | * return -7 if the friend was already there but the nospam was different | ||
178 | * (the nospam for that friend was set to the new one) | ||
179 | * return -8 if increasing the friend list size fails | ||
180 | */ | ||
181 | int m_addfriend(Messenger *m, uint8_t *address, uint8_t *data, uint16_t length); | ||
182 | |||
183 | |||
184 | /* add a friend without sending a friendrequest. | ||
185 | returns the friend number if success | ||
186 | return -1 if failure. */ | ||
187 | int m_addfriend_norequest(Messenger *m, uint8_t *client_id); | ||
188 | |||
189 | /* return the friend id associated to that client id. | ||
190 | return -1 if no such friend */ | ||
191 | int getfriend_id(Messenger *m, uint8_t *client_id); | ||
192 | |||
193 | /* copies the public key associated to that friend id into client_id buffer. | ||
194 | make sure that client_id is of size CLIENT_ID_SIZE. | ||
195 | return 0 if success | ||
196 | return -1 if failure */ | ||
197 | int getclient_id(Messenger *m, int friend_id, uint8_t *client_id); | ||
198 | |||
199 | /* remove a friend */ | ||
200 | int m_delfriend(Messenger *m, int friendnumber); | ||
201 | |||
202 | /* return 4 if friend is online | ||
203 | return 3 if friend is confirmed | ||
204 | return 2 if the friend request was sent | ||
205 | return 1 if the friend was added | ||
206 | return 0 if there is no friend with that number */ | ||
207 | int m_friendstatus(Messenger *m, int friendnumber); | ||
208 | |||
209 | /* send a text chat message to an online friend | ||
210 | returns the message id if packet was successfully put into the send queue | ||
211 | return 0 if it was not | ||
212 | you will want to retain the return value, it will be passed to your read receipt callback | ||
213 | if one is received. | ||
214 | m_sendmessage_withid will send a message with the id of your choosing, | ||
215 | however we can generate an id for you by calling plain m_sendmessage. */ | ||
216 | uint32_t m_sendmessage(Messenger *m, int friendnumber, uint8_t *message, uint32_t length); | ||
217 | uint32_t m_sendmessage_withid(Messenger *m, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length); | ||
218 | |||
219 | /* send an action to an online friend | ||
220 | returns 1 if packet was successfully put into the send queue | ||
221 | return 0 if it was not */ | ||
222 | int m_sendaction(Messenger *m, int friendnumber, uint8_t *action, uint32_t length); | ||
223 | |||
224 | /* Set our nickname | ||
225 | name must be a string of maximum MAX_NAME_LENGTH length. | ||
226 | length must be at least 1 byte | ||
227 | length is the length of name with the NULL terminator | ||
228 | return 0 if success | ||
229 | return -1 if failure */ | ||
230 | int setname(Messenger *m, uint8_t *name, uint16_t length); | ||
231 | |||
232 | /* | ||
233 | Get your nickname. | ||
234 | m The messanger context to use. | ||
235 | name Pointer to a string for the name. | ||
236 | nlen The length of the string buffer. | ||
237 | returns Return the length of the name, 0 on error. | ||
238 | */ | ||
239 | uint16_t getself_name(Messenger *m, uint8_t *name, uint16_t nlen); | ||
240 | |||
241 | /* get name of friendnumber | ||
242 | put it in name | ||
243 | name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes. | ||
244 | return 0 if success | ||
245 | return -1 if failure */ | ||
246 | int getname(Messenger *m, int friendnumber, uint8_t *name); | ||
247 | |||
248 | /* set our user status | ||
249 | you are responsible for freeing status after | ||
250 | returns 0 on success, -1 on failure */ | ||
251 | int m_set_statusmessage(Messenger *m, uint8_t *status, uint16_t length); | ||
252 | int m_set_userstatus(Messenger *m, USERSTATUS status); | ||
253 | |||
254 | /* return the length of friendnumber's status message, | ||
255 | including null | ||
256 | pass it into malloc */ | ||
257 | int m_get_statusmessage_size(Messenger *m, int friendnumber); | ||
258 | |||
259 | /* copy friendnumber's status message into buf, truncating if size is over maxlen | ||
260 | get the size you need to allocate from m_get_statusmessage_size | ||
261 | The self variant will copy our own status message. */ | ||
262 | int m_copy_statusmessage(Messenger *m, int friendnumber, uint8_t *buf, uint32_t maxlen); | ||
263 | int m_copy_self_statusmessage(Messenger *m, uint8_t *buf, uint32_t maxlen); | ||
264 | |||
265 | /* Return one of USERSTATUS values. | ||
266 | * Values unknown to your application should be represented as USERSTATUS_NONE. | ||
267 | * As above, the self variant will return our own USERSTATUS. | ||
268 | * If friendnumber is invalid, this shall return USERSTATUS_INVALID. */ | ||
269 | USERSTATUS m_get_userstatus(Messenger *m, int friendnumber); | ||
270 | USERSTATUS m_get_self_userstatus(Messenger *m); | ||
271 | |||
272 | /* Sets whether we send read receipts for friendnumber. | ||
273 | * This function is not lazy, and it will fail if yesno is not (0 or 1).*/ | ||
274 | void m_set_sends_receipts(Messenger *m, int friendnumber, int yesno); | ||
275 | |||
276 | /* set the function that will be executed when a friend request is received. | ||
277 | function format is function(uint8_t * public_key, uint8_t * data, uint16_t length) */ | ||
278 | void m_callback_friendrequest(Messenger *m, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata); | ||
279 | |||
280 | /* set the function that will be executed when a message from a friend is received. | ||
281 | function format is: function(int friendnumber, uint8_t * message, uint32_t length) */ | ||
282 | void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
283 | void *userdata); | ||
284 | |||
285 | /* set the function that will be executed when an action from a friend is received. | ||
286 | function format is: function(int friendnumber, uint8_t * action, uint32_t length) */ | ||
287 | void m_callback_action(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), void *userdata); | ||
288 | |||
289 | /* set the callback for name changes | ||
290 | function(int friendnumber, uint8_t *newname, uint16_t length) | ||
291 | you are not responsible for freeing newname */ | ||
292 | void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
293 | void *userdata); | ||
294 | |||
295 | /* set the callback for status message changes | ||
296 | function(int friendnumber, uint8_t *newstatus, uint16_t length) | ||
297 | you are not responsible for freeing newstatus */ | ||
298 | void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), | ||
299 | void *userdata); | ||
300 | |||
301 | /* set the callback for status type changes | ||
302 | function(int friendnumber, USERSTATUS kind) */ | ||
303 | void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, int, USERSTATUS, void *), void *userdata); | ||
304 | |||
305 | /* set the callback for read receipts | ||
306 | function(int friendnumber, uint32_t receipt) | ||
307 | if you are keeping a record of returns from m_sendmessage, | ||
308 | receipt might be one of those values, and that means the message | ||
309 | has been received on the other side. since core doesn't | ||
310 | track ids for you, receipt may not correspond to any message | ||
311 | in that case, you should discard it. */ | ||
312 | void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int, uint32_t, void *), void *userdata); | ||
313 | |||
314 | /* set the callback for connection status changes | ||
315 | function(int friendnumber, uint8_t status) | ||
316 | status: | ||
317 | 0 -- friend went offline after being previously online | ||
318 | 1 -- friend went online | ||
319 | note that this callback is not called when adding friends, thus the "after | ||
320 | being previously online" part. it's assumed that when adding friends, | ||
321 | their connection status is offline. */ | ||
322 | void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), void *userdata); | ||
323 | |||
324 | /* run this at startup | ||
325 | * returns allocated instance of Messenger on success | ||
326 | * returns 0 if there are problems */ | ||
327 | Messenger *initMessenger(void); | ||
328 | |||
329 | /* run this before closing shop | ||
330 | * free all datastructures */ | ||
331 | void cleanupMessenger(Messenger *M); | ||
332 | |||
333 | /* the main loop that needs to be run at least 200 times per second */ | ||
334 | void doMessenger(Messenger *m); | ||
335 | |||
336 | /* SAVING AND LOADING FUNCTIONS: */ | ||
337 | |||
338 | /* returns the size of the messenger data (for saving) */ | ||
339 | uint32_t Messenger_size(Messenger *m); | ||
340 | |||
341 | /* save the messenger in data (must be allocated memory of size Messenger_size()) */ | ||
342 | void Messenger_save(Messenger *m, uint8_t *data); | ||
343 | |||
344 | /* load the messenger from data of size length */ | ||
345 | int Messenger_load(Messenger *m, uint8_t *data, uint32_t length); | ||
346 | |||
347 | #ifdef __cplusplus | ||
348 | } | ||
349 | #endif | ||
350 | |||
351 | #endif | ||
diff --git a/toxcore/friend_requests.c b/toxcore/friend_requests.c new file mode 100644 index 00000000..d8c6858b --- /dev/null +++ b/toxcore/friend_requests.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* friend_requests.c | ||
2 | * | ||
3 | * Handle friend requests. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #include "friend_requests.h" | ||
25 | |||
26 | /* Try to send a friendrequest to peer with public_key | ||
27 | data is the data in the request and length is the length. | ||
28 | return -1 if failure. | ||
29 | return 0 if it sent the friend request directly to the friend. | ||
30 | return the number of peers it was routed through if it did not send it directly.*/ | ||
31 | int send_friendrequest(DHT *dht, uint8_t *public_key, uint32_t nospam_num, uint8_t *data, uint32_t length) | ||
32 | { | ||
33 | if (length + sizeof(nospam_num) > MAX_DATA_SIZE) | ||
34 | return -1; | ||
35 | |||
36 | uint8_t temp[MAX_DATA_SIZE]; | ||
37 | memcpy(temp, &nospam_num, sizeof(nospam_num)); | ||
38 | memcpy(temp + sizeof(nospam_num), data, length); | ||
39 | uint8_t packet[MAX_DATA_SIZE]; | ||
40 | int len = create_request(dht->c->self_public_key, dht->c->self_secret_key, packet, public_key, temp, | ||
41 | length + sizeof(nospam_num), | ||
42 | CRYPTO_PACKET_FRIEND_REQ); | ||
43 | |||
44 | if (len == -1) | ||
45 | return -1; | ||
46 | |||
47 | IP_Port ip_port = DHT_getfriendip(dht, public_key); | ||
48 | |||
49 | if (ip_port.ip.i == 1) | ||
50 | return -1; | ||
51 | |||
52 | if (ip_port.ip.i != 0) { | ||
53 | if (sendpacket(dht->c->lossless_udp->net->sock, ip_port, packet, len) != -1) | ||
54 | return 0; | ||
55 | |||
56 | return -1; | ||
57 | } | ||
58 | |||
59 | int num = route_tofriend(dht, public_key, packet, len); | ||
60 | |||
61 | if (num == 0) | ||
62 | return -1; | ||
63 | |||
64 | return num; | ||
65 | } | ||
66 | |||
67 | |||
68 | /* | ||
69 | * Set and get the nospam variable used to prevent one type of friend request spam | ||
70 | */ | ||
71 | void set_nospam(Friend_Requests *fr, uint32_t num) | ||
72 | { | ||
73 | fr->nospam = num; | ||
74 | } | ||
75 | |||
76 | uint32_t get_nospam(Friend_Requests *fr) | ||
77 | { | ||
78 | return fr->nospam; | ||
79 | } | ||
80 | |||
81 | |||
82 | /* set the function that will be executed when a friend request is received. */ | ||
83 | void callback_friendrequest(Friend_Requests *fr, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), | ||
84 | void *userdata) | ||
85 | { | ||
86 | fr->handle_friendrequest = function; | ||
87 | fr->handle_friendrequest_isset = 1; | ||
88 | fr->handle_friendrequest_userdata = userdata; | ||
89 | } | ||
90 | |||
91 | /*Add to list of received friend requests*/ | ||
92 | static void addto_receivedlist(Friend_Requests *fr, uint8_t *client_id) | ||
93 | { | ||
94 | if (fr->received_requests_index >= MAX_RECEIVED_STORED) | ||
95 | fr->received_requests_index = 0; | ||
96 | |||
97 | memcpy(fr->received_requests[fr->received_requests_index], client_id, crypto_box_PUBLICKEYBYTES); | ||
98 | ++fr->received_requests_index; | ||
99 | } | ||
100 | |||
101 | /* Check if a friend request was already received | ||
102 | return 0 if not, 1 if we did */ | ||
103 | static int request_received(Friend_Requests *fr, uint8_t *client_id) | ||
104 | { | ||
105 | uint32_t i; | ||
106 | |||
107 | for (i = 0; i < MAX_RECEIVED_STORED; ++i) { | ||
108 | if (memcmp(fr->received_requests[i], client_id, crypto_box_PUBLICKEYBYTES) == 0) | ||
109 | return 1; | ||
110 | } | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | |||
116 | static int friendreq_handlepacket(void *object, IP_Port source, uint8_t *source_pubkey, uint8_t *packet, | ||
117 | uint32_t length) | ||
118 | { | ||
119 | Friend_Requests *fr = object; | ||
120 | |||
121 | if (fr->handle_friendrequest_isset == 0) | ||
122 | return 1; | ||
123 | |||
124 | if (length <= sizeof(fr->nospam)) | ||
125 | return 1; | ||
126 | |||
127 | if (request_received(fr, source_pubkey)) | ||
128 | return 1; | ||
129 | |||
130 | if (memcmp(packet, &fr->nospam, sizeof(fr->nospam)) != 0) | ||
131 | return 1; | ||
132 | |||
133 | addto_receivedlist(fr, source_pubkey); | ||
134 | (*fr->handle_friendrequest)(source_pubkey, packet + 4, length - 4, fr->handle_friendrequest_userdata); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | void friendreq_init(Friend_Requests *fr, Net_Crypto *c) | ||
139 | { | ||
140 | cryptopacket_registerhandler(c, CRYPTO_PACKET_FRIEND_REQ, &friendreq_handlepacket, fr); | ||
141 | } | ||
diff --git a/toxcore/friend_requests.h b/toxcore/friend_requests.h new file mode 100644 index 00000000..2ebd557b --- /dev/null +++ b/toxcore/friend_requests.h | |||
@@ -0,0 +1,70 @@ | |||
1 | /* friend_requests.h | ||
2 | * | ||
3 | * Handle friend requests. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #ifndef FRIEND_REQUESTS_H | ||
25 | #define FRIEND_REQUESTS_H | ||
26 | |||
27 | #include "DHT.h" | ||
28 | #include "net_crypto.h" | ||
29 | |||
30 | #ifdef __cplusplus | ||
31 | extern "C" { | ||
32 | #endif | ||
33 | |||
34 | typedef struct { | ||
35 | uint32_t nospam; | ||
36 | void (*handle_friendrequest)(uint8_t *, uint8_t *, uint16_t, void *); | ||
37 | uint8_t handle_friendrequest_isset; | ||
38 | void *handle_friendrequest_userdata; | ||
39 | |||
40 | /*NOTE: the following is just a temporary fix for the multiple friend requests received at the same time problem | ||
41 | TODO: Make this better (This will most likely tie in with the way we will handle spam.)*/ | ||
42 | |||
43 | #define MAX_RECEIVED_STORED 32 | ||
44 | |||
45 | uint8_t received_requests[MAX_RECEIVED_STORED][crypto_box_PUBLICKEYBYTES]; | ||
46 | uint16_t received_requests_index; | ||
47 | } Friend_Requests; | ||
48 | |||
49 | /* Try to send a friendrequest to peer with public_key | ||
50 | data is the data in the request and length is the length. */ | ||
51 | int send_friendrequest(DHT *dht, uint8_t *public_key, uint32_t nospam_num, uint8_t *data, uint32_t length); | ||
52 | /* | ||
53 | * Set and get the nospam variable used to prevent one type of friend request spam | ||
54 | */ | ||
55 | void set_nospam(Friend_Requests *fr, uint32_t num); | ||
56 | uint32_t get_nospam(Friend_Requests *fr); | ||
57 | |||
58 | /* set the function that will be executed when a friend request for us is received. | ||
59 | function format is function(uint8_t * public_key, uint8_t * data, uint16_t length) */ | ||
60 | void callback_friendrequest(Friend_Requests *fr, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), | ||
61 | void *userdata); | ||
62 | |||
63 | /* sets up friendreq packet handlers */ | ||
64 | void friendreq_init(Friend_Requests *fr, Net_Crypto *c); | ||
65 | |||
66 | #ifdef __cplusplus | ||
67 | } | ||
68 | #endif | ||
69 | |||
70 | #endif | ||
diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c new file mode 100644 index 00000000..f421c37d --- /dev/null +++ b/toxcore/net_crypto.c | |||
@@ -0,0 +1,771 @@ | |||
1 | /* net_crypto.c | ||
2 | * | ||
3 | * Functions for the core network crypto. | ||
4 | * See also: http://wiki.tox.im/index.php/DHT | ||
5 | * | ||
6 | * NOTE: This code has to be perfect. We don't mess around with encryption. | ||
7 | * | ||
8 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
9 | * | ||
10 | * This file is part of Tox. | ||
11 | * | ||
12 | * Tox is free software: you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation, either version 3 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * Tox is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "net_crypto.h" | ||
28 | |||
29 | #define CONN_NO_CONNECTION 0 | ||
30 | #define CONN_HANDSHAKE_SENT 1 | ||
31 | #define CONN_NOT_CONFIRMED 2 | ||
32 | #define CONN_ESTABLISHED 3 | ||
33 | #define CONN_TIMED_OUT 4 | ||
34 | |||
35 | /* Use this instead of memcmp; not vulnerable to timing attacks. */ | ||
36 | uint8_t crypto_iszero(uint8_t *mem, uint32_t length) | ||
37 | { | ||
38 | uint8_t check = 0; | ||
39 | uint32_t i; | ||
40 | |||
41 | for (i = 0; i < length; ++i) { | ||
42 | check |= mem[i]; | ||
43 | } | ||
44 | |||
45 | return check; // We return zero if mem is made out of zeroes. | ||
46 | } | ||
47 | |||
48 | /* Precomputes the shared key from their public_key and our secret_key. | ||
49 | This way we can avoid an expensive elliptic curve scalar multiply for each | ||
50 | encrypt/decrypt operation. | ||
51 | enc_key has to be crypto_box_BEFORENMBYTES bytes long. */ | ||
52 | void encrypt_precompute(uint8_t *public_key, uint8_t *secret_key, uint8_t *enc_key) | ||
53 | { | ||
54 | crypto_box_beforenm(enc_key, public_key, secret_key); | ||
55 | } | ||
56 | |||
57 | /* Fast encrypt. Depends on enc_key from encrypt_precompute. */ | ||
58 | int encrypt_data_fast(uint8_t *enc_key, uint8_t *nonce, | ||
59 | uint8_t *plain, uint32_t length, uint8_t *encrypted) | ||
60 | { | ||
61 | if (length + crypto_box_MACBYTES > MAX_DATA_SIZE || length == 0) | ||
62 | return -1; | ||
63 | |||
64 | uint8_t temp_plain[MAX_DATA_SIZE + crypto_box_ZEROBYTES] = {0}; | ||
65 | uint8_t temp_encrypted[MAX_DATA_SIZE + crypto_box_BOXZEROBYTES]; | ||
66 | |||
67 | memcpy(temp_plain + crypto_box_ZEROBYTES, plain, length); /* pad the message with 32 0 bytes. */ | ||
68 | |||
69 | crypto_box_afternm(temp_encrypted, temp_plain, length + crypto_box_ZEROBYTES, nonce, enc_key); | ||
70 | |||
71 | if (crypto_iszero(temp_encrypted, crypto_box_BOXZEROBYTES) != 0) | ||
72 | return -1; | ||
73 | |||
74 | /* unpad the encrypted message */ | ||
75 | memcpy(encrypted, temp_encrypted + crypto_box_BOXZEROBYTES, length + crypto_box_MACBYTES); | ||
76 | return length - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES; | ||
77 | } | ||
78 | |||
79 | /* Fast decrypt. Depends on enc_ley from encrypt_precompute. */ | ||
80 | int decrypt_data_fast(uint8_t *enc_key, uint8_t *nonce, | ||
81 | uint8_t *encrypted, uint32_t length, uint8_t *plain) | ||
82 | { | ||
83 | if (length > MAX_DATA_SIZE || length <= crypto_box_BOXZEROBYTES) | ||
84 | return -1; | ||
85 | |||
86 | uint8_t temp_plain[MAX_DATA_SIZE + crypto_box_ZEROBYTES]; | ||
87 | uint8_t temp_encrypted[MAX_DATA_SIZE + crypto_box_BOXZEROBYTES] = {0}; | ||
88 | |||
89 | memcpy(temp_encrypted + crypto_box_BOXZEROBYTES, encrypted, length); /* pad the message with 16 0 bytes. */ | ||
90 | |||
91 | if (crypto_box_open_afternm(temp_plain, temp_encrypted, length + crypto_box_BOXZEROBYTES, | ||
92 | nonce, enc_key) == -1) | ||
93 | return -1; | ||
94 | |||
95 | /* if decryption is successful the first crypto_box_ZEROBYTES of the message will be zero | ||
96 | apparently memcmp should not be used so we do this instead:*/ | ||
97 | if (crypto_iszero(temp_plain, crypto_box_ZEROBYTES) != 0) | ||
98 | return -1; | ||
99 | |||
100 | /* unpad the plain message */ | ||
101 | memcpy(plain, temp_plain + crypto_box_ZEROBYTES, length - crypto_box_MACBYTES); | ||
102 | return length - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES; | ||
103 | } | ||
104 | |||
105 | int encrypt_data(uint8_t *public_key, uint8_t *secret_key, uint8_t *nonce, | ||
106 | uint8_t *plain, uint32_t length, uint8_t *encrypted) | ||
107 | { | ||
108 | uint8_t k[crypto_box_BEFORENMBYTES]; | ||
109 | encrypt_precompute(public_key, secret_key, k); | ||
110 | return encrypt_data_fast(k, nonce, plain, length, encrypted); | ||
111 | } | ||
112 | |||
113 | int decrypt_data(uint8_t *public_key, uint8_t *secret_key, uint8_t *nonce, | ||
114 | uint8_t *encrypted, uint32_t length, uint8_t *plain) | ||
115 | { | ||
116 | uint8_t k[crypto_box_BEFORENMBYTES]; | ||
117 | encrypt_precompute(public_key, secret_key, k); | ||
118 | return decrypt_data_fast(k, nonce, encrypted, length, plain); | ||
119 | } | ||
120 | |||
121 | /* increment the given nonce by 1 */ | ||
122 | static void increment_nonce(uint8_t *nonce) | ||
123 | { | ||
124 | uint32_t i; | ||
125 | |||
126 | for (i = 0; i < crypto_box_NONCEBYTES; ++i) { | ||
127 | ++nonce[i]; | ||
128 | |||
129 | if (nonce[i] != 0) | ||
130 | break; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | /* fill the given nonce with random bytes. */ | ||
135 | void random_nonce(uint8_t *nonce) | ||
136 | { | ||
137 | uint32_t i, temp; | ||
138 | |||
139 | for (i = 0; i < crypto_box_NONCEBYTES / 4; ++i) { | ||
140 | temp = random_int(); | ||
141 | memcpy(nonce + 4 * i, &temp, 4); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /* return 0 if there is no received data in the buffer | ||
146 | return -1 if the packet was discarded. | ||
147 | return length of received data if successful */ | ||
148 | int read_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data) | ||
149 | { | ||
150 | if (crypt_connection_id < 0 || crypt_connection_id >= c->crypto_connections_length) | ||
151 | return 0; | ||
152 | |||
153 | if (c->crypto_connections[crypt_connection_id].status != CONN_ESTABLISHED) | ||
154 | return 0; | ||
155 | |||
156 | uint8_t temp_data[MAX_DATA_SIZE]; | ||
157 | int length = read_packet(c->lossless_udp, c->crypto_connections[crypt_connection_id].number, temp_data); | ||
158 | |||
159 | if (length == 0) | ||
160 | return 0; | ||
161 | |||
162 | if (temp_data[0] != 3) | ||
163 | return -1; | ||
164 | |||
165 | int len = decrypt_data_fast(c->crypto_connections[crypt_connection_id].shared_key, | ||
166 | c->crypto_connections[crypt_connection_id].recv_nonce, | ||
167 | temp_data + 1, length - 1, data); | ||
168 | |||
169 | if (len != -1) { | ||
170 | increment_nonce(c->crypto_connections[crypt_connection_id].recv_nonce); | ||
171 | return len; | ||
172 | } | ||
173 | |||
174 | return -1; | ||
175 | } | ||
176 | |||
177 | /* return 0 if data could not be put in packet queue | ||
178 | return 1 if data was put into the queue */ | ||
179 | int write_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data, uint32_t length) | ||
180 | { | ||
181 | if (crypt_connection_id < 0 || crypt_connection_id >= c->crypto_connections_length) | ||
182 | return 0; | ||
183 | |||
184 | if (length - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES > MAX_DATA_SIZE - 1) | ||
185 | return 0; | ||
186 | |||
187 | if (c->crypto_connections[crypt_connection_id].status != CONN_ESTABLISHED) | ||
188 | return 0; | ||
189 | |||
190 | uint8_t temp_data[MAX_DATA_SIZE]; | ||
191 | int len = encrypt_data_fast(c->crypto_connections[crypt_connection_id].shared_key, | ||
192 | c->crypto_connections[crypt_connection_id].sent_nonce, | ||
193 | data, length, temp_data + 1); | ||
194 | |||
195 | if (len == -1) | ||
196 | return 0; | ||
197 | |||
198 | temp_data[0] = 3; | ||
199 | |||
200 | if (write_packet(c->lossless_udp, c->crypto_connections[crypt_connection_id].number, temp_data, len + 1) == 0) | ||
201 | return 0; | ||
202 | |||
203 | increment_nonce(c->crypto_connections[crypt_connection_id].sent_nonce); | ||
204 | return 1; | ||
205 | } | ||
206 | |||
207 | /* create a request to peer. | ||
208 | send_public_key and send_secret_key are the pub/secret keys of the sender | ||
209 | recv_public_key is public key of reciever | ||
210 | packet must be an array of MAX_DATA_SIZE big. | ||
211 | Data represents the data we send with the request with length being the length of the data. | ||
212 | request_id is the id of the request (32 = friend request, 254 = ping request) | ||
213 | returns -1 on failure | ||
214 | returns the length of the created packet on success */ | ||
215 | int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *packet, uint8_t *recv_public_key, | ||
216 | uint8_t *data, uint32_t length, uint8_t request_id) | ||
217 | { | ||
218 | if (MAX_DATA_SIZE < length + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING) | ||
219 | return -1; | ||
220 | |||
221 | uint8_t nonce[crypto_box_NONCEBYTES]; | ||
222 | uint8_t temp[MAX_DATA_SIZE]; | ||
223 | memcpy(temp + 1, data, length); | ||
224 | temp[0] = request_id; | ||
225 | random_nonce(nonce); | ||
226 | int len = encrypt_data(recv_public_key, send_secret_key, nonce, temp, length + 1, | ||
227 | 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + packet); | ||
228 | |||
229 | if (len == -1) | ||
230 | return -1; | ||
231 | |||
232 | packet[0] = NET_PACKET_CRYPTO; | ||
233 | memcpy(packet + 1, recv_public_key, crypto_box_PUBLICKEYBYTES); | ||
234 | memcpy(packet + 1 + crypto_box_PUBLICKEYBYTES, send_public_key, crypto_box_PUBLICKEYBYTES); | ||
235 | memcpy(packet + 1 + crypto_box_PUBLICKEYBYTES * 2, nonce, crypto_box_NONCEBYTES); | ||
236 | |||
237 | return len + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES; | ||
238 | } | ||
239 | |||
240 | /* puts the senders public key in the request in public_key, the data from the request | ||
241 | in data if a friend or ping request was sent to us and returns the length of the data. | ||
242 | packet is the request packet and length is its length | ||
243 | return -1 if not valid request. */ | ||
244 | static int handle_request(Net_Crypto *c, uint8_t *public_key, uint8_t *data, uint8_t *request_id, uint8_t *packet, | ||
245 | uint16_t length) | ||
246 | { | ||
247 | |||
248 | if (length > crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING && | ||
249 | length <= MAX_DATA_SIZE + ENCRYPTION_PADDING && | ||
250 | memcmp(packet + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) { | ||
251 | memcpy(public_key, packet + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES); | ||
252 | uint8_t nonce[crypto_box_NONCEBYTES]; | ||
253 | uint8_t temp[MAX_DATA_SIZE]; | ||
254 | memcpy(nonce, packet + 1 + crypto_box_PUBLICKEYBYTES * 2, crypto_box_NONCEBYTES); | ||
255 | int len1 = decrypt_data(public_key, c->self_secret_key, nonce, | ||
256 | packet + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES, | ||
257 | length - (crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1), temp); | ||
258 | |||
259 | if (len1 == -1 || len1 == 0) | ||
260 | return -1; | ||
261 | |||
262 | request_id[0] = temp[0]; | ||
263 | --len1; | ||
264 | memcpy(data, temp + 1, len1); | ||
265 | return len1; | ||
266 | } else | ||
267 | return -1; | ||
268 | } | ||
269 | |||
270 | void cryptopacket_registerhandler(Net_Crypto *c, uint8_t byte, cryptopacket_handler_callback cb, void *object) | ||
271 | { | ||
272 | c->cryptopackethandlers[byte].function = cb; | ||
273 | c->cryptopackethandlers[byte].object = object; | ||
274 | } | ||
275 | |||
276 | static int cryptopacket_handle(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
277 | { | ||
278 | DHT *dht = object; | ||
279 | |||
280 | if (packet[0] == NET_PACKET_CRYPTO) { | ||
281 | if (length <= crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING || | ||
282 | length > MAX_DATA_SIZE + ENCRYPTION_PADDING) | ||
283 | return 1; | ||
284 | |||
285 | if (memcmp(packet + 1, dht->c->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {// check if request is for us. | ||
286 | uint8_t public_key[crypto_box_PUBLICKEYBYTES]; | ||
287 | uint8_t data[MAX_DATA_SIZE]; | ||
288 | uint8_t number; | ||
289 | int len = handle_request(dht->c, public_key, data, &number, packet, length); | ||
290 | |||
291 | if (len == -1 || len == 0) | ||
292 | return 1; | ||
293 | |||
294 | if (!dht->c->cryptopackethandlers[number].function) return 1; | ||
295 | |||
296 | dht->c->cryptopackethandlers[number].function(dht->c->cryptopackethandlers[number].object, source, public_key, data, | ||
297 | len); | ||
298 | |||
299 | } else { /* if request is not for us, try routing it. */ | ||
300 | if (route_packet(dht, packet + 1, packet, length) == length) //NOTE | ||
301 | return 0; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | return 1; | ||
306 | } | ||
307 | |||
308 | /* Send a crypto handshake packet containing an encrypted secret nonce and session public key | ||
309 | to peer with connection_id and public_key | ||
310 | the packet is encrypted with a random nonce which is sent in plain text with the packet */ | ||
311 | static int send_cryptohandshake(Net_Crypto *c, int connection_id, uint8_t *public_key, uint8_t *secret_nonce, | ||
312 | uint8_t *session_key) | ||
313 | { | ||
314 | uint8_t temp_data[MAX_DATA_SIZE]; | ||
315 | uint8_t temp[crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES]; | ||
316 | uint8_t nonce[crypto_box_NONCEBYTES]; | ||
317 | |||
318 | random_nonce(nonce); | ||
319 | memcpy(temp, secret_nonce, crypto_box_NONCEBYTES); | ||
320 | memcpy(temp + crypto_box_NONCEBYTES, session_key, crypto_box_PUBLICKEYBYTES); | ||
321 | |||
322 | int len = encrypt_data(public_key, c->self_secret_key, nonce, temp, crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES, | ||
323 | 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + temp_data); | ||
324 | |||
325 | if (len == -1) | ||
326 | return 0; | ||
327 | |||
328 | temp_data[0] = 2; | ||
329 | memcpy(temp_data + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES); | ||
330 | memcpy(temp_data + 1 + crypto_box_PUBLICKEYBYTES, nonce, crypto_box_NONCEBYTES); | ||
331 | return write_packet(c->lossless_udp, connection_id, temp_data, | ||
332 | len + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); | ||
333 | } | ||
334 | |||
335 | /* Extract secret nonce, session public key and public_key from a packet(data) with length length | ||
336 | return 1 if successful | ||
337 | return 0 if failure */ | ||
338 | static int handle_cryptohandshake(Net_Crypto *c, uint8_t *public_key, uint8_t *secret_nonce, | ||
339 | uint8_t *session_key, uint8_t *data, uint16_t length) | ||
340 | { | ||
341 | int pad = (- crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES); | ||
342 | |||
343 | if (length != 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES | ||
344 | + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + pad) { | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | if (data[0] != 2) | ||
349 | return 0; | ||
350 | |||
351 | uint8_t temp[crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES]; | ||
352 | |||
353 | memcpy(public_key, data + 1, crypto_box_PUBLICKEYBYTES); | ||
354 | |||
355 | int len = decrypt_data(public_key, c->self_secret_key, data + 1 + crypto_box_PUBLICKEYBYTES, | ||
356 | data + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, | ||
357 | crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + pad, temp); | ||
358 | |||
359 | if (len != crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES) | ||
360 | return 0; | ||
361 | |||
362 | memcpy(secret_nonce, temp, crypto_box_NONCEBYTES); | ||
363 | memcpy(session_key, temp + crypto_box_NONCEBYTES, crypto_box_PUBLICKEYBYTES); | ||
364 | return 1; | ||
365 | } | ||
366 | |||
367 | /* get crypto connection id from public key of peer | ||
368 | return -1 if there are no connections like we are looking for | ||
369 | return id if it found it */ | ||
370 | static int getcryptconnection_id(Net_Crypto *c, uint8_t *public_key) | ||
371 | { | ||
372 | uint32_t i; | ||
373 | |||
374 | for (i = 0; i < c->crypto_connections_length; ++i) { | ||
375 | if (c->crypto_connections[i].status != CONN_NO_CONNECTION) | ||
376 | if (memcmp(public_key, c->crypto_connections[i].public_key, crypto_box_PUBLICKEYBYTES) == 0) | ||
377 | return i; | ||
378 | } | ||
379 | |||
380 | return -1; | ||
381 | } | ||
382 | |||
383 | /* set the size of the friend list to numfriends | ||
384 | return -1 if realloc fails */ | ||
385 | int realloc_cryptoconnection(Net_Crypto *c, uint32_t num) | ||
386 | { | ||
387 | if (num == 0) { | ||
388 | free(c->crypto_connections); | ||
389 | c->crypto_connections = NULL; | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | Crypto_Connection *newcrypto_connections = realloc(c->crypto_connections, num * sizeof(Crypto_Connection)); | ||
394 | |||
395 | if (newcrypto_connections == NULL) | ||
396 | return -1; | ||
397 | |||
398 | c->crypto_connections = newcrypto_connections; | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | /* Start a secure connection with other peer who has public_key and ip_port | ||
403 | returns -1 if failure | ||
404 | returns crypt_connection_id of the initialized connection if everything went well. */ | ||
405 | int crypto_connect(Net_Crypto *c, uint8_t *public_key, IP_Port ip_port) | ||
406 | { | ||
407 | uint32_t i; | ||
408 | int id = getcryptconnection_id(c, public_key); | ||
409 | |||
410 | if (id != -1) { | ||
411 | IP_Port c_ip = connection_ip(c->lossless_udp, c->crypto_connections[id].number); | ||
412 | |||
413 | if (c_ip.ip.i == ip_port.ip.i && c_ip.port == ip_port.port) | ||
414 | return -1; | ||
415 | } | ||
416 | |||
417 | if (realloc_cryptoconnection(c, c->crypto_connections_length + 1) == -1) | ||
418 | return -1; | ||
419 | |||
420 | memset(&(c->crypto_connections[c->crypto_connections_length]), 0, sizeof(Crypto_Connection)); | ||
421 | c->crypto_connections[c->crypto_connections_length].number = ~0; | ||
422 | |||
423 | for (i = 0; i <= c->crypto_connections_length; ++i) { | ||
424 | if (c->crypto_connections[i].status == CONN_NO_CONNECTION) { | ||
425 | int id = new_connection(c->lossless_udp, ip_port); | ||
426 | |||
427 | if (id == -1) | ||
428 | return -1; | ||
429 | |||
430 | c->crypto_connections[i].number = id; | ||
431 | c->crypto_connections[i].status = CONN_HANDSHAKE_SENT; | ||
432 | random_nonce(c->crypto_connections[i].recv_nonce); | ||
433 | memcpy(c->crypto_connections[i].public_key, public_key, crypto_box_PUBLICKEYBYTES); | ||
434 | crypto_box_keypair(c->crypto_connections[i].sessionpublic_key, c->crypto_connections[i].sessionsecret_key); | ||
435 | |||
436 | if (c->crypto_connections_length == i) | ||
437 | ++c->crypto_connections_length; | ||
438 | |||
439 | if (send_cryptohandshake(c, id, public_key, c->crypto_connections[i].recv_nonce, | ||
440 | c->crypto_connections[i].sessionpublic_key) == 1) { | ||
441 | increment_nonce(c->crypto_connections[i].recv_nonce); | ||
442 | return i; | ||
443 | } | ||
444 | |||
445 | return -1; /* this should never happen. */ | ||
446 | } | ||
447 | } | ||
448 | |||
449 | return -1; | ||
450 | } | ||
451 | |||
452 | /* handle an incoming connection | ||
453 | return -1 if no crypto inbound connection | ||
454 | return incoming connection id (Lossless_UDP one) if there is an incoming crypto connection | ||
455 | Put the public key of the peer in public_key, the secret_nonce from the handshake into secret_nonce | ||
456 | and the session public key for the connection in session_key | ||
457 | to accept it see: accept_crypto_inbound(...) | ||
458 | to refuse it just call kill_connection(...) on the connection id */ | ||
459 | int crypto_inbound(Net_Crypto *c, uint8_t *public_key, uint8_t *secret_nonce, uint8_t *session_key) | ||
460 | { | ||
461 | uint32_t i; | ||
462 | |||
463 | for (i = 0; i < MAX_INCOMING; ++i) { | ||
464 | if (c->incoming_connections[i] != -1) { | ||
465 | if (is_connected(c->lossless_udp, c->incoming_connections[i]) == 4 | ||
466 | || is_connected(c->lossless_udp, c->incoming_connections[i]) == 0) { | ||
467 | kill_connection(c->lossless_udp, c->incoming_connections[i]); | ||
468 | c->incoming_connections[i] = -1; | ||
469 | continue; | ||
470 | } | ||
471 | |||
472 | if (id_packet(c->lossless_udp, c->incoming_connections[i]) == 2) { | ||
473 | uint8_t temp_data[MAX_DATA_SIZE]; | ||
474 | uint16_t len = read_packet(c->lossless_udp, c->incoming_connections[i], temp_data); | ||
475 | |||
476 | if (handle_cryptohandshake(c, public_key, secret_nonce, session_key, temp_data, len)) { | ||
477 | int connection_id = c->incoming_connections[i]; | ||
478 | c->incoming_connections[i] = -1; /* remove this connection from the incoming connection list. */ | ||
479 | return connection_id; | ||
480 | } | ||
481 | } | ||
482 | } | ||
483 | } | ||
484 | |||
485 | return -1; | ||
486 | } | ||
487 | |||
488 | /* kill a crypto connection | ||
489 | return 0 if killed successfully | ||
490 | return 1 if there was a problem. */ | ||
491 | int crypto_kill(Net_Crypto *c, int crypt_connection_id) | ||
492 | { | ||
493 | if (crypt_connection_id < 0 || crypt_connection_id >= c->crypto_connections_length) | ||
494 | return 1; | ||
495 | |||
496 | if (c->crypto_connections[crypt_connection_id].status != CONN_NO_CONNECTION) { | ||
497 | c->crypto_connections[crypt_connection_id].status = CONN_NO_CONNECTION; | ||
498 | kill_connection(c->lossless_udp, c->crypto_connections[crypt_connection_id].number); | ||
499 | memset(&(c->crypto_connections[crypt_connection_id]), 0 , sizeof(Crypto_Connection)); | ||
500 | c->crypto_connections[crypt_connection_id].number = ~0; | ||
501 | uint32_t i; | ||
502 | |||
503 | for (i = c->crypto_connections_length; i != 0; --i) { | ||
504 | if (c->crypto_connections[i - 1].status != CONN_NO_CONNECTION) | ||
505 | break; | ||
506 | } | ||
507 | |||
508 | c->crypto_connections_length = i; | ||
509 | realloc_cryptoconnection(c, c->crypto_connections_length); | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | return 1; | ||
514 | } | ||
515 | |||
516 | /* accept an incoming connection using the parameters provided by crypto_inbound | ||
517 | return -1 if not successful | ||
518 | returns the crypt_connection_id if successful */ | ||
519 | int accept_crypto_inbound(Net_Crypto *c, int connection_id, uint8_t *public_key, uint8_t *secret_nonce, | ||
520 | uint8_t *session_key) | ||
521 | { | ||
522 | uint32_t i; | ||
523 | |||
524 | if (connection_id == -1) | ||
525 | return -1; | ||
526 | |||
527 | /* | ||
528 | if(getcryptconnection_id(public_key) != -1) | ||
529 | { | ||
530 | return -1; | ||
531 | }*/ | ||
532 | if (realloc_cryptoconnection(c, c->crypto_connections_length + 1) == -1) | ||
533 | return -1; | ||
534 | |||
535 | memset(&(c->crypto_connections[c->crypto_connections_length]), 0, sizeof(Crypto_Connection)); | ||
536 | c->crypto_connections[c->crypto_connections_length].number = ~0; | ||
537 | |||
538 | for (i = 0; i <= c->crypto_connections_length; ++i) { | ||
539 | if (c->crypto_connections[i].status == CONN_NO_CONNECTION) { | ||
540 | c->crypto_connections[i].number = connection_id; | ||
541 | c->crypto_connections[i].status = CONN_NOT_CONFIRMED; | ||
542 | random_nonce(c->crypto_connections[i].recv_nonce); | ||
543 | memcpy(c->crypto_connections[i].sent_nonce, secret_nonce, crypto_box_NONCEBYTES); | ||
544 | memcpy(c->crypto_connections[i].peersessionpublic_key, session_key, crypto_box_PUBLICKEYBYTES); | ||
545 | increment_nonce(c->crypto_connections[i].sent_nonce); | ||
546 | memcpy(c->crypto_connections[i].public_key, public_key, crypto_box_PUBLICKEYBYTES); | ||
547 | |||
548 | crypto_box_keypair(c->crypto_connections[i].sessionpublic_key, c->crypto_connections[i].sessionsecret_key); | ||
549 | |||
550 | if (c->crypto_connections_length == i) | ||
551 | ++c->crypto_connections_length; | ||
552 | |||
553 | if (send_cryptohandshake(c, connection_id, public_key, c->crypto_connections[i].recv_nonce, | ||
554 | c->crypto_connections[i].sessionpublic_key) == 1) { | ||
555 | increment_nonce(c->crypto_connections[i].recv_nonce); | ||
556 | uint32_t zero = 0; | ||
557 | encrypt_precompute(c->crypto_connections[i].peersessionpublic_key, | ||
558 | c->crypto_connections[i].sessionsecret_key, | ||
559 | c->crypto_connections[i].shared_key); | ||
560 | c->crypto_connections[i].status = | ||
561 | CONN_ESTABLISHED; /* connection status needs to be 3 for write_cryptpacket() to work */ | ||
562 | write_cryptpacket(c, i, ((uint8_t *)&zero), sizeof(zero)); | ||
563 | c->crypto_connections[i].status = CONN_NOT_CONFIRMED; /* set it to its proper value right after. */ | ||
564 | return i; | ||
565 | } | ||
566 | |||
567 | return -1; /* this should never happen. */ | ||
568 | } | ||
569 | } | ||
570 | |||
571 | return -1; | ||
572 | } | ||
573 | |||
574 | /* return 0 if no connection, 1 we have sent a handshake, 2 if connection is not confirmed yet | ||
575 | (we have received a handshake but no empty data packet), 3 if the connection is established. | ||
576 | 4 if the connection is timed out and waiting to be killed */ | ||
577 | int is_cryptoconnected(Net_Crypto *c, int crypt_connection_id) | ||
578 | { | ||
579 | if (crypt_connection_id >= 0 && crypt_connection_id < c->crypto_connections_length) | ||
580 | return c->crypto_connections[crypt_connection_id].status; | ||
581 | |||
582 | return CONN_NO_CONNECTION; | ||
583 | } | ||
584 | |||
585 | void new_keys(Net_Crypto *c) | ||
586 | { | ||
587 | crypto_box_keypair(c->self_public_key, c->self_secret_key); | ||
588 | } | ||
589 | |||
590 | /* save the public and private keys to the keys array | ||
591 | Length must be crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES */ | ||
592 | void save_keys(Net_Crypto *c, uint8_t *keys) | ||
593 | { | ||
594 | memcpy(keys, c->self_public_key, crypto_box_PUBLICKEYBYTES); | ||
595 | memcpy(keys + crypto_box_PUBLICKEYBYTES, c->self_secret_key, crypto_box_SECRETKEYBYTES); | ||
596 | } | ||
597 | |||
598 | /* load the public and private keys from the keys array | ||
599 | Length must be crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES */ | ||
600 | void load_keys(Net_Crypto *c, uint8_t *keys) | ||
601 | { | ||
602 | memcpy(c->self_public_key, keys, crypto_box_PUBLICKEYBYTES); | ||
603 | memcpy(c->self_secret_key, keys + crypto_box_PUBLICKEYBYTES, crypto_box_SECRETKEYBYTES); | ||
604 | } | ||
605 | |||
606 | /* TODO: optimize this | ||
607 | adds an incoming connection to the incoming_connection list. | ||
608 | returns 0 if successful | ||
609 | returns 1 if failure */ | ||
610 | static int new_incoming(Net_Crypto *c, int id) | ||
611 | { | ||
612 | uint32_t i; | ||
613 | |||
614 | for (i = 0; i < MAX_INCOMING; ++i) { | ||
615 | if (c->incoming_connections[i] == -1) { | ||
616 | c->incoming_connections[i] = id; | ||
617 | return 0; | ||
618 | } | ||
619 | } | ||
620 | |||
621 | return 1; | ||
622 | } | ||
623 | |||
624 | /* TODO: optimize this | ||
625 | handle all new incoming connections. */ | ||
626 | static void handle_incomings(Net_Crypto *c) | ||
627 | { | ||
628 | int income; | ||
629 | |||
630 | while (1) { | ||
631 | income = incoming_connection(c->lossless_udp); | ||
632 | |||
633 | if (income == -1 || new_incoming(c, income) ) | ||
634 | break; | ||
635 | } | ||
636 | } | ||
637 | |||
638 | /* handle received packets for not yet established crypto connections. */ | ||
639 | static void receive_crypto(Net_Crypto *c) | ||
640 | { | ||
641 | uint32_t i; | ||
642 | |||
643 | for (i = 0; i < c->crypto_connections_length; ++i) { | ||
644 | if (c->crypto_connections[i].status == CONN_HANDSHAKE_SENT) { | ||
645 | uint8_t temp_data[MAX_DATA_SIZE]; | ||
646 | uint8_t secret_nonce[crypto_box_NONCEBYTES]; | ||
647 | uint8_t public_key[crypto_box_PUBLICKEYBYTES]; | ||
648 | uint8_t session_key[crypto_box_PUBLICKEYBYTES]; | ||
649 | uint16_t len; | ||
650 | |||
651 | if (id_packet(c->lossless_udp, c->crypto_connections[i].number) == 2) { /* handle handshake packet. */ | ||
652 | len = read_packet(c->lossless_udp, c->crypto_connections[i].number, temp_data); | ||
653 | |||
654 | if (handle_cryptohandshake(c, public_key, secret_nonce, session_key, temp_data, len)) { | ||
655 | if (memcmp(public_key, c->crypto_connections[i].public_key, crypto_box_PUBLICKEYBYTES) == 0) { | ||
656 | memcpy(c->crypto_connections[i].sent_nonce, secret_nonce, crypto_box_NONCEBYTES); | ||
657 | memcpy(c->crypto_connections[i].peersessionpublic_key, session_key, crypto_box_PUBLICKEYBYTES); | ||
658 | increment_nonce(c->crypto_connections[i].sent_nonce); | ||
659 | uint32_t zero = 0; | ||
660 | encrypt_precompute(c->crypto_connections[i].peersessionpublic_key, | ||
661 | c->crypto_connections[i].sessionsecret_key, | ||
662 | c->crypto_connections[i].shared_key); | ||
663 | c->crypto_connections[i].status = | ||
664 | CONN_ESTABLISHED; /* connection status needs to be 3 for write_cryptpacket() to work */ | ||
665 | write_cryptpacket(c, i, ((uint8_t *)&zero), sizeof(zero)); | ||
666 | c->crypto_connections[i].status = CONN_NOT_CONFIRMED; /* set it to its proper value right after. */ | ||
667 | } | ||
668 | } | ||
669 | } else if (id_packet(c->lossless_udp, | ||
670 | c->crypto_connections[i].number) != -1) { // This should not happen kill the connection if it does | ||
671 | crypto_kill(c, i); | ||
672 | return; | ||
673 | } | ||
674 | } | ||
675 | |||
676 | if (c->crypto_connections[i].status == CONN_NOT_CONFIRMED) { | ||
677 | if (id_packet(c->lossless_udp, c->crypto_connections[i].number) == 3) { | ||
678 | uint8_t temp_data[MAX_DATA_SIZE]; | ||
679 | uint8_t data[MAX_DATA_SIZE]; | ||
680 | int length = read_packet(c->lossless_udp, c->crypto_connections[i].number, temp_data); | ||
681 | int len = decrypt_data(c->crypto_connections[i].peersessionpublic_key, | ||
682 | c->crypto_connections[i].sessionsecret_key, | ||
683 | c->crypto_connections[i].recv_nonce, temp_data + 1, length - 1, data); | ||
684 | uint32_t zero = 0; | ||
685 | |||
686 | if (len == sizeof(uint32_t) && memcmp(((uint8_t *)&zero), data, sizeof(uint32_t)) == 0) { | ||
687 | increment_nonce(c->crypto_connections[i].recv_nonce); | ||
688 | encrypt_precompute(c->crypto_connections[i].peersessionpublic_key, | ||
689 | c->crypto_connections[i].sessionsecret_key, | ||
690 | c->crypto_connections[i].shared_key); | ||
691 | c->crypto_connections[i].status = CONN_ESTABLISHED; | ||
692 | |||
693 | /* connection is accepted so we disable the auto kill by setting it to about 1 month from now. */ | ||
694 | kill_connection_in(c->lossless_udp, c->crypto_connections[i].number, 3000000); | ||
695 | } else { | ||
696 | crypto_kill(c, i); // This should not happen kill the connection if it does | ||
697 | return; | ||
698 | } | ||
699 | } else if (id_packet(c->lossless_udp, c->crypto_connections[i].number) != -1) | ||
700 | /* This should not happen | ||
701 | kill the connection if it does */ | ||
702 | crypto_kill(c, i); | ||
703 | |||
704 | return; | ||
705 | } | ||
706 | } | ||
707 | } | ||
708 | |||
709 | /* run this to (re)initialize net_crypto | ||
710 | sets all the global connection variables to their default values. */ | ||
711 | Net_Crypto *new_net_crypto(Networking_Core *net) | ||
712 | { | ||
713 | if (net == NULL) | ||
714 | return NULL; | ||
715 | |||
716 | Net_Crypto *temp = calloc(1, sizeof(Net_Crypto)); | ||
717 | |||
718 | if (temp == NULL) | ||
719 | return NULL; | ||
720 | |||
721 | temp->lossless_udp = new_lossless_udp(net); | ||
722 | |||
723 | if (temp->lossless_udp == NULL) | ||
724 | return NULL; | ||
725 | |||
726 | memset(temp->incoming_connections, -1 , sizeof(int) * MAX_INCOMING); | ||
727 | return temp; | ||
728 | } | ||
729 | |||
730 | void init_cryptopackets(void *dht) | ||
731 | { | ||
732 | DHT *s_dht = dht; | ||
733 | networking_registerhandler(s_dht->c->lossless_udp->net, NET_PACKET_CRYPTO, &cryptopacket_handle, s_dht); | ||
734 | } | ||
735 | |||
736 | static void kill_timedout(Net_Crypto *c) | ||
737 | { | ||
738 | uint32_t i; | ||
739 | |||
740 | for (i = 0; i < c->crypto_connections_length; ++i) { | ||
741 | if (c->crypto_connections[i].status != CONN_NO_CONNECTION | ||
742 | && is_connected(c->lossless_udp, c->crypto_connections[i].number) == 4) | ||
743 | c->crypto_connections[i].status = CONN_TIMED_OUT; | ||
744 | else if (is_connected(c->lossless_udp, c->crypto_connections[i].number) == 4) { | ||
745 | kill_connection(c->lossless_udp, c->crypto_connections[i].number); | ||
746 | c->crypto_connections[i].number = ~0; | ||
747 | } | ||
748 | } | ||
749 | } | ||
750 | |||
751 | /* main loop */ | ||
752 | void do_net_crypto(Net_Crypto *c) | ||
753 | { | ||
754 | do_lossless_udp(c->lossless_udp); | ||
755 | handle_incomings(c); | ||
756 | receive_crypto(c); | ||
757 | kill_timedout(c); | ||
758 | } | ||
759 | |||
760 | void kill_net_crypto(Net_Crypto *c) | ||
761 | { | ||
762 | uint32_t i; | ||
763 | |||
764 | for (i = 0; i < c->crypto_connections_length; ++i) { | ||
765 | crypto_kill(c, i); | ||
766 | } | ||
767 | |||
768 | kill_lossless_udp(c->lossless_udp); | ||
769 | memset(c, 0, sizeof(Net_Crypto)); | ||
770 | free(c); | ||
771 | } | ||
diff --git a/toxcore/net_crypto.h b/toxcore/net_crypto.h new file mode 100644 index 00000000..81670993 --- /dev/null +++ b/toxcore/net_crypto.h | |||
@@ -0,0 +1,200 @@ | |||
1 | /* net_crypto.h | ||
2 | * | ||
3 | * Functions for the core network crypto. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #ifndef NET_CRYPTO_H | ||
25 | #define NET_CRYPTO_H | ||
26 | |||
27 | #include "Lossless_UDP.h" | ||
28 | |||
29 | #ifdef __cplusplus | ||
30 | extern "C" { | ||
31 | #endif | ||
32 | |||
33 | #define MAX_INCOMING 64 | ||
34 | |||
35 | #define CRYPTO_PACKET_FRIEND_REQ 32 /* Friend request crypto packet ID */ | ||
36 | #define CRYPTO_PACKET_NAT_PING 254 /* NAT ping crypto packet ID */ | ||
37 | |||
38 | typedef struct { | ||
39 | uint8_t public_key[crypto_box_PUBLICKEYBYTES]; /* the real public key of the peer. */ | ||
40 | uint8_t recv_nonce[crypto_box_NONCEBYTES]; /* nonce of received packets */ | ||
41 | uint8_t sent_nonce[crypto_box_NONCEBYTES]; /* nonce of sent packets. */ | ||
42 | uint8_t sessionpublic_key[crypto_box_PUBLICKEYBYTES]; /* our public key for this session. */ | ||
43 | uint8_t sessionsecret_key[crypto_box_SECRETKEYBYTES]; /* our private key for this session. */ | ||
44 | uint8_t peersessionpublic_key[crypto_box_PUBLICKEYBYTES]; /* The public key of the peer. */ | ||
45 | uint8_t shared_key[crypto_box_BEFORENMBYTES]; /* the precomputed shared key from encrypt_precompute */ | ||
46 | uint8_t status; /* 0 if no connection, 1 we have sent a handshake, 2 if connexion is not confirmed yet | ||
47 | (we have received a handshake but no empty data packet), 3 if the connection is established. | ||
48 | 4 if the connection is timed out. */ | ||
49 | uint16_t number; /* Lossless_UDP connection number corresponding to this connection. */ | ||
50 | |||
51 | } Crypto_Connection; | ||
52 | |||
53 | typedef int (*cryptopacket_handler_callback)(void *object, IP_Port ip_port, uint8_t *source_pubkey, uint8_t *data, | ||
54 | uint32_t len); | ||
55 | |||
56 | typedef struct { | ||
57 | cryptopacket_handler_callback function; | ||
58 | void *object; | ||
59 | } Cryptopacket_Handles; | ||
60 | |||
61 | typedef struct { | ||
62 | Lossless_UDP *lossless_udp; | ||
63 | |||
64 | Crypto_Connection *crypto_connections; | ||
65 | |||
66 | uint32_t crypto_connections_length; /* Length of connections array */ | ||
67 | |||
68 | /* Our public and secret keys. */ | ||
69 | uint8_t self_public_key[crypto_box_PUBLICKEYBYTES]; | ||
70 | uint8_t self_secret_key[crypto_box_SECRETKEYBYTES]; | ||
71 | |||
72 | /* keeps track of the connection numbers for friends request so we can check later if they were sent */ | ||
73 | int incoming_connections[MAX_INCOMING]; | ||
74 | |||
75 | Cryptopacket_Handles cryptopackethandlers[256]; | ||
76 | } Net_Crypto; | ||
77 | |||
78 | #include "DHT.h" | ||
79 | |||
80 | #define ENCRYPTION_PADDING (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES) | ||
81 | |||
82 | /* returns zero if the buffer contains only zeros */ | ||
83 | uint8_t crypto_iszero(uint8_t *buffer, uint32_t blen); | ||
84 | |||
85 | /* encrypts plain of length length to encrypted of length + 16 using the | ||
86 | public key(32 bytes) of the receiver and the secret key of the sender and a 24 byte nonce | ||
87 | return -1 if there was a problem. | ||
88 | return length of encrypted data if everything was fine. */ | ||
89 | int encrypt_data(uint8_t *public_key, uint8_t *secret_key, uint8_t *nonce, | ||
90 | uint8_t *plain, uint32_t length, uint8_t *encrypted); | ||
91 | |||
92 | |||
93 | /* decrypts encrypted of length length to plain of length length - 16 using the | ||
94 | public key(32 bytes) of the sender, the secret key of the receiver and a 24 byte nonce | ||
95 | return -1 if there was a problem(decryption failed) | ||
96 | return length of plain data if everything was fine. */ | ||
97 | int decrypt_data(uint8_t *public_key, uint8_t *secret_key, uint8_t *nonce, | ||
98 | uint8_t *encrypted, uint32_t length, uint8_t *plain); | ||
99 | |||
100 | /* Fast encrypt/decrypt operations. Use if this is not a one-time communication. | ||
101 | encrypt_precompute does the shared-key generation once so it does not have | ||
102 | to be preformed on every encrypt/decrypt. */ | ||
103 | void encrypt_precompute(uint8_t *public_key, uint8_t *secret_key, uint8_t *enc_key); | ||
104 | |||
105 | /* Fast encrypt. Depends on enc_key from encrypt_precompute. */ | ||
106 | int encrypt_data_fast(uint8_t *enc_key, uint8_t *nonce, | ||
107 | uint8_t *plain, uint32_t length, uint8_t *encrypted); | ||
108 | |||
109 | /* Fast decrypt. Depends on enc_ley from encrypt_precompute. */ | ||
110 | int decrypt_data_fast(uint8_t *enc_key, uint8_t *nonce, | ||
111 | uint8_t *encrypted, uint32_t length, uint8_t *plain); | ||
112 | |||
113 | |||
114 | /* fill the given nonce with random bytes. */ | ||
115 | void random_nonce(uint8_t *nonce); | ||
116 | |||
117 | /* return 0 if there is no received data in the buffer | ||
118 | return -1 if the packet was discarded. | ||
119 | return length of received data if successful */ | ||
120 | int read_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data); | ||
121 | |||
122 | /* return 0 if data could not be put in packet queue | ||
123 | return 1 if data was put into the queue */ | ||
124 | int write_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data, uint32_t length); | ||
125 | |||
126 | /* create a request to peer. | ||
127 | send_public_key and send_secret_key are the pub/secret keys of the sender | ||
128 | recv_public_key is public key of reciever | ||
129 | packet must be an array of MAX_DATA_SIZE big. | ||
130 | Data represents the data we send with the request with length being the length of the data. | ||
131 | request_id is the id of the request (32 = friend request, 254 = ping request) | ||
132 | returns -1 on failure | ||
133 | returns the length of the created packet on success */ | ||
134 | int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *packet, uint8_t *recv_public_key, | ||
135 | uint8_t *data, uint32_t length, uint8_t request_id); | ||
136 | |||
137 | |||
138 | /* Function to call when request beginning with byte is received */ | ||
139 | void cryptopacket_registerhandler(Net_Crypto *c, uint8_t byte, cryptopacket_handler_callback cb, void *object); | ||
140 | |||
141 | /* Start a secure connection with other peer who has public_key and ip_port | ||
142 | returns -1 if failure | ||
143 | returns crypt_connection_id of the initialized connection if everything went well. */ | ||
144 | int crypto_connect(Net_Crypto *c, uint8_t *public_key, IP_Port ip_port); | ||
145 | |||
146 | /* kill a crypto connection | ||
147 | return 0 if killed successfully | ||
148 | return 1 if there was a problem. */ | ||
149 | int crypto_kill(Net_Crypto *c, int crypt_connection_id); | ||
150 | |||
151 | /* handle an incoming connection | ||
152 | return -1 if no crypto inbound connection | ||
153 | return incoming connection id (Lossless_UDP one) if there is an incoming crypto connection | ||
154 | Put the public key of the peer in public_key, the secret_nonce from the handshake into secret_nonce | ||
155 | and the session public key for the connection in session_key | ||
156 | to accept it see: accept_crypto_inbound(...) | ||
157 | to refuse it just call kill_connection(...) on the connection id */ | ||
158 | int crypto_inbound(Net_Crypto *c, uint8_t *public_key, uint8_t *secret_nonce, uint8_t *session_key); | ||
159 | |||
160 | /* accept an incoming connection using the parameters provided by crypto_inbound | ||
161 | return -1 if not successful | ||
162 | returns the crypt_connection_id if successful */ | ||
163 | int accept_crypto_inbound(Net_Crypto *c, int connection_id, uint8_t *public_key, uint8_t *secret_nonce, | ||
164 | uint8_t *session_key); | ||
165 | |||
166 | /* return 0 if no connection, 1 we have sent a handshake, 2 if connexion is not confirmed yet | ||
167 | (we have received a handshake but no empty data packet), 3 if the connection is established. | ||
168 | 4 if the connection is timed out and waiting to be killed */ | ||
169 | int is_cryptoconnected(Net_Crypto *c, int crypt_connection_id); | ||
170 | |||
171 | |||
172 | /* Generate our public and private keys | ||
173 | Only call this function the first time the program starts. */ | ||
174 | void new_keys(Net_Crypto *c); | ||
175 | |||
176 | /* save the public and private keys to the keys array | ||
177 | Length must be crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES */ | ||
178 | void save_keys(Net_Crypto *c, uint8_t *keys); | ||
179 | |||
180 | /* load the public and private keys from the keys array | ||
181 | Length must be crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES */ | ||
182 | void load_keys(Net_Crypto *c, uint8_t *keys); | ||
183 | |||
184 | /* create new instance of Net_Crypto | ||
185 | sets all the global connection variables to their default values. */ | ||
186 | Net_Crypto *new_net_crypto(Networking_Core *net); | ||
187 | |||
188 | /* main loop */ | ||
189 | void do_net_crypto(Net_Crypto *c); | ||
190 | |||
191 | void kill_net_crypto(Net_Crypto *c); | ||
192 | |||
193 | /* Init the cryptopacket handling */ | ||
194 | void init_cryptopackets(void *dht); | ||
195 | |||
196 | #ifdef __cplusplus | ||
197 | } | ||
198 | #endif | ||
199 | |||
200 | #endif | ||
diff --git a/toxcore/network.c b/toxcore/network.c new file mode 100644 index 00000000..2bcf7d61 --- /dev/null +++ b/toxcore/network.c | |||
@@ -0,0 +1,218 @@ | |||
1 | /* network.h | ||
2 | * | ||
3 | * Functions for the core networking. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #include "network.h" | ||
25 | |||
26 | /* returns current UNIX time in microseconds (us). */ | ||
27 | uint64_t current_time(void) | ||
28 | { | ||
29 | uint64_t time; | ||
30 | #ifdef WIN32 | ||
31 | /* This probably works fine */ | ||
32 | FILETIME ft; | ||
33 | GetSystemTimeAsFileTime(&ft); | ||
34 | time = ft.dwHighDateTime; | ||
35 | time <<= 32; | ||
36 | time |= ft.dwLowDateTime; | ||
37 | time -= 116444736000000000UL; | ||
38 | return time / 10; | ||
39 | #else | ||
40 | struct timeval a; | ||
41 | gettimeofday(&a, NULL); | ||
42 | time = 1000000UL * a.tv_sec + a.tv_usec; | ||
43 | return time; | ||
44 | #endif | ||
45 | } | ||
46 | |||
47 | /* return a random number | ||
48 | NOTE: this function should probably not be used where cryptographic randomness is absolutely necessary */ | ||
49 | uint32_t random_int(void) | ||
50 | { | ||
51 | #ifndef VANILLA_NACL | ||
52 | //NOTE: this function comes from libsodium | ||
53 | return randombytes_random(); | ||
54 | #else | ||
55 | return random(); | ||
56 | #endif | ||
57 | } | ||
58 | |||
59 | /* Basic network functions: | ||
60 | Function to send packet(data) of length length to ip_port */ | ||
61 | int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length) | ||
62 | { | ||
63 | ADDR addr = {AF_INET, ip_port.port, ip_port.ip}; | ||
64 | return sendto(sock, (char *) data, length, 0, (struct sockaddr *)&addr, sizeof(addr)); | ||
65 | } | ||
66 | |||
67 | /* Function to receive data, ip and port of sender is put into ip_port | ||
68 | the packet data into data | ||
69 | the packet length into length. | ||
70 | dump all empty packets. */ | ||
71 | static int receivepacket(int sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) | ||
72 | { | ||
73 | ADDR addr; | ||
74 | #ifdef WIN32 | ||
75 | int addrlen = sizeof(addr); | ||
76 | #else | ||
77 | uint32_t addrlen = sizeof(addr); | ||
78 | #endif | ||
79 | (*(int32_t *)length) = recvfrom(sock, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen); | ||
80 | |||
81 | if (*(int32_t *)length <= 0) | ||
82 | return -1; /* nothing received or empty packet */ | ||
83 | |||
84 | ip_port->ip = addr.ip; | ||
85 | ip_port->port = addr.port; | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | void networking_registerhandler(Networking_Core *net, uint8_t byte, packet_handler_callback cb, void *object) | ||
90 | { | ||
91 | net->packethandlers[byte].function = cb; | ||
92 | net->packethandlers[byte].object = object; | ||
93 | } | ||
94 | |||
95 | void networking_poll(Networking_Core *net) | ||
96 | { | ||
97 | IP_Port ip_port; | ||
98 | uint8_t data[MAX_UDP_PACKET_SIZE]; | ||
99 | uint32_t length; | ||
100 | |||
101 | while (receivepacket(net->sock, &ip_port, data, &length) != -1) { | ||
102 | if (length < 1) continue; | ||
103 | |||
104 | if (!(net->packethandlers[data[0]].function)) continue; | ||
105 | |||
106 | net->packethandlers[data[0]].function(net->packethandlers[data[0]].object, ip_port, data, length); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | uint8_t at_startup_ran; | ||
111 | static int at_startup(void) | ||
112 | { | ||
113 | if (at_startup_ran != 0) | ||
114 | return 0; | ||
115 | |||
116 | #ifdef WIN32 | ||
117 | WSADATA wsaData; | ||
118 | |||
119 | if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) | ||
120 | return -1; | ||
121 | |||
122 | #else | ||
123 | srandom((uint32_t)current_time()); | ||
124 | #endif | ||
125 | srand((uint32_t)current_time()); | ||
126 | at_startup_ran = 1; | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | /* TODO: put this somewhere | ||
131 | static void at_shutdown(void) | ||
132 | { | ||
133 | #ifdef WIN32 | ||
134 | WSACleanup(); | ||
135 | #endif | ||
136 | } | ||
137 | */ | ||
138 | |||
139 | /* initialize networking | ||
140 | bind to ip and port | ||
141 | ip must be in network order EX: 127.0.0.1 = (7F000001) | ||
142 | port is in host byte order (this means don't worry about it) | ||
143 | returns Networking_Core object if no problems | ||
144 | returns NULL if there are problems */ | ||
145 | Networking_Core *new_networking(IP ip, uint16_t port) | ||
146 | { | ||
147 | if (at_startup() != 0) | ||
148 | return NULL; | ||
149 | |||
150 | /* initialize our socket */ | ||
151 | Networking_Core *temp = calloc(1, sizeof(Networking_Core)); | ||
152 | |||
153 | if (temp == NULL) | ||
154 | return NULL; | ||
155 | |||
156 | temp->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
157 | |||
158 | /* Check for socket error */ | ||
159 | #ifdef WIN32 | ||
160 | |||
161 | if (temp->sock == INVALID_SOCKET) { /* MSDN recommends this */ | ||
162 | free(temp); | ||
163 | return NULL; | ||
164 | } | ||
165 | |||
166 | #else | ||
167 | |||
168 | if (temp->sock < 0) { | ||
169 | free(temp); | ||
170 | return NULL; | ||
171 | } | ||
172 | |||
173 | #endif | ||
174 | |||
175 | /* Functions to increase the size of the send and receive UDP buffers | ||
176 | NOTE: uncomment if necessary */ | ||
177 | /* | ||
178 | int n = 1024 * 1024 * 2; | ||
179 | if(setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&n, sizeof(n)) == -1) | ||
180 | { | ||
181 | return -1; | ||
182 | } | ||
183 | |||
184 | if(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&n, sizeof(n)) == -1) | ||
185 | return -1; | ||
186 | */ | ||
187 | |||
188 | /* Enable broadcast on socket */ | ||
189 | int broadcast = 1; | ||
190 | setsockopt(temp->sock, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)); | ||
191 | |||
192 | /* Set socket nonblocking */ | ||
193 | #ifdef WIN32 | ||
194 | /* I think this works for windows */ | ||
195 | u_long mode = 1; | ||
196 | /* ioctl(sock, FIONBIO, &mode); */ | ||
197 | ioctlsocket(temp->sock, FIONBIO, &mode); | ||
198 | #else | ||
199 | fcntl(temp->sock, F_SETFL, O_NONBLOCK, 1); | ||
200 | #endif | ||
201 | |||
202 | /* Bind our socket to port PORT and address 0.0.0.0 */ | ||
203 | ADDR addr = {AF_INET, htons(port), ip}; | ||
204 | bind(temp->sock, (struct sockaddr *)&addr, sizeof(addr)); | ||
205 | return temp; | ||
206 | } | ||
207 | |||
208 | /* function to cleanup networking stuff */ | ||
209 | void kill_networking(Networking_Core *net) | ||
210 | { | ||
211 | #ifdef WIN32 | ||
212 | closesocket(net->sock); | ||
213 | #else | ||
214 | close(net->sock); | ||
215 | #endif | ||
216 | free(net); | ||
217 | return; | ||
218 | } | ||
diff --git a/toxcore/network.h b/toxcore/network.h new file mode 100644 index 00000000..3547f79b --- /dev/null +++ b/toxcore/network.h | |||
@@ -0,0 +1,159 @@ | |||
1 | /* network.h | ||
2 | * | ||
3 | * Datatypes, functions and includes for the core networking. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #ifndef NETWORK_H | ||
25 | #define NETWORK_H | ||
26 | |||
27 | #include <stdlib.h> | ||
28 | #include <stdio.h> | ||
29 | #include <stdint.h> | ||
30 | #include <string.h> | ||
31 | #include <time.h> | ||
32 | |||
33 | #ifdef WIN32 /* Put win32 includes here */ | ||
34 | #ifndef WINVER | ||
35 | //Windows XP | ||
36 | #define WINVER 0x0501 | ||
37 | #endif | ||
38 | #include <winsock2.h> | ||
39 | #include <windows.h> | ||
40 | #include <ws2tcpip.h> | ||
41 | |||
42 | #undef VANILLA_NACL /* make sure on windows we use libsodium */ | ||
43 | |||
44 | #else //Linux includes | ||
45 | |||
46 | #include <fcntl.h> | ||
47 | #include <sys/socket.h> | ||
48 | #include <netinet/in.h> | ||
49 | #include <errno.h> | ||
50 | #include <sys/time.h> | ||
51 | #include <sys/types.h> | ||
52 | #include <netdb.h> | ||
53 | #include <unistd.h> | ||
54 | |||
55 | #endif | ||
56 | |||
57 | #ifndef VANILLA_NACL | ||
58 | /* we use libsodium by default */ | ||
59 | #include <sodium.h> | ||
60 | #else | ||
61 | #include <crypto_box.h> | ||
62 | #define crypto_box_MACBYTES (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES) | ||
63 | #endif | ||
64 | |||
65 | #ifdef __cplusplus | ||
66 | extern "C" { | ||
67 | #endif | ||
68 | |||
69 | #define MAX_UDP_PACKET_SIZE 65507 | ||
70 | |||
71 | #define NET_PACKET_PING_REQUEST 0 /* Ping request packet ID */ | ||
72 | #define NET_PACKET_PING_RESPONSE 1 /* Ping response packet ID */ | ||
73 | #define NET_PACKET_GET_NODES 2 /* Get nodes request packet ID */ | ||
74 | #define NET_PACKET_SEND_NODES 3 /* Send nodes response packet ID */ | ||
75 | #define NET_PACKET_HANDSHAKE 16 /* Handshake packet ID */ | ||
76 | #define NET_PACKET_SYNC 17 /* SYNC packet ID */ | ||
77 | #define NET_PACKET_DATA 18 /* Data packet ID */ | ||
78 | #define NET_PACKET_CRYPTO 32 /* Encrypted data packet ID */ | ||
79 | #define NET_PACKET_LAN_DISCOVERY 33 /* LAN discovery packet ID */ | ||
80 | |||
81 | |||
82 | /* Current time, unix format */ | ||
83 | #define unix_time() ((uint64_t)time(NULL)) | ||
84 | |||
85 | |||
86 | typedef union { | ||
87 | uint8_t c[4]; | ||
88 | uint16_t s[2]; | ||
89 | uint32_t i; | ||
90 | } IP; | ||
91 | |||
92 | typedef struct { | ||
93 | IP ip; | ||
94 | uint16_t port; | ||
95 | /* not used for anything right now */ | ||
96 | uint16_t padding; | ||
97 | } IP_Port; | ||
98 | |||
99 | typedef struct { | ||
100 | int16_t family; | ||
101 | uint16_t port; | ||
102 | IP ip; | ||
103 | uint8_t zeroes[8]; | ||
104 | #ifdef ENABLE_IPV6 | ||
105 | uint8_t zeroes2[12]; | ||
106 | #endif | ||
107 | } ADDR; | ||
108 | |||
109 | /* Function to receive data, ip and port of sender is put into ip_port | ||
110 | the packet data into data | ||
111 | the packet length into length. */ | ||
112 | typedef int (*packet_handler_callback)(void *object, IP_Port ip_port, uint8_t *data, uint32_t len); | ||
113 | |||
114 | typedef struct { | ||
115 | packet_handler_callback function; | ||
116 | void *object; | ||
117 | } Packet_Handles; | ||
118 | |||
119 | typedef struct { | ||
120 | Packet_Handles packethandlers[256]; | ||
121 | /* our UDP socket */ | ||
122 | int sock; | ||
123 | } Networking_Core; | ||
124 | |||
125 | /* returns current time in milleseconds since the epoch. */ | ||
126 | uint64_t current_time(void); | ||
127 | |||
128 | /* return a random number | ||
129 | NOTE: this function should probably not be used where cryptographic randomness is absolutely necessary */ | ||
130 | uint32_t random_int(void); | ||
131 | |||
132 | /* Basic network functions: */ | ||
133 | |||
134 | /* Function to send packet(data) of length length to ip_port */ | ||
135 | int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length); | ||
136 | |||
137 | /* Function to call when packet beginning with byte is received */ | ||
138 | void networking_registerhandler(Networking_Core *net, uint8_t byte, packet_handler_callback cb, void *object); | ||
139 | |||
140 | /* call this several times a second */ | ||
141 | void networking_poll(Networking_Core *net); | ||
142 | |||
143 | /* initialize networking | ||
144 | bind to ip and port | ||
145 | ip must be in network order EX: 127.0.0.1 = (7F000001) | ||
146 | port is in host byte order (this means don't worry about it) | ||
147 | returns 0 if no problems | ||
148 | returns -1 if there were problems */ | ||
149 | Networking_Core *new_networking(IP ip, uint16_t port); | ||
150 | |||
151 | /* function to cleanup networking stuff(doesn't do much right now) */ | ||
152 | void kill_networking(Networking_Core *net); | ||
153 | |||
154 | |||
155 | #ifdef __cplusplus | ||
156 | } | ||
157 | #endif | ||
158 | |||
159 | #endif | ||
diff --git a/toxcore/packets.h b/toxcore/packets.h new file mode 100644 index 00000000..4f410fb3 --- /dev/null +++ b/toxcore/packets.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * packet.h -- Packet structure | ||
3 | * | ||
4 | * This file is donated to the Tox Project. | ||
5 | * Copyright 2013 plutooo | ||
6 | */ | ||
7 | |||
8 | typedef struct { | ||
9 | uint8_t id[CLIENT_ID_SIZE]; | ||
10 | |||
11 | } __attribute__((packed)) clientid_t; | ||
12 | |||
13 | // Ping packet | ||
14 | typedef struct { | ||
15 | uint8_t packet_id; | ||
16 | clientid_t client_id; | ||
17 | uint8_t nonce[crypto_box_NONCEBYTES]; | ||
18 | uint64_t ping_id; | ||
19 | uint8_t padding[ENCRYPTION_PADDING]; | ||
20 | |||
21 | } __attribute__((packed)) pingreq_t; | ||
22 | |||
23 | // Pong packet | ||
24 | typedef struct { | ||
25 | uint8_t packet_id; | ||
26 | clientid_t client_id; | ||
27 | uint8_t nonce[crypto_box_NONCEBYTES]; | ||
28 | uint64_t ping_id; | ||
29 | uint8_t padding[ENCRYPTION_PADDING]; | ||
30 | |||
31 | } __attribute__((packed)) pingres_t; | ||
diff --git a/toxcore/ping.c b/toxcore/ping.c new file mode 100644 index 00000000..55d4d261 --- /dev/null +++ b/toxcore/ping.c | |||
@@ -0,0 +1,229 @@ | |||
1 | /* | ||
2 | * ping.c -- Buffered pinging using cyclic arrays. | ||
3 | * | ||
4 | * This file is donated to the Tox Project. | ||
5 | * Copyright 2013 plutooo | ||
6 | */ | ||
7 | |||
8 | #include <stdbool.h> | ||
9 | #include <stdint.h> | ||
10 | |||
11 | #include "DHT.h" | ||
12 | #include "net_crypto.h" | ||
13 | #include "packets.h" | ||
14 | #include "network.h" | ||
15 | #include "util.h" | ||
16 | |||
17 | #define PING_NUM_MAX 256 | ||
18 | #define PING_TIMEOUT 5 // 5s | ||
19 | |||
20 | typedef struct { | ||
21 | IP_Port ipp; | ||
22 | uint64_t id; | ||
23 | uint64_t timestamp; | ||
24 | } pinged_t; | ||
25 | |||
26 | typedef struct { | ||
27 | pinged_t pings[PING_NUM_MAX]; | ||
28 | size_t num_pings; | ||
29 | size_t pos_pings; | ||
30 | } PING; | ||
31 | |||
32 | void *new_ping(void) | ||
33 | { | ||
34 | return calloc(1, sizeof(PING)); | ||
35 | } | ||
36 | |||
37 | void kill_ping(void *ping) | ||
38 | { | ||
39 | free(ping); | ||
40 | } | ||
41 | |||
42 | static bool is_timeout(uint64_t time) | ||
43 | { | ||
44 | return (time + PING_TIMEOUT) < now(); | ||
45 | } | ||
46 | |||
47 | static void remove_timeouts(void *ping) // O(n) | ||
48 | { | ||
49 | PING *png = ping; | ||
50 | size_t i, id; | ||
51 | size_t new_pos = png->pos_pings; | ||
52 | size_t new_num = png->num_pings; | ||
53 | |||
54 | // Loop through buffer, oldest first | ||
55 | for (i = 0; i < png->num_pings; i++) { | ||
56 | id = (png->pos_pings + i) % PING_NUM_MAX; | ||
57 | |||
58 | if (is_timeout(png->pings[id].timestamp)) { | ||
59 | new_pos++; | ||
60 | new_num--; | ||
61 | } | ||
62 | // Break here because list is sorted. | ||
63 | else { | ||
64 | break; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | png->num_pings = new_num; | ||
69 | png->pos_pings = new_pos % PING_NUM_MAX; | ||
70 | } | ||
71 | |||
72 | uint64_t add_ping(void *ping, IP_Port ipp) // O(n) | ||
73 | { | ||
74 | PING *png = ping; | ||
75 | size_t p; | ||
76 | |||
77 | remove_timeouts(ping); | ||
78 | |||
79 | // Remove oldest ping if full buffer | ||
80 | if (png->num_pings == PING_NUM_MAX) { | ||
81 | png->num_pings--; | ||
82 | png->pos_pings = (png->pos_pings + 1) % PING_NUM_MAX; | ||
83 | } | ||
84 | |||
85 | // Insert new ping at end of list | ||
86 | p = (png->pos_pings + png->num_pings) % PING_NUM_MAX; | ||
87 | |||
88 | png->pings[p].ipp = ipp; | ||
89 | png->pings[p].timestamp = now(); | ||
90 | png->pings[p].id = random_64b(); | ||
91 | |||
92 | png->num_pings++; | ||
93 | return png->pings[p].id; | ||
94 | } | ||
95 | |||
96 | bool is_pinging(void *ping, IP_Port ipp, uint64_t ping_id) // O(n) TODO: replace this with something else. | ||
97 | { | ||
98 | PING *png = ping; | ||
99 | |||
100 | if (ipp.ip.i == 0 && ping_id == 0) | ||
101 | return false; | ||
102 | |||
103 | size_t i, id; | ||
104 | |||
105 | remove_timeouts(ping); | ||
106 | |||
107 | for (i = 0; i < png->num_pings; i++) { | ||
108 | id = (png->pos_pings + i) % PING_NUM_MAX; | ||
109 | |||
110 | // ping_id = 0 means match any id | ||
111 | if ((ipp_eq(png->pings[id].ipp, ipp) || ipp.ip.i == 0) && (png->pings[id].id == ping_id || ping_id == 0)) { | ||
112 | return true; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | return false; | ||
117 | } | ||
118 | |||
119 | int send_ping_request(void *ping, Net_Crypto *c, IP_Port ipp, clientid_t *client_id) | ||
120 | { | ||
121 | pingreq_t pk; | ||
122 | int rc; | ||
123 | uint64_t ping_id; | ||
124 | |||
125 | if (is_pinging(ping, ipp, 0) || id_eq(client_id, (clientid_t *)c->self_public_key)) | ||
126 | return 1; | ||
127 | |||
128 | // Generate random ping_id | ||
129 | ping_id = add_ping(ping, ipp); | ||
130 | |||
131 | pk.packet_id = NET_PACKET_PING_REQUEST; | ||
132 | id_cpy(&pk.client_id, (clientid_t *)c->self_public_key); // Our pubkey | ||
133 | random_nonce((uint8_t *) &pk.nonce); // Generate random nonce | ||
134 | |||
135 | // Encrypt ping_id using recipient privkey | ||
136 | rc = encrypt_data((uint8_t *) client_id, | ||
137 | c->self_secret_key, | ||
138 | (uint8_t *) &pk.nonce, | ||
139 | (uint8_t *) &ping_id, sizeof(ping_id), | ||
140 | (uint8_t *) &pk.ping_id); | ||
141 | |||
142 | if (rc != sizeof(ping_id) + ENCRYPTION_PADDING) | ||
143 | return 1; | ||
144 | |||
145 | return sendpacket(c->lossless_udp->net->sock, ipp, (uint8_t *) &pk, sizeof(pk)); | ||
146 | } | ||
147 | |||
148 | int send_ping_response(Net_Crypto *c, IP_Port ipp, clientid_t *client_id, uint64_t ping_id) | ||
149 | { | ||
150 | pingres_t pk; | ||
151 | int rc; | ||
152 | |||
153 | if (id_eq(client_id, (clientid_t *)c->self_public_key)) | ||
154 | return 1; | ||
155 | |||
156 | pk.packet_id = NET_PACKET_PING_RESPONSE; | ||
157 | id_cpy(&pk.client_id, (clientid_t *)c->self_public_key); // Our pubkey | ||
158 | random_nonce((uint8_t *) &pk.nonce); // Generate random nonce | ||
159 | |||
160 | // Encrypt ping_id using recipient privkey | ||
161 | rc = encrypt_data((uint8_t *) client_id, | ||
162 | c->self_secret_key, | ||
163 | (uint8_t *) &pk.nonce, | ||
164 | (uint8_t *) &ping_id, sizeof(ping_id), | ||
165 | (uint8_t *) &pk.ping_id); | ||
166 | |||
167 | if (rc != sizeof(ping_id) + ENCRYPTION_PADDING) | ||
168 | return 1; | ||
169 | |||
170 | return sendpacket(c->lossless_udp->net->sock, ipp, (uint8_t *) &pk, sizeof(pk)); | ||
171 | } | ||
172 | |||
173 | int handle_ping_request(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
174 | { | ||
175 | DHT *dht = object; | ||
176 | pingreq_t *p = (pingreq_t *) packet; | ||
177 | int rc; | ||
178 | uint64_t ping_id; | ||
179 | |||
180 | if (length != sizeof(pingreq_t) || id_eq(&p->client_id, (clientid_t *)dht->c->self_public_key)) | ||
181 | return 1; | ||
182 | |||
183 | // Decrypt ping_id | ||
184 | rc = decrypt_data((uint8_t *) &p->client_id, | ||
185 | dht->c->self_secret_key, | ||
186 | (uint8_t *) &p->nonce, | ||
187 | (uint8_t *) &p->ping_id, | ||
188 | sizeof(ping_id) + ENCRYPTION_PADDING, | ||
189 | (uint8_t *) &ping_id); | ||
190 | |||
191 | if (rc != sizeof(ping_id)) | ||
192 | return 1; | ||
193 | |||
194 | // Send response | ||
195 | send_ping_response(dht->c, source, &p->client_id, ping_id); | ||
196 | add_toping(dht, (uint8_t *) &p->client_id, source); | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | int handle_ping_response(void *object, IP_Port source, uint8_t *packet, uint32_t length) | ||
202 | { | ||
203 | DHT *dht = object; | ||
204 | pingres_t *p = (pingres_t *) packet; | ||
205 | int rc; | ||
206 | uint64_t ping_id; | ||
207 | |||
208 | if (length != sizeof(pingres_t) || id_eq(&p->client_id, (clientid_t *)dht->c->self_public_key)) | ||
209 | return 1; | ||
210 | |||
211 | // Decrypt ping_id | ||
212 | rc = decrypt_data((uint8_t *) &p->client_id, | ||
213 | dht->c->self_secret_key, | ||
214 | (uint8_t *) &p->nonce, | ||
215 | (uint8_t *) &p->ping_id, | ||
216 | sizeof(ping_id) + ENCRYPTION_PADDING, | ||
217 | (uint8_t *) &ping_id); | ||
218 | |||
219 | if (rc != sizeof(ping_id)) | ||
220 | return 1; | ||
221 | |||
222 | // Make sure ping_id is correct | ||
223 | if (!is_pinging(dht->ping, source, ping_id)) | ||
224 | return 1; | ||
225 | |||
226 | // Associate source ip with client_id | ||
227 | addto_lists(dht, source, (uint8_t *) &p->client_id); | ||
228 | return 0; | ||
229 | } | ||
diff --git a/toxcore/ping.h b/toxcore/ping.h new file mode 100644 index 00000000..c04ec80e --- /dev/null +++ b/toxcore/ping.h | |||
@@ -0,0 +1,17 @@ | |||
1 | /* | ||
2 | * ping.h -- Buffered pinging using cyclic arrays. | ||
3 | * | ||
4 | * This file is donated to the Tox Project. | ||
5 | * Copyright 2013 plutooo | ||
6 | */ | ||
7 | |||
8 | #include <stdbool.h> | ||
9 | |||
10 | void *new_ping(void); | ||
11 | void kill_ping(void *ping); | ||
12 | uint64_t add_ping(void *ping, IP_Port ipp); | ||
13 | bool is_pinging(void *ping, IP_Port ipp, uint64_t ping_id); | ||
14 | int send_ping_request(void *ping, Net_Crypto *c, IP_Port ipp, clientid_t *client_id); | ||
15 | int send_ping_response(Net_Crypto *c, IP_Port ipp, clientid_t *client_id, uint64_t ping_id); | ||
16 | int handle_ping_request(void *object, IP_Port source, uint8_t *packet, uint32_t length); | ||
17 | int handle_ping_response(void *object, IP_Port source, uint8_t *packet, uint32_t length); | ||
diff --git a/toxcore/tox.c b/toxcore/tox.c new file mode 100644 index 00000000..a97e52bc --- /dev/null +++ b/toxcore/tox.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* tox.c | ||
2 | * | ||
3 | * The Tox public API. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #include "Messenger.h" | ||
25 | /* | ||
26 | * returns a FRIEND_ADDRESS_SIZE byte address to give to others. | ||
27 | * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)] | ||
28 | * | ||
29 | */ | ||
30 | void tox_getaddress(void *tox, uint8_t *address) | ||
31 | { | ||
32 | Messenger *m = tox; | ||
33 | getaddress(m, address); | ||
34 | } | ||
35 | |||
36 | /* | ||
37 | * add a friend | ||
38 | * set the data that will be sent along with friend request | ||
39 | * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum. | ||
40 | * data is the data and length is the length | ||
41 | * returns the friend number if success | ||
42 | * return FA_TOOLONG if message length is too long | ||
43 | * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte) | ||
44 | * return FAERR_OWNKEY if user's own key | ||
45 | * return FAERR_ALREADYSENT if friend request already sent or already a friend | ||
46 | * return FAERR_UNKNOWN for unknown error | ||
47 | * return FAERR_BADCHECKSUM if bad checksum in address | ||
48 | * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different | ||
49 | * (the nospam for that friend was set to the new one) | ||
50 | * return FAERR_NOMEM if increasing the friend list size fails | ||
51 | */ | ||
52 | int tox_addfriend(void *tox, uint8_t *address, uint8_t *data, uint16_t length) | ||
53 | { | ||
54 | Messenger *m = tox; | ||
55 | return m_addfriend(m, address, data, length); | ||
56 | } | ||
57 | |||
58 | /* add a friend without sending a friendrequest. | ||
59 | returns the friend number if success | ||
60 | return -1 if failure. */ | ||
61 | int tox_addfriend_norequest(void *tox, uint8_t *client_id) | ||
62 | { | ||
63 | Messenger *m = tox; | ||
64 | return m_addfriend_norequest(m, client_id); | ||
65 | } | ||
66 | |||
67 | /* return the friend id associated to that client id. | ||
68 | return -1 if no such friend */ | ||
69 | int tox_getfriend_id(void *tox, uint8_t *client_id) | ||
70 | { | ||
71 | Messenger *m = tox; | ||
72 | return getfriend_id(m, client_id); | ||
73 | } | ||
74 | |||
75 | /* copies the public key associated to that friend id into client_id buffer. | ||
76 | make sure that client_id is of size CLIENT_ID_SIZE. | ||
77 | return 0 if success | ||
78 | return -1 if failure */ | ||
79 | int tox_getclient_id(void *tox, int friend_id, uint8_t *client_id) | ||
80 | { | ||
81 | Messenger *m = tox; | ||
82 | return getclient_id(m, friend_id, client_id); | ||
83 | } | ||
84 | |||
85 | /* remove a friend */ | ||
86 | int tox_delfriend(void *tox, int friendnumber) | ||
87 | { | ||
88 | Messenger *m = tox; | ||
89 | return m_delfriend(m, friendnumber); | ||
90 | } | ||
91 | |||
92 | /* return 4 if friend is online | ||
93 | return 3 if friend is confirmed | ||
94 | return 2 if the friend request was sent | ||
95 | return 1 if the friend was added | ||
96 | return 0 if there is no friend with that number */ | ||
97 | int tox_friendstatus(void *tox, int friendnumber) | ||
98 | { | ||
99 | Messenger *m = tox; | ||
100 | return m_friendstatus(m, friendnumber); | ||
101 | } | ||
102 | |||
103 | /* send a text chat message to an online friend | ||
104 | returns the message id if packet was successfully put into the send queue | ||
105 | return 0 if it was not | ||
106 | you will want to retain the return value, it will be passed to your read receipt callback | ||
107 | if one is received. | ||
108 | m_sendmessage_withid will send a message with the id of your choosing, | ||
109 | however we can generate an id for you by calling plain m_sendmessage. */ | ||
110 | uint32_t tox_sendmessage(void *tox, int friendnumber, uint8_t *message, uint32_t length) | ||
111 | { | ||
112 | Messenger *m = tox; | ||
113 | return m_sendmessage(m, friendnumber, message, length); | ||
114 | } | ||
115 | |||
116 | uint32_t tox_sendmessage_withid(void *tox, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length) | ||
117 | { | ||
118 | Messenger *m = tox; | ||
119 | return m_sendmessage_withid(m, friendnumber, theid, message, length); | ||
120 | } | ||
121 | |||
122 | /* send an action to an online friend | ||
123 | returns 1 if packet was successfully put into the send queue | ||
124 | return 0 if it was not */ | ||
125 | int tox_sendaction(void *tox, int friendnumber, uint8_t *action, uint32_t length) | ||
126 | { | ||
127 | Messenger *m = tox; | ||
128 | return m_sendaction(m, friendnumber, action, length); | ||
129 | } | ||
130 | |||
131 | /* Set our nickname | ||
132 | name must be a string of maximum MAX_NAME_LENGTH length. | ||
133 | length must be at least 1 byte | ||
134 | length is the length of name with the NULL terminator | ||
135 | return 0 if success | ||
136 | return -1 if failure */ | ||
137 | int tox_setname(void *tox, uint8_t *name, uint16_t length) | ||
138 | { | ||
139 | Messenger *m = tox; | ||
140 | return setname(m, name, length); | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | Get your nickname. | ||
145 | m The messanger context to use. | ||
146 | name Pointer to a string for the name. | ||
147 | nlen The length of the string buffer. | ||
148 | returns Return the length of the name, 0 on error. | ||
149 | */ | ||
150 | uint16_t tox_getselfname(void *tox, uint8_t *name, uint16_t nlen) | ||
151 | { | ||
152 | Messenger *m = tox; | ||
153 | return getself_name(m, name, nlen); | ||
154 | } | ||
155 | |||
156 | /* get name of friendnumber | ||
157 | put it in name | ||
158 | name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes. | ||
159 | return 0 if success | ||
160 | return -1 if failure */ | ||
161 | int tox_getname(void *tox, int friendnumber, uint8_t *name) | ||
162 | { | ||
163 | Messenger *m = tox; | ||
164 | return getname(m, friendnumber, name); | ||
165 | } | ||
166 | |||
167 | /* set our user status | ||
168 | you are responsible for freeing status after | ||
169 | returns 0 on success, -1 on failure */ | ||
170 | int tox_set_statusmessage(void *tox, uint8_t *status, uint16_t length) | ||
171 | { | ||
172 | Messenger *m = tox; | ||
173 | return m_set_statusmessage(m, status, length); | ||
174 | } | ||
175 | |||
176 | int tox_set_userstatus(void *tox, USERSTATUS status) | ||
177 | { | ||
178 | Messenger *m = tox; | ||
179 | return m_set_userstatus(m, status); | ||
180 | } | ||
181 | |||
182 | /* return the length of friendnumber's status message, | ||
183 | including null | ||
184 | pass it into malloc */ | ||
185 | int tox_get_statusmessage_size(void *tox, int friendnumber) | ||
186 | { | ||
187 | Messenger *m = tox; | ||
188 | return m_get_statusmessage_size(m, friendnumber); | ||
189 | } | ||
190 | |||
191 | /* copy friendnumber's status message into buf, truncating if size is over maxlen | ||
192 | get the size you need to allocate from m_get_statusmessage_size | ||
193 | The self variant will copy our own status message. */ | ||
194 | int tox_copy_statusmessage(void *tox, int friendnumber, uint8_t *buf, uint32_t maxlen) | ||
195 | { | ||
196 | Messenger *m = tox; | ||
197 | return m_copy_statusmessage(m, friendnumber, buf, maxlen); | ||
198 | } | ||
199 | |||
200 | int tox_copy_self_statusmessage(void *tox, uint8_t *buf, uint32_t maxlen) | ||
201 | { | ||
202 | Messenger *m = tox; | ||
203 | return m_copy_self_statusmessage(m, buf, maxlen); | ||
204 | } | ||
205 | |||
206 | /* Return one of USERSTATUS values. | ||
207 | * Values unknown to your application should be represented as USERSTATUS_NONE. | ||
208 | * As above, the self variant will return our own USERSTATUS. | ||
209 | * If friendnumber is invalid, this shall return USERSTATUS_INVALID. */ | ||
210 | USERSTATUS tox_get_userstatus(void *tox, int friendnumber) | ||
211 | { | ||
212 | Messenger *m = tox; | ||
213 | return m_get_userstatus(m, friendnumber); | ||
214 | } | ||
215 | |||
216 | USERSTATUS tox_get_selfuserstatus(void *tox) | ||
217 | { | ||
218 | Messenger *m = tox; | ||
219 | return m_get_self_userstatus(m); | ||
220 | } | ||
221 | |||
222 | |||
223 | /* Sets whether we send read receipts for friendnumber. | ||
224 | * This function is not lazy, and it will fail if yesno is not (0 or 1).*/ | ||
225 | void tox_set_sends_receipts(void *tox, int friendnumber, int yesno) | ||
226 | { | ||
227 | Messenger *m = tox; | ||
228 | m_set_sends_receipts(m, friendnumber, yesno); | ||
229 | } | ||
230 | |||
231 | |||
232 | /* set the function that will be executed when a friend request is received. | ||
233 | function format is function(uint8_t * public_key, uint8_t * data, uint16_t length) */ | ||
234 | void tox_callback_friendrequest(void *tox, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata) | ||
235 | { | ||
236 | Messenger *m = tox; | ||
237 | m_callback_friendrequest(m, function, userdata); | ||
238 | } | ||
239 | |||
240 | |||
241 | /* set the function that will be executed when a message from a friend is received. | ||
242 | function format is: function(int friendnumber, uint8_t * message, uint32_t length) */ | ||
243 | void tox_callback_friendmessage(void *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *), | ||
244 | void *userdata) | ||
245 | { | ||
246 | Messenger *m = tox; | ||
247 | m_callback_friendmessage(m, function, userdata); | ||
248 | } | ||
249 | |||
250 | /* set the function that will be executed when an action from a friend is received. | ||
251 | function format is: function(int friendnumber, uint8_t * action, uint32_t length) */ | ||
252 | void tox_callback_action(void *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *), void *userdata) | ||
253 | { | ||
254 | Messenger *m = tox; | ||
255 | m_callback_action(m, function, userdata); | ||
256 | } | ||
257 | |||
258 | /* set the callback for name changes | ||
259 | function(int friendnumber, uint8_t *newname, uint16_t length) | ||
260 | you are not responsible for freeing newname */ | ||
261 | void tox_callback_namechange(void *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *), | ||
262 | void *userdata) | ||
263 | { | ||
264 | Messenger *m = tox; | ||
265 | m_callback_namechange(m, function, userdata); | ||
266 | } | ||
267 | |||
268 | /* set the callback for status message changes | ||
269 | function(int friendnumber, uint8_t *newstatus, uint16_t length) | ||
270 | you are not responsible for freeing newstatus */ | ||
271 | void tox_callback_statusmessage(void *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *), | ||
272 | void *userdata) | ||
273 | { | ||
274 | Messenger *m = tox; | ||
275 | m_callback_statusmessage(m, function, userdata); | ||
276 | } | ||
277 | |||
278 | /* set the callback for status type changes | ||
279 | function(int friendnumber, USERSTATUS kind) */ | ||
280 | void tox_callback_userstatus(void *tox, void (*function)(Messenger *tox, int, USERSTATUS, void *), void *userdata) | ||
281 | { | ||
282 | Messenger *m = tox; | ||
283 | m_callback_userstatus(m, function, userdata); | ||
284 | } | ||
285 | |||
286 | /* set the callback for read receipts | ||
287 | function(int friendnumber, uint32_t receipt) | ||
288 | if you are keeping a record of returns from m_sendmessage, | ||
289 | receipt might be one of those values, and that means the message | ||
290 | has been received on the other side. since core doesn't | ||
291 | track ids for you, receipt may not correspond to any message | ||
292 | in that case, you should discard it. */ | ||
293 | void tox_callback_read_receipt(void *tox, void (*function)(Messenger *tox, int, uint32_t, void *), void *userdata) | ||
294 | { | ||
295 | Messenger *m = tox; | ||
296 | m_callback_read_receipt(m, function, userdata); | ||
297 | } | ||
298 | |||
299 | /* set the callback for connection status changes | ||
300 | function(int friendnumber, uint8_t status) | ||
301 | status: | ||
302 | 0 -- friend went offline after being previously online | ||
303 | 1 -- friend went online | ||
304 | note that this callback is not called when adding friends, thus the "after | ||
305 | being previously online" part. it's assumed that when adding friends, | ||
306 | their connection status is offline. */ | ||
307 | void tox_callback_connectionstatus(void *tox, void (*function)(Messenger *tox, int, uint8_t, void *), void *userdata) | ||
308 | { | ||
309 | Messenger *m = tox; | ||
310 | m_callback_connectionstatus(m, function, userdata); | ||
311 | } | ||
312 | |||
313 | /* Use this function to bootstrap the client | ||
314 | Sends a get nodes request to the given node with ip port and public_key */ | ||
315 | void tox_bootstrap(void *tox, IP_Port ip_port, uint8_t *public_key) | ||
316 | { | ||
317 | Messenger *m = tox; | ||
318 | DHT_bootstrap(m->dht, ip_port, public_key); | ||
319 | } | ||
320 | |||
321 | /* returns 0 if we are not connected to the DHT | ||
322 | returns 1 if we are */ | ||
323 | int tox_isconnected(void *tox) | ||
324 | { | ||
325 | Messenger *m = tox; | ||
326 | return DHT_isconnected(m->dht); | ||
327 | } | ||
328 | |||
329 | /* run this at startup | ||
330 | * returns allocated instance of tox on success | ||
331 | * returns 0 if there are problems */ | ||
332 | void *tox_new(void) | ||
333 | { | ||
334 | return initMessenger(); | ||
335 | } | ||
336 | |||
337 | /* run this before closing shop | ||
338 | * free all datastructures */ | ||
339 | void tox_kill(void *tox) | ||
340 | { | ||
341 | Messenger *m = tox; | ||
342 | cleanupMessenger(m); | ||
343 | } | ||
344 | |||
345 | /* the main loop that needs to be run at least 20 times per second */ | ||
346 | void tox_do(void *tox) | ||
347 | { | ||
348 | Messenger *m = tox; | ||
349 | doMessenger(m); | ||
350 | } | ||
351 | |||
352 | /* SAVING AND LOADING FUNCTIONS: */ | ||
353 | |||
354 | /* returns the size of the messenger data (for saving) */ | ||
355 | uint32_t tox_size(void *tox) | ||
356 | { | ||
357 | Messenger *m = tox; | ||
358 | return Messenger_size(m); | ||
359 | } | ||
360 | |||
361 | /* save the messenger in data (must be allocated memory of size Messenger_size()) */ | ||
362 | void tox_save(void *tox, uint8_t *data) | ||
363 | { | ||
364 | Messenger *m = tox; | ||
365 | Messenger_save(m, data); | ||
366 | } | ||
367 | |||
368 | /* load the messenger from data of size length */ | ||
369 | int tox_load(void *tox, uint8_t *data, uint32_t length) | ||
370 | { | ||
371 | Messenger *m = tox; | ||
372 | return Messenger_load(m, data, length); | ||
373 | } | ||
374 | |||
diff --git a/toxcore/tox.h b/toxcore/tox.h new file mode 100644 index 00000000..bdfac1d6 --- /dev/null +++ b/toxcore/tox.h | |||
@@ -0,0 +1,289 @@ | |||
1 | /* tox.h | ||
2 | * | ||
3 | * The Tox public API. | ||
4 | * | ||
5 | * Copyright (C) 2013 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 | #ifndef TOX_H | ||
25 | #define TOX_H | ||
26 | |||
27 | #include <stdint.h> | ||
28 | |||
29 | #ifdef __cplusplus | ||
30 | extern "C" { | ||
31 | #endif | ||
32 | |||
33 | #define TOX_MAX_NAME_LENGTH 128 | ||
34 | #define TOX_MAX_STATUSMESSAGE_LENGTH 128 | ||
35 | #define TOX_CLIENT_ID_SIZE 32 | ||
36 | |||
37 | #define TOX_FRIEND_ADDRESS_SIZE (TOX_CLIENT_ID_SIZE + sizeof(uint32_t) + sizeof(uint16_t)) | ||
38 | |||
39 | |||
40 | typedef union { | ||
41 | uint8_t c[4]; | ||
42 | uint16_t s[2]; | ||
43 | uint32_t i; | ||
44 | } tox_IP; | ||
45 | |||
46 | typedef struct { | ||
47 | tox_IP ip; | ||
48 | uint16_t port; | ||
49 | /* not used for anything right now */ | ||
50 | uint16_t padding; | ||
51 | } tox_IP_Port; | ||
52 | |||
53 | /* status definitions */ | ||
54 | enum { | ||
55 | TOX_NOFRIEND, | ||
56 | TOX_FRIEND_ADDED, | ||
57 | TOX_FRIEND_REQUESTED, | ||
58 | TOX_FRIEND_CONFIRMED, | ||
59 | TOX_FRIEND_ONLINE, | ||
60 | }; | ||
61 | |||
62 | /* errors for m_addfriend | ||
63 | * FAERR - Friend Add Error */ | ||
64 | enum { | ||
65 | TOX_FAERR_TOOLONG = -1, | ||
66 | TOX_FAERR_NOMESSAGE = -2, | ||
67 | TOX_FAERR_OWNKEY = -3, | ||
68 | TOX_FAERR_ALREADYSENT = -4, | ||
69 | TOX_FAERR_UNKNOWN = -5, | ||
70 | TOX_FAERR_BADCHECKSUM = -6, | ||
71 | TOX_FAERR_SETNEWNOSPAM = -7, | ||
72 | TOX_FAERR_NOMEM = -8 | ||
73 | }; | ||
74 | /* USERSTATUS | ||
75 | * Represents userstatuses someone can have. */ | ||
76 | |||
77 | typedef enum { | ||
78 | TOX_USERSTATUS_NONE, | ||
79 | TOX_USERSTATUS_AWAY, | ||
80 | TOX_USERSTATUS_BUSY, | ||
81 | TOX_USERSTATUS_INVALID | ||
82 | } | ||
83 | TOX_USERSTATUS; | ||
84 | |||
85 | typedef void Tox; | ||
86 | |||
87 | /* | ||
88 | * returns a FRIEND_ADDRESS_SIZE byte address to give to others. | ||
89 | * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)] | ||
90 | * | ||
91 | */ | ||
92 | void tox_getaddress(Tox *tox, uint8_t *address); | ||
93 | |||
94 | /* | ||
95 | * add a friend | ||
96 | * set the data that will be sent along with friend request | ||
97 | * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum. | ||
98 | * data is the data and length is the length | ||
99 | * returns the friend number if success | ||
100 | * return TOX_FA_TOOLONG if message length is too long | ||
101 | * return TOX_FAERR_NOMESSAGE if no message (message length must be >= 1 byte) | ||
102 | * return TOX_FAERR_OWNKEY if user's own key | ||
103 | * return TOX_FAERR_ALREADYSENT if friend request already sent or already a friend | ||
104 | * return TOX_FAERR_UNKNOWN for unknown error | ||
105 | * return TOX_FAERR_BADCHECKSUM if bad checksum in address | ||
106 | * return TOX_FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different | ||
107 | * (the nospam for that friend was set to the new one) | ||
108 | * return TOX_FAERR_NOMEM if increasing the friend list size fails | ||
109 | */ | ||
110 | int tox_addfriend(Tox *tox, uint8_t *address, uint8_t *data, uint16_t length); | ||
111 | |||
112 | |||
113 | /* add a friend without sending a friendrequest. | ||
114 | returns the friend number if success | ||
115 | return -1 if failure. */ | ||
116 | int tox_addfriend_norequest(Tox *tox, uint8_t *client_id); | ||
117 | |||
118 | /* return the friend id associated to that client id. | ||
119 | return -1 if no such friend */ | ||
120 | int tox_getfriend_id(Tox *tox, uint8_t *client_id); | ||
121 | |||
122 | /* copies the public key associated to that friend id into client_id buffer. | ||
123 | make sure that client_id is of size CLIENT_ID_SIZE. | ||
124 | return 0 if success | ||
125 | return -1 if failure */ | ||
126 | int tox_getclient_id(Tox *tox, int friend_id, uint8_t *client_id); | ||
127 | |||
128 | /* remove a friend */ | ||
129 | int tox_delfriend(Tox *tox, int friendnumber); | ||
130 | |||
131 | /* return TOX_FRIEND_ONLINE if friend is online | ||
132 | return TOX_FRIEND_CONFIRMED if friend is confirmed | ||
133 | return TOX_FRIEND_REQUESTED if the friend request was sent | ||
134 | return TOX_FRIEND_ADDED if the friend was added | ||
135 | return TOX_NOFRIEND if there is no friend with that number */ | ||
136 | int tox_friendstatus(Tox *tox, int friendnumber); | ||
137 | |||
138 | /* send a text chat message to an online friend | ||
139 | returns the message id if packet was successfully put into the send queue | ||
140 | return 0 if it was not | ||
141 | you will want to retain the return value, it will be passed to your read receipt callback | ||
142 | if one is received. | ||
143 | m_sendmessage_withid will send a message with the id of your choosing, | ||
144 | however we can generate an id for you by calling plain m_sendmessage. */ | ||
145 | uint32_t tox_sendmessage(Tox *tox, int friendnumber, uint8_t *message, uint32_t length); | ||
146 | uint32_t tox_sendmessage_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length); | ||
147 | |||
148 | /* send an action to an online friend | ||
149 | returns 1 if packet was successfully put into the send queue | ||
150 | return 0 if it was not */ | ||
151 | int tox_sendaction(Tox *tox, int friendnumber, uint8_t *action, uint32_t length); | ||
152 | |||
153 | /* Set our nickname | ||
154 | name must be a string of maximum MAX_NAME_LENGTH length. | ||
155 | length must be at least 1 byte | ||
156 | length is the length of name with the NULL terminator | ||
157 | return 0 if success | ||
158 | return -1 if failure */ | ||
159 | int tox_setname(Tox *tox, uint8_t *name, uint16_t length); | ||
160 | |||
161 | /* | ||
162 | Get your nickname. | ||
163 | m The messanger context to use. | ||
164 | name Pointer to a string for the name. | ||
165 | nlen The length of the string buffer. | ||
166 | returns Return the length of the name, 0 on error. | ||
167 | */ | ||
168 | uint16_t tox_getselfname(Tox *tox, uint8_t *name, uint16_t nlen); | ||
169 | |||
170 | /* get name of friendnumber | ||
171 | put it in name | ||
172 | name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes. | ||
173 | return 0 if success | ||
174 | return -1 if failure */ | ||
175 | int tox_getname(Tox *tox, int friendnumber, uint8_t *name); | ||
176 | |||
177 | /* set our user status | ||
178 | you are responsible for freeing status after | ||
179 | returns 0 on success, -1 on failure */ | ||
180 | int tox_set_statusmessage(Tox *tox, uint8_t *status, uint16_t length); | ||
181 | int tox_set_userstatus(Tox *tox, TOX_USERSTATUS status); | ||
182 | |||
183 | /* return the length of friendnumber's status message, | ||
184 | including null | ||
185 | pass it into malloc */ | ||
186 | int tox_get_statusmessage_size(Tox *tox, int friendnumber); | ||
187 | |||
188 | /* copy friendnumber's status message into buf, truncating if size is over maxlen | ||
189 | get the size you need to allocate from m_get_statusmessage_size | ||
190 | The self variant will copy our own status message. */ | ||
191 | int tox_copy_statusmessage(Tox *tox, int friendnumber, uint8_t *buf, uint32_t maxlen); | ||
192 | int tox_copy_self_statusmessage(Tox *tox, uint8_t *buf, uint32_t maxlen); | ||
193 | |||
194 | /* Return one of USERSTATUS values. | ||
195 | * Values unknown to your application should be represented as USERSTATUS_NONE. | ||
196 | * As above, the self variant will return our own USERSTATUS. | ||
197 | * If friendnumber is invalid, this shall return USERSTATUS_INVALID. */ | ||
198 | TOX_USERSTATUS tox_get_userstatus(Tox *tox, int friendnumber); | ||
199 | TOX_USERSTATUS tox_get_selfuserstatus(Tox *tox); | ||
200 | |||
201 | /* Sets whether we send read receipts for friendnumber. | ||
202 | * This function is not lazy, and it will fail if yesno is not (0 or 1).*/ | ||
203 | void tox_set_sends_receipts(Tox *tox, int friendnumber, int yesno); | ||
204 | |||
205 | /* set the function that will be executed when a friend request is received. | ||
206 | function format is function(uint8_t * public_key, uint8_t * data, uint16_t length) */ | ||
207 | void tox_callback_friendrequest(Tox *tox, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata); | ||
208 | |||
209 | /* set the function that will be executed when a message from a friend is received. | ||
210 | function format is: function(int friendnumber, uint8_t * message, uint32_t length) */ | ||
211 | void tox_callback_friendmessage(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *), | ||
212 | void *userdata); | ||
213 | |||
214 | /* set the function that will be executed when an action from a friend is received. | ||
215 | function format is: function(int friendnumber, uint8_t * action, uint32_t length) */ | ||
216 | void tox_callback_action(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *), void *userdata); | ||
217 | |||
218 | /* set the callback for name changes | ||
219 | function(int friendnumber, uint8_t *newname, uint16_t length) | ||
220 | you are not responsible for freeing newname */ | ||
221 | void tox_callback_namechange(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *), | ||
222 | void *userdata); | ||
223 | |||
224 | /* set the callback for status message changes | ||
225 | function(int friendnumber, uint8_t *newstatus, uint16_t length) | ||
226 | you are not responsible for freeing newstatus */ | ||
227 | void tox_callback_statusmessage(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *), | ||
228 | void *userdata); | ||
229 | |||
230 | /* set the callback for status type changes | ||
231 | function(int friendnumber, USERSTATUS kind) */ | ||
232 | void tox_callback_userstatus(Tox *tox, void (*function)(Tox *tox, int, TOX_USERSTATUS, void *), void *userdata); | ||
233 | |||
234 | /* set the callback for read receipts | ||
235 | function(int friendnumber, uint32_t receipt) | ||
236 | if you are keeping a record of returns from m_sendmessage, | ||
237 | receipt might be one of those values, and that means the message | ||
238 | has been received on the other side. since core doesn't | ||
239 | track ids for you, receipt may not correspond to any message | ||
240 | in that case, you should discard it. */ | ||
241 | void tox_callback_read_receipt(Tox *tox, void (*function)(Tox *tox, int, uint32_t, void *), void *userdata); | ||
242 | |||
243 | /* set the callback for connection status changes | ||
244 | function(int friendnumber, uint8_t status) | ||
245 | status: | ||
246 | 0 -- friend went offline after being previously online | ||
247 | 1 -- friend went online | ||
248 | note that this callback is not called when adding friends, thus the "after | ||
249 | being previously online" part. it's assumed that when adding friends, | ||
250 | their connection status is offline. */ | ||
251 | void tox_callback_connectionstatus(Tox *tox, void (*function)(Tox *tox, int, uint8_t, void *), void *userdata); | ||
252 | |||
253 | /* Use this function to bootstrap the client | ||
254 | Sends a get nodes request to the given node with ip port and public_key */ | ||
255 | void tox_bootstrap(Tox *tox, tox_IP_Port ip_port, uint8_t *public_key); | ||
256 | |||
257 | /* returns 0 if we are not connected to the DHT | ||
258 | returns 1 if we are */ | ||
259 | int tox_isconnected(Tox *tox); | ||
260 | |||
261 | /* run this at startup | ||
262 | * returns allocated instance of tox on success | ||
263 | * returns 0 if there are problems */ | ||
264 | Tox *tox_new(void); | ||
265 | |||
266 | /* run this before closing shop | ||
267 | * free all datastructures */ | ||
268 | void tox_kill(Tox *tox); | ||
269 | |||
270 | /* the main loop that needs to be run at least 20 times per second */ | ||
271 | void tox_do(Tox *tox); | ||
272 | |||
273 | /* SAVING AND LOADING FUNCTIONS: */ | ||
274 | |||
275 | /* returns the size of the messenger data (for saving) */ | ||
276 | uint32_t tox_size(Tox *tox); | ||
277 | |||
278 | /* save the messenger in data (must be allocated memory of size Messenger_size()) */ | ||
279 | void tox_save(Tox *tox, uint8_t *data); | ||
280 | |||
281 | /* load the messenger from data of size length */ | ||
282 | int tox_load(Tox *tox, uint8_t *data, uint32_t length); | ||
283 | |||
284 | |||
285 | #ifdef __cplusplus | ||
286 | } | ||
287 | #endif | ||
288 | |||
289 | #endif | ||
diff --git a/toxcore/util.c b/toxcore/util.c new file mode 100644 index 00000000..6f346db1 --- /dev/null +++ b/toxcore/util.c | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * util.c -- Utilities. | ||
3 | * | ||
4 | * This file is donated to the Tox Project. | ||
5 | * Copyright 2013 plutooo | ||
6 | */ | ||
7 | |||
8 | #include <time.h> | ||
9 | #include <stdint.h> | ||
10 | #include <stdbool.h> | ||
11 | |||
12 | #include "DHT.h" | ||
13 | #include "packets.h" | ||
14 | |||
15 | uint64_t now() | ||
16 | { | ||
17 | return time(NULL); | ||
18 | } | ||
19 | |||
20 | uint64_t random_64b() | ||
21 | { | ||
22 | uint64_t r; | ||
23 | |||
24 | // This is probably not random enough? | ||
25 | r = random_int(); | ||
26 | r <<= 32; | ||
27 | r |= random_int(); | ||
28 | |||
29 | return r; | ||
30 | } | ||
31 | |||
32 | bool ipp_eq(IP_Port a, IP_Port b) | ||
33 | { | ||
34 | return (a.ip.i == b.ip.i) && (a.port == b.port); | ||
35 | } | ||
36 | |||
37 | bool id_eq(clientid_t *dest, clientid_t *src) | ||
38 | { | ||
39 | return memcmp(dest, src, sizeof(clientid_t)) == 0; | ||
40 | } | ||
41 | |||
42 | void id_cpy(clientid_t *dest, clientid_t *src) | ||
43 | { | ||
44 | memcpy(dest, src, sizeof(clientid_t)); | ||
45 | } | ||
diff --git a/toxcore/util.h b/toxcore/util.h new file mode 100644 index 00000000..5209c2ca --- /dev/null +++ b/toxcore/util.h | |||
@@ -0,0 +1,12 @@ | |||
1 | /* | ||
2 | * util.h -- Utilities. | ||
3 | * | ||
4 | * This file is donated to the Tox Project. | ||
5 | * Copyright 2013 plutooo | ||
6 | */ | ||
7 | |||
8 | uint64_t now(); | ||
9 | uint64_t random_64b(); | ||
10 | bool ipp_eq(IP_Port a, IP_Port b); | ||
11 | bool id_eq(clientid_t *dest, clientid_t *src); | ||
12 | void id_cpy(clientid_t *dest, clientid_t *src); | ||