root/test/test-tls-tcp-io.c

Revision e4b5a4f6aa56d39289c16ec2d7c58216222ac710, 13.4 kB (checked in by hansp <hansp>, 4 years ago)

Add initial TLS protocol element using GNU TLS. Still a bit rough.

  • Property mode set to 100644
Line 
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3 /* test-tls-tcp-io.c - FlowTlsTcpIO test.
4  *
5  * Copyright (C) 2006 Hans Petter Jansson
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * Authors: Hans Petter Jansson <hpj@copyleft.no>
23  */
24
25 #define TEST_UNIT_NAME "FlowTlsTcpIO"
26 #define TEST_TIMEOUT_S 60
27
28 /* Test variables; adjustable */
29
30 #define SOCKETS_NUM            15
31 #define SOCKETS_CONCURRENT_MAX 5
32
33 #define BUFFER_SIZE            5000000   /* Amount of data to transfer */
34 #define PACKET_MAX_SIZE        8192      /* Max transfer unit */
35 #define PACKET_MIN_SIZE        1         /* Min transfer unit */
36
37 #define TOTAL_PAUSE_TIME_MS    3000      /* Total time to spend *not* reading or writing */
38 #define PAUSE_MIN_LENGTH_MS    10        /* Min pause unit */
39 #define PAUSE_MAX_LENGTH_MS    200       /* Max pause unit */
40
41 /* Calculations to determine the probability of pausing for
42  * each packet processed. No user serviceable parts inside. */
43
44 #define PROBABILITY_MULTIPLIER 1000000   /* For fixed-point fractions */
45 #define PACKET_AVG_SIZE        (PACKET_MIN_SIZE + ((PACKET_MAX_SIZE - PACKET_MIN_SIZE) / 2))
46 #define PAUSE_AVG_LENGTH_MS    (PAUSE_MIN_LENGTH_MS + ((PAUSE_MAX_LENGTH_MS - PAUSE_MIN_LENGTH_MS) / 2))
47 #define NUM_PAUSES             (TOTAL_PAUSE_TIME_MS / PAUSE_AVG_LENGTH_MS)
48 #define TOTAL_EXPECTED_PACKETS (BUFFER_SIZE / PACKET_AVG_SIZE)
49 #define TOTAL_EXPECTED_EVENTS  (TOTAL_EXPECTED_PACKETS * 2)  /* Account for both reads and writes */
50 #define PAUSE_PROBABILITY      ((NUM_PAUSES * PROBABILITY_MULTIPLIER) / TOTAL_EXPECTED_EVENTS)
51
52 #include "test-common.c"
53
54 typedef struct
55 {
56   gint read_offset;
57   gint write_offset;
58 }
59 TransferInfo;
60
61 static guchar            *buffer                 = NULL;
62
63 static FlowIPService     *loopback_service       = NULL;
64 static FlowIPService     *bad_loopback_service   = NULL;
65 static FlowTcpIOListener *tcp_listener           = NULL;
66 static FlowTcpIO         *tcp_reader             = NULL;
67 static FlowTcpIO         *tcp_writer             = NULL;
68
69 static GHashTable        *transfer_info_table    = NULL;
70
71 static GMainLoop         *main_loop              = NULL;
72 static gint               sockets_done           = 0;
73 static gint               sockets_running        = 0;
74
75 static FlowTcpIO         *main_tcp_io            = NULL;
76
77 static void
78 transfer_info_free (TransferInfo *transfer_info)
79 {
80   g_slice_free (TransferInfo, transfer_info);
81 }
82
83 static void
84 add_tls (FlowTcpIO *tcp_io, FlowAgentRole agent_role)
85 {
86   FlowTlsProtocol    *tls_protocol;
87   FlowSimplexElement *user_adapter;
88   FlowSimplexElement *tcp_connector;
89   FlowPad            *tls_upstream_pads   [2];  /* Input, output */
90   FlowPad            *tls_downstream_pads [2];  /* Input, output */
91
92   tls_protocol = flow_tls_protocol_new (agent_role);
93   flow_bin_add_element (FLOW_BIN (tcp_io), FLOW_ELEMENT (tls_protocol), "tls-protocol");
94   g_object_unref (tls_protocol);
95
96   flow_duplex_element_get_upstream_pads   (FLOW_DUPLEX_ELEMENT (tls_protocol),
97                                            (FlowInputPad **) &tls_upstream_pads [0],
98                                            (FlowOutputPad **) &tls_upstream_pads [1]);
99   flow_duplex_element_get_downstream_pads (FLOW_DUPLEX_ELEMENT (tls_protocol),
100                                            (FlowInputPad **) &tls_downstream_pads [0],
101                                            (FlowOutputPad **) &tls_downstream_pads [1]);
102
103   user_adapter  = FLOW_SIMPLEX_ELEMENT (flow_bin_get_element (FLOW_BIN (tcp_io), "user-adapter"));
104   tcp_connector = FLOW_SIMPLEX_ELEMENT (flow_bin_get_element (FLOW_BIN (tcp_io), "tcp-connector"));
105
106   flow_pad_connect (FLOW_PAD (flow_simplex_element_get_input_pad (tcp_connector)),
107                     tls_downstream_pads [1]);
108   flow_pad_connect (FLOW_PAD (flow_simplex_element_get_output_pad (tcp_connector)),
109                     tls_downstream_pads [0]);
110
111   flow_pad_connect (FLOW_PAD (flow_simplex_element_get_input_pad (user_adapter)),
112                     tls_upstream_pads [1]);
113   flow_pad_connect (FLOW_PAD (flow_simplex_element_get_output_pad (user_adapter)),
114                     tls_upstream_pads [0]);
115 }
116
117 static void
118 subthread_main (void)
119 {
120   FlowTcpIO    *tcp_io;
121   TransferInfo  transfer_info;
122   guchar        temp_buffer [PACKET_MAX_SIZE];
123
124   test_print ("Subthread connecting to main thread\n");
125
126   tcp_io = flow_tcp_io_new ();
127   add_tls (tcp_io, FLOW_AGENT_ROLE_CLIENT);
128   flow_tcp_io_sync_connect (tcp_io, loopback_service);
129
130   test_print ("Subthread connected to main thread\n");
131
132   transfer_info.read_offset  = 0;
133   transfer_info.write_offset = 0;
134
135   while (transfer_info.read_offset  < BUFFER_SIZE ||
136          transfer_info.write_offset < BUFFER_SIZE)
137   {
138     if (transfer_info.write_offset < BUFFER_SIZE)
139     {
140       gint len;
141
142       len = g_random_int_range (PACKET_MIN_SIZE, PACKET_MAX_SIZE);
143       len = MIN (len, BUFFER_SIZE - transfer_info.write_offset);
144
145       flow_io_sync_write (FLOW_IO (tcp_io), buffer + transfer_info.write_offset, len);
146       test_print ("Subthread wrote %d bytes\n", len);
147
148       transfer_info.write_offset += len;
149     }
150
151     if (transfer_info.read_offset < BUFFER_SIZE)
152     {
153       gint len;
154
155       len = g_random_int_range (PACKET_MIN_SIZE, PACKET_MAX_SIZE);
156       len = MIN (len, BUFFER_SIZE - transfer_info.read_offset);
157
158       if (!flow_io_sync_read_exact (FLOW_IO (tcp_io), temp_buffer, len))
159         test_end (TEST_RESULT_FAILED, "subthread short read");
160
161       test_print ("Subthread read %d bytes\n", len);
162
163       if (memcmp (buffer + transfer_info.read_offset, temp_buffer, len))
164         test_end (TEST_RESULT_FAILED, "subthread read mismatch");
165
166       transfer_info.read_offset += len;
167     }
168   }
169
170   test_print ("Subthread disconnecting\n");
171   flow_tcp_io_sync_disconnect (tcp_io);
172   test_print ("Subthread disconnected\n");
173
174   g_object_unref (tcp_io);
175   test_print ("Subthread cleaned up\n");
176 }
177
178 static gboolean
179 spawn_subthread (void)
180 {
181   if (sockets_running >= SOCKETS_CONCURRENT_MAX || sockets_done >= SOCKETS_NUM)
182     return TRUE;
183
184   test_print ("Spawning new subthread\n");
185   g_thread_create ((GThreadFunc) subthread_main, NULL, FALSE, NULL);
186
187   sockets_running++;
188   return TRUE;
189 }
190
191 static void
192 read_notify (FlowTcpIO *tcp_io)
193 {
194   TransferInfo *transfer_info;
195   guchar        temp_buffer [PACKET_MAX_SIZE];
196   gint          len;
197
198   test_print ("Main thread read notify (%p)\n", tcp_io);
199
200   transfer_info = g_hash_table_lookup (transfer_info_table, tcp_io);
201   g_assert (transfer_info != NULL);
202
203   len = flow_io_read (FLOW_IO (tcp_io), temp_buffer, PACKET_MAX_SIZE);
204   test_print ("Main thread read %d bytes\n", len);
205
206   if (memcmp (buffer + transfer_info->read_offset, temp_buffer, len))
207     test_end (TEST_RESULT_FAILED, "main thread read mismatch");
208
209   transfer_info->read_offset += len;
210
211   if (transfer_info->read_offset > BUFFER_SIZE)
212     test_end (TEST_RESULT_FAILED, "main thread read past buffer length");
213 }
214
215 static void
216 write_notify (FlowTcpIO *tcp_io)
217 {
218   TransferInfo *transfer_info;
219   gint          len;
220
221   test_print ("Main thread write notify (%p)\n", tcp_io);
222
223   transfer_info = g_hash_table_lookup (transfer_info_table, tcp_io);
224   g_assert (transfer_info != NULL);
225
226   len = g_random_int_range (PACKET_MIN_SIZE, PACKET_MAX_SIZE);
227   len = MIN (len, BUFFER_SIZE - transfer_info->write_offset);
228
229   flow_io_write (FLOW_IO (tcp_io), buffer + transfer_info->write_offset, len);
230   test_print ("Main thread wrote %d bytes\n", len);
231
232   transfer_info->write_offset += len;
233
234   if (transfer_info->write_offset == BUFFER_SIZE)
235   {
236     flow_io_set_write_notify (FLOW_IO (tcp_io), NULL, NULL);
237     test_print ("Main thread read done\n");
238   }
239 }
240
241 static void
242 lost_connection (FlowTcpIO *tcp_io)
243 {
244   TransferInfo *transfer_info;
245
246   test_print ("Main thread got connectivity change\n");
247
248   if (flow_tcp_io_get_connectivity (tcp_io) != FLOW_CONNECTIVITY_DISCONNECTED)
249     return;
250
251   transfer_info = g_hash_table_lookup (transfer_info_table, tcp_io);
252   g_assert (transfer_info != NULL);
253
254   if (transfer_info->read_offset < BUFFER_SIZE)
255     test_end (TEST_RESULT_FAILED, "main thread did not read all data");
256
257   if (transfer_info->write_offset < BUFFER_SIZE)
258     test_end (TEST_RESULT_FAILED, "main thread did not write all data");
259
260   test_print ("Main thread lost connection\n");
261   g_hash_table_remove (transfer_info_table, tcp_io);
262   g_object_unref (tcp_io);
263
264   sockets_done++;
265   sockets_running--;
266
267   if (sockets_done >= SOCKETS_NUM && sockets_running == 0)
268     g_main_loop_quit (main_loop);
269 }
270
271 static void
272 new_connection (void)
273 {
274   FlowTcpIO    *tcp_io;
275   TransferInfo *transfer_info;
276
277   tcp_io = flow_tcp_io_listener_pop_connection (tcp_listener);
278   if (!tcp_io)
279     return;
280
281   add_tls (tcp_io, FLOW_AGENT_ROLE_SERVER);
282
283   main_tcp_io = tcp_io;
284
285   test_print ("Main thread received connection\n");
286
287   transfer_info = g_slice_new0 (TransferInfo);
288   g_hash_table_insert (transfer_info_table, tcp_io, transfer_info);
289
290   flow_io_set_read_notify  (FLOW_IO (tcp_io), (FlowNotifyFunc) read_notify, tcp_io);
291   flow_io_set_write_notify (FLOW_IO (tcp_io), (FlowNotifyFunc) write_notify, tcp_io);
292
293   g_signal_connect (tcp_io, "connectivity-changed", (GCallback) lost_connection, NULL);
294 }
295
296 static void
297 long_test (void)
298 {
299   test_print ("Long test begin\n");
300
301   g_timeout_add (250, (GSourceFunc) spawn_subthread, NULL);
302
303   g_signal_connect (tcp_listener, "new-connection", (GCallback) new_connection, NULL);
304
305   main_loop = g_main_loop_new (g_main_context_default (), FALSE);
306   g_main_loop_run (main_loop);
307
308   test_print ("Long test end\n");
309 }
310
311 static void
312 short_tests (void)
313 {
314   guchar read_buffer [2048];
315
316   test_print ("Short tests begin\n");
317
318   tcp_writer = flow_tcp_io_new ();
319   add_tls (tcp_writer, FLOW_AGENT_ROLE_CLIENT);
320
321 #if 0
322   if (flow_tcp_io_sync_connect (tcp_writer, bad_loopback_service))
323     test_end (TEST_RESULT_FAILED, "bad connect did not fail as expected");
324
325   test_print ("Bad connect failed as expected\n");
326 #endif
327
328   if (!flow_tcp_io_sync_connect (tcp_writer, loopback_service))
329     test_end (TEST_RESULT_FAILED, "could not connect to short-test listener");
330
331   test_print ("Client connect ok\n");
332
333   tcp_reader = flow_tcp_io_listener_sync_pop_connection (tcp_listener);
334   if (!tcp_reader)
335     test_end (TEST_RESULT_FAILED, "missed connection on listener end");
336
337   add_tls (tcp_reader, FLOW_AGENT_ROLE_SERVER);
338
339   test_print ("Server connect ok\n");
340
341   flow_io_write (FLOW_IO (tcp_writer), buffer,        512);
342   flow_io_write (FLOW_IO (tcp_writer), buffer +  512, 512);
343   flow_io_write (FLOW_IO (tcp_writer), buffer + 1024, 512);
344   flow_io_write (FLOW_IO (tcp_writer), buffer + 1536, 512);
345
346   test_print ("Wrote data\n");
347
348   /* Partial read */
349
350   flow_io_sync_read_exact (FLOW_IO (tcp_reader), read_buffer, 2000);
351
352   /* Check read */
353
354   if (memcmp (buffer, read_buffer, 2000))
355     test_end (TEST_RESULT_FAILED, "data mismatch in short transfer (1)");
356
357   /* Read remaining bytes with an oversized request */
358
359   if (flow_io_sync_read (FLOW_IO (tcp_reader), read_buffer, 100) != 48)
360     test_end (TEST_RESULT_FAILED, "oversized read request returned wrong count");
361
362   /* Make sure remaining bytes match */
363
364   if (memcmp (buffer + 2000, read_buffer, 48))
365     test_end (TEST_RESULT_FAILED, "data mismatch in short transfer (2)");
366
367   /* Make sure nothing got clobbered */
368
369   if (memcmp (buffer + 48, read_buffer + 48, 1000))
370     test_end (TEST_RESULT_FAILED, "data mismatch in short transfer (3)");
371
372   /* Disconnect */
373
374   flow_tcp_io_sync_disconnect (tcp_reader);
375   flow_tcp_io_sync_disconnect (tcp_writer);
376
377   g_object_unref (tcp_reader);
378   g_object_unref (tcp_writer);
379
380   test_print ("Short tests end\n");
381 }
382
383 static void
384 test_run (void)
385 {
386   gint i;
387
388   g_random_set_seed (time (NULL));
389
390   /* Set up a buffer with random data */
391
392   buffer = g_malloc (BUFFER_SIZE);
393
394   for (i = 0; i < BUFFER_SIZE; )
395   {
396     guchar *p = buffer + i;
397
398     if (i < BUFFER_SIZE - 4)
399     {
400       *((guint32 *) p) = g_random_int ();
401       i += 4;
402     }
403     else
404     {
405       *p = (guchar) g_random_int ();
406       i++;
407     }
408   }
409
410   transfer_info_table = g_hash_table_new_full (g_direct_hash, g_direct_equal,
411                                                NULL, (GDestroyNotify) transfer_info_free);
412
413   loopback_service = flow_ip_service_new ();
414   flow_ip_addr_set_string (FLOW_IP_ADDR (loopback_service), "127.0.0.1");
415   flow_ip_service_set_port (loopback_service, 2533);
416
417   bad_loopback_service = flow_ip_service_new ();
418   flow_ip_addr_set_string (FLOW_IP_ADDR (bad_loopback_service), "127.0.0.1");
419   flow_ip_service_set_port (bad_loopback_service, 12505);
420
421   tcp_listener = flow_tcp_io_listener_new ();
422   if (!flow_tcp_listener_set_local_service (FLOW_TCP_LISTENER (tcp_listener), loopback_service, NULL))
423     test_end (TEST_RESULT_FAILED, "could not bind short-test listener");
424
425   short_tests ();
426   long_test ();
427
428   g_hash_table_destroy (transfer_info_table);
429   transfer_info_table = NULL;
430
431   g_object_unref (tcp_listener);
432   tcp_listener = NULL;
433
434   g_object_unref (loopback_service);
435   loopback_service = NULL;
436
437   g_free (buffer);
438   buffer = NULL;
439 }
Note: See TracBrowser for help on using the browser.