diff --git a/docs/TCP_Network.txt b/docs/TCP_Network.txt index b9215943..ce52b8e9 100644 --- a/docs/TCP_Network.txt +++ b/docs/TCP_Network.txt @@ -78,6 +78,8 @@ received client sent the server the public key and the public key we sent to the client, the next with base nonce + 1...) +The connection is set to an unconfirmed state until a packet is recieved and +decrypted correctly using the information in the handshake. each packet sent to/from the server has an id (the first byte of the plain text data of the packet.) diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c new file mode 100644 index 00000000..2e3f3996 --- /dev/null +++ b/toxcore/TCP_server.c @@ -0,0 +1,309 @@ +/* +* TCP_server.c -- Implementation of the TCP relay server part of Tox. +* +* Copyright (C) 2013 Tox project All Rights Reserved. +* +* This file is part of Tox. +* +* Tox is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Tox is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Tox. If not, see . +* +*/ + +#include "TCP_server.h" + +#if !defined(_WIN32) && !defined(__WIN32__) && !defined (WIN32) +#include +#endif + +/* return 1 if valid + * return 0 if not valid + */ +static int sock_valid(sock_t sock) +{ +#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) + + if (sock == INVALID_SOCKET) { +#else + + if (sock < 0) { +#endif + return 0; + } + + return 1; +} + +static void kill_sock(sock_t sock) +{ +#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) + closesocket(sock); +#else + close(sock); +#endif +} + +/* return 1 on success + * return 0 on failure + */ +static int set_nonblock(sock_t sock) +{ +#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) + u_long mode = 1; + return (ioctlsocket(sock, FIONBIO, &mode) == 0); +#else + return (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == 0); +#endif +} + +/* return 1 on success + * return 0 on failure + */ +static int set_dualstack(sock_t sock) +{ + char ipv6only = 0; + socklen_t optsize = sizeof(ipv6only); + int res = getsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, &optsize); + + if ((res == 0) && (ipv6only == 0)) + return 1; + + ipv6only = 0; + return (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)) == 0); +} + +/* return 1 on success + * return 0 on failure + */ +static int bind_to_port(sock_t sock, int family, uint16_t port) +{ + struct sockaddr_storage addr = {0}; + size_t addrsize; + + if (family == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + + addrsize = sizeof(struct sockaddr_in); + addr4->sin_family = AF_INET; + addr4->sin_port = htons(port); + } else if (family == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + + addrsize = sizeof(struct sockaddr_in6); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(port); + } else { + return 0; + } + + return (bind(sock, (struct sockaddr *)&addr, addrsize) == 0); +} + +/* return length on success + * return 0 if nothing has been read from socket. + * return ~0 on failure. + */ +static uint16_t read_length(sock_t sock) +{ + int count; + ioctl(sock, FIONREAD, &count); + + if (count >= sizeof(uint16_t)) { + uint16_t length; + int len = recv(sock, &length, sizeof(uint16_t), 0); + + if (len != sizeof(uint16_t)) { + fprintf(stderr, "FAIL recv packet\n"); + return 0; + } + + length = ntohs(length); + + if (length > MAX_PACKET_SIZE) { + return ~0; + } + + return length; + } + + return 0; +} + +/* return length on success + * return -1 on failure + */ +static int read_TCP_packet(sock_t sock, uint8_t *data, uint16_t length) +{ + int count; + ioctl(sock, FIONREAD, &count); + + if (count >= length) { + int len = recv(sock, data, length, 0); + + if (len != length) { + fprintf(stderr, "FAIL recv packet\n"); + return -1; + } + + return length; + } + + return -1; +} + +/* return 0 if everything went well. + * return -1 if the connection must be killed. + */ +static int handle_TCP_handshake(TCP_Secure_Connection *con, uint8_t *data, uint16_t length) +{ + +} + +/* return 0 if everything went well. + * return -1 if the connection must be killed. + */ +static int read_connection_handshake(TCP_Secure_Connection *con) +{ + int ok = 1; + + while (1) { + if (con->next_packet_length == 0) { + uint16_t len = read_length(con->sock); + + if (len == 0) + break; + + if (len != TCP_SERVER_HANDSHAKE_SIZE) + return -1; + + con->next_packet_length = len; + } else { + uint8_t data[con->next_packet_length]; + + if (read_TCP_packet(con->sock, data, con->next_packet_length) != -1) { + return handle_TCP_handshake(con, data, con->next_packet_length); + } else { + break; + } + } + } +} + +/* return 1 on success + * return 0 on failure + */ +static int accept_connection(TCP_Server *TCP_server, sock_t sock) +{ + if (!sock_valid(sock)) + return 0; + + if (!set_nonblock(sock)) { + kill_sock(sock); + return 0; + }//TODO + + printf("accepted %u\n", sock); + + return 1; +} + +static sock_t new_listening_TCP_socket(int family, uint16_t port) +{ + sock_t sock = socket(family, SOCK_STREAM, IPPROTO_TCP); + + if (!sock_valid(sock)) { + return ~0; + } + + int ok = set_nonblock(sock); + + if (ok && family == AF_INET6) { + ok = set_dualstack(sock); + } + + ok = ok && bind_to_port(sock, family, port) && (listen(sock, TCP_MAX_BACKLOG) == 0); + + if (!ok) { + kill_sock(sock); + return ~0; + } + + return sock; +} + +TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t *ports) +{ + if (num_sockets == 0 || ports == NULL) + return NULL; + + TCP_Server *temp = calloc(1, sizeof(Networking_Core)); + + if (temp == NULL) + return NULL; + + temp->socks_listening = calloc(num_sockets, sizeof(sock_t)); + + if (temp->socks_listening == NULL) { + free(temp); + return NULL; + } + + uint8_t family; + + if (ipv6_enabled) { + family = AF_INET6; + } else { + family = AF_INET; + } + + uint32_t i; + + for (i = 0; i < num_sockets; ++i) { + sock_t sock = new_listening_TCP_socket(family, ports[i]); + + if (sock_valid(sock)) { + temp->socks_listening[temp->num_listening_socks] = sock; + ++temp->num_listening_socks; + } + } + + return temp; +} + +void do_TCP_server(TCP_Server *TCP_server) +{ + uint32_t i; + + for (i = 0; i < TCP_server->num_listening_socks; ++i) { + struct sockaddr_storage addr; + int addrlen = sizeof(addr); + sock_t sock; + + do { + sock = accept(TCP_server->socks_listening[i], (struct sockaddr *)&addr, &addrlen); + //TODO + } while (accept_connection(TCP_server, sock)); + } +} + +void kill_TCP_server(TCP_Server *TCP_server) +{ + uint32_t i; + + for (i = 0; i < TCP_server->num_listening_socks; ++i) { + kill_sock(TCP_server->socks_listening[i]); + } + + free(TCP_server->socks_listening); + free(TCP_server); +} diff --git a/toxcore/TCP_server.h b/toxcore/TCP_server.h new file mode 100644 index 00000000..d2c32fda --- /dev/null +++ b/toxcore/TCP_server.h @@ -0,0 +1,68 @@ +/* +* TCP_server.h -- Implementation of the TCP relay server part of Tox. +* +* Copyright (C) 2013 Tox project All Rights Reserved. +* +* This file is part of Tox. +* +* Tox is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Tox is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Tox. If not, see . +* +*/ + +#include "net_crypto.h" + +#define TCP_MAX_BACKLOG 128 + +#define MAX_PACKET_SIZE 8192 + +#define MAX_INCOMMING_CONNECTIONS 32 + +#define TCP_SERVER_HANDSHAKE_SIZE (crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + crypto_box_MACBYTES) +#define TCP_CLIENT_HANDSHAKE_SIZE (crypto_box_PUBLICKEYBYTES + TCP_SERVER_HANDSHAKE_SIZE) + +enum { + TCP_STATUS_NO_STATUS, + TCP_STATUS_CONNECTED, + TCP_STATUS_UNCONFIRMED, + TCP_STATUS_CONFIRMED, +}; + +typedef struct { + uint8_t status; + sock_t sock; + uint8_t public_key[crypto_box_PUBLICKEYBYTES]; + uint8_t recv_nonce[crypto_box_NONCEBYTES]; /* Nonce of received packets. */ + uint8_t sent_nonce[crypto_box_NONCEBYTES]; /* Nonce of sent packets. */ + uint8_t shared_key[crypto_box_BEFORENMBYTES]; + uint16_t next_packet_length; +} TCP_Secure_Connection; + +typedef struct { + sock_t *socks_listening; + unsigned int num_listening_socks; + + TCP_Secure_Connection incomming_connection_queue[MAX_INCOMMING_CONNECTIONS]; +} TCP_Server; + +/* Create new TCP server instance. + */ +TCP_Server *new_TCP_server(uint8_t ipv6_enabled, uint16_t num_sockets, uint16_t *ports); + +/* Run the TCP_server + */ +void do_TCP_server(TCP_Server *TCP_server); + +/* Kill the TCP server + */ +void kill_TCP_server(TCP_Server *TCP_server);