/work/testing/support/doubles/fake_sockets.hh
Line | Count | Source (jump to first uncovered line) |
1 | | #ifndef C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_SOCKETS_H |
2 | | #define C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_SOCKETS_H |
3 | | |
4 | | #include <atomic> |
5 | | #include <cstddef> |
6 | | #include <cstdint> |
7 | | #include <deque> |
8 | | #include <functional> |
9 | | #include <memory> |
10 | | #include <mutex> |
11 | | #include <optional> |
12 | | #include <vector> |
13 | | |
14 | | #ifdef _WIN32 |
15 | | #define WIN32_LEAN_AND_MEAN |
16 | | #define NOMINMAX |
17 | | #include <winsock2.h> |
18 | | #else |
19 | | #include <sys/socket.h> |
20 | | #endif |
21 | | |
22 | | #include "../../../toxcore/attributes.h" |
23 | | #include "../../../toxcore/network.h" |
24 | | |
25 | | namespace tox::test { |
26 | | |
27 | | class NetworkUniverse; |
28 | | struct Packet; |
29 | | |
30 | | /** |
31 | | * @brief Abstract base class for all fake sockets. |
32 | | */ |
33 | | class FakeSocket { |
34 | | public: |
35 | | FakeSocket(NetworkUniverse &universe, int type); |
36 | | virtual ~FakeSocket(); |
37 | | |
38 | | virtual int bind(const IP_Port *_Nonnull addr) = 0; |
39 | | virtual int connect(const IP_Port *_Nonnull addr) = 0; |
40 | | virtual int listen(int backlog) = 0; |
41 | | virtual std::unique_ptr<FakeSocket> accept(IP_Port *_Nullable addr) = 0; |
42 | | |
43 | | virtual int send(const uint8_t *_Nonnull buf, std::size_t len) = 0; |
44 | | virtual int recv(uint8_t *_Nonnull buf, std::size_t len) = 0; |
45 | | |
46 | 0 | virtual std::size_t recv_buffer_size() { return 0; } |
47 | 0 | virtual bool is_readable() { return recv_buffer_size() > 0; } |
48 | 0 | virtual bool is_writable() { return true; } |
49 | | |
50 | | virtual int sendto(const uint8_t *_Nonnull buf, std::size_t len, const IP_Port *_Nonnull addr) |
51 | | = 0; |
52 | | virtual int recvfrom(uint8_t *_Nonnull buf, std::size_t len, IP_Port *_Nonnull addr) = 0; |
53 | | |
54 | | virtual int getsockopt( |
55 | | int level, int optname, void *_Nonnull optval, std::size_t *_Nonnull optlen); |
56 | | virtual int setsockopt(int level, int optname, const void *_Nonnull optval, std::size_t optlen); |
57 | | virtual int socket_nonblock(bool nonblock); |
58 | | |
59 | | virtual int close(); |
60 | | |
61 | 0 | bool is_nonblocking() const { return nonblocking_; } |
62 | 2.31k | int type() const { return type_; } |
63 | 2.87k | uint16_t local_port() const { return local_port_; } |
64 | 0 | const IP &ip_address() const { return ip_; } |
65 | | |
66 | 8.92k | void set_ip(const IP &ip) { ip_ = ip; } |
67 | | |
68 | | protected: |
69 | | NetworkUniverse &universe_; |
70 | | int type_; |
71 | | bool nonblocking_ = false; |
72 | | uint16_t local_port_ = 0; |
73 | | IP ip_; |
74 | | std::mutex mutex_; // Use regular mutex (recursive_mutex not fully supported in MinGW) |
75 | | }; |
76 | | |
77 | | /** |
78 | | * @brief Implements UDP logic. |
79 | | */ |
80 | | class FakeUdpSocket : public FakeSocket { |
81 | | public: |
82 | | static constexpr std::size_t kMaxQueueSize = 1024; |
83 | | |
84 | | explicit FakeUdpSocket(NetworkUniverse &universe); |
85 | | ~FakeUdpSocket() override; |
86 | | |
87 | | int bind(const IP_Port *_Nonnull addr) override; |
88 | | int connect(const IP_Port *_Nonnull addr) override; |
89 | | int listen(int backlog) override; |
90 | | std::unique_ptr<FakeSocket> accept(IP_Port *_Nullable addr) override; |
91 | | int close() override; |
92 | | |
93 | | int send(const uint8_t *_Nonnull buf, std::size_t len) override; |
94 | | int recv(uint8_t *_Nonnull buf, std::size_t len) override; |
95 | | std::size_t recv_buffer_size() override; |
96 | | |
97 | | int sendto(const uint8_t *_Nonnull buf, std::size_t len, const IP_Port *_Nonnull addr) override; |
98 | | int recvfrom(uint8_t *_Nonnull buf, std::size_t len, IP_Port *_Nonnull addr) override; |
99 | | |
100 | | // Called by Universe to deliver a packet |
101 | | void push_packet(std::vector<uint8_t> data, IP_Port from); |
102 | | |
103 | | using PacketSource = std::function<bool(std::vector<uint8_t> &data, IP_Port &from)>; |
104 | | void set_packet_source(PacketSource source); |
105 | | |
106 | | using RecvObserver = std::function<void(const std::vector<uint8_t> &data, const IP_Port &from)>; |
107 | | void set_recv_observer(RecvObserver observer); |
108 | | |
109 | | private: |
110 | | void close_impl(); |
111 | | |
112 | | struct PendingPacket { |
113 | | std::vector<uint8_t> data; |
114 | | IP_Port from; |
115 | | }; |
116 | | std::deque<PendingPacket> recv_queue_; |
117 | | PacketSource packet_source_; |
118 | | RecvObserver recv_observer_; |
119 | | }; |
120 | | |
121 | | /** |
122 | | * @brief Implements TCP logic (Simplified for simulation). |
123 | | * |
124 | | * Supports: |
125 | | * - 3-way handshake simulation (instant or delayed) |
126 | | * - Stream buffering |
127 | | * - Connection states |
128 | | */ |
129 | | class FakeTcpSocket : public FakeSocket { |
130 | | public: |
131 | | enum State { CLOSED, LISTEN, SYN_SENT, SYN_RECEIVED, ESTABLISHED, CLOSE_WAIT }; |
132 | | |
133 | | static constexpr std::size_t kMaxBufferSize = 1024 * 1024; |
134 | | static constexpr std::size_t kMaxBacklog = 128; |
135 | | |
136 | | explicit FakeTcpSocket(NetworkUniverse &universe); |
137 | | ~FakeTcpSocket() override; |
138 | | |
139 | | int bind(const IP_Port *_Nonnull addr) override; |
140 | | int connect(const IP_Port *_Nonnull addr) override; |
141 | | int listen(int backlog) override; |
142 | | std::unique_ptr<FakeSocket> accept(IP_Port *_Nullable addr) override; |
143 | | int close() override; |
144 | | |
145 | | int send(const uint8_t *_Nonnull buf, std::size_t len) override; |
146 | | int recv(uint8_t *_Nonnull buf, std::size_t len) override; |
147 | | std::size_t recv_buffer_size() override; |
148 | | bool is_readable() override; |
149 | | bool is_writable() override; |
150 | | |
151 | | int sendto(const uint8_t *_Nonnull buf, std::size_t len, const IP_Port *_Nonnull addr) override; |
152 | | int recvfrom(uint8_t *_Nonnull buf, std::size_t len, IP_Port *_Nonnull addr) override; |
153 | | |
154 | | int getsockopt( |
155 | | int level, int optname, void *_Nonnull optval, std::size_t *_Nonnull optlen) override; |
156 | | |
157 | | // Internal events |
158 | | bool handle_packet(const Packet &p); |
159 | | |
160 | 155 | State state() const { return state_; } |
161 | 77 | const IP_Port &remote_addr() const { return remote_addr_; } |
162 | | |
163 | | // Factory for accepting connections |
164 | | static std::unique_ptr<FakeTcpSocket> create_connected( |
165 | | NetworkUniverse &universe, const IP_Port &remote, uint16_t local_port); |
166 | | |
167 | | private: |
168 | | void close_impl(); |
169 | | |
170 | | State state_ = CLOSED; |
171 | | IP_Port remote_addr_; |
172 | | std::deque<uint8_t> recv_buffer_; |
173 | | std::deque<std::unique_ptr<FakeTcpSocket>> pending_connections_; |
174 | | int backlog_ = 0; |
175 | | |
176 | | uint32_t next_seq_ = 0; |
177 | | uint32_t last_ack_ = 0; |
178 | | }; |
179 | | |
180 | | std::ostream &operator<<(std::ostream &os, FakeTcpSocket::State state); |
181 | | |
182 | | } // namespace tox::test |
183 | | |
184 | | #endif // C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_SOCKETS_H |