/work/auto_tests/scenarios/scenario_dht_nodes_response_api_test.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "framework/framework.h" |
2 | | #include "../../toxcore/tox_private.h" |
3 | | #include <stdio.h> |
4 | | #include <string.h> |
5 | | #include <stdlib.h> |
6 | | |
7 | 29.9k | #define NUM_TOXES 30 |
8 | | |
9 | | typedef struct { |
10 | | uint8_t public_key[TOX_DHT_NODE_PUBLIC_KEY_SIZE]; |
11 | | char ip[TOX_DHT_NODE_IP_STRING_SIZE]; |
12 | | uint16_t port; |
13 | | } Dht_Node; |
14 | | |
15 | | typedef struct { |
16 | | Dht_Node *nodes[NUM_TOXES]; |
17 | | size_t num_nodes; |
18 | | uint8_t public_key_list[NUM_TOXES][TOX_PUBLIC_KEY_SIZE]; |
19 | | } State; |
20 | | |
21 | | static bool node_crawled(const State *state, const uint8_t *public_key) |
22 | 101k | { |
23 | 1.39M | for (size_t i = 0; i < state->num_nodes; ++i) { |
24 | 1.39M | if (memcmp(state->nodes[i]->public_key, public_key, TOX_DHT_NODE_PUBLIC_KEY_SIZE) == 0) { |
25 | 100k | return true; |
26 | 100k | } |
27 | 1.39M | } |
28 | | |
29 | 906 | return false; |
30 | 101k | } |
31 | | |
32 | | static void on_dht_nodes_response(const Tox_Event_Dht_Nodes_Response *event, void *user_data) |
33 | 101k | { |
34 | 101k | ToxNode *self = (ToxNode *)user_data; |
35 | 101k | State *state = (State *)tox_node_get_script_ctx(self); |
36 | | |
37 | 101k | const uint8_t *public_key = tox_event_dht_nodes_response_get_public_key(event); |
38 | 101k | const char *ip = (const char *)tox_event_dht_nodes_response_get_ip(event); |
39 | 101k | const uint16_t port = tox_event_dht_nodes_response_get_port(event); |
40 | | |
41 | 101k | if (node_crawled(state, public_key)) { |
42 | 100k | return; |
43 | 100k | } |
44 | | |
45 | 902 | if (state->num_nodes >= NUM_TOXES) { |
46 | 0 | return; |
47 | 0 | } |
48 | | |
49 | 902 | Dht_Node *node = (Dht_Node *)calloc(1, sizeof(Dht_Node)); |
50 | 902 | ck_assert(node != nullptr); |
51 | | |
52 | 902 | memcpy(node->public_key, public_key, TOX_DHT_NODE_PUBLIC_KEY_SIZE); |
53 | 902 | snprintf(node->ip, sizeof(node->ip), "%s", ip); |
54 | 902 | node->port = port; |
55 | | |
56 | 902 | state->nodes[state->num_nodes] = node; |
57 | 902 | ++state->num_nodes; |
58 | | |
59 | | // ask new node to give us their close nodes to every public key |
60 | 27.8k | for (size_t i = 0; i < NUM_TOXES; ++i) { |
61 | 26.9k | tox_dht_send_nodes_request(tox_node_get_tox(self), public_key, ip, port, state->public_key_list[i], nullptr); |
62 | 26.9k | } |
63 | 902 | } |
64 | | |
65 | | static void peer_script(ToxNode *self, void *ctx) |
66 | 30 | { |
67 | 30 | State *state = (State *)ctx; |
68 | 30 | tox_events_callback_dht_nodes_response(tox_node_get_dispatch(self), on_dht_nodes_response); |
69 | | |
70 | | // Initial bootstrap: ask the node we bootstrapped from for all other nodes |
71 | | // Wait for self connection first |
72 | 30 | WAIT_UNTIL(tox_node_is_self_connected(self)); |
73 | | |
74 | | // After connecting, we should start receiving responses because we bootstrapped |
75 | | // but the original test calls tox_dht_send_nodes_request for all nodes. |
76 | | // In our case, we bootstrap linearly, so Peer-i bootstraps from Peer-(i-1). |
77 | | // Peer-0 doesn't bootstrap from anyone. |
78 | | |
79 | | // To kick off crawling, each node can ask its bootstrap source for all nodes. |
80 | 30 | uint32_t my_index = tox_node_get_index(self); |
81 | 30 | if (my_index > 0) { |
82 | 29 | ToxNode *bootstrap_source = tox_scenario_get_node(tox_node_get_scenario(self), my_index - 1); |
83 | 29 | uint8_t bs_pk[TOX_PUBLIC_KEY_SIZE]; |
84 | 29 | tox_self_get_dht_id(tox_node_get_tox(bootstrap_source), bs_pk); |
85 | | |
86 | 29 | Tox_Err_Get_Port port_err; |
87 | 29 | uint16_t bs_port = tox_self_get_udp_port(tox_node_get_tox(bootstrap_source), &port_err); |
88 | | |
89 | 899 | for (size_t i = 0; i < NUM_TOXES; ++i) { |
90 | 870 | tox_dht_send_nodes_request(tox_node_get_tox(self), bs_pk, "127.0.0.1", bs_port, state->public_key_list[i], nullptr); |
91 | 870 | } |
92 | 29 | } |
93 | | |
94 | | // Wait until we have crawled all nodes |
95 | 30 | uint64_t last_log_time = 0; |
96 | 30 | uint64_t last_request_time = 0; |
97 | 32 | while (state->num_nodes < NUM_TOXES) { |
98 | 2 | tox_scenario_yield(self); |
99 | 2 | uint64_t now = tox_scenario_get_time(tox_node_get_scenario(self)); |
100 | | |
101 | 2 | if (now - last_request_time >= 2000) { |
102 | 1 | last_request_time = now; |
103 | | |
104 | | // Retry strategy: ask a random known node for missing nodes |
105 | 1 | if (state->num_nodes > 0) { |
106 | 1 | int idx = rand() % state->num_nodes; |
107 | 1 | const Dht_Node *n = state->nodes[idx]; |
108 | 31 | for (int i = 0; i < NUM_TOXES; ++i) { |
109 | 30 | if (!node_crawled(state, state->public_key_list[i])) { |
110 | 3 | tox_dht_send_nodes_request(tox_node_get_tox(self), |
111 | 3 | n->public_key, n->ip, n->port, state->public_key_list[i], nullptr); |
112 | 3 | } |
113 | 30 | } |
114 | 1 | } else if (my_index > 0) { |
115 | | // Fallback: if we haven't found anyone yet, ask bootstrap peer again |
116 | 0 | ToxNode *bootstrap_source = tox_scenario_get_node(tox_node_get_scenario(self), my_index - 1); |
117 | 0 | uint8_t bs_pk[TOX_PUBLIC_KEY_SIZE]; |
118 | 0 | tox_self_get_dht_id(tox_node_get_tox(bootstrap_source), bs_pk); |
119 | 0 | Tox_Err_Get_Port port_err; |
120 | 0 | uint16_t bs_port = tox_self_get_udp_port(tox_node_get_tox(bootstrap_source), &port_err); |
121 | |
|
122 | 0 | for (int i = 0; i < NUM_TOXES; ++i) { |
123 | 0 | tox_dht_send_nodes_request(tox_node_get_tox(self), bs_pk, "127.0.0.1", bs_port, state->public_key_list[i], nullptr); |
124 | 0 | } |
125 | 0 | } |
126 | 1 | } |
127 | | |
128 | 2 | if (now - last_log_time >= 5000) { |
129 | 1 | last_log_time = now; |
130 | 1 | tox_node_log(self, "Still crawling... found %zu/%d nodes", state->num_nodes, NUM_TOXES); |
131 | 1 | if (state->num_nodes < NUM_TOXES) { |
132 | 1 | char missing[256] = {0}; |
133 | 1 | size_t pos = 0; |
134 | 31 | for (int i = 0; i < NUM_TOXES; ++i) { |
135 | 30 | if (!node_crawled(state, state->public_key_list[i])) { |
136 | 3 | pos += snprintf(missing + pos, sizeof(missing) - pos, "%d ", i); |
137 | 3 | if (pos >= sizeof(missing) - 1) { |
138 | 0 | break; |
139 | 0 | } |
140 | 3 | } |
141 | 30 | } |
142 | 1 | tox_node_log(self, "Missing nodes: %s", missing); |
143 | 1 | } |
144 | 1 | } |
145 | 2 | } |
146 | | |
147 | 30 | tox_node_log(self, "Finished crawling all %u nodes.", (unsigned int)state->num_nodes); |
148 | 30 | } |
149 | | |
150 | | int main(int argc, char *argv[]) |
151 | 1 | { |
152 | 1 | ToxScenario *s = tox_scenario_new(argc, argv, 60000); |
153 | 1 | State *states = (State *)calloc(NUM_TOXES, sizeof(State)); |
154 | | |
155 | 1 | ToxNode *nodes[NUM_TOXES]; |
156 | | |
157 | 31 | for (uint32_t i = 0; i < NUM_TOXES; ++i) { |
158 | 30 | char alias[32]; |
159 | 30 | snprintf(alias, sizeof(alias), "Peer-%u", i); |
160 | 30 | nodes[i] = tox_scenario_add_node(s, alias, peer_script, &states[i], sizeof(State)); |
161 | 30 | } |
162 | | |
163 | | // Fill the public_key_list for all nodes |
164 | 1 | uint8_t public_keys[NUM_TOXES][TOX_PUBLIC_KEY_SIZE]; |
165 | 31 | for (uint32_t i = 0; i < NUM_TOXES; ++i) { |
166 | 30 | tox_self_get_dht_id(tox_node_get_tox(nodes[i]), public_keys[i]); |
167 | 30 | } |
168 | | |
169 | 31 | for (uint32_t i = 0; i < NUM_TOXES; ++i) { |
170 | 30 | memcpy(states[i].public_key_list, public_keys, sizeof(public_keys)); |
171 | 30 | } |
172 | | |
173 | | // Create a linear graph |
174 | 30 | for (uint32_t i = 1; i < NUM_TOXES; ++i) { |
175 | 29 | tox_node_bootstrap(nodes[i], nodes[i - 1]); |
176 | 29 | } |
177 | | |
178 | 1 | ToxScenarioStatus res = tox_scenario_run(s); |
179 | 1 | if (res != TOX_SCENARIO_DONE) { |
180 | 0 | fprintf(stderr, "Scenario failed with status %u\n", res); |
181 | 0 | return 1; |
182 | 0 | } |
183 | | |
184 | 31 | for (uint32_t i = 0; i < NUM_TOXES; ++i) { |
185 | 930 | for (size_t j = 0; j < states[i].num_nodes; ++j) { |
186 | 900 | free(states[i].nodes[j]); |
187 | 900 | } |
188 | 30 | } |
189 | | |
190 | 1 | free(states); |
191 | 1 | tox_scenario_free(s); |
192 | 1 | return 0; |
193 | 1 | } |
194 | | |
195 | | #undef NUM_TOXES |