summaryrefslogtreecommitdiff
path: root/toxcore/ping.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxcore/ping.c')
-rw-r--r--toxcore/ping.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/toxcore/ping.c b/toxcore/ping.c
new file mode 100644
index 00000000..55d4d261
--- /dev/null
+++ b/toxcore/ping.c
@@ -0,0 +1,229 @@
1/*
2 * ping.c -- Buffered pinging using cyclic arrays.
3 *
4 * This file is donated to the Tox Project.
5 * Copyright 2013 plutooo
6 */
7
8#include <stdbool.h>
9#include <stdint.h>
10
11#include "DHT.h"
12#include "net_crypto.h"
13#include "packets.h"
14#include "network.h"
15#include "util.h"
16
17#define PING_NUM_MAX 256
18#define PING_TIMEOUT 5 // 5s
19
20typedef struct {
21 IP_Port ipp;
22 uint64_t id;
23 uint64_t timestamp;
24} pinged_t;
25
26typedef struct {
27 pinged_t pings[PING_NUM_MAX];
28 size_t num_pings;
29 size_t pos_pings;
30} PING;
31
32void *new_ping(void)
33{
34 return calloc(1, sizeof(PING));
35}
36
37void kill_ping(void *ping)
38{
39 free(ping);
40}
41
42static bool is_timeout(uint64_t time)
43{
44 return (time + PING_TIMEOUT) < now();
45}
46
47static void remove_timeouts(void *ping) // O(n)
48{
49 PING *png = ping;
50 size_t i, id;
51 size_t new_pos = png->pos_pings;
52 size_t new_num = png->num_pings;
53
54 // Loop through buffer, oldest first
55 for (i = 0; i < png->num_pings; i++) {
56 id = (png->pos_pings + i) % PING_NUM_MAX;
57
58 if (is_timeout(png->pings[id].timestamp)) {
59 new_pos++;
60 new_num--;
61 }
62 // Break here because list is sorted.
63 else {
64 break;
65 }
66 }
67
68 png->num_pings = new_num;
69 png->pos_pings = new_pos % PING_NUM_MAX;
70}
71
72uint64_t add_ping(void *ping, IP_Port ipp) // O(n)
73{
74 PING *png = ping;
75 size_t p;
76
77 remove_timeouts(ping);
78
79 // Remove oldest ping if full buffer
80 if (png->num_pings == PING_NUM_MAX) {
81 png->num_pings--;
82 png->pos_pings = (png->pos_pings + 1) % PING_NUM_MAX;
83 }
84
85 // Insert new ping at end of list
86 p = (png->pos_pings + png->num_pings) % PING_NUM_MAX;
87
88 png->pings[p].ipp = ipp;
89 png->pings[p].timestamp = now();
90 png->pings[p].id = random_64b();
91
92 png->num_pings++;
93 return png->pings[p].id;
94}
95
96bool is_pinging(void *ping, IP_Port ipp, uint64_t ping_id) // O(n) TODO: replace this with something else.
97{
98 PING *png = ping;
99
100 if (ipp.ip.i == 0 && ping_id == 0)
101 return false;
102
103 size_t i, id;
104
105 remove_timeouts(ping);
106
107 for (i = 0; i < png->num_pings; i++) {
108 id = (png->pos_pings + i) % PING_NUM_MAX;
109
110 // ping_id = 0 means match any id
111 if ((ipp_eq(png->pings[id].ipp, ipp) || ipp.ip.i == 0) && (png->pings[id].id == ping_id || ping_id == 0)) {
112 return true;
113 }
114 }
115
116 return false;
117}
118
119int send_ping_request(void *ping, Net_Crypto *c, IP_Port ipp, clientid_t *client_id)
120{
121 pingreq_t pk;
122 int rc;
123 uint64_t ping_id;
124
125 if (is_pinging(ping, ipp, 0) || id_eq(client_id, (clientid_t *)c->self_public_key))
126 return 1;
127
128 // Generate random ping_id
129 ping_id = add_ping(ping, ipp);
130
131 pk.packet_id = NET_PACKET_PING_REQUEST;
132 id_cpy(&pk.client_id, (clientid_t *)c->self_public_key); // Our pubkey
133 random_nonce((uint8_t *) &pk.nonce); // Generate random nonce
134
135 // Encrypt ping_id using recipient privkey
136 rc = encrypt_data((uint8_t *) client_id,
137 c->self_secret_key,
138 (uint8_t *) &pk.nonce,
139 (uint8_t *) &ping_id, sizeof(ping_id),
140 (uint8_t *) &pk.ping_id);
141
142 if (rc != sizeof(ping_id) + ENCRYPTION_PADDING)
143 return 1;
144
145 return sendpacket(c->lossless_udp->net->sock, ipp, (uint8_t *) &pk, sizeof(pk));
146}
147
148int send_ping_response(Net_Crypto *c, IP_Port ipp, clientid_t *client_id, uint64_t ping_id)
149{
150 pingres_t pk;
151 int rc;
152
153 if (id_eq(client_id, (clientid_t *)c->self_public_key))
154 return 1;
155
156 pk.packet_id = NET_PACKET_PING_RESPONSE;
157 id_cpy(&pk.client_id, (clientid_t *)c->self_public_key); // Our pubkey
158 random_nonce((uint8_t *) &pk.nonce); // Generate random nonce
159
160 // Encrypt ping_id using recipient privkey
161 rc = encrypt_data((uint8_t *) client_id,
162 c->self_secret_key,
163 (uint8_t *) &pk.nonce,
164 (uint8_t *) &ping_id, sizeof(ping_id),
165 (uint8_t *) &pk.ping_id);
166
167 if (rc != sizeof(ping_id) + ENCRYPTION_PADDING)
168 return 1;
169
170 return sendpacket(c->lossless_udp->net->sock, ipp, (uint8_t *) &pk, sizeof(pk));
171}
172
173int handle_ping_request(void *object, IP_Port source, uint8_t *packet, uint32_t length)
174{
175 DHT *dht = object;
176 pingreq_t *p = (pingreq_t *) packet;
177 int rc;
178 uint64_t ping_id;
179
180 if (length != sizeof(pingreq_t) || id_eq(&p->client_id, (clientid_t *)dht->c->self_public_key))
181 return 1;
182
183 // Decrypt ping_id
184 rc = decrypt_data((uint8_t *) &p->client_id,
185 dht->c->self_secret_key,
186 (uint8_t *) &p->nonce,
187 (uint8_t *) &p->ping_id,
188 sizeof(ping_id) + ENCRYPTION_PADDING,
189 (uint8_t *) &ping_id);
190
191 if (rc != sizeof(ping_id))
192 return 1;
193
194 // Send response
195 send_ping_response(dht->c, source, &p->client_id, ping_id);
196 add_toping(dht, (uint8_t *) &p->client_id, source);
197
198 return 0;
199}
200
201int handle_ping_response(void *object, IP_Port source, uint8_t *packet, uint32_t length)
202{
203 DHT *dht = object;
204 pingres_t *p = (pingres_t *) packet;
205 int rc;
206 uint64_t ping_id;
207
208 if (length != sizeof(pingres_t) || id_eq(&p->client_id, (clientid_t *)dht->c->self_public_key))
209 return 1;
210
211 // Decrypt ping_id
212 rc = decrypt_data((uint8_t *) &p->client_id,
213 dht->c->self_secret_key,
214 (uint8_t *) &p->nonce,
215 (uint8_t *) &p->ping_id,
216 sizeof(ping_id) + ENCRYPTION_PADDING,
217 (uint8_t *) &ping_id);
218
219 if (rc != sizeof(ping_id))
220 return 1;
221
222 // Make sure ping_id is correct
223 if (!is_pinging(dht->ping, source, ping_id))
224 return 1;
225
226 // Associate source ip with client_id
227 addto_lists(dht, source, (uint8_t *) &p->client_id);
228 return 0;
229}