1 /* unit tests for /core/Messenger.c
2  *  Design:
3  *      Just call every non-static function in Messenger.c, checking that
4  *      they return as they should with check calls. "Bad" calls of the type
5  *      function(bad_data, good_length) are _not_ checked for, this type
6  *      of call is the fault of the client code.
7  *
8  *  Note:
9  *      None of the functions here test things that rely on the network, i.e.
10  *      checking that status changes are received, messages can be sent, etc.
11  *      All of that is done in a separate test, with two local clients running. */
12 
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16 
17 #include <string.h>
18 
19 #ifdef VANILLA_NACL
20 #include <crypto_box.h> // crypto_box_PUBLICKEYBYTES and other defines.
21 #else
22 #include <sodium.h>
23 #endif
24 
25 #include "check_compat.h"
26 #include "../testing/misc_tools.h"
27 #include "../toxcore/Messenger.h"
28 
29 #define REALLY_BIG_NUMBER ((1) << (sizeof(uint16_t) * 7))
30 
31 static bool enable_broken_tests = false;
32 
33 static const char *friend_id_str = "e4b3d5030bc99494605aecc33ceec8875640c1d74aa32790e821b17e98771c4a00000000f1db";
34 static const char *good_id_str   = "d3f14b6d384d8f5f2a66cff637e69f28f539c5de61bc29744785291fa4ef4d64";
35 static const char *bad_id_str    = "9B569D14ff637e69f2";
36 
37 static uint8_t *friend_id = nullptr;
38 static uint8_t *good_id   = nullptr;
39 static uint8_t *bad_id    = nullptr;
40 
41 static int friend_id_num = 0;
42 
43 static Messenger *m;
44 
START_TEST(test_m_sendmesage)45 START_TEST(test_m_sendmesage)
46 {
47     const char *message = "h-hi :3";
48     int good_len = strlen(message);
49     int bad_len = MAX_CRYPTO_PACKET_SIZE;
50 
51 
52     ck_assert(m_send_message_generic(
53                   m, -1, MESSAGE_NORMAL, (const uint8_t *)message, good_len, nullptr) == -1);
54     ck_assert(m_send_message_generic(
55                   m, REALLY_BIG_NUMBER, MESSAGE_NORMAL, (const uint8_t *)message, good_len, nullptr) == -1);
56     ck_assert(m_send_message_generic(
57                   m, 17, MESSAGE_NORMAL, (const uint8_t *)message, good_len, nullptr) == -1);
58     ck_assert(m_send_message_generic(
59                   m, friend_id_num, MESSAGE_NORMAL, (const uint8_t *)message, bad_len, nullptr) == -2);
60 }
61 END_TEST
62 
START_TEST(test_m_get_userstatus_size)63 START_TEST(test_m_get_userstatus_size)
64 {
65     int rc = 0;
66     ck_assert_msg((m_get_statusmessage_size(m, -1) == -1),
67                   "m_get_statusmessage_size did NOT catch an argument of -1");
68     ck_assert_msg((m_get_statusmessage_size(m, REALLY_BIG_NUMBER) == -1),
69                   "m_get_statusmessage_size did NOT catch the following argument: %d\n",
70                   REALLY_BIG_NUMBER);
71     rc = m_get_statusmessage_size(m, friend_id_num);
72 
73     /* this WILL error if the original m_addfriend_norequest() failed */
74     ck_assert_msg((rc >= 0 && rc <= MAX_STATUSMESSAGE_LENGTH),
75                   "m_get_statusmessage_size is returning out of range values! (%i)\n"
76                   "(this can be caused by the error of m_addfriend_norequest"
77                   " in the beginning of the suite)\n", rc);
78 }
79 END_TEST
80 
START_TEST(test_m_set_userstatus)81 START_TEST(test_m_set_userstatus)
82 {
83     const char *status = "online!";
84     uint16_t good_length = strlen(status);
85     uint16_t bad_length = REALLY_BIG_NUMBER;
86 
87     ck_assert_msg((m_set_statusmessage(m, (const uint8_t *)status, bad_length) == -1),
88                   "m_set_userstatus did NOT catch the following length: %d\n",
89                   REALLY_BIG_NUMBER);
90 
91     ck_assert_msg((m_set_statusmessage(m, (const uint8_t *)status, good_length) == 0),
92                   "m_set_userstatus did NOT return 0 on the following length: %d\n"
93                   "MAX_STATUSMESSAGE_LENGTH: %d\n", good_length, MAX_STATUSMESSAGE_LENGTH);
94 }
95 END_TEST
96 
START_TEST(test_m_get_friend_connectionstatus)97 START_TEST(test_m_get_friend_connectionstatus)
98 {
99     ck_assert_msg((m_get_friend_connectionstatus(m, -1) == -1),
100                   "m_get_friend_connectionstatus did NOT catch an argument of -1.\n");
101     ck_assert_msg((m_get_friend_connectionstatus(m, REALLY_BIG_NUMBER) == -1),
102                   "m_get_friend_connectionstatus did NOT catch an argument of %d.\n",
103                   REALLY_BIG_NUMBER);
104 }
105 END_TEST
106 
START_TEST(test_m_friend_exists)107 START_TEST(test_m_friend_exists)
108 {
109     ck_assert_msg((m_friend_exists(m, -1) == 0),
110                   "m_friend_exists did NOT catch an argument of -1.\n");
111     ck_assert_msg((m_friend_exists(m, REALLY_BIG_NUMBER) == 0),
112                   "m_friend_exists did NOT catch an argument of %d.\n",
113                   REALLY_BIG_NUMBER);
114 }
115 END_TEST
116 
START_TEST(test_m_delfriend)117 START_TEST(test_m_delfriend)
118 {
119     ck_assert_msg((m_delfriend(m, -1) == -1),
120                   "m_delfriend did NOT catch an argument of -1\n");
121     ck_assert_msg((m_delfriend(m, REALLY_BIG_NUMBER) == -1),
122                   "m_delfriend did NOT catch the following number: %d\n",
123                   REALLY_BIG_NUMBER);
124 }
125 END_TEST
126 
START_TEST(test_m_addfriend)127 START_TEST(test_m_addfriend)
128 {
129     const char *good_data = "test";
130     const char *bad_data = "";
131 
132     int good_len = strlen(good_data);
133     int bad_len = strlen(bad_data);
134     int really_bad_len = (MAX_CRYPTO_PACKET_SIZE - crypto_box_PUBLICKEYBYTES
135                           - crypto_box_NONCEBYTES - crypto_box_BOXZEROBYTES
136                           + crypto_box_ZEROBYTES + 100);
137 
138     /* TODO(irungentoo): Update this properly to latest master */
139     if (m_addfriend(m, friend_id, (const uint8_t *)good_data, really_bad_len) != FAERR_TOOLONG) {
140         ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", really_bad_len);
141     }
142 
143     /* this will return an error if the original m_addfriend_norequest() failed */
144     if (m_addfriend(m, friend_id, (const uint8_t *)good_data, good_len) != FAERR_ALREADYSENT) {
145         ck_abort_msg("m_addfriend did NOT catch adding a friend we already have.\n"
146                      "(this can be caused by the error of m_addfriend_norequest in"
147                      " the beginning of the suite)\n");
148     }
149 
150     if (m_addfriend(m, good_id, (const uint8_t *)bad_data, bad_len) != FAERR_NOMESSAGE) {
151         ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", bad_len);
152     }
153 
154     /* this should REALLY return an error */
155     /* TODO(irungentoo): validate client_id in m_addfriend? */
156     if (m_addfriend(m, bad_id, (const uint8_t *)good_data, good_len) >= 0) {
157         ck_abort_msg("The following ID passed through "
158                      "m_addfriend without an error:\n'%s'\n", bad_id_str);
159     }
160 }
161 END_TEST
162 
START_TEST(test_setname)163 START_TEST(test_setname)
164 {
165     const char *good_name = "consensualCorn";
166     int good_length = strlen(good_name);
167     int bad_length = REALLY_BIG_NUMBER;
168 
169     ck_assert_msg((setname(m, (const uint8_t *)good_name, bad_length) == -1),
170                   "setname() did NOT error on %d as a length argument!\n", bad_length);
171 
172     ck_assert_msg((setname(m, (const uint8_t *)good_name, good_length) == 0),
173                   "setname() did NOT return 0 on good arguments!\n");
174 }
175 END_TEST
176 
START_TEST(test_getself_name)177 START_TEST(test_getself_name)
178 {
179     const char *nickname = "testGallop";
180     size_t len = strlen(nickname);
181     char *nick_check = (char *)calloc(len + 1, 1);
182 
183     setname(m, (const uint8_t *)nickname, len);
184     getself_name(m, (uint8_t *)nick_check);
185 
186     ck_assert_msg((memcmp(nickname, nick_check, len) == 0),
187                   "getself_name failed to return the known name!\n"
188                   "known name: %s\nreturned: %s\n", nickname, nick_check);
189     free(nick_check);
190 }
191 END_TEST
192 
193 /* this test is excluded for now, due to lack of a way
194  *  to set a friend's status for now.
195  *  ideas:
196  *      if we have access to the friends list, we could
197  *      just add a status manually ourselves. */
198 #if 0
199 START_TEST(test_m_copy_userstatus)
200 {
201     assert(m_copy_userstatus(-1, buf, MAX_USERSTATUS_LENGTH) == -1);
202     assert(m_copy_userstatus(REALLY_BIG_NUMBER, buf, MAX_USERSTATUS_LENGTH) == -1);
203     m_copy_userstatus(friend_id_num, buf, MAX_USERSTATUS_LENGTH + 6);
204 
205     assert(strcmp(name_buf, friend_id_status) == 0);
206 }
207 END_TEST
208 #endif
209 
START_TEST(test_getname)210 START_TEST(test_getname)
211 {
212     uint8_t name_buf[MAX_NAME_LENGTH];
213     uint8_t test_name[] = {'f', 'o', 'o'};
214 
215     ck_assert(getname(m, -1, name_buf) == -1);
216     ck_assert(getname(m, REALLY_BIG_NUMBER, name_buf) == -1);
217 
218     memcpy(m->friendlist[0].name, &test_name[0], 3);
219     m->friendlist[0].name_length = 4;
220     ck_assert(getname(m, 0, &name_buf[0]) == 4);
221 
222     ck_assert(strcmp((char *)&name_buf[0], "foo") == 0);
223 }
224 END_TEST
225 
START_TEST(test_dht_state_saveloadsave)226 START_TEST(test_dht_state_saveloadsave)
227 {
228     /* validate that:
229      * a) saving stays within the confined space
230      * b) a save()d state can be load()ed back successfully
231      * c) a second save() is of equal size
232      * d) the second save() is of equal content */
233     const size_t extra = 64;
234     const size_t size = dht_size(m->dht);
235     VLA(uint8_t, buffer, size + 2 * extra);
236     memset(buffer, 0xCD, extra);
237     memset(buffer + extra + size, 0xCD, extra);
238     dht_save(m->dht, buffer + extra);
239 
240     for (size_t i = 0; i < extra; i++) {
241         ck_assert_msg(buffer[i] == 0xCD, "Buffer underwritten from dht_save() @%u", (unsigned)i);
242         ck_assert_msg(buffer[extra + size + i] == 0xCD, "Buffer overwritten from dht_save() @%u", (unsigned)i);
243     }
244 
245     const int res = dht_load(m->dht, buffer + extra, size);
246 
247     if (res == -1) {
248         ck_assert_msg(res == 0, "Failed to load back stored buffer: res == -1");
249     } else {
250         const size_t offset = res >> 4;
251         const uint8_t *ptr = buffer + extra + offset;
252         ck_assert_msg(res == 0, "Failed to load back stored buffer: 0x%02x%02x%02x%02x%02x%02x%02x%02x @%u/%u, code %d",
253                       ptr[-2], ptr[-1], ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5],
254                       (unsigned)offset, (unsigned)size, res & 0x0F);
255     }
256 
257     const size_t size2 = dht_size(m->dht);
258     ck_assert_msg(size == size2, "Messenger \"grew\" in size from a store/load cycle: %u -> %u", (unsigned)size,
259                   (unsigned)size2);
260 
261     VLA(uint8_t, buffer2, size2);
262     dht_save(m->dht, buffer2);
263 
264     ck_assert_msg(!memcmp(buffer + extra, buffer2, size), "DHT state changed by store/load/store cycle");
265 }
266 END_TEST
267 
messenger_suite(void)268 static Suite *messenger_suite(void)
269 {
270     Suite *s = suite_create("Messenger");
271 
272     DEFTESTCASE(dht_state_saveloadsave);
273 
274     DEFTESTCASE(getself_name);
275     DEFTESTCASE(m_get_userstatus_size);
276     DEFTESTCASE(m_set_userstatus);
277 
278     if (enable_broken_tests) {
279         DEFTESTCASE(m_addfriend);
280     }
281 
282     DEFTESTCASE(m_friend_exists);
283     DEFTESTCASE(m_get_friend_connectionstatus);
284     DEFTESTCASE(m_delfriend);
285 
286     DEFTESTCASE(setname);
287     DEFTESTCASE(getname);
288     DEFTESTCASE(m_sendmesage);
289 
290     return s;
291 }
292 
main(void)293 int main(void)
294 {
295     setvbuf(stdout, nullptr, _IONBF, 0);
296 
297     friend_id = hex_string_to_bin(friend_id_str);
298     good_id   = hex_string_to_bin(good_id_str);
299     bad_id    = hex_string_to_bin(bad_id_str);
300 
301     Mono_Time *mono_time = mono_time_new();
302 
303     /* IPv6 status from global define */
304     Messenger_Options options = {0};
305     options.ipv6enabled = TOX_ENABLE_IPV6_DEFAULT;
306     options.port_range[0] = 41234;
307     options.port_range[1] = 44234;
308     options.log_callback = (logger_cb *)print_debug_log;
309     m = new_messenger(mono_time, &options, nullptr);
310 
311     /* setup a default friend and friendnum */
312     if (m_addfriend_norequest(m, friend_id) < 0) {
313         fputs("m_addfriend_norequest() failed on a valid ID!\n"
314               "this was CRITICAL to the test, and the build WILL fail.\n"
315               "the tests will continue now...\n\n", stderr);
316     }
317 
318     if ((friend_id_num = getfriend_id(m, friend_id)) < 0) {
319         fputs("getfriend_id() failed on a valid ID!\n"
320               "this was CRITICAL to the test, and the build WILL fail.\n"
321               "the tests will continue now...\n\n", stderr);
322     }
323 
324     Suite *messenger = messenger_suite();
325     SRunner *test_runner = srunner_create(messenger);
326     int number_failed = 0;
327 
328     srunner_run_all(test_runner, CK_NORMAL);
329     number_failed = srunner_ntests_failed(test_runner);
330 
331     srunner_free(test_runner);
332     free(friend_id);
333     free(good_id);
334     free(bad_id);
335 
336     kill_messenger(m);
337     mono_time_free(mono_time);
338 
339     return number_failed;
340 }
341