diff options
Diffstat (limited to 'toxcore')
-rw-r--r-- | toxcore/Messenger.c | 2 | ||||
-rw-r--r-- | toxcore/TCP_server.c | 391 | ||||
-rw-r--r-- | toxcore/TCP_server.h | 75 |
3 files changed, 467 insertions, 1 deletions
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index f4e497d0..9d5fe22a 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c | |||
@@ -2416,7 +2416,7 @@ static int friends_list_load(Messenger *m, uint8_t *data, uint32_t length) | |||
2416 | memcpy(address + crypto_box_PUBLICKEYBYTES, &(temp.friendrequest_nospam), sizeof(uint32_t)); | 2416 | memcpy(address + crypto_box_PUBLICKEYBYTES, &(temp.friendrequest_nospam), sizeof(uint32_t)); |
2417 | uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); | 2417 | uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum)); |
2418 | memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), &checksum, sizeof(checksum)); | 2418 | memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), &checksum, sizeof(checksum)); |
2419 | m_addfriend(m, address, temp.info, temp.info_size); | 2419 | m_addfriend(m, address, temp.info, ntohs(temp.info_size)); |
2420 | } | 2420 | } |
2421 | } | 2421 | } |
2422 | 2422 | ||
diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c new file mode 100644 index 00000000..df56f490 --- /dev/null +++ b/toxcore/TCP_server.c | |||
@@ -0,0 +1,391 @@ | |||
1 | /* | ||
2 | * TCP_server.c -- Implementation of the TCP relay server part of Tox. | ||
3 | * | ||
4 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
5 | * | ||
6 | * This file is part of Tox. | ||
7 | * | ||
8 | * Tox is free software: you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation, either version 3 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * Tox is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include "TCP_server.h" | ||
24 | |||
25 | #if !defined(_WIN32) && !defined(__WIN32__) && !defined (WIN32) | ||
26 | #include <sys/ioctl.h> | ||
27 | #endif | ||
28 | |||
29 | /* return 1 if valid | ||
30 | * return 0 if not valid | ||
31 | */ | ||
32 | static int sock_valid(sock_t sock) | ||
33 | { | ||
34 | #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) | ||
35 | |||
36 | if (sock == INVALID_SOCKET) { | ||
37 | #else | ||
38 | |||
39 | if (sock < 0) { | ||
40 | #endif | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | return 1; | ||
45 | } | ||
46 | |||
47 | static void kill_sock(sock_t sock) | ||
48 | { | ||
49 | #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) | ||
50 | closesocket(sock); | ||
51 | #else | ||
52 | close(sock); | ||
53 | #endif | ||
54 | } | ||
55 | |||
56 | /* return 1 on success | ||
57 | * return 0 on failure | ||
58 | */ | ||
59 | static int set_nonblock(sock_t sock) | ||
60 | { | ||
61 | #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) | ||
62 | u_long mode = 1; | ||
63 | return (ioctlsocket(sock, FIONBIO, &mode) == 0); | ||
64 | #else | ||
65 | return (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == 0); | ||
66 | #endif | ||
67 | } | ||
68 | |||
69 | /* return 1 on success | ||
70 | * return 0 on failure | ||
71 | */ | ||
72 | static int set_dualstack(sock_t sock) | ||
73 | { | ||
74 | char ipv6only = 0; | ||
75 | socklen_t optsize = sizeof(ipv6only); | ||
76 | int res = getsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &optsize); | ||
77 | |||
78 | if ((res == 0) && (ipv6only == 0)) | ||
79 | return 1; | ||
80 | |||
81 | ipv6only = 0; | ||
82 | return (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)) == 0); | ||
83 | } | ||
84 | |||
85 | /* return 1 on success | ||
86 | * return 0 on failure | ||
87 | */ | ||
88 | static int bind_to_port(sock_t sock, int family, uint16_t port) | ||
89 | { | ||
90 | struct sockaddr_storage addr = {0}; | ||
91 | size_t addrsize; | ||
92 | |||
93 | if (family == AF_INET) { | ||
94 | struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; | ||
95 | |||
96 | addrsize = sizeof(struct sockaddr_in); | ||
97 | addr4->sin_family = AF_INET; | ||
98 | addr4->sin_port = htons(port); | ||
99 | } else if (family == AF_INET6) { | ||
100 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; | ||
101 | |||
102 | addrsize = sizeof(struct sockaddr_in6); | ||
103 | addr6->sin6_family = AF_INET6; | ||
104 | addr6->sin6_port = htons(port); | ||
105 | } else { | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | return (bind(sock, (struct sockaddr *)&addr, addrsize) == 0); | ||
110 | } | ||
111 | |||
112 | /* return length on success | ||
113 | * return 0 if nothing has been read from socket. | ||
114 | * return ~0 on failure. | ||
115 | */ | ||
116 | static uint16_t read_length(sock_t sock) | ||
117 | { | ||
118 | int count; | ||
119 | ioctl(sock, FIONREAD, &count); | ||
120 | |||
121 | if ((unsigned int)count >= sizeof(uint16_t)) { | ||
122 | uint16_t length; | ||
123 | int len = recv(sock, &length, sizeof(uint16_t), 0); | ||
124 | |||
125 | if (len != sizeof(uint16_t)) { | ||
126 | fprintf(stderr, "FAIL recv packet\n"); | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | length = ntohs(length); | ||
131 | |||
132 | if (length > MAX_PACKET_SIZE) { | ||
133 | return ~0; | ||
134 | } | ||
135 | |||
136 | return length; | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /* return length on success | ||
143 | * return -1 on failure | ||
144 | */ | ||
145 | static int read_TCP_packet(sock_t sock, uint8_t *data, uint16_t length) | ||
146 | { | ||
147 | int count; | ||
148 | ioctl(sock, FIONREAD, &count); | ||
149 | |||
150 | if (count >= length) { | ||
151 | int len = recv(sock, data, length, 0); | ||
152 | |||
153 | if (len != length) { | ||
154 | fprintf(stderr, "FAIL recv packet\n"); | ||
155 | return -1; | ||
156 | } | ||
157 | |||
158 | return length; | ||
159 | } | ||
160 | |||
161 | return -1; | ||
162 | } | ||
163 | |||
164 | /* Kill a TCP_Secure_Connection | ||
165 | */ | ||
166 | static void kill_TCP_connection(TCP_Secure_Connection *con) | ||
167 | { | ||
168 | kill_sock(con->sock); | ||
169 | memset(con, 0, sizeof(TCP_Secure_Connection)); | ||
170 | } | ||
171 | |||
172 | /* return 1 if everything went well. | ||
173 | * return -1 if the connection must be killed. | ||
174 | */ | ||
175 | static int handle_TCP_handshake(TCP_Secure_Connection *con, uint8_t *data, uint16_t length, uint8_t *self_secret_key) | ||
176 | { | ||
177 | if (length != TCP_CLIENT_HANDSHAKE_SIZE) | ||
178 | return -1; | ||
179 | |||
180 | if (con->status != TCP_STATUS_CONNECTED) | ||
181 | return -1; | ||
182 | |||
183 | uint8_t shared_key[crypto_box_BEFORENMBYTES]; | ||
184 | encrypt_precompute(data, self_secret_key, shared_key); | ||
185 | uint8_t plain[TCP_HANDSHAKE_PLAIN_SIZE]; | ||
186 | int len = decrypt_data_fast(shared_key, data + crypto_box_PUBLICKEYBYTES, | ||
187 | data + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, TCP_HANDSHAKE_PLAIN_SIZE + crypto_box_MACBYTES, plain); | ||
188 | |||
189 | if (len != TCP_HANDSHAKE_PLAIN_SIZE) | ||
190 | return -1; | ||
191 | |||
192 | uint8_t temp_secret_key[crypto_box_SECRETKEYBYTES]; | ||
193 | uint8_t resp_plain[TCP_HANDSHAKE_PLAIN_SIZE]; | ||
194 | crypto_box_keypair(resp_plain, temp_secret_key); | ||
195 | random_nonce(con->sent_nonce); | ||
196 | memcpy(resp_plain + crypto_box_PUBLICKEYBYTES, con->sent_nonce, crypto_box_NONCEBYTES); | ||
197 | memcpy(con->recv_nonce, plain + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES); | ||
198 | |||
199 | uint8_t response[TCP_SERVER_HANDSHAKE_SIZE]; | ||
200 | new_nonce(response); | ||
201 | |||
202 | len = encrypt_data_fast(shared_key, response, resp_plain, TCP_HANDSHAKE_PLAIN_SIZE, response + crypto_box_NONCEBYTES); | ||
203 | |||
204 | if (len != TCP_HANDSHAKE_PLAIN_SIZE + crypto_box_MACBYTES) | ||
205 | return -1; | ||
206 | |||
207 | if (TCP_SERVER_HANDSHAKE_SIZE != send(con->sock, response, TCP_SERVER_HANDSHAKE_SIZE, 0)) | ||
208 | return -1; | ||
209 | |||
210 | encrypt_precompute(plain, temp_secret_key, con->shared_key); | ||
211 | con->status = TCP_STATUS_UNCONFIRMED; | ||
212 | return 1; | ||
213 | } | ||
214 | |||
215 | /* return 1 if connection handshake was handled correctly. | ||
216 | * return 0 if we didn't get it yet. | ||
217 | * return -1 if the connection must be killed. | ||
218 | */ | ||
219 | static int read_connection_handshake(TCP_Secure_Connection *con, uint8_t *self_secret_key) | ||
220 | { | ||
221 | uint8_t data[TCP_CLIENT_HANDSHAKE_SIZE]; | ||
222 | int len = 0; | ||
223 | |||
224 | if ((len = read_TCP_packet(con->sock, data, TCP_CLIENT_HANDSHAKE_SIZE)) != -1) { | ||
225 | return handle_TCP_handshake(con, data, len, self_secret_key); | ||
226 | } | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /* return 1 on success | ||
232 | * return 0 on failure | ||
233 | */ | ||
234 | static int accept_connection(TCP_Server *TCP_server, sock_t sock) | ||
235 | { | ||
236 | if (!sock_valid(sock)) | ||
237 | return 0; | ||
238 | |||
239 | if (!set_nonblock(sock)) { | ||
240 | kill_sock(sock); | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | TCP_Secure_Connection *conn = | ||
245 | &TCP_server->incomming_connection_queue[TCP_server->incomming_connection_queue_index % MAX_INCOMMING_CONNECTIONS]; | ||
246 | |||
247 | if (conn->status != TCP_STATUS_NO_STATUS) | ||
248 | kill_TCP_connection(conn); | ||
249 | |||
250 | conn->status = TCP_STATUS_CONNECTED; | ||
251 | conn->sock = sock; | ||
252 | conn->next_packet_length = 0; | ||
253 | |||
254 | ++TCP_server->incomming_connection_queue_index; | ||
255 | return 1; | ||
256 | } | ||
257 | |||
258 | static sock_t new_listening_TCP_socket(int family, uint16_t port) | ||
259 | { | ||
260 | sock_t sock = socket(family, SOCK_STREAM, IPPROTO_TCP); | ||
261 | |||
262 | if (!sock_valid(sock)) { | ||
263 | return ~0; | ||
264 | } | ||
265 | |||
266 | int ok = set_nonblock(sock); | ||
267 | |||
268 | if (ok && family == AF_INET6) { | ||
269 | ok = set_dualstack(sock); | ||
270 | } | ||
271 | |||
272 | ok = ok && bind_to_port(sock, family, port) && (listen(sock, TCP_MAX_BACKLOG) == 0); | ||
273 | |||
274 | if (!ok) { | ||
275 | kill_sock(sock); | ||
276 | return ~0; | ||
277 | } | ||
278 | |||
279 | return sock; | ||
280 | } | ||
281 | |||
282 | TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t *ports, uint8_t *public_key, | ||
283 | uint8_t *secret_key) | ||
284 | { | ||
285 | if (num_sockets == 0 || ports == NULL) | ||
286 | return NULL; | ||
287 | |||
288 | TCP_Server *temp = calloc(1, sizeof(TCP_Server)); | ||
289 | |||
290 | if (temp == NULL) | ||
291 | return NULL; | ||
292 | |||
293 | temp->socks_listening = calloc(num_sockets, sizeof(sock_t)); | ||
294 | |||
295 | if (temp->socks_listening == NULL) { | ||
296 | free(temp); | ||
297 | return NULL; | ||
298 | } | ||
299 | |||
300 | uint8_t family; | ||
301 | |||
302 | if (ipv6_enabled) { | ||
303 | family = AF_INET6; | ||
304 | } else { | ||
305 | family = AF_INET; | ||
306 | } | ||
307 | |||
308 | uint32_t i; | ||
309 | |||
310 | for (i = 0; i < num_sockets; ++i) { | ||
311 | sock_t sock = new_listening_TCP_socket(family, ports[i]); | ||
312 | |||
313 | if (sock_valid(sock)) { | ||
314 | temp->socks_listening[temp->num_listening_socks] = sock; | ||
315 | ++temp->num_listening_socks; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | memcpy(temp->public_key, public_key, crypto_box_PUBLICKEYBYTES); | ||
320 | memcpy(temp->secret_key, secret_key, crypto_box_SECRETKEYBYTES); | ||
321 | return temp; | ||
322 | } | ||
323 | |||
324 | static void do_TCP_accept_new(TCP_Server *TCP_server) | ||
325 | { | ||
326 | uint32_t i; | ||
327 | |||
328 | for (i = 0; i < TCP_server->num_listening_socks; ++i) { | ||
329 | struct sockaddr_storage addr; | ||
330 | unsigned int addrlen = sizeof(addr); | ||
331 | sock_t sock; | ||
332 | |||
333 | do { | ||
334 | sock = accept(TCP_server->socks_listening[i], (struct sockaddr *)&addr, &addrlen); | ||
335 | } while (accept_connection(TCP_server, sock)); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | static void do_TCP_incomming(TCP_Server *TCP_server) | ||
340 | { | ||
341 | uint32_t i; | ||
342 | |||
343 | for (i = 0; i < MAX_INCOMMING_CONNECTIONS; ++i) { | ||
344 | if (TCP_server->incomming_connection_queue[i].status != TCP_STATUS_CONNECTED) | ||
345 | continue; | ||
346 | |||
347 | int ret = read_connection_handshake(&TCP_server->incomming_connection_queue[i], TCP_server->secret_key); | ||
348 | |||
349 | if (ret == -1) { | ||
350 | kill_TCP_connection(&TCP_server->incomming_connection_queue[i]); | ||
351 | } else if (ret == 1) { | ||
352 | TCP_Secure_Connection *conn_old = &TCP_server->incomming_connection_queue[i]; | ||
353 | TCP_Secure_Connection *conn_new = | ||
354 | &TCP_server->unconfirmed_connection_queue[TCP_server->unconfirmed_connection_queue_index % MAX_INCOMMING_CONNECTIONS]; | ||
355 | |||
356 | if (conn_new->status != TCP_STATUS_NO_STATUS) | ||
357 | kill_TCP_connection(conn_new); | ||
358 | |||
359 | memcpy(conn_new, conn_old, sizeof(TCP_Secure_Connection)); | ||
360 | memset(conn_old, 0, sizeof(TCP_Secure_Connection)); | ||
361 | ++TCP_server->unconfirmed_connection_queue_index; | ||
362 | } | ||
363 | } | ||
364 | } | ||
365 | |||
366 | static void do_TCP_unconfirmed(TCP_Server *TCP_server) | ||
367 | { | ||
368 | uint32_t i; | ||
369 | |||
370 | for (i = 0; i < MAX_INCOMMING_CONNECTIONS; ++i) { | ||
371 | if (TCP_server->incomming_connection_queue[i].status != TCP_STATUS_CONNECTED) | ||
372 | continue; | ||
373 | } | ||
374 | } | ||
375 | void do_TCP_server(TCP_Server *TCP_server) | ||
376 | { | ||
377 | do_TCP_accept_new(TCP_server); | ||
378 | do_TCP_incomming(TCP_server); | ||
379 | } | ||
380 | |||
381 | void kill_TCP_server(TCP_Server *TCP_server) | ||
382 | { | ||
383 | uint32_t i; | ||
384 | |||
385 | for (i = 0; i < TCP_server->num_listening_socks; ++i) { | ||
386 | kill_sock(TCP_server->socks_listening[i]); | ||
387 | } | ||
388 | |||
389 | free(TCP_server->socks_listening); | ||
390 | free(TCP_server); | ||
391 | } | ||
diff --git a/toxcore/TCP_server.h b/toxcore/TCP_server.h new file mode 100644 index 00000000..89ccb50f --- /dev/null +++ b/toxcore/TCP_server.h | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * TCP_server.h -- Implementation of the TCP relay server part of Tox. | ||
3 | * | ||
4 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
5 | * | ||
6 | * This file is part of Tox. | ||
7 | * | ||
8 | * Tox is free software: you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation, either version 3 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * Tox is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include "net_crypto.h" | ||
24 | |||
25 | #define MAX_INCOMMING_CONNECTIONS 32 | ||
26 | |||
27 | #define TCP_MAX_BACKLOG MAX_INCOMMING_CONNECTIONS | ||
28 | |||
29 | #define MAX_PACKET_SIZE 8192 | ||
30 | |||
31 | #define TCP_HANDSHAKE_PLAIN_SIZE (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES) | ||
32 | #define TCP_SERVER_HANDSHAKE_SIZE (crypto_box_NONCEBYTES + TCP_HANDSHAKE_PLAIN_SIZE + crypto_box_MACBYTES) | ||
33 | #define TCP_CLIENT_HANDSHAKE_SIZE (crypto_box_PUBLICKEYBYTES + TCP_SERVER_HANDSHAKE_SIZE) | ||
34 | |||
35 | enum { | ||
36 | TCP_STATUS_NO_STATUS, | ||
37 | TCP_STATUS_CONNECTED, | ||
38 | TCP_STATUS_UNCONFIRMED, | ||
39 | TCP_STATUS_CONFIRMED, | ||
40 | }; | ||
41 | |||
42 | typedef struct { | ||
43 | uint8_t status; | ||
44 | sock_t sock; | ||
45 | uint8_t public_key[crypto_box_PUBLICKEYBYTES]; | ||
46 | uint8_t recv_nonce[crypto_box_NONCEBYTES]; /* Nonce of received packets. */ | ||
47 | uint8_t sent_nonce[crypto_box_NONCEBYTES]; /* Nonce of sent packets. */ | ||
48 | uint8_t shared_key[crypto_box_BEFORENMBYTES]; | ||
49 | uint16_t next_packet_length; | ||
50 | } TCP_Secure_Connection; | ||
51 | |||
52 | typedef struct { | ||
53 | sock_t *socks_listening; | ||
54 | unsigned int num_listening_socks; | ||
55 | |||
56 | uint8_t public_key[crypto_box_PUBLICKEYBYTES]; | ||
57 | uint8_t secret_key[crypto_box_SECRETKEYBYTES]; | ||
58 | TCP_Secure_Connection incomming_connection_queue[MAX_INCOMMING_CONNECTIONS]; | ||
59 | uint16_t incomming_connection_queue_index; | ||
60 | TCP_Secure_Connection unconfirmed_connection_queue[MAX_INCOMMING_CONNECTIONS]; | ||
61 | uint16_t unconfirmed_connection_queue_index; | ||
62 | } TCP_Server; | ||
63 | |||
64 | /* Create new TCP server instance. | ||
65 | */ | ||
66 | TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t *ports, uint8_t *public_key, | ||
67 | uint8_t *secret_key); | ||
68 | |||
69 | /* Run the TCP_server | ||
70 | */ | ||
71 | void do_TCP_server(TCP_Server *TCP_server); | ||
72 | |||
73 | /* Kill the TCP server | ||
74 | */ | ||
75 | void kill_TCP_server(TCP_Server *TCP_server); | ||