]> git.meshlink.io Git - meshlink-tiny/blob - test/invite-join.c
Update the test suite for Meshlink-tiny.
[meshlink-tiny] / test / invite-join.c
1 #ifdef NDEBUG
2 #undef NDEBUG
3 #endif
4
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/time.h>
10 #include <assert.h>
11 #include <errno.h>
12
13 #include "meshlink-tiny.h"
14 #include "devtools.h"
15 #include "utils.h"
16
17 #include "full.h"
18
19 static struct sync_flag baz_reachable;
20 static struct sync_flag seven_reachable;
21 static struct sync_flag commits_first_flag;
22
23 static void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
24         (void)mesh;
25
26         if(reachable && !strcmp(node->name, "baz")) {
27                 set_sync_flag(&baz_reachable, true);
28         }
29
30         if(reachable && !strcmp(node->name, "seven")) {
31                 set_sync_flag(&seven_reachable, true);
32         }
33 }
34
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);
40 }
41
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);
47 }
48
49 int main(void) {
50         init_full();
51
52         init_sync_flag(&baz_reachable);
53         init_sync_flag(&seven_reachable);
54         init_sync_flag(&commits_first_flag);
55
56         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
57
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"));
67
68         // Open thee new meshlink instance.
69
70         meshlink_handle_t *mesh1 = full_meshlink_open("invite_join_conf.1", "foo", "invite-join", DEV_CLASS_BACKBONE);
71         assert(mesh1);
72
73         meshlink_handle_t *mesh2 = meshlink_open("invite_join_conf.2", "bar", "invite-join", DEV_CLASS_BACKBONE);
74         assert(mesh2);
75
76         meshlink_handle_t *mesh3 = meshlink_open("invite_join_conf.3", "quux", "invite-join", DEV_CLASS_BACKBONE);
77         assert(mesh3);
78
79         // Have the first instance generate invitations.
80
81         full_meshlink_set_node_status_cb(mesh1, status_cb);
82
83         assert(full_meshlink_set_canonical_address(mesh1, full_meshlink_get_self(mesh1), "localhost", NULL));
84
85         char *baz_url = full_meshlink_invite(mesh1, NULL, "baz");
86         assert(baz_url);
87
88         char *quux_url = full_meshlink_invite(mesh1, NULL, "quux");
89         assert(quux_url);
90
91         // Check that the second instances cannot join if it is already started
92
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);
98
99         // Have the second instance join the first.
100
101         meshlink_stop(mesh2);
102         assert(meshlink_join(mesh2, baz_url));
103         assert(meshlink_start(mesh2));
104
105         // Wait for the two to connect.
106
107         assert(wait_sync_flag(&baz_reachable, 20));
108
109         // Wait for UDP communication to become possible.
110
111         int pmtu = full_meshlink_get_pmtu(mesh1, meshlink_get_node(mesh1, "baz"));
112
113         for(int i = 0; i < 10 && !pmtu; i++) {
114                 sleep(1);
115                 pmtu = full_meshlink_get_pmtu(mesh1, meshlink_get_node(mesh1, "baz"));
116         }
117
118         assert(pmtu);
119
120         // Check that an invitation cannot be used twice
121
122         assert(!meshlink_join(mesh3, baz_url));
123         free(baz_url);
124
125         // Check that nodes cannot join with expired invitations
126
127         full_meshlink_set_invitation_timeout(mesh1, 0);
128
129         assert(!meshlink_join(mesh3, quux_url));
130         free(quux_url);
131
132         // Check that existing nodes cannot join another mesh
133
134         char *corge_url = full_meshlink_invite(mesh1, NULL, "corge");
135         assert(corge_url);
136
137         meshlink_stop(mesh2);
138
139         assert(!meshlink_join(mesh2, corge_url));
140         free(corge_url);
141
142         // Check that invitations work correctly after changing ports
143
144         full_meshlink_set_invitation_timeout(mesh1, 86400);
145         full_meshlink_stop(mesh1);
146         meshlink_stop(mesh3);
147
148         int oldport = full_meshlink_get_port(mesh1);
149         bool success = false;
150
151         for(int i = 0; !success && i < 100; i++) {
152                 success = full_meshlink_set_port(mesh1, 0x9000 + rand() % 0x1000);
153         }
154
155         assert(success);
156         int newport = full_meshlink_get_port(mesh1);
157         assert(oldport != newport);
158
159         assert(full_meshlink_set_canonical_address(mesh1, meshlink_get_self(mesh1), "localhost", NULL));
160
161         assert(full_meshlink_start(mesh1));
162         quux_url = full_meshlink_invite(mesh1, NULL, "quux");
163         assert(quux_url);
164
165         // The old port should not be in the invitation URL
166
167         char portstr[10];
168         snprintf(portstr, sizeof(portstr), ":%d", oldport);
169         assert(!strstr(quux_url, portstr));
170
171         // The new port should be in the invitation URL
172
173         snprintf(portstr, sizeof(portstr), ":%d", newport);
174         assert(strstr(quux_url, portstr));
175
176         // The invitation should work
177
178         assert(meshlink_join(mesh3, quux_url));
179         free(quux_url);
180
181         // Check that adding duplicate addresses get removed correctly
182
183         assert(full_meshlink_add_invitation_address(mesh1, "localhost", portstr + 1));
184         corge_url = full_meshlink_invite(mesh1, NULL, "corge");
185         assert(corge_url);
186         char *localhost = strstr(corge_url, "localhost");
187         assert(localhost);
188         assert(!strstr(localhost + 1, "localhost"));
189         free(corge_url);
190
191         // Check that resetting and adding multiple, different invitation address works
192
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");
200         assert(grault_url);
201         localhost = strstr(grault_url, "localhost");
202         assert(localhost);
203         char *invalid1 = strstr(grault_url, "1.invalid.:12345");
204         assert(invalid1);
205         char *invalid5 = strstr(grault_url, "5.invalid.");
206         assert(invalid5);
207
208         // Check that explicitly added invitation addresses come before others, in the order they were specified.
209
210         assert(invalid1 < invalid5);
211         assert(invalid5 < localhost);
212         free(grault_url);
213
214         // Check inviting nodes into a submesh
215
216         assert(!full_meshlink_get_node_submesh(mesh1, meshlink_get_self(mesh1)));
217
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);
221         assert(mesh4);
222         assert(mesh5);
223         assert(mesh6);
224
225         meshlink_submesh_t *submesh1 = full_meshlink_submesh_open(mesh1, "submesh1");
226         meshlink_submesh_t *submesh2 = full_meshlink_submesh_open(mesh1, "submesh2");
227         assert(submesh1);
228         assert(submesh2);
229
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);
233         assert(four_url);
234         assert(five_url);
235         assert(six_url);
236
237         assert(meshlink_join(mesh4, four_url));
238         assert(meshlink_join(mesh5, five_url));
239         assert(meshlink_join(mesh6, six_url));
240
241         free(four_url);
242         free(five_url);
243         free(six_url);
244
245         assert(meshlink_start(mesh2));
246         assert(meshlink_start(mesh4));
247         assert(meshlink_start(mesh5));
248         assert(meshlink_start(mesh6));
249
250         // Wait for nodes to connect, and check that foo sees the right submeshes
251
252         sleep(2);
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);
258
259         // Check that none of the tiny nodes can see each other, regardless of which submesh they are in
260
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));
267
268         assert(!meshlink_get_node(mesh4, mesh5->name));
269         assert(!meshlink_get_node(mesh5, mesh4->name));
270
271
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));
276
277         // Test case #2: check invalid parameters
278
279         meshlink_handle_t *mesh7 = meshlink_open("invite_join_conf.7", "seven", "invite-join", DEV_CLASS_BACKBONE);
280         assert(mesh7);
281         char *seven_url = full_meshlink_invite(mesh1, NULL, "seven");
282         assert(seven_url);
283
284         meshlink_errno = MESHLINK_OK;
285         assert(!full_meshlink_invite(NULL, NULL, "seven"));
286         assert(*full_meshlink_errno == MESHLINK_EINVAL);
287
288         meshlink_errno = MESHLINK_OK;
289         assert(!full_meshlink_invite(mesh1, NULL, NULL));
290         assert(*full_meshlink_errno == MESHLINK_EINVAL);
291
292         meshlink_errno = MESHLINK_OK;
293         assert(!full_meshlink_invite(mesh1, NULL, ""));
294         assert(*full_meshlink_errno == MESHLINK_EINVAL);
295
296         meshlink_errno = MESHLINK_OK;
297         assert(!meshlink_join(NULL, seven_url));
298         assert(meshlink_errno == MESHLINK_EINVAL);
299
300         meshlink_errno = MESHLINK_OK;
301         assert(!meshlink_join(mesh7, NULL));
302         assert(meshlink_errno == MESHLINK_EINVAL);
303
304         meshlink_errno = MESHLINK_OK;
305         assert(!meshlink_join(mesh7, ""));
306         assert(meshlink_errno == MESHLINK_EINVAL);
307
308         // Test case #3 and #4: check persistence of inviter and invitee
309
310         assert(meshlink_join(mesh7, seven_url));
311         free(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);
321         assert(mesh1);
322         assert(mesh7);
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);
329
330         // Test case #6 and #7: check invalid inviter_commits_first combinations
331
332         meshlink_handle_t *mesh8 = meshlink_open("invite_join_conf.8", "eight", "invite-join", DEV_CLASS_BACKBONE);
333         assert(mesh8);
334         char *eight_url = full_meshlink_invite(mesh1, NULL, "eight");
335         assert(eight_url);
336         full_meshlink_set_inviter_commits_first(mesh1, true);
337         meshlink_set_inviter_commits_first(mesh8, false);
338         assert(!meshlink_join(mesh8, eight_url));
339         free(eight_url);
340
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));
345         free(eight_url);
346
347         // Test case #5: test invitee committing first scenario
348
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));
354         free(eight_url);
355         assert(wait_sync_flag(&commits_first_flag, 5));
356
357         // Test case #6: test inviter committing first scenario
358
359         meshlink_handle_t *mesh9 = meshlink_open("invite_join_conf.9", "nine", "invite-join", DEV_CLASS_BACKBONE);
360         assert(mesh9);
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));
367         free(nine_url);
368         assert(wait_sync_flag(&commits_first_flag, 5));
369
370         // Clean up.
371
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);
381 }