Added/updated some basic docs and cleaned up some things.

This commit is contained in:
irungentoo 2013-12-07 19:06:41 -05:00
parent d078c5e9c2
commit 1c45e59938
12 changed files with 197 additions and 34 deletions

View File

@ -122,6 +122,16 @@ START_TEST(test_ip_equal)
} }
END_TEST 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) \ #define DEFTESTCASE(NAME) \
TCase *tc_##NAME = tcase_create(#NAME); \ TCase *tc_##NAME = tcase_create(#NAME); \
tcase_add_test(tc_##NAME, test_##NAME); \ tcase_add_test(tc_##NAME, test_##NAME); \
@ -133,6 +143,7 @@ Suite *network_suite(void)
DEFTESTCASE(addr_resolv_localhost); DEFTESTCASE(addr_resolv_localhost);
DEFTESTCASE(ip_equal); DEFTESTCASE(ip_equal);
DEFTESTCASE(struct_sizes);
return s; return s;
} }

21
docs/Hardening_docs.txt Normal file
View File

@ -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).

113
docs/updates/DHT.md Normal file
View File

@ -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]]
```

View File

@ -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? * 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; uint32_t num_nodes = 0, i;
get_close_nodes_inner(dht, client_id, nodes_list, sa_family, 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; 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) if (!dht->assoc)
return get_somewhat_close_nodes(dht, client_id, nodes_list, sa_family, is_LAN, want_good); return get_somewhat_close_nodes(dht, client_id, nodes_list, sa_family, is_LAN, want_good);
@ -650,6 +652,7 @@ int addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id)
if (!replace_good(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, if (!replace_good(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS,
client_id, ip_port, dht->friends_list[i].client_id)) client_id, ip_port, dht->friends_list[i].client_id))
used++; used++;
/*} else /*} else
used++;*/ used++;*/
} else } else
@ -801,7 +804,7 @@ static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cl
new_nonce(nonce); new_nonce(nonce);
Node4_format *nodes4_list = (Node4_format *)(plain); 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++) { for (i = 0; i < num_nodes; i++) {
memcpy(nodes4_list[num_nodes_ok].client_id, nodes_list[i].client_id, CLIENT_ID_SIZE); 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); memcpy(nodes_list[i].client_id, nodes4_list[i].client_id, CLIENT_ID_SIZE);
ipport_copy(&nodes_list[i].ip_port, &ippts.ip_port); ipport_copy(&nodes_list[i].ip_port, &ippts.ip_port);
if (dht->assoc) if (dht->assoc)
Assoc_add_entry(dht->assoc, nodes4_list[i].client_id, &ippts, NULL, used ? 1 : 0); Assoc_add_entry(dht->assoc, nodes4_list[i].client_id, &ippts, NULL, used ? 1 : 0);
} }

View File

@ -720,6 +720,7 @@ static uint8_t groupnumber_not_valid(Messenger *m, int groupnumber)
if (m->chats[groupnumber] == NULL) if (m->chats[groupnumber] == NULL)
return 1; return 1;
return 0; 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) * It gets called every time the name list changes(new peer/name, deleted peer)
* Function(Tox *tox, int groupnumber, void *userdata) * 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 = function;
m->group_namelistchange_userdata = userdata; 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) static int get_chat_num(Messenger *m, Group_Chat *chat)
{ {
uint32_t i; uint32_t i;
for (i = 0; i < m->numchats; ++i) { //TODO: remove this for (i = 0; i < m->numchats; ++i) { //TODO: remove this
if (m->chats[i] == chat) if (m->chats[i] == chat)
return i; return i;
} }
return -1; return -1;
} }
@ -805,6 +809,7 @@ static void group_message_function(Group_Chat *chat, int peer_number, uint8_t *m
{ {
Messenger *m = userdata; Messenger *m = userdata;
int i = get_chat_num(m, chat); int i = get_chat_num(m, chat);
if (i == -1) if (i == -1)
return; return;
@ -816,6 +821,7 @@ static void group_namelistchange_function(Group_Chat *chat, int peer, uint8_t ch
{ {
Messenger *m = userdata; Messenger *m = userdata;
int i = get_chat_num(m, chat); int i = get_chat_num(m, chat);
if (i == -1) if (i == -1)
return; return;
@ -1528,6 +1534,7 @@ void kill_messenger(Messenger *m)
* This requires the other modules to expose cleanup functions. * This requires the other modules to expose cleanup functions.
*/ */
uint32_t i, numchats = m->numchats; uint32_t i, numchats = m->numchats;
for (i = 0; i < numchats; ++i) for (i = 0; i < numchats; ++i)
del_groupchat(m, i); del_groupchat(m, i);

View File

@ -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) * It gets called every time the name list changes(new peer/name, deleted peer)
* Function(Tox *tox, int groupnumber, void *userdata) * 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. /* Creates a new groupchat and puts it in the chats array.
* *

View File

@ -236,6 +236,7 @@ static int delpeer(Group_Chat *chat, int peernum)
return -1; return -1;
uint32_t i; uint32_t i;
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* If peer is in close list, time it out forcefully. */ 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)) { if (id_equal(chat->close[i].client_id, chat->group[peernum].client_id)) {
chat->close[i].last_recv = 0; 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; 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->peer_namelistchange = function;
chat->group_namelistchange_userdata = userdata; 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 group_client_names(Group_Chat *chat, uint8_t names[][MAX_NICK_BYTES], uint16_t length)
{ {
uint32_t i; uint32_t i;
for (i = 0; i < chat->numpeers && i < length; ++i) { for (i = 0; i < chat->numpeers && i < length; ++i) {
group_peername(chat, i, names[i]); group_peername(chat, i, names[i]);
} }
return i; return i;
} }

View File

@ -109,7 +109,8 @@ typedef enum {
CHAT_CHANGE_PEER_NAME, CHAT_CHANGE_PEER_NAME,
} CHAT_CHANGE; } 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. * Send a message to the group.

View File

@ -129,7 +129,7 @@ typedef union {
} IP6; } IP6;
typedef struct { typedef struct {
sa_family_t family; uint16_t family;
union { union {
IP4 ip4; IP4 ip4;
IP6 ip6; IP6 ip6;
@ -238,9 +238,9 @@ typedef struct {
typedef struct { typedef struct {
Packet_Handles packethandlers[256]; Packet_Handles packethandlers[256];
/* Our UDP socket. */
sa_family_t family; sa_family_t family;
uint16_t port; uint16_t port;
/* Our UDP socket. */
sock_t sock; sock_t sock;
uint64_t send_fail_eagain; uint64_t send_fail_eagain;
} Networking_Core; } Networking_Core;

View File

@ -374,7 +374,8 @@ typedef enum {
TOX_CHAT_CHANGE_PEER_NAME, TOX_CHAT_CHANGE_PEER_NAME,
} TOX_CHAT_CHANGE; } 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. /* Creates a new groupchat and puts it in the chats array.
* *