mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Work in progress group chats.
Not done yet.
This commit is contained in:
parent
b73ba8244a
commit
c59975dd7e
71
docs/Group_chats.txt
Normal file
71
docs/Group_chats.txt
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
Massive public group chats.
|
||||||
|
|
||||||
|
Everyone generates a short term public private key pair right before joining
|
||||||
|
the chat.
|
||||||
|
|
||||||
|
Note that for public group chats it is impossible to protect the chat from
|
||||||
|
being spied on by a very dedicated attacker, encryption is therefor used as a
|
||||||
|
form of spam/access control.
|
||||||
|
|
||||||
|
## Joining the chats
|
||||||
|
|
||||||
|
|
||||||
|
## 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]
|
||||||
|
```
|
||||||
|
|
||||||
|
Get nodes (Request):
|
||||||
|
Packet contents:
|
||||||
|
```
|
||||||
|
[char with a value of 48][Bob's (The reciever's) Public key (client_id) (32 bytes))][Alice's (The sender's) Public key (client_id) (32 bytes)][Random nonce (24 bytes)][Encrypted with the nonce and private key of the sender:[char with a value of 48][random 8 byte (ping_id)]
|
||||||
|
```
|
||||||
|
Valid replies: a send_nodes packet
|
||||||
|
|
||||||
|
Send_nodes (response):
|
||||||
|
```
|
||||||
|
[char with a value of 48][Bob's (The reciever's) Public key (client_id) (32 bytes))][Alice's (The sender's) Public key (client_id) (32 bytes)][Random nonce (24 bytes)][Encrypted with the nonce and private key of the sender:[char with a value of 49][random 8 byte (ping_id)][Nodes in node format, length=40 * (number of nodes (maximum of 6 nodes)) bytes]]
|
||||||
|
```
|
||||||
|
|
||||||
|
Broadcast packet:
|
||||||
|
```
|
||||||
|
[char with a value of 48][Bob's (The reciever's) Public key (client_id) (32 bytes))][Alice's (The sender's) Public key (client_id) (32 bytes)][nonce][Encrypted with the nonce and private key of the sender:[char with a value of 50][Data to send to everyone]]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Data to send to everyone:
|
||||||
|
TODO: signing and spam control + permissions.
|
||||||
|
[client_id of sender][uint32_t message number][char with a value representing id of message][data]
|
||||||
|
|
||||||
|
Note: the message number is increased by 1 for each sent message.
|
||||||
|
|
||||||
|
message ids:
|
||||||
|
0 - ping
|
||||||
|
sent every ~60 seconds by every peer.
|
||||||
|
No data.
|
||||||
|
|
||||||
|
16 - new_peer
|
||||||
|
Tell everyone about a new peer in the chat.
|
||||||
|
[uint8_t public_key[public_key_len]]
|
||||||
|
|
||||||
|
17 - ban_peer
|
||||||
|
Ban a peer
|
||||||
|
[uint8_t public_key[public_key_len]]
|
||||||
|
|
||||||
|
18 - topic change
|
||||||
|
[uint8_t topic[topiclen]]
|
||||||
|
|
||||||
|
48 - name change
|
||||||
|
[uint8_t name[namelen]]
|
||||||
|
|
||||||
|
49 - status change
|
||||||
|
[uint8_t (status id)]
|
||||||
|
|
||||||
|
64 - chat message
|
||||||
|
[uint8_t message[messagelen]]
|
||||||
|
|
||||||
|
65 - action (/me)
|
||||||
|
[uint8_t message[messagelen]]
|
470
testing/experiment/group_chats.c
Normal file
470
testing/experiment/group_chats.c
Normal file
|
@ -0,0 +1,470 @@
|
||||||
|
/* group_chats.c
|
||||||
|
*
|
||||||
|
* An implementation of massive text only group chats.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "group_chats.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define GROUPCHAT_MAXDATA_LENGTH (MAX_DATA_SIZE - (1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES))
|
||||||
|
#define GROUPCHAT_MAXPLAINDATA_LENGTH (GROUPCHAT_MAXDATA_LENGTH - crypto_box_MACBYTES)
|
||||||
|
|
||||||
|
#define GROUP_MAX_SENDNODES (GROUP_CLOSE_CONNECTIONS * 2)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t pingid;
|
||||||
|
//uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||||
|
|
||||||
|
} getnodes_data;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||||
|
IP_Port ip_port;
|
||||||
|
|
||||||
|
} groupchat_nodes;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t pingid;
|
||||||
|
groupchat_nodes nodes[GROUP_CLOSE_CONNECTIONS];
|
||||||
|
//uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||||
|
|
||||||
|
} sendnodes_data;
|
||||||
|
|
||||||
|
/* Compares client_id1 and client_id2 with client_id
|
||||||
|
* return 0 if both are same distance
|
||||||
|
* return 1 if client_id1 is closer
|
||||||
|
* return 2 if client_id2 is closer
|
||||||
|
*/
|
||||||
|
static int id_closest(uint8_t *id, uint8_t *id1, uint8_t *id2)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint8_t distance1, distance2;
|
||||||
|
|
||||||
|
for (i = 0; i < CLIENT_ID_SIZE; ++i) {
|
||||||
|
|
||||||
|
distance1 = abs(((int8_t *)id)[i] ^ ((int8_t *)id1)[i]);
|
||||||
|
distance2 = abs(((int8_t *)id)[i] ^ ((int8_t *)id2)[i]);
|
||||||
|
|
||||||
|
if (distance1 < distance2)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (distance1 > distance2)
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check if peer with client_id is in peer array.
|
||||||
|
*
|
||||||
|
* return peer number if peer is in chat.
|
||||||
|
* return -1 if peer is not in chat.
|
||||||
|
*
|
||||||
|
* TODO: make this more efficient.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int peer_in_chat(Group_Chat *chat, uint8_t *client_id)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < chat->numpeers; ++i) {
|
||||||
|
/* Equal */
|
||||||
|
if (memcmp(chat->group[i].client_id, client_id, crypto_box_PUBLICKEYBYTES) == 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BAD_NODE_TIMEOUT 30
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if peer is closer to us that the other peers in the list and if the peer is in the list.
|
||||||
|
* Return the number of peers it is closer to if it is not in the closelist.
|
||||||
|
* Return -1 if the peer is in the closelist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int peer_okping(Group_Chat *chat, uint8_t *client_id)
|
||||||
|
{
|
||||||
|
uint32_t i, j = 0;
|
||||||
|
uint64_t temp_time = unix_time();
|
||||||
|
|
||||||
|
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
|
||||||
|
if (chat->close[i].last_recv < temp_time + BAD_NODE_TIMEOUT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Equal */
|
||||||
|
if (memcmp(chat->close[i].client_id, client_id, crypto_box_PUBLICKEYBYTES) == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (id_closest(chat->self_public_key, chat->close[i].client_id, client_id) == 2)
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Attempt to add a peer to the close list.
|
||||||
|
* Update last_recv if it is in list.
|
||||||
|
* Attempt to add it to list if it is not.
|
||||||
|
*
|
||||||
|
* Return 0 if success.
|
||||||
|
* Return -1 if peer was not put in list/updated.
|
||||||
|
*/
|
||||||
|
static int add_closepeer(Group_Chat *chat, uint8_t *client_id, IP_Port ip_port)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint64_t temp_time = unix_time();
|
||||||
|
|
||||||
|
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Check if node is already in list, if it is update its last_recv */
|
||||||
|
if (memcmp(chat->close[i].client_id, client_id, crypto_box_PUBLICKEYBYTES) == 0) {
|
||||||
|
chat->close[i].last_recv = temp_time;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Try replacing bad nodes first */
|
||||||
|
if (chat->close[i].last_recv < temp_time + BAD_NODE_TIMEOUT) {
|
||||||
|
memcpy(chat->close[i].client_id, client_id, crypto_box_PUBLICKEYBYTES);
|
||||||
|
chat->close[i].ip_port = ip_port;
|
||||||
|
chat->close[i].last_recv = temp_time;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Replace nodes if given one is closer. */
|
||||||
|
if (id_closest(chat->self_public_key, chat->close[i].client_id, client_id) == 2) {
|
||||||
|
memcpy(chat->close[i].client_id, client_id, crypto_box_PUBLICKEYBYTES);
|
||||||
|
chat->close[i].ip_port = ip_port;
|
||||||
|
chat->close[i].last_recv = temp_time;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_groupchatpacket(Group_Chat *chat, IP_Port ip_port, uint8_t *public_key, uint8_t *data, uint32_t length,
|
||||||
|
uint8_t request_id)
|
||||||
|
{
|
||||||
|
if (memcmp(chat->self_public_key, public_key, crypto_box_PUBLICKEYBYTES) == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint8_t packet[MAX_DATA_SIZE];
|
||||||
|
int len = create_request(chat->self_public_key, chat->self_secret_key, packet, public_key, data, length, request_id);
|
||||||
|
packet[0] = 48;
|
||||||
|
|
||||||
|
if (len == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (sendpacket(chat->net->sock, ip_port, packet, len) == len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send data to all peers in close peer list.
|
||||||
|
*
|
||||||
|
* return the number of peers the packet was sent to.
|
||||||
|
*/
|
||||||
|
static uint8_t sendto_allpeers(Group_Chat *chat, uint8_t *data, uint16_t length, uint8_t request_id)
|
||||||
|
{
|
||||||
|
uint16_t sent = 0;
|
||||||
|
uint32_t i;
|
||||||
|
uint64_t temp_time = unix_time();
|
||||||
|
|
||||||
|
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
|
||||||
|
if (chat->close[i].ip_port.ip.uint32 != 0 && chat->close[i].last_recv > temp_time + BAD_NODE_TIMEOUT) {
|
||||||
|
if (send_groupchatpacket(chat, chat->close[i].ip_port, chat->close[i].client_id, data, length, request_id) == 0)
|
||||||
|
++sent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a peer to the group chat.
|
||||||
|
*
|
||||||
|
* return peernum if success or peer already in chat.
|
||||||
|
* return -1 if error.
|
||||||
|
*/
|
||||||
|
static int addpeer(Group_Chat *chat, uint8_t *client_id)
|
||||||
|
{
|
||||||
|
int peernum = peer_in_chat(chat, client_id);
|
||||||
|
|
||||||
|
if (peernum != -1)
|
||||||
|
return peernum;
|
||||||
|
|
||||||
|
Group_Peer *temp;
|
||||||
|
temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers + 1));
|
||||||
|
|
||||||
|
if (temp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
chat->group = temp;
|
||||||
|
memcpy(chat->group[chat->numpeers].client_id, client_id, crypto_box_PUBLICKEYBYTES);
|
||||||
|
++chat->numpeers;
|
||||||
|
return (chat->numpeers - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete a peer to the group chat.
|
||||||
|
*
|
||||||
|
* return 0 if success
|
||||||
|
* return -1 if error.
|
||||||
|
*/
|
||||||
|
static int delpeer(Group_Chat *chat, uint8_t *client_id)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
Group_Peer *temp;
|
||||||
|
|
||||||
|
for (i = 0; i < chat->numpeers; ++i) {
|
||||||
|
/* Equal */
|
||||||
|
if (memcmp(chat->group[i].client_id, client_id, crypto_box_PUBLICKEYBYTES) == 0) {
|
||||||
|
--chat->numpeers;
|
||||||
|
|
||||||
|
if (chat->numpeers != i) {
|
||||||
|
memcpy( chat->group[i].client_id,
|
||||||
|
chat->group[chat->numpeers].client_id,
|
||||||
|
crypto_box_PUBLICKEYBYTES );
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = realloc(chat->group, sizeof(Group_Peer) * (chat->numpeers));
|
||||||
|
|
||||||
|
if (temp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
chat->group = temp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* min time between pings sent to one peer in seconds */
|
||||||
|
#define PING_TIMEOUT 5
|
||||||
|
static int send_getnodes(Group_Chat *chat, IP_Port ip_port, int peernum)
|
||||||
|
{
|
||||||
|
if (peernum < 0 || peernum >= chat->numpeers)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint64_t temp_time = unix_time();
|
||||||
|
|
||||||
|
getnodes_data contents;
|
||||||
|
|
||||||
|
if (chat->group[peernum].last_pinged + PING_TIMEOUT > temp_time)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
contents.pingid = ((uint64_t)random_int() << 32) + random_int();
|
||||||
|
chat->group[peernum].last_pinged = temp_time;
|
||||||
|
chat->group[peernum].pingid = contents.pingid;
|
||||||
|
return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents, sizeof(contents), 48);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_sendnodes(Group_Chat *chat, IP_Port ip_port, int peernum, uint64_t pingid)
|
||||||
|
{
|
||||||
|
if (peernum < 0 || peernum >= chat->numpeers)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sendnodes_data contents;
|
||||||
|
contents.pingid = pingid;
|
||||||
|
uint32_t i, j = 0;
|
||||||
|
uint64_t temp_time = unix_time();
|
||||||
|
|
||||||
|
for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) {
|
||||||
|
if (chat->close[i].last_recv + BAD_NODE_TIMEOUT > temp_time) {
|
||||||
|
memcpy(contents.nodes[j].client_id, chat->close[i].client_id, crypto_box_PUBLICKEYBYTES);
|
||||||
|
contents.nodes[j].ip_port = ip_port;
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents,
|
||||||
|
sizeof(contents.pingid) + sizeof(groupchat_nodes) * j, 49);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_getnodes(Group_Chat *chat, IP_Port source, int peernum, uint8_t *data, uint32_t len)
|
||||||
|
{
|
||||||
|
if (len != sizeof(getnodes_data))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (peernum < 0 || peernum >= chat->numpeers)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
getnodes_data contents;
|
||||||
|
memcpy(&contents, data, sizeof(contents));
|
||||||
|
send_sendnodes(chat, source, peernum, contents.pingid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, uint8_t *data, uint32_t len)
|
||||||
|
{
|
||||||
|
if (peernum < 0 || peernum >= chat->numpeers)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (len > sizeof(sendnodes_data) || len < (sizeof(uint64_t) + sizeof(groupchat_nodes)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if ((len - sizeof(uint64_t)) % sizeof(groupchat_nodes) != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (chat->group[peernum].last_pinged + PING_TIMEOUT < unix_time())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
sendnodes_data contents;
|
||||||
|
memcpy(&contents, data, len);
|
||||||
|
|
||||||
|
if (contents.pingid != chat->group[peernum].pingid)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
uint16_t numnodes = (len - sizeof(contents.pingid)) / sizeof(groupchat_nodes);
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < numnodes; ++i) {
|
||||||
|
if (peer_okping(chat, contents.nodes[i].client_id) > 0) {
|
||||||
|
int peern = peer_in_chat(chat, contents.nodes[i].client_id);
|
||||||
|
|
||||||
|
if (peern == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
send_getnodes(chat, contents.nodes[i].ip_port, peern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_closepeer(chat, chat->group[peernum].client_id, source);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_data(Group_Chat *chat, uint8_t *data, uint32_t len)
|
||||||
|
{
|
||||||
|
if (len < 2)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
int handled = 0;
|
||||||
|
|
||||||
|
if (data[0] == 64 && chat->group_message != NULL) {
|
||||||
|
//TODO
|
||||||
|
(*chat->group_message)(chat, 0, data + 1, len - 1, chat->group_message_userdata);
|
||||||
|
handled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handled == 1) {
|
||||||
|
sendto_allpeers(chat, data, len, 50);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle get nodes group packet.
|
||||||
|
*
|
||||||
|
* return 0 if handled correctly.
|
||||||
|
* return 1 if error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int handle_groupchatpacket(Group_Chat *chat, IP_Port source, uint8_t *packet, uint32_t length)
|
||||||
|
{
|
||||||
|
if (length > MAX_DATA_SIZE)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
|
||||||
|
uint8_t data[MAX_DATA_SIZE];
|
||||||
|
uint8_t number;
|
||||||
|
int len = handle_request(chat->self_public_key, chat->self_secret_key, public_key, data, &number, packet, length);
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (memcmp(chat->self_public_key, public_key, crypto_box_PUBLICKEYBYTES) == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
int peernum = peer_in_chat(chat, public_key);
|
||||||
|
|
||||||
|
if (peernum == -1)/*NOTE: This is just for testing and will be removed later.*/
|
||||||
|
peernum = addpeer(chat, public_key);
|
||||||
|
|
||||||
|
if (peernum == -1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
|
||||||
|
switch (number) {
|
||||||
|
case 48:
|
||||||
|
return handle_getnodes(chat, source, peernum, data, len);
|
||||||
|
|
||||||
|
case 49:
|
||||||
|
return handle_sendnodes(chat, source, peernum, data, len);
|
||||||
|
|
||||||
|
case 50:
|
||||||
|
return handle_data(chat, data, len);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t m_sendmessage(Group_Chat *chat, uint8_t *message, uint32_t length)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, int, uint8_t *, uint16_t, void *),
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
chat->group_message = function;
|
||||||
|
chat->group_message_userdata = userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
Group_Chat *new_groupchat(Networking_Core *net)
|
||||||
|
{
|
||||||
|
if (net == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Group_Chat *chat = calloc(1, sizeof(Group_Chat));
|
||||||
|
chat->net = net;
|
||||||
|
return chat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void kill_groupchat(Group_Chat *chat)
|
||||||
|
{
|
||||||
|
free(chat->group);
|
||||||
|
free(chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void chat_bootstrap(Group_Chat *chat, IP_Port ip_port, int peernum)
|
||||||
|
{
|
||||||
|
send_getnodes(chat, ip_port, peernum);
|
||||||
|
}
|
109
testing/experiment/group_chats.h
Normal file
109
testing/experiment/group_chats.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/* group_chats.h
|
||||||
|
*
|
||||||
|
* An implementation of massive text only group chats.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GROUP_CHATS_H
|
||||||
|
#define GROUP_CHATS_H
|
||||||
|
|
||||||
|
#include "../../toxcore/net_crypto.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||||
|
uint64_t last_recv;
|
||||||
|
uint64_t pingid;
|
||||||
|
uint64_t last_pinged;
|
||||||
|
uint32_t last_message_number;
|
||||||
|
} Group_Peer;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t client_id[crypto_box_PUBLICKEYBYTES];
|
||||||
|
IP_Port ip_port;
|
||||||
|
uint64_t last_recv;
|
||||||
|
|
||||||
|
} Group_Close;
|
||||||
|
|
||||||
|
#define GROUP_CLOSE_CONNECTIONS 6
|
||||||
|
|
||||||
|
typedef struct Group_Chat {
|
||||||
|
Networking_Core *net;
|
||||||
|
uint8_t self_public_key[crypto_box_PUBLICKEYBYTES];
|
||||||
|
uint8_t self_secret_key[crypto_box_SECRETKEYBYTES];
|
||||||
|
|
||||||
|
Group_Peer *group;
|
||||||
|
Group_Close close[GROUP_CLOSE_CONNECTIONS];
|
||||||
|
uint32_t numpeers;
|
||||||
|
|
||||||
|
uint32_t message_number;
|
||||||
|
void (*group_message)(struct Group_Chat *m, int, uint8_t *, uint16_t, void *);
|
||||||
|
void *group_message_userdata;
|
||||||
|
|
||||||
|
} Group_Chat;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set callback function for chat messages.
|
||||||
|
*
|
||||||
|
* format of function is: function(Group_Chat *chat, peer number, message, message length, userdata)
|
||||||
|
*/
|
||||||
|
|
||||||
|
void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, int, uint8_t *, uint16_t, void *),
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a message to the group.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint32_t m_sendmessage(Group_Chat *chat, uint8_t *message, uint32_t length);
|
||||||
|
|
||||||
|
/* Create a new group chat.
|
||||||
|
*
|
||||||
|
* Returns a new group chat instance if success.
|
||||||
|
*
|
||||||
|
* Returns a NULL pointer if fail.
|
||||||
|
*/
|
||||||
|
Group_Chat *new_groupchat(Networking_Core *net);
|
||||||
|
|
||||||
|
|
||||||
|
/* Kill a group chat
|
||||||
|
*
|
||||||
|
* Frees the memory and everything.
|
||||||
|
*/
|
||||||
|
void kill_groupchat(Group_Chat *chat);
|
||||||
|
|
||||||
|
|
||||||
|
/* if we receive a group chat packet we call this function so it can be handled.
|
||||||
|
return 0 if packet is handled correctly.
|
||||||
|
return 1 if it didn't handle the packet or if the packet was shit. */
|
||||||
|
int handle_groupchatpacket(Group_Chat *chat, IP_Port source, uint8_t *packet, uint32_t length);
|
||||||
|
|
||||||
|
|
||||||
|
void chat_bootstrap(Group_Chat *chat, IP_Port ip_port, int peernum);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
19
testing/experiment/group_chats_test.c
Normal file
19
testing/experiment/group_chats_test.c
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include "group_chats.h"
|
||||||
|
#define NUM_CHATS 8
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
IP ip;
|
||||||
|
ip.uint32 = 0;
|
||||||
|
uint32_t i;
|
||||||
|
Group_Chat *chats[NUM_CHATS];
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_CHATS; ++i) {
|
||||||
|
chats[i] = new_groupchat(new_networking(ip, 1234));
|
||||||
|
|
||||||
|
if (chats[i] == 0)
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -483,12 +483,12 @@ static int getnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cli
|
||||||
{
|
{
|
||||||
/* Check if packet is going to be sent to ourself. */
|
/* Check if packet is going to be sent to ourself. */
|
||||||
if (id_equal(public_key, dht->c->self_public_key) || is_gettingnodes(dht, ip_port, 0))
|
if (id_equal(public_key, dht->c->self_public_key) || is_gettingnodes(dht, ip_port, 0))
|
||||||
return 1;
|
return -1;
|
||||||
|
|
||||||
uint64_t ping_id = add_gettingnodes(dht, ip_port);
|
uint64_t ping_id = add_gettingnodes(dht, ip_port);
|
||||||
|
|
||||||
if (ping_id == 0)
|
if (ping_id == 0)
|
||||||
return 1;
|
return -1;
|
||||||
|
|
||||||
uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING];
|
uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING];
|
||||||
uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE];
|
uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE];
|
||||||
|
@ -522,7 +522,7 @@ static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *cl
|
||||||
{
|
{
|
||||||
/* Check if packet is going to be sent to ourself. */
|
/* Check if packet is going to be sent to ourself. */
|
||||||
if (id_equal(public_key, dht->c->self_public_key))
|
if (id_equal(public_key, dht->c->self_public_key))
|
||||||
return 1;
|
return -1;
|
||||||
|
|
||||||
uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id)
|
uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id)
|
||||||
+ sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING];
|
+ sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING];
|
||||||
|
|
|
@ -254,18 +254,17 @@ int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *
|
||||||
*
|
*
|
||||||
* return -1 if not valid request.
|
* return -1 if not valid request.
|
||||||
*/
|
*/
|
||||||
static int handle_request(Net_Crypto *c, uint8_t *public_key, uint8_t *data, uint8_t *request_id, uint8_t *packet,
|
int handle_request(uint8_t *self_public_key, uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data,
|
||||||
uint16_t length)
|
uint8_t *request_id, uint8_t *packet, uint16_t length)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (length > crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING &&
|
if (length > crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING &&
|
||||||
length <= MAX_DATA_SIZE + ENCRYPTION_PADDING &&
|
length <= MAX_DATA_SIZE) {
|
||||||
memcmp(packet + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {
|
if (memcmp(packet + 1, self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {
|
||||||
memcpy(public_key, packet + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
|
memcpy(public_key, packet + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
|
||||||
uint8_t nonce[crypto_box_NONCEBYTES];
|
uint8_t nonce[crypto_box_NONCEBYTES];
|
||||||
uint8_t temp[MAX_DATA_SIZE];
|
uint8_t temp[MAX_DATA_SIZE];
|
||||||
memcpy(nonce, packet + 1 + crypto_box_PUBLICKEYBYTES * 2, crypto_box_NONCEBYTES);
|
memcpy(nonce, packet + 1 + crypto_box_PUBLICKEYBYTES * 2, crypto_box_NONCEBYTES);
|
||||||
int len1 = decrypt_data(public_key, c->self_secret_key, nonce,
|
int len1 = decrypt_data(public_key, self_secret_key, nonce,
|
||||||
packet + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES,
|
packet + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES,
|
||||||
length - (crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1), temp);
|
length - (crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1), temp);
|
||||||
|
|
||||||
|
@ -276,7 +275,9 @@ static int handle_request(Net_Crypto *c, uint8_t *public_key, uint8_t *data, uin
|
||||||
--len1;
|
--len1;
|
||||||
memcpy(data, temp + 1, len1);
|
memcpy(data, temp + 1, len1);
|
||||||
return len1;
|
return len1;
|
||||||
} else
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +300,7 @@ static int cryptopacket_handle(void *object, IP_Port source, uint8_t *packet, ui
|
||||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
|
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
|
||||||
uint8_t data[MAX_DATA_SIZE];
|
uint8_t data[MAX_DATA_SIZE];
|
||||||
uint8_t number;
|
uint8_t number;
|
||||||
int len = handle_request(dht->c, public_key, data, &number, packet, length);
|
int len = handle_request(dht->c->self_public_key, dht->c->self_secret_key, public_key, data, &number, packet, length);
|
||||||
|
|
||||||
if (len == -1 || len == 0)
|
if (len == -1 || len == 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -139,6 +139,12 @@ int write_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data, uin
|
||||||
int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *packet, uint8_t *recv_public_key,
|
int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *packet, uint8_t *recv_public_key,
|
||||||
uint8_t *data, uint32_t length, uint8_t request_id);
|
uint8_t *data, uint32_t length, uint8_t request_id);
|
||||||
|
|
||||||
|
/* puts the senders public key in the request in public_key, the data from the request
|
||||||
|
in data if a friend or ping request was sent to us and returns the length of the data.
|
||||||
|
packet is the request packet and length is its length
|
||||||
|
return -1 if not valid request. */
|
||||||
|
int handle_request(uint8_t *self_public_key, uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data,
|
||||||
|
uint8_t *request_id, uint8_t *packet, uint16_t length);
|
||||||
|
|
||||||
/* Function to call when request beginning with byte is received. */
|
/* Function to call when request beginning with byte is received. */
|
||||||
void cryptopacket_registerhandler(Net_Crypto *c, uint8_t byte, cryptopacket_handler_callback cb, void *object);
|
void cryptopacket_registerhandler(Net_Crypto *c, uint8_t byte, cryptopacket_handler_callback cb, void *object);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user