toxcore/core/ping.c

230 lines
5.9 KiB
C
Raw Normal View History

/*
* ping.c -- Buffered pinging using cyclic arrays.
*
* This file is donated to the Tox Project.
* Copyright 2013 plutooo
*/
#include <stdbool.h>
#include <stdint.h>
#include "DHT.h"
#include "net_crypto.h"
#include "packets.h"
#include "network.h"
#include "util.h"
#define PING_NUM_MAX 256
#define PING_TIMEOUT 5 // 5s
typedef struct {
2013-08-06 08:35:47 +08:00
IP_Port ipp;
uint64_t id;
uint64_t timestamp;
} pinged_t;
2013-08-21 02:47:32 +08:00
typedef struct {
pinged_t pings[PING_NUM_MAX];
size_t num_pings;
size_t pos_pings;
} PING;
void *new_ping(void)
2013-08-21 02:47:32 +08:00
{
return calloc(1, sizeof(PING));
}
void kill_ping(void *ping)
2013-08-06 08:35:47 +08:00
{
2013-08-21 02:47:32 +08:00
free(ping);
}
2013-08-06 08:35:47 +08:00
static bool is_timeout(uint64_t time)
{
return (time + PING_TIMEOUT) < now();
}
static void remove_timeouts(void *ping) // O(n)
2013-08-06 08:35:47 +08:00
{
PING *png = ping;
2013-08-06 08:35:47 +08:00
size_t i, id;
2013-08-21 02:47:32 +08:00
size_t new_pos = png->pos_pings;
size_t new_num = png->num_pings;
2013-08-06 08:35:47 +08:00
// Loop through buffer, oldest first
2013-08-21 02:47:32 +08:00
for (i = 0; i < png->num_pings; i++) {
id = (png->pos_pings + i) % PING_NUM_MAX;
2013-08-06 08:35:47 +08:00
2013-08-21 02:47:32 +08:00
if (is_timeout(png->pings[id].timestamp)) {
2013-08-06 08:35:47 +08:00
new_pos++;
new_num--;
}
// Break here because list is sorted.
else {
2013-08-06 08:35:47 +08:00
break;
}
}
2013-08-21 02:47:32 +08:00
png->num_pings = new_num;
png->pos_pings = new_pos % PING_NUM_MAX;
}
uint64_t add_ping(void *ping, IP_Port ipp) // O(n)
2013-08-06 08:35:47 +08:00
{
PING *png = ping;
2013-08-06 08:35:47 +08:00
size_t p;
2013-08-21 02:47:32 +08:00
remove_timeouts(ping);
2013-08-06 08:35:47 +08:00
// Remove oldest ping if full buffer
2013-08-21 02:47:32 +08:00
if (png->num_pings == PING_NUM_MAX) {
png->num_pings--;
png->pos_pings = (png->pos_pings + 1) % PING_NUM_MAX;
2013-08-06 08:35:47 +08:00
}
2013-08-06 08:35:47 +08:00
// Insert new ping at end of list
2013-08-21 02:47:32 +08:00
p = (png->pos_pings + png->num_pings) % PING_NUM_MAX;
2013-08-21 02:47:32 +08:00
png->pings[p].ipp = ipp;
png->pings[p].timestamp = now();
png->pings[p].id = random_64b();
2013-08-21 02:47:32 +08:00
png->num_pings++;
return png->pings[p].id;
}
bool is_pinging(void *ping, IP_Port ipp, uint64_t ping_id) // O(n) TODO: replace this with something else.
2013-08-06 08:35:47 +08:00
{
PING *png = ping;
2013-08-06 08:35:47 +08:00
if (ipp.ip.i == 0 && ping_id == 0)
return false;
2013-08-17 01:11:09 +08:00
2013-08-06 08:35:47 +08:00
size_t i, id;
2013-08-21 02:47:32 +08:00
remove_timeouts(ping);
2013-08-21 02:47:32 +08:00
for (i = 0; i < png->num_pings; i++) {
id = (png->pos_pings + i) % PING_NUM_MAX;
// ping_id = 0 means match any id
2013-08-21 02:47:32 +08:00
if ((ipp_eq(png->pings[id].ipp, ipp) || ipp.ip.i == 0) && (png->pings[id].id == ping_id || ping_id == 0)) {
2013-08-06 08:35:47 +08:00
return true;
}
}
2013-08-06 08:35:47 +08:00
return false;
}
int send_ping_request(void *ping, Net_Crypto *c, IP_Port ipp, clientid_t *client_id)
{
pingreq_t pk;
int rc;
uint64_t ping_id;
2013-08-21 02:47:32 +08:00
if (is_pinging(ping, ipp, 0) || id_eq(client_id, (clientid_t *)c->self_public_key))
return 1;
// Generate random ping_id
2013-08-21 02:47:32 +08:00
ping_id = add_ping(ping, ipp);
2013-08-21 15:55:43 +08:00
pk.packet_id = NET_PACKET_PING_REQUEST;
2013-08-21 02:47:32 +08:00
id_cpy(&pk.client_id, (clientid_t *)c->self_public_key); // Our pubkey
2013-08-17 01:11:09 +08:00
random_nonce((uint8_t *) &pk.nonce); // Generate random nonce
// Encrypt ping_id using recipient privkey
2013-08-17 01:11:09 +08:00
rc = encrypt_data((uint8_t *) client_id,
2013-08-21 02:47:32 +08:00
c->self_secret_key,
2013-08-17 01:11:09 +08:00
(uint8_t *) &pk.nonce,
(uint8_t *) &ping_id, sizeof(ping_id),
(uint8_t *) &pk.ping_id);
if (rc != sizeof(ping_id) + ENCRYPTION_PADDING)
return 1;
2013-08-21 02:47:32 +08:00
return sendpacket(c->lossless_udp->net->sock, ipp, (uint8_t *) &pk, sizeof(pk));
}
2013-08-21 02:47:32 +08:00
int send_ping_response(Net_Crypto *c, IP_Port ipp, clientid_t *client_id, uint64_t ping_id)
{
pingres_t pk;
int rc;
2013-08-21 02:47:32 +08:00
if (id_eq(client_id, (clientid_t *)c->self_public_key))
return 1;
2013-08-21 15:55:43 +08:00
pk.packet_id = NET_PACKET_PING_RESPONSE;
2013-08-21 02:47:32 +08:00
id_cpy(&pk.client_id, (clientid_t *)c->self_public_key); // Our pubkey
2013-08-17 01:11:09 +08:00
random_nonce((uint8_t *) &pk.nonce); // Generate random nonce
// Encrypt ping_id using recipient privkey
2013-08-17 01:11:09 +08:00
rc = encrypt_data((uint8_t *) client_id,
2013-08-21 02:47:32 +08:00
c->self_secret_key,
2013-08-17 01:11:09 +08:00
(uint8_t *) &pk.nonce,
(uint8_t *) &ping_id, sizeof(ping_id),
(uint8_t *) &pk.ping_id);
if (rc != sizeof(ping_id) + ENCRYPTION_PADDING)
return 1;
2013-08-21 02:47:32 +08:00
return sendpacket(c->lossless_udp->net->sock, ipp, (uint8_t *) &pk, sizeof(pk));
}
int handle_ping_request(void *object, IP_Port source, uint8_t *packet, uint32_t length)
{
DHT *dht = object;
2013-08-17 01:11:09 +08:00
pingreq_t *p = (pingreq_t *) packet;
int rc;
uint64_t ping_id;
2013-08-21 00:08:55 +08:00
if (length != sizeof(pingreq_t) || id_eq(&p->client_id, (clientid_t *)dht->c->self_public_key))
return 1;
// Decrypt ping_id
2013-08-17 01:11:09 +08:00
rc = decrypt_data((uint8_t *) &p->client_id,
2013-08-21 00:08:55 +08:00
dht->c->self_secret_key,
2013-08-17 01:11:09 +08:00
(uint8_t *) &p->nonce,
(uint8_t *) &p->ping_id,
sizeof(ping_id) + ENCRYPTION_PADDING,
2013-08-17 01:11:09 +08:00
(uint8_t *) &ping_id);
if (rc != sizeof(ping_id))
return 1;
// Send response
2013-08-21 02:47:32 +08:00
send_ping_response(dht->c, source, &p->client_id, ping_id);
2013-08-21 00:08:55 +08:00
add_toping(dht, (uint8_t *) &p->client_id, source);
return 0;
}
int handle_ping_response(void *object, IP_Port source, uint8_t *packet, uint32_t length)
{
DHT *dht = object;
2013-08-17 01:11:09 +08:00
pingres_t *p = (pingres_t *) packet;
int rc;
uint64_t ping_id;
2013-08-21 00:08:55 +08:00
if (length != sizeof(pingres_t) || id_eq(&p->client_id, (clientid_t *)dht->c->self_public_key))
return 1;
// Decrypt ping_id
2013-08-17 01:11:09 +08:00
rc = decrypt_data((uint8_t *) &p->client_id,
2013-08-21 00:08:55 +08:00
dht->c->self_secret_key,
2013-08-17 01:11:09 +08:00
(uint8_t *) &p->nonce,
(uint8_t *) &p->ping_id,
sizeof(ping_id) + ENCRYPTION_PADDING,
2013-08-17 01:11:09 +08:00
(uint8_t *) &ping_id);
if (rc != sizeof(ping_id))
return 1;
// Make sure ping_id is correct
2013-08-21 02:47:32 +08:00
if (!is_pinging(dht->ping, source, ping_id))
return 1;
// Associate source ip with client_id
2013-08-21 00:08:55 +08:00
addto_lists(dht, source, (uint8_t *) &p->client_id);
return 0;
}