13 #include "meshlink-tiny.h"
19 static struct sync_flag baz_reachable;
20 static struct sync_flag seven_reachable;
21 static struct sync_flag commits_first_flag;
23 static void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
26 if(reachable && !strcmp(node->name, "baz")) {
27 set_sync_flag(&baz_reachable, true);
30 if(reachable && !strcmp(node->name, "seven")) {
31 set_sync_flag(&seven_reachable, true);
35 static void invitee_commits_first_cb(bool inviter_first) {
36 // Check that eight has committed foo's host config file, but foo hasn't committed eight's
37 assert(access("invite_join_conf.8/current/hosts/foo", F_OK) == 0);
38 assert(access("invite_join_conf.1/current/hosts/eight", F_OK) == -1 && errno == ENOENT);
39 set_sync_flag(&commits_first_flag, !inviter_first);
42 static void inviter_commits_first_cb(bool inviter_first) {
43 // Check that foo has committed nine's host config file, but nine hasn't committed foo's
44 assert(access("invite_join_conf.1/current/hosts/nine", F_OK) == 0);
45 assert(access("invite_join_conf.9/current/hosts/foo", F_OK) == -1 && errno == ENOENT);
46 set_sync_flag(&commits_first_flag, inviter_first);
52 init_sync_flag(&baz_reachable);
53 init_sync_flag(&seven_reachable);
54 init_sync_flag(&commits_first_flag);
56 meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
58 assert(meshlink_destroy("invite_join_conf.1"));
59 assert(meshlink_destroy("invite_join_conf.2"));
60 assert(meshlink_destroy("invite_join_conf.3"));
61 assert(meshlink_destroy("invite_join_conf.4"));
62 assert(meshlink_destroy("invite_join_conf.5"));
63 assert(meshlink_destroy("invite_join_conf.6"));
64 assert(meshlink_destroy("invite_join_conf.7"));
65 assert(meshlink_destroy("invite_join_conf.8"));
66 assert(meshlink_destroy("invite_join_conf.9"));
68 // Open thee new meshlink instance.
70 meshlink_handle_t *mesh1 = full_meshlink_open("invite_join_conf.1", "foo", "invite-join", DEV_CLASS_BACKBONE);
73 meshlink_handle_t *mesh2 = meshlink_open("invite_join_conf.2", "bar", "invite-join", DEV_CLASS_BACKBONE);
76 meshlink_handle_t *mesh3 = meshlink_open("invite_join_conf.3", "quux", "invite-join", DEV_CLASS_BACKBONE);
79 // Have the first instance generate invitations.
81 full_meshlink_set_node_status_cb(mesh1, status_cb);
83 assert(full_meshlink_set_canonical_address(mesh1, full_meshlink_get_self(mesh1), "localhost", NULL));
85 char *baz_url = full_meshlink_invite(mesh1, NULL, "baz");
88 char *quux_url = full_meshlink_invite(mesh1, NULL, "quux");
91 // Check that the second instances cannot join if it is already started
93 assert(full_meshlink_start(mesh1));
94 assert(meshlink_start(mesh2));
95 meshlink_errno = MESHLINK_OK;
96 assert(!meshlink_join(mesh2, baz_url));
97 assert(meshlink_errno = MESHLINK_EINVAL);
99 // Have the second instance join the first.
101 meshlink_stop(mesh2);
102 assert(meshlink_join(mesh2, baz_url));
103 assert(meshlink_start(mesh2));
105 // Wait for the two to connect.
107 assert(wait_sync_flag(&baz_reachable, 20));
109 // Wait for UDP communication to become possible.
111 int pmtu = full_meshlink_get_pmtu(mesh1, meshlink_get_node(mesh1, "baz"));
113 for(int i = 0; i < 10 && !pmtu; i++) {
115 pmtu = full_meshlink_get_pmtu(mesh1, meshlink_get_node(mesh1, "baz"));
120 // Check that an invitation cannot be used twice
122 assert(!meshlink_join(mesh3, baz_url));
125 // Check that nodes cannot join with expired invitations
127 full_meshlink_set_invitation_timeout(mesh1, 0);
129 assert(!meshlink_join(mesh3, quux_url));
132 // Check that existing nodes cannot join another mesh
134 char *corge_url = full_meshlink_invite(mesh1, NULL, "corge");
137 meshlink_stop(mesh2);
139 assert(!meshlink_join(mesh2, corge_url));
142 // Check that invitations work correctly after changing ports
144 full_meshlink_set_invitation_timeout(mesh1, 86400);
145 full_meshlink_stop(mesh1);
146 meshlink_stop(mesh3);
148 int oldport = full_meshlink_get_port(mesh1);
149 bool success = false;
151 for(int i = 0; !success && i < 100; i++) {
152 success = full_meshlink_set_port(mesh1, 0x9000 + rand() % 0x1000);
156 int newport = full_meshlink_get_port(mesh1);
157 assert(oldport != newport);
159 assert(full_meshlink_set_canonical_address(mesh1, meshlink_get_self(mesh1), "localhost", NULL));
161 assert(full_meshlink_start(mesh1));
162 quux_url = full_meshlink_invite(mesh1, NULL, "quux");
165 // The old port should not be in the invitation URL
168 snprintf(portstr, sizeof(portstr), ":%d", oldport);
169 assert(!strstr(quux_url, portstr));
171 // The new port should be in the invitation URL
173 snprintf(portstr, sizeof(portstr), ":%d", newport);
174 assert(strstr(quux_url, portstr));
176 // The invitation should work
178 assert(meshlink_join(mesh3, quux_url));
181 // Check that adding duplicate addresses get removed correctly
183 assert(full_meshlink_add_invitation_address(mesh1, "localhost", portstr + 1));
184 corge_url = full_meshlink_invite(mesh1, NULL, "corge");
186 char *localhost = strstr(corge_url, "localhost");
188 assert(!strstr(localhost + 1, "localhost"));
191 // Check that resetting and adding multiple, different invitation address works
193 full_meshlink_clear_invitation_addresses(mesh1);
194 assert(full_meshlink_add_invitation_address(mesh1, "1.invalid.", "12345"));
195 assert(full_meshlink_add_invitation_address(mesh1, "2.invalid.", NULL));
196 assert(full_meshlink_add_invitation_address(mesh1, "3.invalid.", NULL));
197 assert(full_meshlink_add_invitation_address(mesh1, "4.invalid.", NULL));
198 assert(full_meshlink_add_invitation_address(mesh1, "5.invalid.", NULL));
199 char *grault_url = full_meshlink_invite(mesh1, NULL, "grault");
201 localhost = strstr(grault_url, "localhost");
203 char *invalid1 = strstr(grault_url, "1.invalid.:12345");
205 char *invalid5 = strstr(grault_url, "5.invalid.");
208 // Check that explicitly added invitation addresses come before others, in the order they were specified.
210 assert(invalid1 < invalid5);
211 assert(invalid5 < localhost);
214 // Check inviting nodes into a submesh
216 assert(!full_meshlink_get_node_submesh(mesh1, meshlink_get_self(mesh1)));
218 meshlink_handle_t *mesh4 = meshlink_open("invite_join_conf.4", "four", "invite-join", DEV_CLASS_BACKBONE);
219 meshlink_handle_t *mesh5 = meshlink_open("invite_join_conf.5", "five", "invite-join", DEV_CLASS_BACKBONE);
220 meshlink_handle_t *mesh6 = meshlink_open("invite_join_conf.6", "six", "invite-join", DEV_CLASS_BACKBONE);
225 meshlink_submesh_t *submesh1 = full_meshlink_submesh_open(mesh1, "submesh1");
226 meshlink_submesh_t *submesh2 = full_meshlink_submesh_open(mesh1, "submesh2");
230 char *four_url = full_meshlink_invite(mesh1, submesh1, mesh4->name);
231 char *five_url = full_meshlink_invite(mesh1, submesh1, mesh5->name);
232 char *six_url = full_meshlink_invite(mesh1, submesh2, mesh6->name);
237 assert(meshlink_join(mesh4, four_url));
238 assert(meshlink_join(mesh5, five_url));
239 assert(meshlink_join(mesh6, six_url));
245 assert(meshlink_start(mesh2));
246 assert(meshlink_start(mesh4));
247 assert(meshlink_start(mesh5));
248 assert(meshlink_start(mesh6));
250 // Wait for nodes to connect, and check that foo sees the right submeshes
253 meshlink_node_t *mesh1_four = full_meshlink_get_node(mesh1, mesh4->name);
254 meshlink_node_t *mesh1_six = full_meshlink_get_node(mesh1, mesh6->name);
255 assert(full_meshlink_get_node_submesh(mesh1, meshlink_get_self(mesh1)) == NULL);
256 assert(full_meshlink_get_node_submesh(mesh1, mesh1_four) == submesh1);
257 assert(full_meshlink_get_node_submesh(mesh1, mesh1_six) == submesh2);
259 // Check that none of the tiny nodes can see each other, regardless of which submesh they are in
261 assert(!meshlink_get_node(mesh2, mesh4->name));
262 assert(!meshlink_get_node(mesh2, mesh5->name));
263 assert(!meshlink_get_node(mesh2, mesh6->name));
264 assert(!meshlink_get_node(mesh4, mesh2->name));
265 assert(!meshlink_get_node(mesh5, mesh2->name));
266 assert(!meshlink_get_node(mesh6, mesh2->name));
268 assert(!meshlink_get_node(mesh4, mesh5->name));
269 assert(!meshlink_get_node(mesh5, mesh4->name));
272 assert(!meshlink_get_node(mesh4, mesh6->name));
273 assert(!meshlink_get_node(mesh5, mesh6->name));
274 assert(!meshlink_get_node(mesh6, mesh4->name));
275 assert(!meshlink_get_node(mesh6, mesh5->name));
277 // Test case #2: check invalid parameters
279 meshlink_handle_t *mesh7 = meshlink_open("invite_join_conf.7", "seven", "invite-join", DEV_CLASS_BACKBONE);
281 char *seven_url = full_meshlink_invite(mesh1, NULL, "seven");
284 meshlink_errno = MESHLINK_OK;
285 assert(!full_meshlink_invite(NULL, NULL, "seven"));
286 assert(*full_meshlink_errno == MESHLINK_EINVAL);
288 meshlink_errno = MESHLINK_OK;
289 assert(!full_meshlink_invite(mesh1, NULL, NULL));
290 assert(*full_meshlink_errno == MESHLINK_EINVAL);
292 meshlink_errno = MESHLINK_OK;
293 assert(!full_meshlink_invite(mesh1, NULL, ""));
294 assert(*full_meshlink_errno == MESHLINK_EINVAL);
296 meshlink_errno = MESHLINK_OK;
297 assert(!meshlink_join(NULL, seven_url));
298 assert(meshlink_errno == MESHLINK_EINVAL);
300 meshlink_errno = MESHLINK_OK;
301 assert(!meshlink_join(mesh7, NULL));
302 assert(meshlink_errno == MESHLINK_EINVAL);
304 meshlink_errno = MESHLINK_OK;
305 assert(!meshlink_join(mesh7, ""));
306 assert(meshlink_errno == MESHLINK_EINVAL);
308 // Test case #3 and #4: check persistence of inviter and invitee
310 assert(meshlink_join(mesh7, seven_url));
312 full_meshlink_close(mesh1);
313 meshlink_stop(mesh2);
314 meshlink_stop(mesh3);
315 meshlink_stop(mesh4);
316 meshlink_stop(mesh5);
317 meshlink_stop(mesh6);
318 meshlink_close(mesh7);
319 mesh1 = full_meshlink_open("invite_join_conf.1", "foo", "invite-join", DEV_CLASS_BACKBONE);
320 mesh7 = meshlink_open("invite_join_conf.7", "seven", "invite-join", DEV_CLASS_BACKBONE);
323 full_meshlink_enable_discovery(mesh1, false);
324 full_meshlink_set_node_status_cb(mesh1, status_cb);
325 assert(full_meshlink_start(mesh1));
326 assert(meshlink_start(mesh7));
327 assert(wait_sync_flag(&seven_reachable, 5));
328 meshlink_stop(mesh7);
330 // Test case #6 and #7: check invalid inviter_commits_first combinations
332 meshlink_handle_t *mesh8 = meshlink_open("invite_join_conf.8", "eight", "invite-join", DEV_CLASS_BACKBONE);
334 char *eight_url = full_meshlink_invite(mesh1, NULL, "eight");
336 full_meshlink_set_inviter_commits_first(mesh1, true);
337 meshlink_set_inviter_commits_first(mesh8, false);
338 assert(!meshlink_join(mesh8, eight_url));
341 eight_url = full_meshlink_invite(mesh1, NULL, "eight");
342 full_meshlink_set_inviter_commits_first(mesh1, false);
343 meshlink_set_inviter_commits_first(mesh8, true);
344 assert(!meshlink_join(mesh8, eight_url));
347 // Test case #5: test invitee committing first scenario
349 eight_url = full_meshlink_invite(mesh1, NULL, "eight");
350 full_meshlink_set_inviter_commits_first(mesh1, false);
351 meshlink_set_inviter_commits_first(mesh8, false);
352 devtool_set_inviter_commits_first = invitee_commits_first_cb;
353 assert(meshlink_join(mesh8, eight_url));
355 assert(wait_sync_flag(&commits_first_flag, 5));
357 // Test case #6: test inviter committing first scenario
359 meshlink_handle_t *mesh9 = meshlink_open("invite_join_conf.9", "nine", "invite-join", DEV_CLASS_BACKBONE);
361 char *nine_url = full_meshlink_invite(mesh1, NULL, "nine");
362 full_meshlink_set_inviter_commits_first(mesh1, true);
363 meshlink_set_inviter_commits_first(mesh9, true);
364 *full_devtool_set_inviter_commits_first = inviter_commits_first_cb;
365 reset_sync_flag(&commits_first_flag);
366 assert(meshlink_join(mesh9, nine_url));
368 assert(wait_sync_flag(&commits_first_flag, 5));
372 meshlink_close(mesh9);
373 meshlink_close(mesh8);
374 meshlink_close(mesh7);
375 meshlink_close(mesh6);
376 meshlink_close(mesh5);
377 meshlink_close(mesh4);
378 meshlink_close(mesh3);
379 meshlink_close(mesh2);
380 full_meshlink_close(mesh1);