Imported Upstream version 0.1.17
[platform/upstream/libnice.git] / tests / test-pseudotcp-fuzzy.c
1 /* vim: et ts=2 sw=2 tw=80: */
2 /*
3  * This file is part of the Nice GLib ICE library.
4  *
5  * (C) 2010, 2014 Collabora Ltd.
6  *  Contact: Philip Withnall
7  *
8  * The contents of this file are subject to the Mozilla Public License Version
9  * 1.1 (the "License"); you may not use this file except in compliance with
10  * the License. You may obtain a copy of the License at
11  * http://www.mozilla.org/MPL/
12  *
13  * Software distributed under the License is distributed on an "AS IS" basis,
14  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15  * for the specific language governing rights and limitations under the
16  * License.
17  *
18  * The Original Code is the Nice GLib ICE library.
19  *
20  * The Initial Developers of the Original Code are Collabora Ltd and Nokia
21  * Corporation. All Rights Reserved.
22  *
23  * Contributors:
24  *   Philip Withnall, Collabora Ltd.
25  *   Youness Alaoui, Collabora Ltd.
26  *
27  * Alternatively, the contents of this file may be used under the terms of the
28  * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
29  * case the provisions of LGPL are applicable instead of those above. If you
30  * wish to allow use of your version of this file only under the terms of the
31  * LGPL and not to allow others to use your version of this file under the
32  * MPL, indicate your decision by deleting the provisions above and replace
33  * them with the notice and other provisions required by the LGPL. If you do
34  * not delete the provisions above, a recipient may use your version of this
35  * file under either the MPL or the LGPL.
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <locale.h>
43 #include <string.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <errno.h>
47 #include <math.h>
48
49 #include "pseudotcp.h"
50
51
52 /**
53  * A fuzzing test for the pseudotcp socket. This connects two sockets in a
54  * loopback arrangement, with the packet output from one being fed to the other,
55  * and vice-versa. Fuzzing happens on the packet interface between the two,
56  * mutating the packets slightly and seeing what happens.
57  *
58  * The input data to the left-most socket is read from a file. The output data
59  * from the loopback is written to another file, although this probably isn’t
60  * very useful. If no files are provided, a small amount of dummy data is sent
61  * through the sockets instead. This almost certainly won’t catch any bugs, and
62  * is just present to allow this test to be run as part of `make check` so it
63  * doesn’t bit rot.
64  *
65  * A good command to generate an input file is:
66  *     dd if=/dev/urandom of=rand count=10000 ibs=1024
67  *
68  * None of the data is validated, and the test results are effectively the 1-bit
69  * value of ‘did it crash?’. In particular, the output file is not validated,
70  * and the TCP packets emitted by both sockets are not checked for validity.
71  *
72  * It is suggested that this test is run under GDB and Valgrind. Any crashes or
73  * errors which are detected can be reproduced by providing the same input file
74  * and seed (using the --seed option). The seed is printed out at the beginning
75  * of each test run.
76  */
77
78
79 PseudoTcpSocket *left;
80 PseudoTcpSocket *right;
81 GMainLoop *main_loop = NULL;
82 GRand *prng = NULL;
83 gint retval = 0;
84 FILE *in = NULL;
85 FILE *out = NULL;
86 int total_read = 0;
87 int total_wrote = 0;
88 guint left_clock = 0;
89 guint right_clock = 0;
90 gboolean left_closed = FALSE;
91 gboolean right_closed = FALSE;
92 gboolean reading_done = FALSE;
93
94 /* Number of bytes of payload each socket has received so far. */
95 guint32 left_stream_pos = 0;
96 guint32 right_stream_pos = 0;
97
98 /* Configuration options. */
99 gint64 seed = 0;
100 guint32 fuzz_start_pos = 1;  /* bytes into stream payload; after the SYN-ACKs */
101 guint n_changes_lambda = 1;  /* lambda parameter for a Poisson distribution
102                               * controlling the number of mutations made to each
103                               * packet */
104
105
106 static void
107 adjust_clock (PseudoTcpSocket *sock);
108
109
110 static void
111 write_to_sock (PseudoTcpSocket *sock)
112 {
113   gchar buf[1024];
114   gsize len;
115   gint wlen;
116   guint total = 0;
117
118   while (TRUE) {
119     len = fread (buf, 1, sizeof(buf), in);
120     if (len == 0) {
121       g_debug ("Done reading data from file");
122       g_assert (feof (in));
123       reading_done = TRUE;
124       pseudo_tcp_socket_close (sock, FALSE);
125       break;
126     } else {
127       wlen = pseudo_tcp_socket_send (sock, buf, len);
128       g_debug ("Sending %" G_GSIZE_FORMAT " bytes : %d", len, wlen);
129       total += wlen;
130       total_read += wlen;
131       if (wlen < (gint) len) {
132         g_debug ("seeking  %" G_GSIZE_FORMAT " from %lu", wlen - len, ftell (in));
133         fseek (in, wlen - len, SEEK_CUR);
134         g_assert (!feof (in));
135         g_debug ("Socket queue full after %d bytes written", total);
136         break;
137       }
138     }
139   }
140   adjust_clock (sock);
141 }
142
143 static void
144 opened (PseudoTcpSocket *sock, gpointer data)
145 {
146   g_debug ("Socket %p Opened", sock);
147   if (sock == left) {
148     if (in)
149       write_to_sock (sock);
150     else {
151       pseudo_tcp_socket_send (sock, "abcdefghijklmnopqrstuvwxyz", 26);
152       reading_done = TRUE;
153       pseudo_tcp_socket_close (sock, FALSE);
154     }
155   }
156 }
157
158 static void
159 readable (PseudoTcpSocket *sock, gpointer data)
160 {
161   gchar buf[1024];
162   gint len;
163   g_debug ("Socket %p Readable", sock);
164
165   do {
166     len = pseudo_tcp_socket_recv (sock, buf, sizeof(buf));
167
168     if (len > 0) {
169       g_debug ("Read %d bytes", len);
170       if (out) {
171         if (fwrite (buf, len, 1, out) == 0)
172           g_debug ("Error writing to output file");
173         else {
174           total_wrote += len;
175
176           g_assert_cmpint (total_wrote, <=, total_read);
177           g_debug ("Written %d bytes, need %d bytes", total_wrote, total_read);
178           if (total_wrote == total_read && feof (in)) {
179             g_assert (reading_done);
180             pseudo_tcp_socket_close (sock, FALSE);
181           }
182         }
183       } else {
184         pseudo_tcp_socket_close (sock, FALSE);
185       }
186     } else if (len == 0) {
187       pseudo_tcp_socket_close (sock, FALSE);
188     }
189   } while (len > 0);
190
191   if (len == -1 &&
192       pseudo_tcp_socket_get_error (sock) != EWOULDBLOCK) {
193     g_printerr ("Error reading from socket %p: %s.\n",
194         sock, g_strerror (pseudo_tcp_socket_get_error (sock)));
195
196     retval = -1;
197     g_main_loop_quit (main_loop);
198     return;
199   }
200 }
201
202 static void
203 writable (PseudoTcpSocket *sock, gpointer data)
204 {
205   g_debug ("Socket %p Writable", sock);
206   if (in && sock == left)
207     write_to_sock (sock);
208 }
209
210 static void
211 closed (PseudoTcpSocket *sock, guint32 err, gpointer data)
212 {
213   /* Don’t treat this as an error, since we’re throwing rubbish into the
214    * socket and can hardly expect it to complete successfully. */
215   g_debug ("Socket %p Closed: %s", sock, g_strerror (err));
216   retval = 0;
217   g_main_loop_quit (main_loop);
218 }
219
220 struct notify_data {
221   PseudoTcpSocket *sock;
222   guint32 len;
223   guint32 stream_pos;
224   guint8 buffer[];
225 };
226
227 /**
228  * random_int_poisson:
229  * @lambda: Lambda parameter for the distribution function, which must be
230  * non-zero
231  *
232  * Generate a random variable from a Poisson distribution with parameter
233  * @lambda. This consumes one %gdouble’s worth of randomness from the global
234  * @prng.
235  *
236  * This is implemented using the inverse transform of the Poisson CDF, and is
237  * guaranteed to return in time linearly proportional to @lambda.
238  *
239  * Returns: Poisson-distributed pseudo-random variable
240  */
241 static guint32
242 random_int_poisson (guint lambda)
243 {
244   gdouble U;
245   guint32 i;
246   gdouble p, F;
247
248   g_return_val_if_fail (lambda > 0, 0);
249
250   /*
251    * Reference: http://www.cs.bgu.ac.il/~mps042/invtransnote.htm,
252    * §Simulating a Poisson random variable.
253    */
254   U = g_rand_double (prng);  /* step 1 */
255   i = 0;
256   p = exp (0.0 - (gdouble) lambda);
257   F = p;  /* step 2 */
258
259   while (U >= F) {  /* step 3 */
260     p = (lambda * p) / (i + 1);
261     F += p;
262     i += 1;  /* step 4 and 5 */
263   }
264
265   return i;
266 }
267
268 static guint32
269 fuzz_packet (guint8 *buf, guint32 len, guint32 stream_pos)
270 {
271   guint32 i;
272   guint n_changes;
273 #define HEADER_LENGTH 24 /* bytes; or thereabouts (include some options) */
274
275   /* Do we want to fuzz this packet? */
276   if (stream_pos < fuzz_start_pos) {
277     return len;
278   }
279
280   /* Get fuzzing. Only bother fuzzing the header; fuzzing the payload is
281    * pointless. Weight the number of changes towards having only a few changes,
282    * since that makes them less likely to be summarily rejected. */
283   n_changes = random_int_poisson (n_changes_lambda);
284   g_debug ("Making %u changes for bytes at stream position %u:",
285       n_changes, stream_pos);
286
287   for (i = 0; i < n_changes; i++) {
288     guint32 pos = g_rand_int_range (prng, 0, MIN (len, HEADER_LENGTH));
289     g_debug (" • Changing byte %u.", stream_pos + pos);
290     buf[pos] = g_rand_int_range (prng, 0, G_MAXUINT8 + 1);
291   }
292
293   return len;
294 }
295
296 static gboolean
297 notify_packet (gpointer user_data)
298 {
299   struct notify_data *data = (struct notify_data*) user_data;
300
301   /* Fuzz the packet. */
302   data->len = fuzz_packet (data->buffer, data->len, data->stream_pos);
303
304   pseudo_tcp_socket_notify_packet (data->sock,
305       (gchar *) data->buffer, data->len);
306   adjust_clock (data->sock);
307
308   g_free (data);
309   return FALSE;
310 }
311
312 static PseudoTcpWriteResult
313 write_packet (PseudoTcpSocket *sock, const gchar *buffer, guint32 len,
314     gpointer user_data)
315 {
316   struct notify_data *data;
317   PseudoTcpState state;
318   g_object_get (sock, "state", &state, NULL);
319
320   data = g_malloc (sizeof(struct notify_data) + len);
321
322   g_debug ("Socket %p(%d) Writing : %d bytes", sock, state, len);
323
324   memcpy (data->buffer, buffer, len);
325   data->len = len;
326
327   if (sock == left) {
328     data->stream_pos = left_stream_pos;
329     left_stream_pos += len;
330     data->sock = right;
331   } else {
332     data->stream_pos = right_stream_pos;
333     right_stream_pos += len;
334     data->sock = left;
335   }
336
337   g_idle_add (notify_packet, data);
338
339   return WR_SUCCESS;
340 }
341
342
343 static gboolean notify_clock (gpointer data)
344 {
345   PseudoTcpSocket *sock = (PseudoTcpSocket *)data;
346   //g_debug ("Socket %p: Notifying clock", sock);
347   pseudo_tcp_socket_notify_clock (sock);
348   adjust_clock (sock);
349   return FALSE;
350 }
351
352 static void adjust_clock (PseudoTcpSocket *sock)
353 {
354   guint64 timeout = 0;
355
356   if (pseudo_tcp_socket_get_next_clock (sock, &timeout)) {
357     timeout -= g_get_monotonic_time () / 1000;
358     g_debug ("Socket %p: Adjusting clock to %" G_GUINT64_FORMAT " ms", sock, timeout);
359     if (sock == left) {
360       if (left_clock != 0)
361          g_source_remove (left_clock);
362       left_clock = g_timeout_add (timeout, notify_clock, sock);
363     } else {
364       if (right_clock != 0)
365          g_source_remove (right_clock);
366       right_clock = g_timeout_add (timeout, notify_clock, sock);
367     }
368   } else {
369     g_debug ("Socket %p should be destroyed, it's done", sock);
370     if (sock == left)
371       left_closed = TRUE;
372     else
373       right_closed = TRUE;
374     if (left_closed && right_closed)
375       g_main_loop_quit (main_loop);
376   }
377 }
378
379 static GOptionEntry entries[] = {
380   { "seed", 's', 0, G_OPTION_ARG_INT64, &seed, "PRNG seed", "N" },
381   { "fuzz-start-position", 'p', 0, G_OPTION_ARG_INT, &fuzz_start_pos,
382     "Number of bytes into the stream to start fuzzing after", "B" },
383   { "fuzz-n-changes-lambda", 'l', 0, G_OPTION_ARG_INT, &n_changes_lambda,
384     "Lambda value for the Poisson distribution controlling the number of "
385     "changes made to each packet", "λ" },
386   { NULL }
387 };
388
389 int main (int argc, char *argv[])
390 {
391   PseudoTcpCallbacks cbs = {
392     NULL, opened, readable, writable, closed, write_packet
393   };
394   GOptionContext *context;
395   GError *error = NULL;
396
397   setlocale (LC_ALL, "");
398
399   /* Configuration. */
400   context = g_option_context_new ("— fuzz-test the pseudotcp socket");
401   g_option_context_add_main_entries (context, entries, NULL);
402
403   if (!g_option_context_parse (context, &argc, &argv, &error)) {
404     g_printerr ("Option parsing failed: %s\n", error->message);
405     goto context_error;
406   }
407
408   if (n_changes_lambda == 0) {
409     g_printerr ("Option parsing failed: %s\n",
410         "Lambda values must be positive.");
411     goto context_error;
412   }
413
414   g_option_context_free (context);
415
416   /* Tweak the configuration. */
417   if (seed == 0) {
418     seed = g_get_real_time ();
419   }
420
421   /* Open the input and output files */
422   if (argc >= 3) {
423     in = fopen (argv[1], "r");
424     out = fopen (argv[2], "w");
425   }
426
427   /* Set up the main loop and sockets. */
428   main_loop = g_main_loop_new (NULL, FALSE);
429
430   g_print ("Using seed: %" G_GINT64_FORMAT ", start position: %u, λ: %u\n",
431       seed, fuzz_start_pos, n_changes_lambda);
432   prng = g_rand_new_with_seed (seed);
433
434   pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE);
435
436   left = pseudo_tcp_socket_new (0, &cbs);
437   right = pseudo_tcp_socket_new (0, &cbs);
438   g_debug ("Left: %p. Right: %p", left, right);
439
440   pseudo_tcp_socket_notify_mtu (left, 1496);
441   pseudo_tcp_socket_notify_mtu (right, 1496);
442
443   pseudo_tcp_socket_connect (left);
444   adjust_clock (left);
445   adjust_clock (right);
446
447   /* Run the main loop. */
448   g_main_loop_run (main_loop);
449   g_main_loop_unref (main_loop);
450
451   g_object_unref (left);
452   g_object_unref (right);
453
454   g_rand_free (prng);
455
456   if (in != NULL)
457     fclose (in);
458   if (out != NULL)
459     fclose (out);
460
461   return retval;
462
463 context_error:
464   g_printerr ("\n%s\n", g_option_context_get_help (context, TRUE, NULL));
465   g_option_context_free (context);
466
467   return 1;
468 }