diff --git a/auto_tests/network_test.c b/auto_tests/network_test.c index 2383c0cb..d65b8100 100644 --- a/auto_tests/network_test.c +++ b/auto_tests/network_test.c @@ -122,6 +122,16 @@ START_TEST(test_ip_equal) } END_TEST +START_TEST(test_struct_sizes) +{ + ck_assert_msg(sizeof(IP4) == 4, "sizeof(IP4): expected result 4, got %u.", sizeof(IP4)); + ck_assert_msg(sizeof(IP6) == 16, "sizeof(IP6): expected result 16, got %u.", sizeof(IP6)); + ck_assert_msg(sizeof(IP) == 20, "sizeof(IP): expected result 20, got %u.", sizeof(IP)); + ck_assert_msg(sizeof(IP_Port) == 24, "sizeof(IP_Port): expected result 24, got %u.", sizeof(IP_Port)); + ck_assert_msg(sizeof(IP4_Port) == 8, "sizeof(IP4_Port): expected result 8, got %u.", sizeof(IP4_Port)); +} +END_TEST + #define DEFTESTCASE(NAME) \ TCase *tc_##NAME = tcase_create(#NAME); \ tcase_add_test(tc_##NAME, test_##NAME); \ @@ -133,6 +143,7 @@ Suite *network_suite(void) DEFTESTCASE(addr_resolv_localhost); DEFTESTCASE(ip_equal); + DEFTESTCASE(struct_sizes); return s; } diff --git a/docs/Hardening_docs.txt b/docs/Hardening_docs.txt new file mode 100644 index 00000000..08c6ef13 --- /dev/null +++ b/docs/Hardening_docs.txt @@ -0,0 +1,21 @@ +Hardening request packets are sent as crypto request packets (see crypto docs.) +NOTE: currently only get nodes requests are tested in the code which is why there is only one test (more will be added soon.) + +All hardening requests must contain exactly 768 bytes of data. (The data sent must be padded with zeros if it is smaller than that.) + +1. Get the information (IP_port, client_id) of the node we want to test. +2. Find a couple random nodes that is not that node (one for each test.) +3. Send crypto request packets to each of these random nodes with the data being: + +[byte with value: 02 (get nodes test request)][struct Node_format (the node to test.)][client_id(32 bytes) the id to query the node with.][padding] + +4. The random node receives a packet. +-The packet is a get nodes test request: + send a get_node request to that node with the id to query in the request. + when a send_node response is received, send the following response to the person who sent us the get nodes test request packet: + [byte with value: 03 (get nodes test response)][client_id(32 bytes): the id that the node was queried with][The list of nodes it responded with in IPv6 Node format (struct Node_Format)] + PROTIP: (get node requests and response contain an encrypted part that you can use to store information so that you don't + have to store in your memory where/if to send back the response from the send node) + +5. Receive the test responses. +-If the test(s) pass (the nodes behave in a satisfactory manner), make these nodes have priority over those who don't pass the test(s). diff --git a/docs/updates/DHT.md b/docs/updates/DHT.md new file mode 100644 index 00000000..79ec8c4b --- /dev/null +++ b/docs/updates/DHT.md @@ -0,0 +1,113 @@ +DHT protocol +============ + +Follows pretty much the principle of the torrent DHT: http://www.bittorrent.org/beps/bep_0005.html (READ IT) + +But: +Vastly simplified packet format and encryption. + +Boostrapping: +The first time you install the client we bootstrap it with a node on our servers.(bandwidth should not be a problem as the client only needs to be sent one reply.) + + +Basics +------ +(All the numbers here are just guesses and are probably not optimal values) + +client list: A list of node ids closest (mathematically see bittorrent doc) to ours matched with ip addresses + port number corresponding to that id and a timestamp containing the time or time since the client was successfully pinged. + +"friends" list: A list containing the node_ids of all our "friends" or clients we want to connect to. +Also contains the ip addresses + port + node_ids + timestamp(of last ping like in the client list) of the 8 clients closest (mathematically see bittorrent doc) to each "friend" + +One pinged lists: +-One for storing a list of ips along with their ping_ids and a timestamp for the ping requests +Entries in the pinged lists expire after 5 seconds. +If one of the lists becomes full, the expire rate reduces itself one second or the new ping takes the place of the oldest one. + + +Entries in client list and "friends" list expire after 300 seconds without ping response. +Each client stores a maximum of 32 entries in its client list. +Each client in the client list and "friends" list is pinged every 60 seconds. +Each client in the client list and "friends" list has a timestamp which denote the last time it was successfully pinged. +If the corresponding clients timestamp is more than 130 seconds old it is considered bad. +Send a get nodes request every 20 seconds to a random good node for each "friend" in our "friends" list. +Send a get nodes request every 20 seconds to a random good node in the client list. + + +When a client receives any request from another +----------------------------------------------- +-Respond to the request + -Ping request is replied to with with a ping response containing the same encrypted data + -Get nodes request is replied with a send nodes reply containing the same encrypted data and the good nodes from the client list and/or the "friends" list that are closest to the requested_node_id + +-If the requesting client is not in the client list: + -If there are no bad clients in the list and the list is full: + -If the id of the other client is closer (mathematically see bittorrent doc) than at least one of the clients in the list or our "friends" list: + -Send a ping request to the client. + -if not forget about the client. + + -If there are bad clients and/or the list isn't full: + -Send a ping request to the client + +When a client receives a response +--------------------------------- +-Ping response + -If the node was previously pinged with a matching ping_id (check in the corresponding pinged list.) + -If the node is in the client list the matching client's timestamp is set to current time. + -If the node is in the "friends" list the matching client's timestamp is set to current time for every occurrence. + -If the node is not in the client list: + -If the list isn't full, add it to the list. + -If the list is full, the furthest away (mathematically see bittorrent doc) bad client is replaced by the new one. + -If the list is filled with good nodes replace the furthest client with it only if it is closer than the replaced node. + -for each friend in the "friends" list: + -If that friend's client list isn't full, add that client to it + -If that friend's client list contains bad clients, replace the furthest one with that client. + -If that friend's client list contains only good clients + -If the client is closer to the friend than one of the other clients, it replaces the farthest one + -If not, nothing happens. + + -Send nodes + -If the ping_id matches what we sent previously (check in the corresponding pinged list.): + -Each node in the response is pinged. + + + + + +Protocol +-------- + +Node format: +``` +[char array (node_id), length=32 bytes][ip (in network byte order), length=4 bytes][port (in network byte order), length=2 bytes][Padding , length=2 bytes] +``` +see also: DHT.h (Node4_format struct) + +IPv6 Node format: +see: DHT.h (Node_format struct) + +Valid queries and Responses: + +Ping(Request and response): +``` +[byte with value: 00 for request, 01 for response][char array (client node_id), length=32 bytes][random 24 byte nonce][Encrypted with the nonce and private key of the sender: [random 8 byte (ping_id)]] +``` +ping_id = a random integer, the response must contain the exact same number as the request + + +Get nodes (Request): +Packet contents: +``` +[byte with value: 02][char array (client node_id), length=32 bytes][random 24 byte nonce][Encrypted with the nonce and private key of the sender:[char array: requested_node_id (node_id of which we want the ip), length=32 bytes][Encrypted data (must be sent back unmodified by in the response), length=NODES_ENCRYPTED_MESSAGE_LENGTH bytes]] +``` +Valid replies: a send_nodes packet + +Send_nodes (response (for ipv4 addresses)): +``` +[byte with value: 03][char array (client node_id), length=32 bytes][random 24 byte nonce][Encrypted with the nonce and private key of the sender:[Nodes in node format, length=40 * (number of nodes (maximum of 8 nodes)) bytes][Encrypted data, length=NODES_ENCRYPTED_MESSAGE_LENGTH bytes]] +``` + +Send_nodes_IPv6 (response (for ipv6 addresses)): +``` +[byte with value: 04][char array (client node_id), length=32 bytes][random 24 byte nonce][Encrypted with the nonce and private key of the sender:[Nodes in ipv6_node format, length=56 * (number of nodes (maximum of 8 nodes)) bytes][Encrypted data, length=NODES_ENCRYPTED_MESSAGE_LENGTH bytes]] +``` diff --git a/toxcore/DHT.c b/toxcore/DHT.c index fb059f5b..40ad3929 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -351,7 +351,8 @@ static void get_close_nodes_inner(DHT *dht, uint8_t *client_id, Node_format *nod * * want_good : do we want only good nodes as checked with the hardening returned or not? */ -static int get_somewhat_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family, uint8_t is_LAN, uint8_t want_good) +static int get_somewhat_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family, + uint8_t is_LAN, uint8_t want_good) { uint32_t num_nodes = 0, i; get_close_nodes_inner(dht, client_id, nodes_list, sa_family, @@ -365,7 +366,8 @@ static int get_somewhat_close_nodes(DHT *dht, uint8_t *client_id, Node_format *n return num_nodes; } -static int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family, uint8_t is_LAN, uint8_t want_good) +static int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family, uint8_t is_LAN, + uint8_t want_good) { if (!dht->assoc) return get_somewhat_close_nodes(dht, client_id, nodes_list, sa_family, is_LAN, want_good); @@ -492,10 +494,10 @@ static void sort_list(Client_data *list, uint32_t length, uint8_t *comp_client_i * return 1 if not (list contains no bad nodes). */ static int replace_possible_bad( Client_data *list, - uint32_t length, - uint8_t *client_id, - IP_Port ip_port, - uint8_t *comp_client_id ) + uint32_t length, + uint8_t *client_id, + IP_Port ip_port, + uint8_t *comp_client_id ) { if ((ip_port.ip.family != AF_INET) && (ip_port.ip.family != AF_INET6)) return 1; @@ -626,10 +628,10 @@ int addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id) if (!client_or_ip_port_in_list(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) { if (replace_bad(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) { if (replace_possible_bad(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port, - dht->c->self_public_key)) { + dht->c->self_public_key)) { /* If we can't replace bad nodes we try replacing good ones. */ if (!replace_good(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port, - dht->c->self_public_key)) + dht->c->self_public_key)) used++; } else used++; @@ -647,9 +649,10 @@ int addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id) /*if (replace_possible_bad(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, client_id, ip_port, dht->friends_list[i].client_id)) {*/ /* If we can't replace bad nodes we try replacing good ones. */ - if (!replace_good(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, - client_id, ip_port, dht->friends_list[i].client_id)) - used++; + if (!replace_good(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, + client_id, ip_port, dht->friends_list[i].client_id)) + used++; + /*} else used++;*/ } else @@ -801,7 +804,7 @@ static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cl new_nonce(nonce); Node4_format *nodes4_list = (Node4_format *)(plain); - int i, num_nodes_ok = 0; + uint32_t i, num_nodes_ok = 0; for (i = 0; i < num_nodes; i++) { memcpy(nodes4_list[num_nodes_ok].client_id, nodes_list[i].client_id, CLIENT_ID_SIZE); @@ -1042,6 +1045,7 @@ static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint3 memcpy(nodes_list[i].client_id, nodes4_list[i].client_id, CLIENT_ID_SIZE); ipport_copy(&nodes_list[i].ip_port, &ippts.ip_port); + if (dht->assoc) Assoc_add_entry(dht->assoc, nodes4_list[i].client_id, &ippts, NULL, used ? 1 : 0); } diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 32234784..2eebd772 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -720,6 +720,7 @@ static uint8_t groupnumber_not_valid(Messenger *m, int groupnumber) if (m->chats[groupnumber] == NULL) return 1; + return 0; } @@ -785,7 +786,8 @@ void m_callback_group_message(Messenger *m, void (*function)(Messenger *m, int, * It gets called every time the name list changes(new peer/name, deleted peer) * Function(Tox *tox, int groupnumber, void *userdata) */ -void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t, void *), void *userdata) +void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t, void *), + void *userdata) { m->group_namelistchange = function; m->group_namelistchange_userdata = userdata; @@ -794,10 +796,12 @@ void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m static int get_chat_num(Messenger *m, Group_Chat *chat) { uint32_t i; + for (i = 0; i < m->numchats; ++i) { //TODO: remove this if (m->chats[i] == chat) return i; } + return -1; } @@ -805,6 +809,7 @@ static void group_message_function(Group_Chat *chat, int peer_number, uint8_t *m { Messenger *m = userdata; int i = get_chat_num(m, chat); + if (i == -1) return; @@ -816,6 +821,7 @@ static void group_namelistchange_function(Group_Chat *chat, int peer, uint8_t ch { Messenger *m = userdata; int i = get_chat_num(m, chat); + if (i == -1) return; @@ -1528,6 +1534,7 @@ void kill_messenger(Messenger *m) * This requires the other modules to expose cleanup functions. */ uint32_t i, numchats = m->numchats; + for (i = 0; i < numchats; ++i) del_groupchat(m, i); diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index 6fc23db4..579516f8 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -462,7 +462,8 @@ void m_callback_group_message(Messenger *m, void (*function)(Messenger *m, int, * It gets called every time the name list changes(new peer/name, deleted peer) * Function(Tox *tox, int groupnumber, void *userdata) */ -void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t, void *), void *userdata); +void m_callback_group_namelistchange(Messenger *m, void (*function)(Messenger *m, int, int, uint8_t, void *), + void *userdata); /* Creates a new groupchat and puts it in the chats array. * @@ -512,7 +513,7 @@ int group_message_send(Messenger *m, int groupnumber, uint8_t *message, uint32_t int group_number_peers(Messenger *m, int groupnumber); /* List all the peers in the group chat. - * + * * Copies the names of the peers to the name[length][MAX_NICK_BYTES] array. * * returns the number of peers on success. diff --git a/toxcore/assoc.c b/toxcore/assoc.c index 4dc91671..0177ea0c 100644 --- a/toxcore/assoc.c +++ b/toxcore/assoc.c @@ -908,8 +908,8 @@ void Assoc_status(Assoc *assoc) entry->used_at ? (int)(unix_time() - entry->used_at) : 0, entry->seen_at ? (int)(unix_time() - entry->seen_at) : 0, entry->seen_at ? (entry->seen_family == AF_INET ? '4' : (entry->seen_family == AF_INET6 ? '6' : '?')) : '?', - entry->heard_at ? (int)(unix_time() - entry->heard_at) : 0, - entry->heard_at ? (entry->heard_family == AF_INET ? '4' : (entry->heard_family == AF_INET6 ? '6' : '?')) : '?'); + entry->heard_at ? (int)(unix_time() - entry->heard_at) : 0, + entry->heard_at ? (entry->heard_family == AF_INET ? '4' : (entry->heard_family == AF_INET6 ? '6' : '?')) : '?'); loglog(logbuffer); total++; } diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c index 16d8f344..f6574ae9 100644 --- a/toxcore/group_chats.c +++ b/toxcore/group_chats.c @@ -236,6 +236,7 @@ static int delpeer(Group_Chat *chat, int peernum) return -1; uint32_t i; + for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* If peer is in close list, time it out forcefully. */ if (id_equal(chat->close[i].client_id, chat->group[peernum].client_id)) { chat->close[i].last_recv = 0; @@ -641,7 +642,8 @@ void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, chat->group_message_userdata = userdata; } -void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *), void *userdata) +void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *), + void *userdata) { chat->peer_namelistchange = function; chat->group_namelistchange_userdata = userdata; @@ -655,9 +657,11 @@ uint32_t group_numpeers(Group_Chat *chat) uint32_t group_client_names(Group_Chat *chat, uint8_t names[][MAX_NICK_BYTES], uint16_t length) { uint32_t i; + for (i = 0; i < chat->numpeers && i < length; ++i) { - group_peername(chat, i, names[i]); + group_peername(chat, i, names[i]); } + return i; } diff --git a/toxcore/group_chats.h b/toxcore/group_chats.h index 33773785..a43173ab 100644 --- a/toxcore/group_chats.h +++ b/toxcore/group_chats.h @@ -109,7 +109,8 @@ typedef enum { CHAT_CHANGE_PEER_NAME, } CHAT_CHANGE; -void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *), void *userdata); +void callback_namelistchange(Group_Chat *chat, void (*function)(Group_Chat *chat, int peer, uint8_t change, void *), + void *userdata); /* * Send a message to the group. diff --git a/toxcore/network.h b/toxcore/network.h index 88cfaf17..060240f4 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -129,7 +129,7 @@ typedef union { } IP6; typedef struct { - sa_family_t family; + uint16_t family; union { IP4 ip4; IP6 ip6; @@ -238,9 +238,9 @@ typedef struct { typedef struct { Packet_Handles packethandlers[256]; - /* Our UDP socket. */ sa_family_t family; uint16_t port; + /* Our UDP socket. */ sock_t sock; uint64_t send_fail_eagain; } Networking_Core; diff --git a/toxcore/tox.c b/toxcore/tox.c index 487f2517..a8d41d7f 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -314,7 +314,7 @@ void tox_callback_friend_request(Tox *tox, void (*function)(uint8_t *, uint8_t * * Function format is: function(int friendnumber, uint8_t * message, uint32_t length) */ void tox_callback_friend_message(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *), - void *userdata) + void *userdata) { Messenger *m = tox; m_callback_friendmessage(m, function, userdata); @@ -334,7 +334,7 @@ void tox_callback_action(Tox *tox, void (*function)(Messenger *tox, int, uint8_t * You are not responsible for freeing newname. */ void tox_callback_name_change(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *), - void *userdata) + void *userdata) { Messenger *m = tox; m_callback_namechange(m, function, userdata); @@ -345,7 +345,7 @@ void tox_callback_name_change(Tox *tox, void (*function)(Messenger *tox, int, ui * You are not responsible for freeing newstatus. */ void tox_callback_status_message(Tox *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *), - void *userdata) + void *userdata) { Messenger *m = tox; m_callback_statusmessage(m, function, userdata); @@ -542,8 +542,8 @@ uint32_t tox_get_chatlist(Tox *tox, int *out_list, uint32_t list_size) * Function(Tox *tox, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length, void *userdata) */ void tox_callback_file_send_request(Tox *tox, void (*function)(Messenger *tox, int, uint8_t, uint64_t, uint8_t *, - uint16_t, - void *), void *userdata) + uint16_t, + void *), void *userdata) { Messenger *m = tox; callback_file_sendrequest(m, function, userdata); @@ -589,7 +589,7 @@ int tox_new_file_sender(Tox *tox, int friendnumber, uint64_t filesize, uint8_t * * return -1 on failure */ int tox_file_send_control(Tox *tox, int friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id, - uint8_t *data, uint16_t length) + uint8_t *data, uint16_t length) { Messenger *m = tox; return file_control(m, friendnumber, send_receive, filenumber, message_id, data, length); diff --git a/toxcore/tox.h b/toxcore/tox.h index 9f84876e..d19548fb 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -298,7 +298,7 @@ void tox_callback_friend_request(Tox *tox, void (*function)(uint8_t *, uint8_t * * Function format is: function(int friendnumber, uint8_t * message, uint32_t length) */ void tox_callback_friend_message(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *), - void *userdata); + void *userdata); /* Set the function that will be executed when an action from a friend is received. * Function format is: function(int friendnumber, uint8_t * action, uint32_t length) @@ -310,14 +310,14 @@ void tox_callback_action(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, ui * You are not responsible for freeing newname */ void tox_callback_name_change(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *), - void *userdata); + void *userdata); /* Set the callback for status message changes. * function(int friendnumber, uint8_t *newstatus, uint16_t length) * You are not responsible for freeing newstatus. */ void tox_callback_status_message(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *), - void *userdata); + void *userdata); /* Set the callback for status type changes. * function(int friendnumber, USERSTATUS kind) @@ -374,7 +374,8 @@ typedef enum { TOX_CHAT_CHANGE_PEER_NAME, } TOX_CHAT_CHANGE; -void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int, int, uint8_t, void *), void *userdata); +void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int, int, uint8_t, void *), + void *userdata); /* Creates a new groupchat and puts it in the chats array. * @@ -485,7 +486,7 @@ enum { * Function(Tox *tox, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length, void *userdata) */ void tox_callback_file_send_request(Tox *tox, void (*function)(Tox *m, int, uint8_t, uint64_t, uint8_t *, uint16_t, - void *), void *userdata); + void *), void *userdata); /* Set the callback for file control requests. * @@ -523,7 +524,7 @@ int tox_new_file_sender(Tox *tox, int friendnumber, uint64_t filesize, uint8_t * * return -1 on failure */ int tox_file_send_control(Tox *tox, int friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id, - uint8_t *data, uint16_t length); + uint8_t *data, uint16_t length); /* Send file data. *