Coverage Report

Created: 2026-10-24 08:16

/work/testing/support/doubles/fake_network_tcp_test.cc
Line
Count
Source
1
#include <gtest/gtest.h>
2
3
#include "fake_sockets.hh"
4
#include "network_universe.hh"
5
6
namespace tox::test {
7
namespace {
8
9
    class FakeNetworkTcpTest : public ::testing::Test {
10
    protected:
11
        NetworkUniverse universe;
12
13
        IP make_ip(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
14
18
        {
15
18
            IP ip;
16
18
            ip_init(&ip, false);
17
18
            ip.ip.v4.uint8[0] = a;
18
18
            ip.ip.v4.uint8[1] = b;
19
18
            ip.ip.v4.uint8[2] = c;
20
18
            ip.ip.v4.uint8[3] = d;
21
18
            return ip;
22
18
        }
23
    };
24
25
    TEST_F(FakeNetworkTcpTest, MultipleConnectionsToSamePort)
26
1
    {
27
1
        universe.set_verbose(true);
28
1
        IP server_ip = make_ip(10, 0, 0, 1);
29
1
        uint16_t server_port = 12345;
30
31
1
        FakeTcpSocket server(universe);
32
1
        server.set_ip(server_ip);
33
1
        IP_Port server_addr{server_ip, net_htons(server_port)};
34
1
        ASSERT_EQ(server.bind(&server_addr), 0);
35
1
        ASSERT_EQ(server.listen(5), 0);
36
37
        // Client 1
38
1
        IP client1_ip = make_ip(10, 0, 0, 2);
39
1
        FakeTcpSocket client1(universe);
40
1
        client1.set_ip(client1_ip);
41
1
        client1.connect(&server_addr);
42
43
        // Client 2 (same IP as client 1, different port)
44
1
        FakeTcpSocket client2(universe);
45
1
        client2.set_ip(client1_ip);
46
1
        client2.connect(&server_addr);
47
48
        // Handshake for both
49
        // 1. SYNs
50
1
        universe.process_events(0);
51
1
        universe.process_events(0);
52
53
        // 2. SYN-ACKs
54
1
        universe.process_events(0);
55
1
        universe.process_events(0);
56
57
        // 3. ACKs
58
1
        universe.process_events(0);
59
1
        universe.process_events(0);
60
61
1
        auto accepted1 = server.accept(nullptr);
62
1
        auto accepted2 = server.accept(nullptr);
63
64
1
        ASSERT_NE(accepted1, nullptr);
65
1
        ASSERT_NE(accepted2, nullptr);
66
67
1
        EXPECT_EQ(
68
1
            static_cast<FakeTcpSocket *>(accepted1.get())->state(), FakeTcpSocket::ESTABLISHED);
69
1
        EXPECT_EQ(
70
1
            static_cast<FakeTcpSocket *>(accepted2.get())->state(), FakeTcpSocket::ESTABLISHED);
71
72
        // Verify data isolation
73
1
        const char *msg1 = "Message 1";
74
1
        const char *msg2 = "Message 2";
75
76
1
        client1.send(reinterpret_cast<const uint8_t *>(msg1), strlen(msg1));
77
1
        client2.send(reinterpret_cast<const uint8_t *>(msg2), strlen(msg2));
78
79
1
        universe.process_events(0);
80
1
        universe.process_events(0);
81
82
1
        uint8_t buf[100];
83
1
        int len1 = accepted1->recv(buf, sizeof(buf));
84
1
        EXPECT_EQ(len1, strlen(msg1));
85
1
        int len2 = accepted2->recv(buf, sizeof(buf));
86
1
        EXPECT_EQ(len2, strlen(msg2));
87
1
    }
88
89
    TEST_F(FakeNetworkTcpTest, DuplicateSynCreatesDuplicateConnections)
90
1
    {
91
1
        universe.set_verbose(true);
92
1
        IP server_ip = make_ip(10, 0, 0, 1);
93
1
        uint16_t server_port = 12345;
94
95
1
        FakeTcpSocket server(universe);
96
1
        server.set_ip(server_ip);
97
1
        IP_Port server_addr{server_ip, net_htons(server_port)};
98
1
        server.bind(&server_addr);
99
1
        server.listen(5);
100
101
1
        IP client_ip = make_ip(10, 0, 0, 2);
102
1
        IP_Port client_addr{client_ip, net_htons(33445)};
103
104
1
        Packet p{};
105
1
        p.from = client_addr;
106
1
        p.to = server_addr;
107
1
        p.is_tcp = true;
108
1
        p.tcp_flags = 0x02;  // SYN
109
1
        p.seq = 100;
110
111
1
        universe.send_packet(p);
112
1
        universe.send_packet(p);  // Duplicate SYN
113
114
1
        universe.process_events(0);
115
1
        universe.process_events(0);
116
117
        // Now send ACK from client
118
1
        Packet ack{};
119
1
        ack.from = client_addr;
120
1
        ack.to = server_addr;
121
1
        ack.is_tcp = true;
122
1
        ack.tcp_flags = 0x10;  // ACK
123
1
        ack.ack = 101;
124
125
1
        universe.send_packet(ack);
126
1
        universe.process_events(0);
127
128
1
        auto accepted1 = server.accept(nullptr);
129
1
        auto accepted2 = server.accept(nullptr);
130
131
1
        ASSERT_NE(accepted1, nullptr);
132
1
        EXPECT_EQ(accepted2, nullptr);  // This should pass now
133
1
    }
134
135
    TEST_F(FakeNetworkTcpTest, PeerCloseClearsConnection)
136
1
    {
137
1
        universe.set_verbose(true);
138
1
        IP server_ip = make_ip(10, 0, 0, 1);
139
1
        uint16_t server_port = 12345;
140
141
1
        FakeTcpSocket server(universe);
142
1
        server.set_ip(server_ip);
143
1
        IP_Port server_addr{server_ip, net_htons(server_port)};
144
1
        server.bind(&server_addr);
145
1
        server.listen(5);
146
147
1
        IP client_ip = make_ip(10, 0, 0, 2);
148
1
        FakeTcpSocket client(universe);
149
1
        client.set_ip(client_ip);
150
1
        client.connect(&server_addr);
151
152
        // Handshake
153
1
        universe.process_events(0);  // SYN
154
1
        universe.process_events(0);  // SYN-ACK
155
1
        universe.process_events(0);  // ACK
156
157
1
        auto accepted = server.accept(nullptr);
158
1
        ASSERT_NE(accepted, nullptr);
159
1
        EXPECT_EQ(
160
1
            static_cast<FakeTcpSocket *>(accepted.get())->state(), FakeTcpSocket::ESTABLISHED);
161
162
        // Client closes
163
1
        client.close();
164
1
        universe.process_events(0);  // Deliver RST/FIN
165
166
        // Server should no longer be ESTABLISHED
167
1
        EXPECT_EQ(static_cast<FakeTcpSocket *>(accepted.get())->state(), FakeTcpSocket::CLOSED);
168
169
        // Now if client reconnects with same port
170
1
        FakeTcpSocket client2(universe);
171
1
        client2.set_ip(client_ip);
172
1
        client2.connect(&server_addr);
173
1
        universe.process_events(0);  // Deliver SYN
174
175
        // Node 2 port 20002 should have: 1 LISTEN, 0 ESTABLISHED (old one gone), 1 SYN_RECEIVED
176
        // (new one) Total targets should be 2.
177
1
    }
178
179
    TEST_F(FakeNetworkTcpTest, DataNotProcessedByMultipleSockets)
180
1
    {
181
1
        universe.set_verbose(true);
182
1
        IP server_ip = make_ip(10, 0, 0, 1);
183
1
        uint16_t server_port = 12345;
184
185
1
        FakeTcpSocket server(universe);
186
1
        server.set_ip(server_ip);
187
1
        IP_Port server_addr{server_ip, net_htons(server_port)};
188
1
        server.bind(&server_addr);
189
1
        server.listen(5);
190
191
1
        IP client_ip = make_ip(10, 0, 0, 2);
192
1
        IP_Port client_addr{client_ip, net_htons(33445)};
193
194
        // Manually create two "established" sockets on the same port for the same peer
195
        // This simulates a bug where duplicate connections were allowed.
196
1
        auto sock1 = FakeTcpSocket::create_connected(universe, client_addr, server_port);
197
1
        sock1->set_ip(server_ip);
198
1
        auto sock2 = FakeTcpSocket::create_connected(universe, client_addr, server_port);
199
1
        sock2->set_ip(server_ip);
200
201
1
        universe.bind_tcp(server_ip, server_port, sock1.get());
202
1
        universe.bind_tcp(server_ip, server_port, sock2.get());
203
204
        // Send data from client to server
205
1
        Packet p{};
206
1
        p.from = client_addr;
207
1
        p.to = server_addr;
208
1
        p.is_tcp = true;
209
1
        p.tcp_flags = 0x10;  // ACK (Data)
210
1
        const char *data = "Unique";
211
1
        p.data.assign(data, data + strlen(data));
212
213
1
        universe.send_packet(p);
214
1
        universe.process_events(0);
215
216
        // Only ONE of them should have received it, or at least they shouldn't BOTH have it
217
        // in a way that suggests duplicate delivery.
218
1
        EXPECT_TRUE((sock1->recv_buffer_size() == strlen(data))
219
1
            ^ (sock2->recv_buffer_size() == strlen(data)));
220
1
    }
221
222
    TEST_F(FakeNetworkTcpTest, ConnectionCollision)
223
1
    {
224
1
        universe.set_verbose(true);
225
1
        IP server_ip = make_ip(10, 0, 0, 1);
226
1
        uint16_t server_port = 12345;
227
228
1
        FakeTcpSocket server(universe);
229
1
        server.set_ip(server_ip);
230
1
        IP_Port server_addr{server_ip, net_htons(server_port)};
231
1
        server.bind(&server_addr);
232
1
        server.listen(5);
233
234
1
        IP client_ip = make_ip(10, 0, 0, 2);
235
236
1
        FakeTcpSocket client1(universe);
237
1
        client1.set_ip(client_ip);
238
        // Bind to specific port to force collision later
239
1
        IP_Port client_bind_addr{client_ip, net_htons(33445)};
240
1
        client1.bind(&client_bind_addr);
241
1
        client1.connect(&server_addr);
242
243
        // Handshake 1
244
1
        universe.process_events(0);  // SYN
245
1
        universe.process_events(0);  // SYN-ACK
246
1
        universe.process_events(0);  // ACK
247
248
1
        auto accepted1 = server.accept(nullptr);
249
1
        ASSERT_NE(accepted1, nullptr);
250
1
        EXPECT_EQ(
251
1
            static_cast<FakeTcpSocket *>(accepted1.get())->state(), FakeTcpSocket::ESTABLISHED);
252
253
        // Now client 1 "reconnects" (e.g. after a crash or timeout, but using same port)
254
1
        FakeTcpSocket client2(universe);
255
1
        client2.set_ip(client_ip);
256
1
        client2.bind(&client_bind_addr);  // Forced collision
257
1
        client2.connect(&server_addr);
258
259
        // Deliver new SYN
260
1
        universe.process_events(0);
261
262
        // server_addr port 12345 now has:
263
        // 1. LISTEN socket
264
        // 2. accepted1 (ESTABLISHED with 10.0.0.2:33445)
265
266
        // In our simplified simulation, the ESTABLISHED socket now handles the SYN by returning
267
        // true (ignoring it). So no new connection is created.
268
1
        auto accepted2 = server.accept(nullptr);
269
1
        EXPECT_EQ(accepted2, nullptr);
270
271
1
        const char *msg1 = "Data 1";
272
1
        client1.send(reinterpret_cast<const uint8_t *>(msg1), strlen(msg1));
273
1
        universe.process_events(0);
274
275
        // Data should still go to accepted1
276
1
        EXPECT_EQ(accepted1->recv_buffer_size(), strlen(msg1));
277
1
    }
278
279
    TEST_F(FakeNetworkTcpTest, LoopbackConnection)
280
1
    {
281
1
        universe.set_verbose(true);
282
1
        IP node_ip = make_ip(10, 0, 0, 1);
283
1
        uint16_t port = 12345;
284
285
1
        FakeTcpSocket server(universe);
286
1
        server.set_ip(node_ip);
287
1
        IP_Port listen_addr{node_ip, net_htons(port)};
288
1
        server.bind(&listen_addr);
289
1
        server.listen(5);
290
291
1
        FakeTcpSocket client(universe);
292
1
        client.set_ip(node_ip);
293
1
        IP loopback_ip;
294
1
        ip_init(&loopback_ip, false);
295
1
        loopback_ip.ip.v4.uint32 = net_htonl(0x7F000001);
296
1
        IP_Port server_loopback_addr{loopback_ip, net_htons(port)};
297
298
1
        client.connect(&server_loopback_addr);
299
300
        // SYN (Client -> 127.0.0.1:12345)
301
1
        universe.process_events(0);
302
303
        // SYN-ACK (Server -> Client)
304
1
        universe.process_events(0);
305
306
        // ACK (Client -> Server)
307
1
        universe.process_events(0);
308
309
1
        EXPECT_EQ(client.state(), FakeTcpSocket::ESTABLISHED);
310
1
        auto accepted = server.accept(nullptr);
311
1
        ASSERT_NE(accepted, nullptr);
312
1
        EXPECT_EQ(
313
1
            static_cast<FakeTcpSocket *>(accepted.get())->state(), FakeTcpSocket::ESTABLISHED);
314
315
        // Data Transfer
316
1
        const char *msg = "Loopback";
317
1
        client.send(reinterpret_cast<const uint8_t *>(msg), strlen(msg));
318
1
        universe.process_events(0);
319
320
1
        uint8_t buf[100];
321
1
        int len = accepted->recv(buf, sizeof(buf));
322
1
        ASSERT_EQ(len, strlen(msg));
323
1
        EXPECT_EQ(std::string(reinterpret_cast<char *>(buf), len), msg);
324
1
    }
325
326
    TEST_F(FakeNetworkTcpTest, SimultaneousConnect)
327
1
    {
328
1
        universe.set_verbose(true);
329
1
        IP ipA = make_ip(10, 0, 0, 1);
330
1
        IP ipB = make_ip(10, 0, 0, 2);
331
1
        uint16_t portA = 10001;
332
1
        uint16_t portB = 10002;
333
334
1
        FakeTcpSocket sockA(universe);
335
1
        sockA.set_ip(ipA);
336
1
        IP_Port addrA{ipA, net_htons(portA)};
337
1
        sockA.bind(&addrA);
338
1
        sockA.listen(5);
339
340
1
        FakeTcpSocket sockB(universe);
341
1
        sockB.set_ip(ipB);
342
1
        IP_Port addrB{ipB, net_htons(portB)};
343
1
        sockB.bind(&addrB);
344
1
        sockB.listen(5);
345
346
        // A connects to B
347
1
        sockA.connect(&addrB);
348
        // B connects to A
349
1
        sockB.connect(&addrA);
350
351
        // This is "simultaneous open" in TCP but here they are also LISTENing.
352
        // Toxcore uses this pattern sometimes.
353
354
1
        universe.process_events(0);  // SYN from A to B
355
1
        universe.process_events(0);  // SYN from B to A
356
357
1
        universe.process_events(0);  // SYN-ACK from B to A (for A's SYN)
358
1
        universe.process_events(0);  // SYN-ACK from A to B (for B's SYN)
359
360
1
        universe.process_events(0);  // ACK from A to B
361
1
        universe.process_events(0);  // ACK from B to A
362
363
1
        EXPECT_EQ(sockA.state(), FakeTcpSocket::ESTABLISHED);
364
1
        EXPECT_EQ(sockB.state(), FakeTcpSocket::ESTABLISHED);
365
1
    }
366
367
    TEST_F(FakeNetworkTcpTest, DataInHandshakeAck)
368
1
    {
369
1
        universe.set_verbose(true);
370
1
        IP server_ip = make_ip(10, 0, 0, 1);
371
1
        uint16_t server_port = 12345;
372
373
1
        FakeTcpSocket server(universe);
374
1
        server.set_ip(server_ip);
375
1
        IP_Port server_addr{server_ip, net_htons(server_port)};
376
1
        server.bind(&server_addr);
377
1
        server.listen(5);
378
379
1
        IP client_ip = make_ip(10, 0, 0, 2);
380
1
        IP_Port client_addr{client_ip, net_htons(33445)};
381
382
        // 1. SYN
383
1
        Packet syn{};
384
1
        syn.from = client_addr;
385
1
        syn.to = server_addr;
386
1
        syn.is_tcp = true;
387
1
        syn.tcp_flags = 0x02;
388
1
        universe.send_packet(syn);
389
1
        universe.process_events(0);
390
391
        // 2. SYN-ACK (Server -> Client)
392
1
        universe.process_events(0);
393
394
        // 3. ACK + Data (Client -> Server)
395
1
        Packet ack{};
396
1
        ack.from = client_addr;
397
1
        ack.to = server_addr;
398
1
        ack.is_tcp = true;
399
1
        ack.tcp_flags = 0x10;
400
1
        const char *data = "HandshakeData";
401
1
        ack.data.assign(data, data + strlen(data));
402
1
        universe.send_packet(ack);
403
1
        universe.process_events(0);
404
405
1
        auto accepted = server.accept(nullptr);
406
1
        ASSERT_NE(accepted, nullptr);
407
1
        EXPECT_EQ(accepted->recv_buffer_size(), strlen(data));
408
1
    }
409
410
    TEST_F(FakeNetworkTcpTest, DataInSynAck)
411
1
    {
412
1
        universe.set_verbose(true);
413
1
        IP ipA = make_ip(10, 0, 0, 1);
414
1
        IP ipB = make_ip(10, 0, 0, 2);
415
1
        uint16_t portA = 10001;
416
1
        uint16_t portB = 10002;
417
418
1
        FakeTcpSocket sockA(universe);
419
1
        sockA.set_ip(ipA);
420
1
        IP_Port addrA{ipA, net_htons(portA)};
421
1
        sockA.bind(&addrA);
422
1
        sockA.listen(5);
423
424
1
        FakeTcpSocket sockB(universe);
425
1
        sockB.set_ip(ipB);
426
1
        IP_Port addrB{ipB, net_htons(portB)};
427
1
        sockB.bind(&addrB);
428
1
        sockB.listen(5);
429
430
        // A connects to B
431
1
        sockA.connect(&addrB);
432
        // B connects to A
433
1
        sockB.connect(&addrA);
434
435
1
        universe.process_events(0);  // SYN from A to B
436
1
        universe.process_events(0);  // SYN from B to A
437
438
        // Now A receives B's SYN and transitions to SYN_RECEIVED, sending SYN-ACK.
439
        // We want to simulate B sending SYN-ACK WITH DATA to A.
440
1
        Packet syn_ack_data{};
441
1
        syn_ack_data.from = addrB;
442
1
        syn_ack_data.to = addrA;
443
1
        syn_ack_data.is_tcp = true;
444
1
        syn_ack_data.tcp_flags = 0x12;  // SYN | ACK
445
1
        const char *msg = "SynAckData";
446
1
        syn_ack_data.data.assign(msg, msg + strlen(msg));
447
448
1
        universe.send_packet(syn_ack_data);
449
1
        universe.process_events(0);  // A receives SYN-ACK with data
450
451
1
        EXPECT_EQ(sockA.state(), FakeTcpSocket::ESTABLISHED);
452
1
        EXPECT_EQ(sockA.recv_buffer_size(), strlen(msg));
453
1
    }
454
455
    TEST_F(FakeNetworkTcpTest, LoopbackWithNodeIPMixed)
456
1
    {
457
1
        universe.set_verbose(true);
458
1
        IP node_ip = make_ip(10, 0, 0, 1);
459
1
        uint16_t port = 12345;
460
461
1
        FakeTcpSocket server(universe);
462
1
        server.set_ip(node_ip);
463
1
        IP_Port listen_addr{node_ip, net_htons(port)};
464
1
        server.bind(&listen_addr);
465
1
        server.listen(5);
466
467
1
        FakeTcpSocket client(universe);
468
1
        client.set_ip(node_ip);
469
1
        IP loopback_ip;
470
1
        ip_init(&loopback_ip, false);
471
1
        loopback_ip.ip.v4.uint32 = net_htonl(0x7F000001);
472
1
        IP_Port server_loopback_addr{loopback_ip, net_htons(port)};
473
474
        // Client connects to 127.0.0.1
475
1
        client.connect(&server_loopback_addr);
476
477
1
        universe.process_events(0);  // SYN (Client -> 127.0.0.1)
478
1
        universe.process_events(0);  // SYN-ACK (Server -> Client)
479
1
        universe.process_events(0);  // ACK (Client -> Server)
480
481
1
        EXPECT_EQ(client.state(), FakeTcpSocket::ESTABLISHED);
482
1
        auto accepted = server.accept(nullptr);
483
1
        ASSERT_NE(accepted, nullptr);
484
485
        // Now manually simulate a packet coming from the server's EXTERNAL IP to the client.
486
        // This happens because the server socket is bound to node_ip, so its packets might
487
        // be delivered as coming from node_ip even if the client connected to 127.0.0.1.
488
1
        Packet p{};
489
1
        p.from = listen_addr;  // node_ip:port
490
1
        p.to.ip = node_ip;
491
1
        p.to.port = net_htons(client.local_port());
492
1
        p.is_tcp = true;
493
1
        p.tcp_flags = 0x10;  // ACK
494
1
        const char *msg = "MixedIP";
495
1
        p.data.assign(msg, msg + strlen(msg));
496
497
1
        universe.send_packet(p);
498
1
        universe.process_events(0);
499
500
1
        EXPECT_EQ(client.recv_buffer_size(), strlen(msg));
501
1
    }
502
503
}
504
}