Imported Upstream version 2.72.alpha
[platform/upstream/glib-networking.git] / tls / tests / dtls-connection.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * GIO TLS tests
4  *
5  * Copyright 2011, 2015, 2016 Collabora, Ltd.
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
18  * Public License along with this library; if not, see
19  * <http://www.gnu.org/licenses/>.
20  *
21  * In addition, when the library is used with OpenSSL, a special
22  * exception applies. Refer to the LICENSE_EXCEPTION file for details.
23  *
24  * Author: Stef Walter <stefw@collabora.co.uk>
25  *         Philip Withnall <philip.withnall@collabora.co.uk>
26  */
27
28 #include "config.h"
29
30 #include "lossy-socket.h"
31 #include "mock-interaction.h"
32
33 #include <gio/gio.h>
34 #ifdef BACKEND_IS_GNUTLS
35 #include <gnutls/gnutls.h>
36 #endif
37
38 #include <sys/types.h>
39 #include <string.h>
40
41 static const gchar *
42 tls_test_file_path (const char *name)
43 {
44   const gchar *const_path;
45   gchar *path;
46
47   path = g_test_build_filename (G_TEST_DIST, "files", name, NULL);
48   if (!g_path_is_absolute (path))
49     {
50       gchar *cwd, *abs;
51
52       cwd = g_get_current_dir ();
53       abs = g_build_filename (cwd, path, NULL);
54       g_free (cwd);
55       g_free (path);
56       path = abs;
57     }
58
59   const_path = g_intern_string (path);
60   g_free (path);
61   return const_path;
62 }
63
64 #define TEST_DATA "You win again, gravity!\n"
65 #define TEST_DATA_LENGTH 24
66
67 /* Static test parameters. */
68 typedef struct {
69   gint64 server_timeout;  /* microseconds */
70   gint64 client_timeout;  /* microseconds */
71   gboolean server_should_disappear;  /* whether the server should stop responding before sending a message */
72   gboolean server_should_close;  /* whether the server should close gracefully once it’s sent a message */
73   GTlsAuthenticationMode auth_mode;
74   IOPredicateFunc client_loss_inducer;
75   IOPredicateFunc server_loss_inducer;
76 } TestData;
77
78 typedef struct {
79   const TestData *test_data;
80
81   GMainContext *client_context;
82   GMainContext *server_context;
83   gboolean loop_finished;
84   GSocket *server_socket;
85   GDatagramBased *server_transport;
86   GSource *server_source;
87   GTlsDatabase *database;
88   GDatagramBased *server_connection;
89   GDatagramBased *client_connection;
90   GSocketConnectable *identity;
91   GSocketAddress *address;
92   gboolean rehandshake;
93   GTlsCertificateFlags accept_flags;
94   GError *read_error;
95   gboolean expect_server_error;
96   GError *server_error;
97   gboolean server_running;
98   const gchar * const *server_protocols;
99
100   char buf[128];
101   gssize nread, nwrote;
102 } TestConnection;
103
104 static void
105 setup_connection (TestConnection *test, gconstpointer data)
106 {
107   test->test_data = data;
108
109   test->client_context = g_main_context_default ();
110   test->loop_finished = FALSE;
111 }
112
113 /* Waits about 10 seconds for @var to be NULL/FALSE */
114 #define WAIT_UNTIL_UNSET(var)                                     \
115   if (var)                                                        \
116     {                                                             \
117       int i;                                                      \
118                                                                   \
119       for (i = 0; i < 13 && (var); i++)                           \
120         {                                                         \
121           g_usleep (1000 * (1 << i));                             \
122           g_main_context_iteration (test->client_context, FALSE); \
123         }                                                         \
124                                                                   \
125       g_assert_true (!(var));                                     \
126     }
127
128 /* Waits about 10 seconds for @var's ref_count to drop to 1 */
129 #define WAIT_UNTIL_UNREFFED(var)                                  \
130   if (var)                                                        \
131     {                                                             \
132       int i;                                                      \
133                                                                   \
134       for (i = 0; i < 13 && G_OBJECT (var)->ref_count > 1; i++)   \
135         {                                                         \
136           g_usleep (1000 * (1 << i));                             \
137           g_main_context_iteration (test->client_context, FALSE); \
138         }                                                         \
139                                                                   \
140       g_assert_cmpuint (G_OBJECT (var)->ref_count, ==, 1);        \
141     }
142
143 static void
144 teardown_connection (TestConnection *test, gconstpointer data)
145 {
146   GError *error = NULL;
147
148   if (test->server_source)
149     {
150       g_source_destroy (test->server_source);
151       g_source_unref (test->server_source);
152       test->server_source = NULL;
153     }
154
155   if (test->server_connection)
156     {
157       WAIT_UNTIL_UNSET (test->server_running);
158
159       WAIT_UNTIL_UNREFFED (test->server_connection);
160       g_object_unref (test->server_connection);
161       test->server_connection = NULL;
162     }
163
164   g_clear_object (&test->server_transport);
165
166   if (test->server_socket)
167     {
168       g_socket_close (test->server_socket, &error);
169       g_assert_no_error (error);
170
171       /* The outstanding accept_async will hold a ref on test->server_socket,
172        * which we want to wait for it to release if we're valgrinding.
173        */
174       WAIT_UNTIL_UNREFFED (test->server_socket);
175       g_object_unref (test->server_socket);
176       test->server_socket = NULL;
177     }
178
179   if (test->client_connection)
180     {
181       WAIT_UNTIL_UNREFFED (test->client_connection);
182       g_object_unref (test->client_connection);
183       test->client_connection = NULL;
184     }
185
186   if (test->database)
187     {
188       WAIT_UNTIL_UNREFFED (test->database);
189       g_object_unref (test->database);
190       test->database = NULL;
191     }
192
193   g_clear_object (&test->address);
194   g_clear_object (&test->identity);
195   g_clear_error (&test->read_error);
196   g_clear_error (&test->server_error);
197 }
198
199 static void
200 start_server (TestConnection *test)
201 {
202   GInetAddress *inet;
203   GSocketAddress *addr;
204   GInetSocketAddress *iaddr;
205   GSocket *socket = NULL;
206   GError *error = NULL;
207
208   inet = g_inet_address_new_from_string ("127.0.0.1");
209   addr = g_inet_socket_address_new (inet, 0);
210   g_object_unref (inet);
211
212   socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
213                          G_SOCKET_PROTOCOL_UDP, &error);
214   g_assert_no_error (error);
215
216   g_socket_bind (socket, addr, FALSE, &error);
217   g_assert_no_error (error);
218
219   test->address = g_socket_get_local_address (socket, &error);
220   g_assert_no_error (error);
221
222   g_object_unref (addr);
223
224   /* The hostname in test->identity matches the server certificate. */
225   iaddr = G_INET_SOCKET_ADDRESS (test->address);
226   test->identity = g_network_address_new ("server.example.com",
227                                           g_inet_socket_address_get_port (iaddr));
228
229   test->server_socket = socket;
230   if (test->test_data->server_loss_inducer)
231     {
232       test->server_transport = lossy_socket_new (G_DATAGRAM_BASED (socket),
233                                                  test->test_data->server_loss_inducer,
234                                                  test);
235     }
236   else
237     {
238       test->server_transport = G_DATAGRAM_BASED (g_object_ref (socket));
239     }
240   test->server_running = TRUE;
241 }
242
243 static gboolean
244 on_accept_certificate (GTlsClientConnection *conn, GTlsCertificate *cert,
245                        GTlsCertificateFlags errors, gpointer user_data)
246 {
247   TestConnection *test = user_data;
248   return errors == test->accept_flags;
249 }
250
251 static void close_server_connection (TestConnection *test,
252                                      gboolean        graceful);
253
254 static void
255 on_rehandshake_finish (GObject        *object,
256                        GAsyncResult   *res,
257                        gpointer        user_data)
258 {
259   TestConnection *test = user_data;
260   GError *error = NULL;
261   GOutputVector vectors[2] = {
262     { TEST_DATA + TEST_DATA_LENGTH / 2, TEST_DATA_LENGTH / 4 },
263     { TEST_DATA + 3 * TEST_DATA_LENGTH / 4, TEST_DATA_LENGTH / 4},
264   };
265   GOutputMessage message = { NULL, vectors, G_N_ELEMENTS (vectors), 0, NULL, 0 };
266   gint n_sent;
267
268   g_dtls_connection_handshake_finish (G_DTLS_CONNECTION (object), res, &error);
269   g_assert_no_error (error);
270
271   do
272     {
273       g_clear_error (&test->server_error);
274       n_sent = g_datagram_based_send_messages (test->server_connection,
275                                                &message, 1,
276                                                G_SOCKET_MSG_NONE, 0, NULL,
277                                                &test->server_error);
278       g_main_context_iteration (test->server_context, FALSE);
279     }
280   while (g_error_matches (test->server_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK));
281
282   if (!test->server_error)
283     {
284       g_assert_cmpint (n_sent, ==, 1);
285       g_assert_cmpuint (message.bytes_sent, ==, TEST_DATA_LENGTH / 2);
286     }
287
288   if (!test->server_error && test->rehandshake)
289     {
290       test->rehandshake = FALSE;
291       g_dtls_connection_handshake_async (G_DTLS_CONNECTION (test->server_connection),
292                                          G_PRIORITY_DEFAULT, NULL,
293                                          on_rehandshake_finish, test);
294       return;
295     }
296
297   if (test->test_data->server_should_close)
298     close_server_connection (test, TRUE);
299 }
300
301 static void
302 on_rehandshake_finish_threaded (GObject      *object,
303                                 GAsyncResult *res,
304                                 gpointer      user_data)
305 {
306   TestConnection *test = user_data;
307   GError *error = NULL;
308   GOutputVector vectors[2] = {
309     { TEST_DATA + TEST_DATA_LENGTH / 2, TEST_DATA_LENGTH / 4 },
310     { TEST_DATA + 3 * TEST_DATA_LENGTH / 4, TEST_DATA_LENGTH / 4},
311   };
312   GOutputMessage message = { NULL, vectors, G_N_ELEMENTS (vectors), 0, NULL, 0 };
313   gint n_sent;
314
315   g_dtls_connection_handshake_finish (G_DTLS_CONNECTION (object), res, &error);
316   g_assert_no_error (error);
317
318   do
319     {
320       g_clear_error (&test->server_error);
321       n_sent = g_datagram_based_send_messages (test->server_connection,
322                                                &message, 1,
323                                                G_SOCKET_MSG_NONE, 0, NULL,
324                                                &test->server_error);
325       g_main_context_iteration (test->server_context, FALSE);
326     }
327   while (g_error_matches (test->server_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK));
328
329   if (!test->server_error)
330     {
331       g_assert_cmpint (n_sent, ==, 1);
332       g_assert_cmpuint (message.bytes_sent, ==, TEST_DATA_LENGTH / 2);
333     }
334
335   if (!test->server_error && test->rehandshake)
336     {
337       test->rehandshake = FALSE;
338       g_dtls_connection_handshake_async (G_DTLS_CONNECTION (test->server_connection),
339                                          G_PRIORITY_DEFAULT, NULL,
340                                          on_rehandshake_finish_threaded, test);
341       return;
342     }
343
344   if (test->test_data->server_should_close)
345     close_server_connection (test, TRUE);
346 }
347
348 static void
349 close_server_connection (TestConnection *test,
350                          gboolean        graceful)
351 {
352   GError *error = NULL;
353
354   if (graceful)
355     g_dtls_connection_close (G_DTLS_CONNECTION (test->server_connection),
356                              NULL, &error);
357
358   /* Clear pending dispatches from the context. */
359   while (g_main_context_iteration (test->server_context, FALSE));
360
361   if (graceful && test->expect_server_error)
362     g_assert_nonnull (error);
363   else if (graceful)
364     g_assert_no_error (error);
365
366   test->server_running = FALSE;
367 }
368
369 static gboolean
370 on_incoming_connection (GSocket       *socket,
371                         GIOCondition   condition,
372                         gpointer       user_data)
373 {
374   TestConnection *test = user_data;
375   GTlsCertificate *cert;
376   GError *error = NULL;
377   GOutputVector vector = {
378     TEST_DATA,
379     test->rehandshake ? TEST_DATA_LENGTH / 2 : TEST_DATA_LENGTH
380   };
381   GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
382   gint n_sent;
383   GSocketAddress *addr = NULL;  /* owned */
384   guint8 databuf[65536];
385   GInputVector vec = {databuf, sizeof (databuf)};
386   gint flags = G_SOCKET_MSG_PEEK;
387   gssize ret;
388
389   /* Ignore this if the source has already been destroyed. */
390   if (g_source_is_destroyed (test->server_source))
391     return G_SOURCE_REMOVE;
392
393   /* Remove the source as the first thing. */
394   g_source_destroy (test->server_source);
395   g_source_unref (test->server_source);
396   test->server_source = NULL;
397
398   /* Peek at the incoming packet to get the peer’s address. */
399   ret = g_socket_receive_message (socket, &addr, &vec, 1, NULL, NULL,
400                                   &flags, NULL, NULL);
401
402   if (ret <= 0)
403     return G_SOURCE_REMOVE;
404
405   if (!g_socket_connect (socket, addr, NULL, NULL))
406     {
407       g_object_unref (addr);
408       return G_SOURCE_CONTINUE;
409     }
410
411   g_clear_object (&addr);
412
413   /* Wrap the socket in a GDtlsServerConnection. */
414   cert = g_tls_certificate_new_from_file (tls_test_file_path ("server-and-key.pem"), &error);
415   g_assert_no_error (error);
416
417   test->server_connection = g_dtls_server_connection_new (test->server_transport,
418                                                           cert, &error);
419   g_debug ("%s: Server connection %p on socket %p", G_STRFUNC, test->server_connection, socket);
420   g_assert_no_error (error);
421   g_object_unref (cert);
422
423   g_object_set (test->server_connection, "authentication-mode",
424                 test->test_data->auth_mode, NULL);
425   g_signal_connect (test->server_connection, "accept-certificate",
426                     G_CALLBACK (on_accept_certificate), test);
427
428   if (test->database)
429     g_dtls_connection_set_database (G_DTLS_CONNECTION (test->server_connection), test->database);
430
431   if (test->server_protocols)
432     {
433       g_dtls_connection_set_advertised_protocols (G_DTLS_CONNECTION (test->server_connection),
434                                                   test->server_protocols);
435     }
436
437   if (test->test_data->server_should_disappear)
438     {
439       close_server_connection (test, FALSE);
440       return G_SOURCE_REMOVE;
441     }
442
443   do
444     {
445       g_clear_error (&test->server_error);
446       n_sent = g_datagram_based_send_messages (test->server_connection,
447                                                &message, 1,
448                                                G_SOCKET_MSG_NONE, 0, NULL,
449                                                &test->server_error);
450       g_main_context_iteration (test->server_context, FALSE);
451     }
452   while (g_error_matches (test->server_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK));
453
454   if (!test->server_error)
455     {
456       g_assert_cmpint (n_sent, ==, 1);
457       g_assert_cmpuint (message.bytes_sent, ==, vector.size);
458     }
459
460   if (!test->server_error && test->rehandshake)
461     {
462       test->rehandshake = FALSE;
463       g_dtls_connection_handshake_async (G_DTLS_CONNECTION (test->server_connection),
464                                          G_PRIORITY_DEFAULT, NULL,
465                                          on_rehandshake_finish, test);
466       return G_SOURCE_REMOVE;
467     }
468
469   if (test->test_data->server_should_close)
470     close_server_connection (test, TRUE);
471
472   return G_SOURCE_REMOVE;
473 }
474
475 static gboolean
476 on_incoming_connection_threaded (GSocket      *socket,
477                                  GIOCondition  condition,
478                                  gpointer      user_data)
479 {
480   TestConnection *test = user_data;
481   GTlsCertificate *cert;
482   GError *error = NULL;
483   GOutputVector vector = {
484     TEST_DATA,
485     test->rehandshake ? TEST_DATA_LENGTH / 2 : TEST_DATA_LENGTH
486   };
487   GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
488   gint n_sent;
489   GSocketAddress *addr = NULL;  /* owned */
490   guint8 databuf[65536];
491   GInputVector vec = {databuf, sizeof (databuf)};
492   gint flags = G_SOCKET_MSG_PEEK;
493   gssize ret;
494
495   /* Ignore this if the source has already been destroyed. */
496   if (g_source_is_destroyed (test->server_source))
497     return G_SOURCE_REMOVE;
498
499   /* Remove the source as the first thing. */
500   g_source_destroy (test->server_source);
501   g_source_unref (test->server_source);
502   test->server_source = NULL;
503
504   /* Peek at the incoming packet to get the peer’s address. */
505   ret = g_socket_receive_message (socket, &addr, &vec, 1, NULL, NULL,
506                                   &flags, NULL, NULL);
507
508   if (ret <= 0)
509     return G_SOURCE_REMOVE;
510
511   if (!g_socket_connect (socket, addr, NULL, NULL))
512     {
513       g_object_unref (addr);
514       return G_SOURCE_CONTINUE;
515     }
516
517   g_clear_object (&addr);
518
519   /* Wrap the socket in a GDtlsServerConnection. */
520   cert = g_tls_certificate_new_from_file (tls_test_file_path ("server-and-key.pem"), &error);
521   g_assert_no_error (error);
522
523   test->server_connection = g_dtls_server_connection_new (test->server_transport,
524                                                           cert, &error);
525   g_debug ("%s: Server connection %p on socket %p", G_STRFUNC, test->server_connection, socket);
526   g_assert_no_error (error);
527   g_object_unref (cert);
528
529   g_object_set (test->server_connection, "authentication-mode",
530                 test->test_data->auth_mode, NULL);
531   g_signal_connect (test->server_connection, "accept-certificate",
532                     G_CALLBACK (on_accept_certificate), test);
533
534   if (test->database)
535     g_dtls_connection_set_database (G_DTLS_CONNECTION (test->server_connection), test->database);
536
537   if (test->test_data->server_should_disappear)
538     {
539       close_server_connection (test, FALSE);
540       return G_SOURCE_REMOVE;
541     }
542
543   do
544     {
545       g_clear_error (&test->server_error);
546       n_sent = g_datagram_based_send_messages (test->server_connection,
547                                                &message, 1,
548                                                G_SOCKET_MSG_NONE,
549                                                test->test_data->server_timeout, NULL,
550                                                &test->server_error);
551       g_main_context_iteration (test->server_context, FALSE);
552     }
553   while (g_error_matches (test->server_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK));
554
555   if (!test->server_error)
556     {
557       g_assert_cmpint (n_sent, ==, 1);
558       g_assert_cmpuint (message.bytes_sent, ==, vector.size);
559     }
560
561   if (!test->server_error && test->rehandshake)
562     {
563       test->rehandshake = FALSE;
564       g_dtls_connection_handshake_async (G_DTLS_CONNECTION (test->server_connection),
565                                          G_PRIORITY_DEFAULT, NULL,
566                                          on_rehandshake_finish_threaded, test);
567       return G_SOURCE_REMOVE;
568     }
569
570   if (test->test_data->server_should_close)
571     close_server_connection (test, TRUE);
572
573   return G_SOURCE_REMOVE;
574 }
575
576 static gpointer
577 server_service_cb (gpointer user_data)
578 {
579   TestConnection *test = user_data;
580
581   test->server_context = g_main_context_new ();
582   g_main_context_push_thread_default (test->server_context);
583
584   test->server_source = g_socket_create_source (test->server_socket, G_IO_IN,
585                                                 NULL);
586   g_source_set_callback (test->server_source,
587                          (GSourceFunc) on_incoming_connection_threaded, test, NULL);
588   g_source_attach (test->server_source, test->server_context);
589
590   /* Run the server until it should stop. */
591   while (test->server_running)
592     g_main_context_iteration (test->server_context, TRUE);
593
594   g_main_context_pop_thread_default (test->server_context);
595
596   return NULL;
597 }
598
599 static void
600 start_server_service (TestConnection         *test,
601                       gboolean                threaded)
602 {
603   start_server (test);
604
605   if (threaded)
606     {
607       g_thread_new ("dtls-server", server_service_cb, test);
608       return;
609     }
610
611   test->server_source = g_socket_create_source (test->server_socket, G_IO_IN,
612                                                 NULL);
613   g_source_set_callback (test->server_source,
614                          (GSourceFunc) on_incoming_connection, test, NULL);
615   g_source_attach (test->server_source, NULL);
616 }
617
618 static GDatagramBased *
619 start_server_and_connect_to_it (TestConnection         *test,
620                                 gboolean                threaded)
621 {
622   GError *error = NULL;
623   GSocket *socket;
624   GDatagramBased *transport;
625
626   start_server_service (test, threaded);
627
628   socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
629                          G_SOCKET_PROTOCOL_UDP, &error);
630   g_assert_no_error (error);
631
632   g_socket_connect (socket, test->address, NULL, &error);
633   g_assert_no_error (error);
634
635   if (test->test_data->client_loss_inducer)
636     {
637       transport = lossy_socket_new (G_DATAGRAM_BASED (socket),
638                                     test->test_data->client_loss_inducer,
639                                     test);
640       g_object_unref (socket);
641     }
642   else
643     {
644       transport = G_DATAGRAM_BASED (socket);
645     }
646
647   return transport;
648 }
649
650 static void
651 read_test_data_async (TestConnection *test)
652 {
653   gchar *check;
654   GError *error = NULL;
655   guint8 buf[TEST_DATA_LENGTH * 2];
656   GInputVector vectors[2] = {
657     { buf, sizeof (buf) / 2 },
658     { buf + sizeof (buf) / 2, sizeof (buf) / 2 },
659   };
660   GInputMessage message = { NULL, vectors, G_N_ELEMENTS (vectors), 0, 0, NULL, NULL };
661   gint n_read;
662
663   do
664     {
665       g_clear_error (&test->read_error);
666       n_read = g_datagram_based_receive_messages (test->client_connection,
667                                                   &message, 1,
668                                                   G_SOCKET_MSG_NONE,
669                                                   test->test_data->client_timeout,
670                                                   NULL, &test->read_error);
671       g_main_context_iteration (test->client_context, FALSE);
672     }
673   while (g_error_matches (test->read_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK));
674
675   if (!test->read_error)
676     {
677       g_assert_cmpint (n_read, ==, 1);
678
679       check = g_strdup (TEST_DATA);
680       g_assert_cmpuint (strlen (check), ==, message.bytes_received);
681       g_assert_cmpint (strncmp (check, (const char *)buf, message.bytes_received), ==, 0);
682       g_free (check);
683     }
684
685   g_dtls_connection_close (G_DTLS_CONNECTION (test->client_connection),
686                            NULL, &error);
687   g_assert_no_error (error);
688
689   test->loop_finished = TRUE;
690 }
691
692 /* Test that connecting a client to a server, both using main contexts in the
693  * same thread, works; and that sending a message from the server to the client
694  * before shutting down gracefully works. */
695 static void
696 test_basic_connection (TestConnection *test,
697                        gconstpointer   data)
698 {
699   GDatagramBased *connection;
700   GError *error = NULL;
701
702   connection = start_server_and_connect_to_it (test, FALSE);
703   test->client_connection = g_dtls_client_connection_new (connection, test->identity, &error);
704   g_debug ("%s: Client connection %p on socket %p", G_STRFUNC, test->client_connection, connection);
705   g_assert_no_error (error);
706   g_object_unref (connection);
707
708   /* No validation at all in this test */
709   g_dtls_client_connection_set_validation_flags (G_DTLS_CLIENT_CONNECTION (test->client_connection),
710                                                  0);
711
712   read_test_data_async (test);
713   while (!test->loop_finished)
714     g_main_context_iteration (test->client_context, TRUE);
715
716   g_assert_no_error (test->server_error);
717   g_assert_no_error (test->read_error);
718 }
719
720 /* Test that connecting a client to a server, both using separate threads,
721  * works; and that sending a message from the server to the client before
722  * shutting down gracefully works. */
723 static void
724 test_threaded_connection (TestConnection *test,
725                           gconstpointer   data)
726 {
727   GDatagramBased *connection;
728   GError *error = NULL;
729
730   connection = start_server_and_connect_to_it (test, TRUE);
731   test->client_connection = g_dtls_client_connection_new (connection, test->identity, &error);
732   g_debug ("%s: Client connection %p on socket %p", G_STRFUNC, test->client_connection, connection);
733   g_assert_no_error (error);
734   g_object_unref (connection);
735
736   /* No validation at all in this test */
737   g_dtls_client_connection_set_validation_flags (G_DTLS_CLIENT_CONNECTION (test->client_connection),
738                                                  0);
739
740   read_test_data_async (test);
741   while (!test->loop_finished)
742     g_main_context_iteration (test->client_context, TRUE);
743
744   g_assert_no_error (test->server_error);
745   g_assert_no_error (test->read_error);
746 }
747
748 /* Test that a client can successfully connect to a server, then the server
749  * disappears, and when the client tries to read from it, the client hits a
750  * timeout error (rather than blocking indefinitely or returning another
751  * error). */
752 static void
753 test_connection_timeouts_read (TestConnection *test,
754                                gconstpointer   data)
755 {
756   GDatagramBased *connection;
757   GError *error = NULL;
758
759   connection = start_server_and_connect_to_it (test, TRUE);
760   test->client_connection = g_dtls_client_connection_new (connection,
761                                                           test->identity, &error);
762   g_debug ("%s: Client connection %p on socket %p", G_STRFUNC,
763            test->client_connection, connection);
764   g_assert_no_error (error);
765   g_object_unref (connection);
766
767   /* No validation at all in this test */
768   g_dtls_client_connection_set_validation_flags (G_DTLS_CLIENT_CONNECTION (test->client_connection),
769                                                  0);
770
771   read_test_data_async (test);
772   while (!test->loop_finished)
773     g_main_context_iteration (test->client_context, TRUE);
774
775   g_assert_no_error (test->server_error);
776   g_assert_error (test->read_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
777 }
778
779 static IODecision
780 drop_first_outgoing (const IODetails *io,
781                      gpointer         user_data)
782 {
783   if (io->direction == IO_OUT && io->serial == 1)
784     return IO_DROP;
785
786   return IO_KEEP;
787 }
788
789 int
790 main (int   argc,
791       char *argv[])
792 {
793   const TestData blocking = {
794     -1,  /* server_timeout */
795     0,  /* client_timeout */
796     FALSE,  /* server_should_disappear */
797     TRUE, /* server_should_close */
798     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
799     NULL, NULL, /* loss inducers */
800   };
801   const TestData server_timeout = {
802     1000 * G_USEC_PER_SEC,  /* server_timeout */
803     0,  /* client_timeout */
804     FALSE,  /* server_should_disappear */
805     TRUE, /* server_should_close */
806     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
807     NULL, NULL, /* loss inducers */
808   };
809   const TestData nonblocking = {
810     0,  /* server_timeout */
811     0,  /* client_timeout */
812     FALSE,  /* server_should_disappear */
813     TRUE, /* server_should_close */
814     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
815     NULL, NULL, /* loss inducers */
816   };
817   const TestData client_timeout = {
818     0,  /* server_timeout */
819     (gint64) (0.5 * G_USEC_PER_SEC),  /* client_timeout */
820     TRUE,  /* server_should_disappear */
821     TRUE, /* server_should_close */
822     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
823     NULL, NULL, /* loss inducers */
824   };
825   const TestData client_loss = {
826     -1,  /* server_timeout */
827     0,  /* client_timeout */
828     FALSE,  /* server_should_disappear */
829     TRUE, /* server_should_close */
830     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
831     drop_first_outgoing, NULL, /* loss inducers */
832   };
833   const TestData server_loss = {
834     -1,  /* server_timeout */
835     0,  /* client_timeout */
836     FALSE,  /* server_should_disappear */
837     TRUE, /* server_should_close */
838     G_TLS_AUTHENTICATION_NONE,  /* auth_mode */
839     NULL, drop_first_outgoing, /* loss inducers */
840   };
841   int ret;
842   int i;
843
844   /* Check if this is a subprocess, and set G_TLS_GNUTLS_PRIORITY
845    * appropriately if so.
846    */
847   for (i = 1; i < argc - 1; i++)
848     {
849       if (!strcmp (argv[i], "-p"))
850         {
851           const char *priority = argv[i + 1];
852
853           priority = strrchr (priority, '/');
854           if (priority++ &&
855               (g_str_has_prefix (priority, "NORMAL:") ||
856                g_str_has_prefix (priority, "NONE:")))
857             g_setenv ("G_TLS_GNUTLS_PRIORITY", priority, TRUE);
858           break;
859         }
860     }
861
862   g_test_init (&argc, &argv, NULL);
863   g_test_bug_base ("http://bugzilla.gnome.org/");
864
865   g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
866   g_setenv ("GIO_USE_TLS", BACKEND, TRUE);
867   g_assert_cmpint (g_ascii_strcasecmp (G_OBJECT_TYPE_NAME (g_tls_backend_get_default ()), "GTlsBackend" BACKEND), ==, 0);
868
869   g_test_add ("/dtls/" BACKEND "/connection/basic/blocking", TestConnection, &blocking,
870               setup_connection, test_basic_connection, teardown_connection);
871   g_test_add ("/dtls/" BACKEND "/connection/basic/timeout", TestConnection, &server_timeout,
872               setup_connection, test_basic_connection, teardown_connection);
873   g_test_add ("/dtls/" BACKEND "/connection/basic/nonblocking",
874               TestConnection, &nonblocking,
875               setup_connection, test_basic_connection, teardown_connection);
876
877   g_test_add ("/dtls/" BACKEND "/connection/threaded/blocking", TestConnection, &blocking,
878               setup_connection, test_threaded_connection, teardown_connection);
879   g_test_add ("/dtls/" BACKEND "/connection/threaded/timeout",
880               TestConnection, &server_timeout,
881               setup_connection, test_threaded_connection, teardown_connection);
882   g_test_add ("/dtls/" BACKEND "/connection/threaded/nonblocking",
883               TestConnection, &nonblocking,
884               setup_connection, test_threaded_connection, teardown_connection);
885
886   g_test_add ("/dtls/" BACKEND "/connection/timeouts/read", TestConnection, &client_timeout,
887               setup_connection, test_connection_timeouts_read,
888               teardown_connection);
889
890   g_test_add ("/dtls/" BACKEND "/connection/lossy/client", TestConnection, &client_loss,
891               setup_connection, test_basic_connection, teardown_connection);
892   g_test_add ("/dtls/" BACKEND "/connection/lossy/server", TestConnection, &server_loss,
893               setup_connection, test_basic_connection, teardown_connection);
894
895   ret = g_test_run ();
896
897   /* for valgrinding */
898   g_main_context_unref (g_main_context_default ());
899
900   return ret;
901 }