/work/auto_tests/forwarding_test.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include <assert.h> |
2 | | #include <stdlib.h> |
3 | | #include <string.h> |
4 | | |
5 | | #include "../toxcore/tox.h" |
6 | | #include "../toxcore/announce.h" |
7 | | #include "../testing/misc_tools.h" |
8 | | #include "../toxcore/mono_time.h" |
9 | | #include "../toxcore/forwarding.h" |
10 | | #include "../toxcore/net_crypto.h" |
11 | | #include "../toxcore/os_memory.h" |
12 | | #include "../toxcore/os_random.h" |
13 | | #include "auto_test_support.h" |
14 | | #include "check_compat.h" |
15 | | |
16 | | #ifndef USE_IPV6 |
17 | | #define USE_IPV6 1 |
18 | | #endif |
19 | | |
20 | | static inline IP get_loopback(void) |
21 | 136 | { |
22 | 136 | IP ip; |
23 | | #if USE_IPV6 |
24 | | ip.family = net_family_ipv6(); |
25 | | ip.ip.v6 = get_ip6_loopback(); |
26 | | #else |
27 | 136 | ip.family = net_family_ipv4(); |
28 | 136 | ip.ip.v4 = get_ip4_loopback(); |
29 | 136 | #endif |
30 | 136 | return ip; |
31 | 136 | } |
32 | | |
33 | 3.21k | #define NUM_FORWARDER 20 |
34 | 343 | #define NUM_FORWARDER_TCP 5 |
35 | 125 | #define NUM_FORWARDER_DHT (NUM_FORWARDER - NUM_FORWARDER_TCP) |
36 | 2 | #define NUM_FORWARDING_ITERATIONS 1 |
37 | 93 | #define FORWARD_SEND_INTERVAL 2 |
38 | 41 | #define FORWARDING_BASE_PORT 36571 |
39 | | |
40 | | typedef struct Test_Data { |
41 | | Networking_Core *net; |
42 | | uint32_t send_back; |
43 | | uint64_t sent; |
44 | | bool returned; |
45 | | } Test_Data; |
46 | | |
47 | | static void test_forwarded_request_cb(void *object, const IP_Port *forwarder, |
48 | | const uint8_t *sendback, uint16_t sendback_length, |
49 | | const uint8_t *data, uint16_t length, void *userdata) |
50 | 20 | { |
51 | 20 | const Test_Data *test_data = (const Test_Data *)object; |
52 | 20 | const uint8_t *index = (const uint8_t *)userdata; |
53 | | |
54 | 20 | if (length != 12 || memcmp("hello: ", data, 8) != 0) { |
55 | 0 | printf("[%u] got unexpected data of length %d\n", *index, length); |
56 | 0 | return; |
57 | 0 | } |
58 | | |
59 | 20 | uint8_t reply[12]; |
60 | 20 | memcpy(reply, "reply: ", 8); |
61 | 20 | memcpy(reply + 8, data + 8, 4); |
62 | 20 | ck_assert_msg(forward_reply(test_data->net, forwarder, sendback, sendback_length, reply, 12), |
63 | 20 | "[%u] forward_reply failed", *index); |
64 | 20 | } |
65 | | |
66 | | static void test_forwarded_response_cb(void *object, |
67 | | const uint8_t *data, uint16_t length, void *userdata) |
68 | 20 | { |
69 | 20 | Test_Data *test_data = (Test_Data *)object; |
70 | 20 | const uint8_t *index = (const uint8_t *)userdata; |
71 | | |
72 | 20 | if (length != 12 || memcmp("reply: ", data, 8) != 0) { |
73 | 0 | printf("[%u] got unexpected data of length %d\n", *index, length); |
74 | 0 | return; |
75 | 0 | } |
76 | | |
77 | 20 | uint32_t send_back; |
78 | 20 | net_unpack_u32(data + 8, &send_back); |
79 | | |
80 | 20 | if (test_data->send_back == send_back) { |
81 | 20 | test_data->returned = true; |
82 | 20 | } |
83 | 20 | } |
84 | | |
85 | | static bool all_returned(Test_Data *test_data) |
86 | 124 | { |
87 | 392 | for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { |
88 | 391 | if (!test_data[i].returned) { |
89 | 123 | return false; |
90 | 123 | } |
91 | 391 | } |
92 | | |
93 | 1 | return true; |
94 | 124 | } |
95 | | |
96 | | typedef struct Forwarding_Subtox { |
97 | | Logger *log; |
98 | | Mono_Time *mono_time; |
99 | | Networking_Core *net; |
100 | | Net_Profile *tcp_np; |
101 | | DHT *dht; |
102 | | Net_Crypto *c; |
103 | | Forwarding *forwarding; |
104 | | Announcements *announce; |
105 | | } Forwarding_Subtox; |
106 | | |
107 | | static Forwarding_Subtox *new_forwarding_subtox(const Memory *mem, bool no_udp, uint32_t *index, uint16_t port) |
108 | 20 | { |
109 | 20 | const Random *rng = os_random(); |
110 | 20 | ck_assert(rng != nullptr); |
111 | 20 | const Network *ns = os_network(); |
112 | 20 | ck_assert(ns != nullptr); |
113 | | |
114 | 20 | Forwarding_Subtox *subtox = (Forwarding_Subtox *)calloc(1, sizeof(Forwarding_Subtox)); |
115 | 20 | ck_assert(subtox != nullptr); |
116 | | |
117 | 20 | subtox->log = logger_new(mem); |
118 | 20 | ck_assert(subtox->log != nullptr); |
119 | 20 | logger_callback_log(subtox->log, print_debug_logger, nullptr, index); |
120 | 20 | subtox->mono_time = mono_time_new(mem, nullptr, nullptr); |
121 | | |
122 | 20 | if (no_udp) { |
123 | 5 | subtox->net = new_networking_no_udp(subtox->log, mem, ns); |
124 | 15 | } else { |
125 | 15 | const IP ip = get_loopback(); |
126 | 15 | subtox->net = new_networking_ex(subtox->log, mem, ns, &ip, port, port, nullptr); |
127 | 15 | } |
128 | | |
129 | 20 | subtox->dht = new_dht(subtox->log, mem, rng, ns, subtox->mono_time, subtox->net, true, true); |
130 | | |
131 | 20 | subtox->tcp_np = netprof_new(subtox->log, mem); |
132 | 20 | ck_assert(subtox->tcp_np != nullptr); |
133 | | |
134 | 20 | const TCP_Proxy_Info inf = {{{{0}}}}; |
135 | 20 | subtox->c = new_net_crypto(subtox->log, mem, rng, ns, subtox->mono_time, subtox->net, subtox->dht, &auto_test_dht_funcs, &inf, subtox->tcp_np); |
136 | | |
137 | 20 | subtox->forwarding = new_forwarding(subtox->log, mem, rng, subtox->mono_time, subtox->dht, subtox->net); |
138 | 20 | ck_assert(subtox->forwarding != nullptr); |
139 | | |
140 | 20 | subtox->announce = new_announcements(subtox->log, mem, rng, subtox->mono_time, subtox->forwarding, subtox->dht, subtox->net); |
141 | 20 | ck_assert(subtox->announce != nullptr); |
142 | | |
143 | 20 | return subtox; |
144 | 20 | } |
145 | | |
146 | | static void kill_forwarding_subtox(const Memory *mem, Forwarding_Subtox *subtox) |
147 | 20 | { |
148 | 20 | kill_announcements(subtox->announce); |
149 | 20 | kill_forwarding(subtox->forwarding); |
150 | 20 | kill_net_crypto(subtox->c); |
151 | 20 | netprof_kill(mem, subtox->tcp_np); |
152 | 20 | kill_dht(subtox->dht); |
153 | 20 | kill_networking(subtox->net); |
154 | 20 | mono_time_free(mem, subtox->mono_time); |
155 | 20 | logger_kill(subtox->log); |
156 | 20 | free(subtox); |
157 | 20 | } |
158 | | |
159 | | static void test_forwarding(void) |
160 | 1 | { |
161 | 1 | const Memory *mem = os_memory(); |
162 | 1 | ck_assert(mem != nullptr); |
163 | 1 | const Random *rng = os_random(); |
164 | 1 | ck_assert(rng != nullptr); |
165 | 1 | const Network *ns = os_network(); |
166 | 1 | ck_assert(ns != nullptr); |
167 | | |
168 | 1 | uint32_t index[NUM_FORWARDER]; |
169 | 1 | Forwarding_Subtox *subtoxes[NUM_FORWARDER]; |
170 | 1 | Test_Data test_data[NUM_FORWARDER]; |
171 | | |
172 | 1 | const IP ip = get_loopback(); |
173 | | |
174 | 21 | for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { |
175 | 20 | index[i] = i + 1; |
176 | 20 | subtoxes[i] = new_forwarding_subtox(mem, i < NUM_FORWARDER_TCP, &index[i], FORWARDING_BASE_PORT + i); |
177 | | |
178 | 20 | test_data[i].net = subtoxes[i]->net; |
179 | 20 | test_data[i].send_back = 0; |
180 | 20 | test_data[i].sent = 0; |
181 | 20 | test_data[i].returned = false; |
182 | 20 | set_callback_forwarded_request(subtoxes[i]->forwarding, test_forwarded_request_cb, &test_data[i]); |
183 | 20 | set_callback_forwarded_response(subtoxes[i]->forwarding, test_forwarded_response_cb, &test_data[i]); |
184 | 20 | set_forwarding_packet_tcp_connection_callback(nc_get_tcp_c(subtoxes[i]->c), test_forwarded_response_cb, &test_data[i]); |
185 | 20 | } |
186 | | |
187 | 1 | printf("testing forwarding via tcp relays and dht\n"); |
188 | | |
189 | 1 | struct Tox_Options *opts = tox_options_new(nullptr); |
190 | 1 | uint16_t forwarder_tcp_relay_port = 36570; |
191 | 1 | Tox *relay = nullptr; |
192 | | // Try a few different ports. |
193 | 1 | for (uint8_t i = 0; i < 100; ++i) { |
194 | 1 | tox_options_set_tcp_port(opts, forwarder_tcp_relay_port); |
195 | 1 | relay = tox_new_log(opts, nullptr, nullptr); |
196 | 1 | if (relay != nullptr) { |
197 | 1 | break; |
198 | 1 | } |
199 | 0 | ++forwarder_tcp_relay_port; |
200 | 0 | } |
201 | 1 | tox_options_free(opts); |
202 | 1 | ck_assert_msg(relay != nullptr, "Failed to create TCP relay"); |
203 | | |
204 | 1 | IP_Port relay_ipport_tcp = {ip, net_htons(forwarder_tcp_relay_port)}; |
205 | | |
206 | 1 | uint8_t dpk[TOX_PUBLIC_KEY_SIZE]; |
207 | 1 | tox_self_get_dht_id(relay, dpk); |
208 | | |
209 | 1 | printf("1-%d connected only to TCP server; %d-%d connected only to DHT\n", |
210 | 1 | NUM_FORWARDER_TCP, NUM_FORWARDER_TCP + 1, NUM_FORWARDER); |
211 | | |
212 | 6 | for (uint32_t i = 0; i < NUM_FORWARDER_TCP; ++i) { |
213 | 5 | set_tcp_onion_status(nc_get_tcp_c(subtoxes[i]->c), 1); |
214 | 5 | ck_assert_msg(add_tcp_relay(subtoxes[i]->c, &relay_ipport_tcp, dpk) == 0, |
215 | 5 | "Failed to add TCP relay"); |
216 | 5 | } |
217 | | |
218 | 1 | IP_Port relay_ipport_udp = {ip, net_htons(tox_self_get_udp_port(relay, nullptr))}; |
219 | | |
220 | 16 | for (uint32_t i = NUM_FORWARDER_TCP; i < NUM_FORWARDER; ++i) { |
221 | 15 | dht_bootstrap(subtoxes[i]->dht, &relay_ipport_udp, dpk); |
222 | 15 | } |
223 | | |
224 | 1 | printf("allowing DHT to populate\n"); |
225 | 1 | uint16_t dht_establish_iterations = NUM_FORWARDER * 5; |
226 | | |
227 | 2 | for (uint32_t n = 0; n < NUM_FORWARDING_ITERATIONS; ++n) { |
228 | 21 | for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { |
229 | 20 | test_data[i].sent = 0; |
230 | 20 | test_data[i].returned = false; |
231 | 20 | } |
232 | | |
233 | 124 | do { |
234 | 2.60k | for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { |
235 | 2.48k | Forwarding_Subtox *const subtox = subtoxes[i]; |
236 | 2.48k | mono_time_update(subtox->mono_time); |
237 | 2.48k | networking_poll(subtox->net, &index[i]); |
238 | 2.48k | do_net_crypto(subtox->c, &index[i]); |
239 | 2.48k | do_dht(subtox->dht); |
240 | | |
241 | 2.48k | if (dht_establish_iterations || |
242 | 2.48k | test_data[i].returned || |
243 | 2.48k | !mono_time_is_timeout(subtox->mono_time, test_data[i].sent, FORWARD_SEND_INTERVAL)) { |
244 | 2.45k | continue; |
245 | 2.45k | } |
246 | | |
247 | 21 | printf("%u", i + 1); |
248 | | |
249 | 21 | if (i < NUM_FORWARDER_TCP) { |
250 | 5 | printf(" --> TCPRelay"); |
251 | 5 | } |
252 | | |
253 | 21 | const uint16_t chain_length = i < NUM_FORWARDER_TCP ? i % 5 : i % 4 + 1; |
254 | 21 | uint8_t chain_keys[4 * CRYPTO_PUBLIC_KEY_SIZE]; |
255 | | |
256 | 21 | uint32_t chain_i = NUM_FORWARDER_TCP + (random_u32(rng) % NUM_FORWARDER_DHT); |
257 | 21 | const IP_Port first_ipp = {ip, net_htons(FORWARDING_BASE_PORT + chain_i)}; |
258 | | |
259 | 21 | printf(" --> %u", chain_i + 1); |
260 | | |
261 | 73 | for (uint16_t j = 0; j < chain_length; ++j) { |
262 | | // pick random different dht node: |
263 | 52 | chain_i += 1 + random_u32(rng) % (NUM_FORWARDER_DHT - 1); |
264 | 52 | chain_i = NUM_FORWARDER_TCP + (chain_i - NUM_FORWARDER_TCP) % NUM_FORWARDER_DHT; |
265 | | |
266 | 52 | const uint8_t *dest_pubkey = dht_get_self_public_key(subtoxes[chain_i]->dht); |
267 | | |
268 | 52 | memcpy(chain_keys + j * CRYPTO_PUBLIC_KEY_SIZE, dest_pubkey, CRYPTO_PUBLIC_KEY_SIZE); |
269 | 52 | printf(" --> %u", chain_i + 1); |
270 | 52 | } |
271 | | |
272 | 21 | printf("\n"); |
273 | | |
274 | 21 | const uint16_t length = 12; |
275 | 21 | uint8_t data[12]; |
276 | | |
277 | 21 | memcpy(data, "hello: ", 8); |
278 | 21 | test_data[i].send_back = random_u32(rng); |
279 | 21 | net_pack_u32(data + 8, test_data[i].send_back); |
280 | | |
281 | 21 | if (i < NUM_FORWARDER_TCP) { |
282 | 5 | IP_Port tcp_forwarder; |
283 | | |
284 | 5 | if (!get_random_tcp_conn_ip_port(subtox->c, &tcp_forwarder)) { |
285 | 0 | continue; |
286 | 0 | } |
287 | | |
288 | 5 | if (send_tcp_forward_request(subtox->log, subtox->c, &tcp_forwarder, &first_ipp, |
289 | 5 | chain_keys, chain_length, data, length) == 0) { |
290 | 5 | test_data[i].sent = mono_time_get(subtox->mono_time); |
291 | 5 | } |
292 | 16 | } else { |
293 | 16 | if (send_forward_request(subtox->net, &first_ipp, |
294 | 16 | chain_keys, chain_length, data, length)) { |
295 | 16 | test_data[i].sent = mono_time_get(subtox->mono_time); |
296 | 16 | } |
297 | 16 | } |
298 | 21 | } |
299 | | |
300 | 124 | tox_iterate(relay, nullptr); |
301 | | |
302 | 124 | if (dht_establish_iterations) { |
303 | 100 | --dht_establish_iterations; |
304 | | |
305 | 100 | if (!dht_establish_iterations) { |
306 | 1 | printf("making forward requests and expecting replies\n"); |
307 | 1 | } |
308 | 100 | } |
309 | | |
310 | 124 | c_sleep(50); |
311 | 124 | } while (!all_returned(test_data)); |
312 | | |
313 | | // This doesn't really belong in this test. |
314 | | // It can be removed once the full announce client test is in place. |
315 | 1 | printf("checking that nodes are marked as announce nodes\n"); |
316 | 1 | Node_format nodes[MAX_SENT_NODES]; |
317 | 1 | ck_assert(NUM_FORWARDER - NUM_FORWARDER_TCP > 1); |
318 | | |
319 | 16 | for (uint32_t i = NUM_FORWARDER_TCP; i < NUM_FORWARDER; ++i) { |
320 | 15 | ck_assert_msg(get_close_nodes(subtoxes[i]->dht, dht_get_self_public_key(subtoxes[i]->dht), nodes, net_family_unspec(), true, |
321 | 15 | true) > 0, |
322 | 15 | "node %u has no nodes marked as announce nodes", i); |
323 | 15 | } |
324 | 1 | } |
325 | | |
326 | 21 | for (uint32_t i = 0; i < NUM_FORWARDER; ++i) { |
327 | 20 | kill_forwarding_subtox(mem, subtoxes[i]); |
328 | 20 | } |
329 | | |
330 | 1 | tox_kill(relay); |
331 | 1 | } |
332 | | |
333 | | int main(void) |
334 | 84 | { |
335 | 84 | setvbuf(stdout, nullptr, _IONBF, 0); |
336 | | |
337 | 84 | test_forwarding(); |
338 | | |
339 | 84 | return 0; |
340 | 84 | } |