Coverage Report

Created: 2023-11-22 10:24

/src/c-toxcore/toxcore/ping.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2018 The TokTok team.
3
 * Copyright © 2013 Tox project.
4
 * Copyright © 2013 plutooo
5
 */
6
7
/**
8
 * Buffered pinging using cyclic arrays.
9
 */
10
#include "ping.h"
11
12
#include <stdlib.h>
13
#include <string.h>
14
15
#include "DHT.h"
16
#include "ccompat.h"
17
#include "mono_time.h"
18
#include "network.h"
19
#include "ping_array.h"
20
#include "util.h"
21
22
3.10k
#define PING_NUM_MAX 512
23
24
/** Maximum newly announced nodes to ping per TIME_TO_PING seconds. */
25
1.28k
#define MAX_TO_PING 32
26
27
/** Ping newly announced nodes to ping per TIME_TO_PING seconds*/
28
2.26k
#define TIME_TO_PING 2
29
30
31
struct Ping {
32
    const Mono_Time *mono_time;
33
    const Random *rng;
34
    DHT *dht;
35
36
    Ping_Array  *ping_array;
37
    Node_format to_ping[MAX_TO_PING];
38
    uint64_t    last_to_ping;
39
};
40
41
42
1.15k
#define PING_PLAIN_SIZE (1 + sizeof(uint64_t))
43
433
#define DHT_PING_SIZE (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + PING_PLAIN_SIZE + CRYPTO_MAC_SIZE)
44
#define PING_DATA_SIZE (CRYPTO_PUBLIC_KEY_SIZE + sizeof(IP_Port))
45
46
void ping_send_request(Ping *ping, const IP_Port *ipp, const uint8_t *public_key)
47
121
{
48
121
    uint8_t   pk[DHT_PING_SIZE];
49
121
    int       rc;
50
121
    uint64_t  ping_id;
51
52
121
    if (pk_equal(public_key, dht_get_self_public_key(ping->dht))) {
53
0
        return;
54
0
    }
55
56
57
    // generate key to encrypt ping_id with recipient privkey
58
121
    const uint8_t *shared_key = dht_get_shared_key_sent(ping->dht, public_key);
59
    // Generate random ping_id.
60
121
    uint8_t data[PING_DATA_SIZE];
61
121
    pk_copy(data, public_key);
62
121
    memcpy(data + CRYPTO_PUBLIC_KEY_SIZE, ipp, sizeof(IP_Port));
63
121
    ping_id = ping_array_add(ping->ping_array, ping->mono_time, ping->rng, data, sizeof(data));
64
65
121
    if (ping_id == 0) {
66
36
        return;
67
36
    }
68
69
85
    uint8_t ping_plain[PING_PLAIN_SIZE];
70
85
    ping_plain[0] = NET_PACKET_PING_REQUEST;
71
85
    memcpy(ping_plain + 1, &ping_id, sizeof(ping_id));
72
73
85
    pk[0] = NET_PACKET_PING_REQUEST;
74
85
    pk_copy(pk + 1, dht_get_self_public_key(ping->dht));     // Our pubkey
75
85
    random_nonce(ping->rng, pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce
76
77
78
85
    rc = encrypt_data_symmetric(shared_key,
79
85
                                pk + 1 + CRYPTO_PUBLIC_KEY_SIZE,
80
85
                                ping_plain, sizeof(ping_plain),
81
85
                                pk + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE);
82
83
85
    if (rc != PING_PLAIN_SIZE + CRYPTO_MAC_SIZE) {
84
0
        return;
85
0
    }
86
87
    // We never check this return value and failures in sendpacket are already logged
88
85
    sendpacket(dht_get_net(ping->dht), ipp, pk, sizeof(pk));
89
85
}
90
91
non_null()
92
static int ping_send_response(const Ping *ping, const IP_Port *ipp, const uint8_t *public_key,
93
                              uint64_t ping_id, const uint8_t *shared_encryption_key)
94
294
{
95
294
    uint8_t pk[DHT_PING_SIZE];
96
97
294
    if (pk_equal(public_key, dht_get_self_public_key(ping->dht))) {
98
0
        return 1;
99
0
    }
100
101
294
    uint8_t ping_plain[PING_PLAIN_SIZE];
102
294
    ping_plain[0] = NET_PACKET_PING_RESPONSE;
103
294
    memcpy(ping_plain + 1, &ping_id, sizeof(ping_id));
104
105
294
    pk[0] = NET_PACKET_PING_RESPONSE;
106
294
    pk_copy(pk + 1, dht_get_self_public_key(ping->dht));     // Our pubkey
107
294
    random_nonce(ping->rng, pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce
108
109
    // Encrypt ping_id using recipient privkey
110
294
    const int rc = encrypt_data_symmetric(shared_encryption_key,
111
294
                                          pk + 1 + CRYPTO_PUBLIC_KEY_SIZE,
112
294
                                          ping_plain, sizeof(ping_plain),
113
294
                                          pk + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE);
114
115
294
    if (rc != PING_PLAIN_SIZE + CRYPTO_MAC_SIZE) {
116
0
        return 1;
117
0
    }
118
119
294
    return sendpacket(dht_get_net(ping->dht), ipp, pk, sizeof(pk));
120
294
}
121
122
non_null()
123
static int handle_ping_request(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
124
                               void *userdata)
125
420
{
126
420
    DHT *dht = (DHT *)object;
127
128
420
    if (length != DHT_PING_SIZE) {
129
75
        return 1;
130
75
    }
131
132
345
    Ping *ping = dht_get_ping(dht);
133
134
345
    if (pk_equal(packet + 1, dht_get_self_public_key(ping->dht))) {
135
3
        return 1;
136
3
    }
137
138
342
    const uint8_t *shared_key = dht_get_shared_key_recv(dht, packet + 1);
139
140
342
    uint8_t ping_plain[PING_PLAIN_SIZE];
141
142
    // Decrypt ping_id
143
342
    const int rc = decrypt_data_symmetric(shared_key,
144
342
                                          packet + 1 + CRYPTO_PUBLIC_KEY_SIZE,
145
342
                                          packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
146
342
                                          PING_PLAIN_SIZE + CRYPTO_MAC_SIZE,
147
342
                                          ping_plain);
148
149
342
    if (rc != sizeof(ping_plain)) {
150
0
        return 1;
151
0
    }
152
153
342
    if (ping_plain[0] != NET_PACKET_PING_REQUEST) {
154
48
        return 1;
155
48
    }
156
157
294
    uint64_t ping_id;
158
294
    memcpy(&ping_id, ping_plain + 1, sizeof(ping_id));
159
    // Send response
160
294
    ping_send_response(ping, source, packet + 1, ping_id, shared_key);
161
294
    ping_add(ping, packet + 1, source);
162
163
294
    return 0;
164
342
}
165
166
non_null()
167
static int handle_ping_response(void *object, const IP_Port *source, const uint8_t *packet, uint16_t length,
168
                                void *userdata)
169
13
{
170
13
    DHT      *dht = (DHT *)object;
171
13
    int       rc;
172
173
13
    if (length != DHT_PING_SIZE) {
174
10
        return 1;
175
10
    }
176
177
3
    Ping *ping = dht_get_ping(dht);
178
179
3
    if (pk_equal(packet + 1, dht_get_self_public_key(ping->dht))) {
180
1
        return 1;
181
1
    }
182
183
    // generate key to encrypt ping_id with recipient privkey
184
2
    const uint8_t *shared_key = dht_get_shared_key_sent(ping->dht, packet + 1);
185
186
2
    uint8_t ping_plain[PING_PLAIN_SIZE];
187
    // Decrypt ping_id
188
2
    rc = decrypt_data_symmetric(shared_key,
189
2
                                packet + 1 + CRYPTO_PUBLIC_KEY_SIZE,
190
2
                                packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
191
2
                                PING_PLAIN_SIZE + CRYPTO_MAC_SIZE,
192
2
                                ping_plain);
193
194
2
    if (rc != sizeof(ping_plain)) {
195
0
        return 1;
196
0
    }
197
198
2
    if (ping_plain[0] != NET_PACKET_PING_RESPONSE) {
199
1
        return 1;
200
1
    }
201
202
1
    uint64_t   ping_id;
203
1
    memcpy(&ping_id, ping_plain + 1, sizeof(ping_id));
204
1
    uint8_t data[PING_DATA_SIZE];
205
206
1
    if (ping_array_check(ping->ping_array, ping->mono_time, data, sizeof(data), ping_id) != sizeof(data)) {
207
1
        return 1;
208
1
    }
209
210
0
    if (!pk_equal(packet + 1, data)) {
211
0
        return 1;
212
0
    }
213
214
0
    IP_Port ipp;
215
0
    memcpy(&ipp, data + CRYPTO_PUBLIC_KEY_SIZE, sizeof(IP_Port));
216
217
0
    if (!ipport_equal(&ipp, source)) {
218
0
        return 1;
219
0
    }
220
221
0
    addto_lists(dht, source, packet + 1);
222
0
    return 0;
223
0
}
224
225
/** @brief Check if public_key with ip_port is in the list.
226
 *
227
 * return true if it is.
228
 * return false if it isn't.
229
 */
230
non_null()
231
static bool in_list(const Client_data *list, uint16_t length, const Mono_Time *mono_time, const uint8_t *public_key,
232
                    const IP_Port *ip_port)
233
295
{
234
302k
    for (unsigned int i = 0; i < length; ++i) {
235
302k
        if (pk_equal(list[i].public_key, public_key)) {
236
2.04k
            const IPPTsPng *ipptp;
237
238
2.04k
            if (net_family_is_ipv4(ip_port->ip.family)) {
239
2.04k
                ipptp = &list[i].assoc4;
240
2.04k
            } else {
241
0
                ipptp = &list[i].assoc6;
242
0
            }
243
244
2.04k
            if (!mono_time_is_timeout(mono_time, ipptp->timestamp, BAD_NODE_TIMEOUT)
245
2.04k
                    && ipport_equal(&ipptp->ip_port, ip_port)) {
246
0
                return true;
247
0
            }
248
2.04k
        }
249
302k
    }
250
251
295
    return false;
252
295
}
253
254
/** @brief Add nodes to the to_ping list.
255
 * All nodes in this list are pinged every TIME_TO_PING seconds
256
 * and are then removed from the list.
257
 * If the list is full the nodes farthest from our public_key are replaced.
258
 * The purpose of this list is to enable quick integration of new nodes into the
259
 * network while preventing amplification attacks.
260
 *
261
 * @retval 0 if node was added.
262
 * @retval -1 if node was not added.
263
 */
264
int32_t ping_add(Ping *ping, const uint8_t *public_key, const IP_Port *ip_port)
265
295
{
266
295
    if (!ip_isset(&ip_port->ip)) {
267
0
        return -1;
268
0
    }
269
270
295
    if (!node_addable_to_close_list(ping->dht, public_key, ip_port)) {
271
0
        return -1;
272
0
    }
273
274
295
    if (in_list(dht_get_close_clientlist(ping->dht), LCLIENT_LIST, ping->mono_time, public_key, ip_port)) {
275
0
        return -1;
276
0
    }
277
278
295
    IP_Port temp;
279
280
295
    if (dht_getfriendip(ping->dht, public_key, &temp) == 0) {
281
2
        ping_send_request(ping, ip_port, public_key);
282
2
        return -1;
283
2
    }
284
285
1.11k
    for (unsigned int i = 0; i < MAX_TO_PING; ++i) {
286
1.11k
        if (!ip_isset(&ping->to_ping[i].ip_port.ip)) {
287
221
            memcpy(ping->to_ping[i].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
288
221
            ipport_copy(&ping->to_ping[i].ip_port, ip_port);
289
221
            return 0;
290
221
        }
291
292
891
        if (pk_equal(ping->to_ping[i].public_key, public_key)) {
293
72
            return -1;
294
72
        }
295
891
    }
296
297
0
    if (add_to_list(ping->to_ping, MAX_TO_PING, public_key, ip_port, dht_get_self_public_key(ping->dht))) {
298
0
        return 0;
299
0
    }
300
301
0
    return -1;
302
0
}
303
304
305
/** @brief Ping all the valid nodes in the to_ping list every TIME_TO_PING seconds.
306
 * This function must be run at least once every TIME_TO_PING seconds.
307
 */
308
void ping_iterate(Ping *ping)
309
2.26k
{
310
2.26k
    if (!mono_time_is_timeout(ping->mono_time, ping->last_to_ping, TIME_TO_PING)) {
311
27
        return;
312
27
    }
313
314
2.24k
    if (!ip_isset(&ping->to_ping[0].ip_port.ip)) {
315
2.19k
        return;
316
2.19k
    }
317
318
49
    unsigned int i;
319
320
168
    for (i = 0; i < MAX_TO_PING; ++i) {
321
168
        if (!ip_isset(&ping->to_ping[i].ip_port.ip)) {
322
49
            break;
323
49
        }
324
325
119
        if (!node_addable_to_close_list(ping->dht, ping->to_ping[i].public_key, &ping->to_ping[i].ip_port)) {
326
0
            continue;
327
0
        }
328
329
119
        ping_send_request(ping, &ping->to_ping[i].ip_port, ping->to_ping[i].public_key);
330
119
        ip_reset(&ping->to_ping[i].ip_port.ip);
331
119
    }
332
333
49
    if (i != 0) {
334
49
        ping->last_to_ping = mono_time_get(ping->mono_time);
335
49
    }
336
49
}
337
338
339
Ping *ping_new(const Memory *mem, const Mono_Time *mono_time, const Random *rng, DHT *dht)
340
3.10k
{
341
3.10k
    Ping *ping = (Ping *)mem_alloc(mem, sizeof(Ping));
342
343
3.10k
    if (ping == nullptr) {
344
1
        return nullptr;
345
1
    }
346
347
3.10k
    ping->ping_array = ping_array_new(mem, PING_NUM_MAX, PING_TIMEOUT);
348
349
3.10k
    if (ping->ping_array == nullptr) {
350
1
        mem_delete(mem, ping);
351
1
        return nullptr;
352
1
    }
353
354
3.10k
    ping->mono_time = mono_time;
355
3.10k
    ping->rng = rng;
356
3.10k
    ping->dht = dht;
357
3.10k
    networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_REQUEST, &handle_ping_request, dht);
358
3.10k
    networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_RESPONSE, &handle_ping_response, dht);
359
360
3.10k
    return ping;
361
3.10k
}
362
363
void ping_kill(const Memory *mem, Ping *ping)
364
3.10k
{
365
3.10k
    if (ping == nullptr) {
366
2
        return;
367
2
    }
368
369
3.10k
    networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_REQUEST, nullptr, nullptr);
370
3.10k
    networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_RESPONSE, nullptr, nullptr);
371
3.10k
    ping_array_kill(ping->ping_array);
372
373
3.10k
    mem_delete(mem, ping);
374
3.10k
}