Coverage Report

Created: 2024-01-26 01:52

/work/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 <string.h>
13
14
#include "DHT.h"
15
#include "ccompat.h"
16
#include "crypto_core.h"
17
#include "mem.h"
18
#include "mono_time.h"
19
#include "network.h"
20
#include "ping_array.h"
21
22
4.13k
#define PING_NUM_MAX 512
23
24
/** Maximum newly announced nodes to ping per TIME_TO_PING seconds. */
25
38.5k
#define MAX_TO_PING 32
26
27
/** Ping newly announced nodes to ping per TIME_TO_PING seconds*/
28
23.5k
#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
31.2k
#define PING_PLAIN_SIZE (1 + sizeof(uint64_t))
43
10.4k
#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
5.14k
{
48
5.14k
    uint8_t   pk[DHT_PING_SIZE];
49
5.14k
    int       rc;
50
5.14k
    uint64_t  ping_id;
51
52
5.14k
    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
5.14k
    const uint8_t *shared_key = dht_get_shared_key_sent(ping->dht, public_key);
59
    // Generate random ping_id.
60
5.14k
    uint8_t data[PING_DATA_SIZE];
61
5.14k
    pk_copy(data, public_key);
62
5.14k
    memcpy(data + CRYPTO_PUBLIC_KEY_SIZE, ipp, sizeof(IP_Port));
63
5.14k
    ping_id = ping_array_add(ping->ping_array, ping->mono_time, ping->rng, data, sizeof(data));
64
65
5.14k
    if (ping_id == 0) {
66
26
        return;
67
26
    }
68
69
5.11k
    uint8_t ping_plain[PING_PLAIN_SIZE];
70
5.11k
    ping_plain[0] = NET_PACKET_PING_REQUEST;
71
5.11k
    memcpy(ping_plain + 1, &ping_id, sizeof(ping_id));
72
73
5.11k
    pk[0] = NET_PACKET_PING_REQUEST;
74
5.11k
    pk_copy(pk + 1, dht_get_self_public_key(ping->dht));     // Our pubkey
75
5.11k
    random_nonce(ping->rng, pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce
76
77
78
5.11k
    rc = encrypt_data_symmetric(shared_key,
79
5.11k
                                pk + 1 + CRYPTO_PUBLIC_KEY_SIZE,
80
5.11k
                                ping_plain, sizeof(ping_plain),
81
5.11k
                                pk + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE);
82
83
5.11k
    if (rc != PING_PLAIN_SIZE + CRYPTO_MAC_SIZE) {
84
1
        return;
85
1
    }
86
87
    // We never check this return value and failures in sendpacket are already logged
88
5.11k
    sendpacket(dht_get_net(ping->dht), ipp, pk, sizeof(pk));
89
5.11k
}
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
5.30k
{
95
5.30k
    uint8_t pk[DHT_PING_SIZE];
96
97
5.30k
    if (pk_equal(public_key, dht_get_self_public_key(ping->dht))) {
98
0
        return 1;
99
0
    }
100
101
5.30k
    uint8_t ping_plain[PING_PLAIN_SIZE];
102
5.30k
    ping_plain[0] = NET_PACKET_PING_RESPONSE;
103
5.30k
    memcpy(ping_plain + 1, &ping_id, sizeof(ping_id));
104
105
5.30k
    pk[0] = NET_PACKET_PING_RESPONSE;
106
5.30k
    pk_copy(pk + 1, dht_get_self_public_key(ping->dht));     // Our pubkey
107
5.30k
    random_nonce(ping->rng, pk + 1 + CRYPTO_PUBLIC_KEY_SIZE); // Generate new nonce
108
109
    // Encrypt ping_id using recipient privkey
110
5.30k
    const int rc = encrypt_data_symmetric(shared_encryption_key,
111
5.30k
                                          pk + 1 + CRYPTO_PUBLIC_KEY_SIZE,
112
5.30k
                                          ping_plain, sizeof(ping_plain),
113
5.30k
                                          pk + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE);
114
115
5.30k
    if (rc != PING_PLAIN_SIZE + CRYPTO_MAC_SIZE) {
116
8
        return 1;
117
8
    }
118
119
5.29k
    return sendpacket(dht_get_net(ping->dht), ipp, pk, sizeof(pk));
120
5.30k
}
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
5.42k
{
126
5.42k
    DHT *dht = (DHT *)object;
127
128
5.42k
    if (length != DHT_PING_SIZE) {
129
75
        return 1;
130
75
    }
131
132
5.34k
    Ping *ping = dht_get_ping(dht);
133
134
5.34k
    if (pk_equal(packet + 1, dht_get_self_public_key(ping->dht))) {
135
0
        return 1;
136
0
    }
137
138
5.34k
    const uint8_t *shared_key = dht_get_shared_key_recv(dht, packet + 1);
139
140
5.34k
    uint8_t ping_plain[PING_PLAIN_SIZE];
141
142
    // Decrypt ping_id
143
5.34k
    const int rc = decrypt_data_symmetric(shared_key,
144
5.34k
                                          packet + 1 + CRYPTO_PUBLIC_KEY_SIZE,
145
5.34k
                                          packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
146
5.34k
                                          PING_PLAIN_SIZE + CRYPTO_MAC_SIZE,
147
5.34k
                                          ping_plain);
148
149
5.34k
    if (rc != sizeof(ping_plain)) {
150
2
        return 1;
151
2
    }
152
153
5.34k
    if (ping_plain[0] != NET_PACKET_PING_REQUEST) {
154
42
        return 1;
155
42
    }
156
157
5.30k
    uint64_t ping_id;
158
5.30k
    memcpy(&ping_id, ping_plain + 1, sizeof(ping_id));
159
    // Send response
160
5.30k
    ping_send_response(ping, source, packet + 1, ping_id, shared_key);
161
5.30k
    ping_add(ping, packet + 1, source);
162
163
5.30k
    return 0;
164
5.34k
}
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
5.03k
{
170
5.03k
    DHT      *dht = (DHT *)object;
171
5.03k
    int       rc;
172
173
5.03k
    if (length != DHT_PING_SIZE) {
174
10
        return 1;
175
10
    }
176
177
5.02k
    Ping *ping = dht_get_ping(dht);
178
179
5.02k
    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
5.02k
    const uint8_t *shared_key = dht_get_shared_key_sent(ping->dht, packet + 1);
185
186
5.02k
    uint8_t ping_plain[PING_PLAIN_SIZE];
187
    // Decrypt ping_id
188
5.02k
    rc = decrypt_data_symmetric(shared_key,
189
5.02k
                                packet + 1 + CRYPTO_PUBLIC_KEY_SIZE,
190
5.02k
                                packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE,
191
5.02k
                                PING_PLAIN_SIZE + CRYPTO_MAC_SIZE,
192
5.02k
                                ping_plain);
193
194
5.02k
    if (rc != sizeof(ping_plain)) {
195
8
        return 1;
196
8
    }
197
198
5.01k
    if (ping_plain[0] != NET_PACKET_PING_RESPONSE) {
199
1
        return 1;
200
1
    }
201
202
5.01k
    uint64_t   ping_id;
203
5.01k
    memcpy(&ping_id, ping_plain + 1, sizeof(ping_id));
204
5.01k
    uint8_t data[PING_DATA_SIZE];
205
206
5.01k
    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
5.01k
    if (!pk_equal(packet + 1, data)) {
211
0
        return 1;
212
0
    }
213
214
5.01k
    IP_Port ipp;
215
5.01k
    memcpy(&ipp, data + CRYPTO_PUBLIC_KEY_SIZE, sizeof(IP_Port));
216
217
5.01k
    if (!ipport_equal(&ipp, source)) {
218
0
        return 1;
219
0
    }
220
221
5.01k
    addto_lists(dht, source, packet + 1);
222
5.01k
    return 0;
223
5.01k
}
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
67.0k
{
234
11.9M
    for (unsigned int i = 0; i < length; ++i) {
235
11.9M
        if (pk_equal(list[i].public_key, public_key)) {
236
61.2k
            const IPPTsPng *ipptp;
237
238
61.2k
            if (net_family_is_ipv4(ip_port->ip.family)) {
239
61.2k
                ipptp = &list[i].assoc4;
240
61.2k
            } else {
241
0
                ipptp = &list[i].assoc6;
242
0
            }
243
244
61.2k
            if (!mono_time_is_timeout(mono_time, ipptp->timestamp, BAD_NODE_TIMEOUT)
245
61.2k
                    && ipport_equal(&ipptp->ip_port, ip_port)) {
246
56.1k
                return true;
247
56.1k
            }
248
61.2k
        }
249
11.9M
    }
250
251
10.9k
    return false;
252
67.0k
}
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
90.3k
{
266
90.3k
    if (!ip_isset(&ip_port->ip)) {
267
0
        return -1;
268
0
    }
269
270
90.3k
    if (!node_addable_to_close_list(ping->dht, public_key, ip_port)) {
271
23.2k
        return -1;
272
23.2k
    }
273
274
67.0k
    if (in_list(dht_get_close_clientlist(ping->dht), LCLIENT_LIST, ping->mono_time, public_key, ip_port)) {
275
56.1k
        return -1;
276
56.1k
    }
277
278
10.9k
    IP_Port temp;
279
280
10.9k
    if (dht_getfriendip(ping->dht, public_key, &temp) == 0) {
281
11
        ping_send_request(ping, ip_port, public_key);
282
11
        return -1;
283
11
    }
284
285
30.0k
    for (unsigned int i = 0; i < MAX_TO_PING; ++i) {
286
30.0k
        if (!ip_isset(&ping->to_ping[i].ip_port.ip)) {
287
5.44k
            memcpy(ping->to_ping[i].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
288
5.44k
            ipport_copy(&ping->to_ping[i].ip_port, ip_port);
289
5.44k
            return 0;
290
5.44k
        }
291
292
24.5k
        if (pk_equal(ping->to_ping[i].public_key, public_key)) {
293
5.52k
            return -1;
294
5.52k
        }
295
24.5k
    }
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
23.5k
{
310
23.5k
    if (!mono_time_is_timeout(ping->mono_time, ping->last_to_ping, TIME_TO_PING)) {
311
2.55k
        return;
312
2.55k
    }
313
314
20.9k
    if (!ip_isset(&ping->to_ping[0].ip_port.ip)) {
315
18.3k
        return;
316
18.3k
    }
317
318
2.64k
    unsigned int i;
319
320
8.49k
    for (i = 0; i < MAX_TO_PING; ++i) {
321
8.49k
        if (!ip_isset(&ping->to_ping[i].ip_port.ip)) {
322
2.64k
            break;
323
2.64k
        }
324
325
5.84k
        if (!node_addable_to_close_list(ping->dht, ping->to_ping[i].public_key, &ping->to_ping[i].ip_port)) {
326
710
            continue;
327
710
        }
328
329
5.13k
        ping_send_request(ping, &ping->to_ping[i].ip_port, ping->to_ping[i].public_key);
330
5.13k
        ip_reset(&ping->to_ping[i].ip_port.ip);
331
5.13k
    }
332
333
2.64k
    if (i != 0) {
334
2.64k
        ping->last_to_ping = mono_time_get(ping->mono_time);
335
2.64k
    }
336
2.64k
}
337
338
339
Ping *ping_new(const Memory *mem, const Mono_Time *mono_time, const Random *rng, DHT *dht)
340
4.16k
{
341
4.16k
    Ping *ping = (Ping *)mem_alloc(mem, sizeof(Ping));
342
343
4.16k
    if (ping == nullptr) {
344
21
        return nullptr;
345
21
    }
346
347
4.13k
    ping->ping_array = ping_array_new(mem, PING_NUM_MAX, PING_TIMEOUT);
348
349
4.13k
    if (ping->ping_array == nullptr) {
350
42
        mem_delete(mem, ping);
351
42
        return nullptr;
352
42
    }
353
354
4.09k
    ping->mono_time = mono_time;
355
4.09k
    ping->rng = rng;
356
4.09k
    ping->dht = dht;
357
4.09k
    networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_REQUEST, &handle_ping_request, dht);
358
4.09k
    networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_RESPONSE, &handle_ping_response, dht);
359
360
4.09k
    return ping;
361
4.13k
}
362
363
void ping_kill(const Memory *mem, Ping *ping)
364
3.11k
{
365
3.11k
    if (ping == nullptr) {
366
63
        return;
367
63
    }
368
369
3.04k
    networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_REQUEST, nullptr, nullptr);
370
3.04k
    networking_registerhandler(dht_get_net(ping->dht), NET_PACKET_PING_RESPONSE, nullptr, nullptr);
371
3.04k
    ping_array_kill(ping->ping_array);
372
373
3.04k
    mem_delete(mem, ping);
374
3.04k
}