tests: added check for operation under different threads and DTLS
[gnutls:gnutls.git] / tests / mini-dtls-pthread.c
1 /*
2  * Copyright (C) 2015 Red Hat, Inc.
3  *
4  * Author: Nikos Mavrogiannopoulos
5  *
6  * This file is part of GnuTLS.
7  *
8  * GnuTLS is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * GnuTLS is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with GnuTLS; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <gnutls/gnutls.h>
33 #include <gnutls/dtls.h>
34 #include <signal.h>
35 #include <unistd.h>
36 #include <netinet/in.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/wait.h>
40 #include <pthread.h>
41 #include "utils.h"
42
43 #ifdef _WIN32
44
45 void doit(void)
46 {
47         exit(77);
48 }
49
50 #else
51
52 /* These are global */
53 pid_t child;
54
55 static void terminate(void)
56 {
57         int status;
58
59         kill(child, SIGTERM);
60         wait(&status);
61         exit(1);
62 }
63
64 /* Tests whether we can send and receive from different threads
65  * using DTLS, either as server or client. DTLS is a superset of
66  * TLS, so correct behavior under fork means TLS would operate too.
67  */
68
69 const char *side = "";
70
71 static void tls_log_func(int level, const char *str)
72 {
73         fprintf(stderr, "%s|<%d>| %s", side, level, str);
74 }
75
76 static unsigned char server_cert_pem[] =
77   "-----BEGIN CERTIFICATE-----\n"
78   "MIICHzCCAaWgAwIBAgIBCTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\n"
79   "A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\n"
80   "MTMwOTI0MTU1MjA0WhcNMjMwOTIyMTU1MjA0WjA0MQswCQYDVQQGEwJOTDERMA8G\n"
81   "A1UEChMIUG9sYXJTU0wxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG\n"
82   "CCqGSM49AwEHA0IABDfMVtl2CR5acj7HWS3/IG7ufPkGkXTQrRS192giWWKSTuUA\n"
83   "2CMR/+ov0jRdXRa9iojCa3cNVc2KKg76Aci07f+jgZ0wgZowCQYDVR0TBAIwADAd\n"
84   "BgNVHQ4EFgQUUGGlj9QH2deCAQzlZX+MY0anE74wbgYDVR0jBGcwZYAUnW0gJEkB\n"
85   "PyvLeLUZvH4kydv7NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKEwhQb2xh\n"
86   "clNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAoG\n"
87   "CCqGSM49BAMCA2gAMGUCMQCaLFzXptui5WQN8LlO3ddh1hMxx6tzgLvT03MTVK2S\n"
88   "C12r0Lz3ri/moSEpNZWqPjkCMCE2f53GXcYLqyfyJR078c/xNSUU5+Xxl7VZ414V\n"
89   "fGa5kHvHARBPc8YAIVIqDvHH1Q==\n"
90   "-----END CERTIFICATE-----\n";
91
92 const gnutls_datum_t server_cert = { server_cert_pem,
93         sizeof(server_cert_pem)
94 };
95
96 static unsigned char server_key_pem[] =
97   "-----BEGIN EC PRIVATE KEY-----\n"
98   "MHcCAQEEIPEqEyB2AnCoPL/9U/YDHvdqXYbIogTywwyp6/UfDw6noAoGCCqGSM49\n"
99   "AwEHoUQDQgAEN8xW2XYJHlpyPsdZLf8gbu58+QaRdNCtFLX3aCJZYpJO5QDYIxH/\n"
100   "6i/SNF1dFr2KiMJrdw1VzYoqDvoByLTt/w==\n"
101   "-----END EC PRIVATE KEY-----\n";
102
103 const gnutls_datum_t server_key = { server_key_pem,
104         sizeof(server_key_pem)
105 };
106
107 #define MSG "hello1111"
108 #define MSG2 "xxxxxxxxxxxx"
109
110 static void *start_thread(void *arg)
111 {
112         gnutls_session_t session = arg;
113         int ret;
114         char buf[64];
115
116         if (debug)
117                 success("client: TLS version is: %s\n",
118                         gnutls_protocol_get_name
119                         (gnutls_protocol_get_version(session)));
120         sleep(1);
121         /* the server should reflect our messages */
122         ret = gnutls_record_recv(session, buf, sizeof(buf));
123         if (ret != sizeof(MSG)-1 || memcmp(buf, MSG, sizeof(MSG)-1) != 0) {
124                 fail("client: recv failed: %s\n", gnutls_strerror(ret));
125                 exit(1);
126         }
127
128         if (debug) {
129                 fprintf(stderr, "client received: %.*s\n", ret, buf);
130         }
131
132         ret = gnutls_record_recv(session, buf, sizeof(buf));
133         if (ret != sizeof(MSG2)-1 || memcmp(buf, MSG2, sizeof(MSG2)-1) != 0) {
134                 fail("client: recv2 failed: %s\n", gnutls_strerror(ret));
135                 exit(1);
136         }
137
138         if (debug) {
139                 fprintf(stderr, "client received: %.*s\n", ret, buf);
140         }
141
142         ret = gnutls_record_recv(session, buf, sizeof(buf));
143         if (ret != 0) {
144                 fail("client: recv3 failed: %s\n", gnutls_strerror(ret));
145                 exit(1);
146         }
147
148         pthread_exit(0);
149 }
150
151 static
152 void do_thread_stuff(gnutls_session_t session)
153 {
154         int ret;
155         pthread_t id;
156
157         /* separate sending from receiving */
158         ret = pthread_create(&id, NULL, start_thread, session);
159         if (ret != 0) {
160                 exit(1);
161         }
162
163         ret = gnutls_record_send(session, MSG, sizeof(MSG)-1);
164         if (ret != sizeof(MSG)-1) {
165                 fail("client: send failed: %s\n", gnutls_strerror(ret));
166                 exit(1);
167         }
168
169         ret = gnutls_record_send(session, MSG2, sizeof(MSG2)-1);
170         if (ret != sizeof(MSG2)-1) {
171                 fail("client: send2 failed: %s\n", gnutls_strerror(ret));
172                 exit(1);
173         }
174         sleep(2);
175         gnutls_bye(session, GNUTLS_SHUT_WR);
176 }
177
178 static void do_reflect_stuff(gnutls_session_t session)
179 {
180         char buf[64];
181         unsigned buf_size;
182         int ret;
183
184         do {
185                 ret = gnutls_record_recv(session, buf, sizeof(buf));
186                 if (ret < 0) {
187                         fail("server: recv failed: %s\n", gnutls_strerror(ret));
188                         terminate();
189                 }
190
191                 if (ret == 0)
192                         break;
193
194                 buf_size = ret;
195                 if (debug) {
196                         fprintf(stderr, "server received: %.*s\n", buf_size, buf);
197                 }
198
199                 ret = gnutls_record_send(session, buf, buf_size);
200                 if (ret < 0) {
201                         fail("server: send failed: %s\n", gnutls_strerror(ret));
202                         terminate();
203                 }
204         } while(1);
205
206         /* do not wait for the peer to close the connection.
207          */
208         gnutls_bye(session, GNUTLS_SHUT_WR);
209 }
210
211 static void client(int fd, unsigned do_thread)
212 {
213         int ret;
214         gnutls_certificate_credentials_t x509_cred;
215         gnutls_session_t session;
216         /* Need to enable anonymous KX specifically. */
217
218         global_init();
219
220         if (debug) {
221                 side = "client";
222                 gnutls_global_set_log_function(tls_log_func);
223                 gnutls_global_set_log_level(4711);
224         }
225
226         gnutls_certificate_allocate_credentials(&x509_cred);
227
228         /* Initialize TLS session
229          */
230         gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM);
231         gnutls_dtls_set_mtu(session, 1500);
232         gnutls_dtls_set_timeouts(session, 6 * 1000, 60 * 1000);
233         //gnutls_transport_set_push_function(session, push);
234
235         /* Use default priorities */
236         gnutls_priority_set_direct(session,
237                                    "NONE:+VERS-DTLS-ALL:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ECDHE-ECDSA:+CURVE-ALL",
238                                    NULL);
239
240         /* put the anonymous credentials to the current session
241          */
242         gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
243
244         gnutls_transport_set_int(session, fd);
245
246         /* Perform the TLS handshake
247          */
248         do {
249                 ret = gnutls_handshake(session);
250         }
251         while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
252
253         if (ret < 0) {
254                 fail("client: Handshake failed\n");
255                 gnutls_perror(ret);
256                 exit(1);
257         } else {
258                 if (debug)
259                         success("client: Handshake was completed\n");
260         }
261
262         if (do_thread)
263                 do_thread_stuff(session);
264         else
265                 do_reflect_stuff(session);
266
267         close(fd);
268
269         gnutls_deinit(session);
270
271         gnutls_certificate_free_credentials(x509_cred);
272
273         gnutls_global_deinit();
274         exit(0);
275 }
276
277
278 static void server(int fd, unsigned do_thread)
279 {
280         int ret;
281         gnutls_certificate_credentials_t x509_cred;
282         gnutls_session_t session;
283
284         /* this must be called once in the program
285          */
286         global_init();
287
288 #if 0
289         if (debug) {
290                 side = "server";
291                 gnutls_global_set_log_function(tls_log_func);
292                 gnutls_global_set_log_level(4711);
293         }
294 #endif
295
296         gnutls_certificate_allocate_credentials(&x509_cred);
297         gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
298                                             &server_key,
299                                             GNUTLS_X509_FMT_PEM);
300
301         gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
302         gnutls_dtls_set_timeouts(session, 5 * 1000, 60 * 1000);
303         gnutls_dtls_set_mtu(session, 400);
304
305         /* avoid calling all the priority functions, since the defaults
306          * are adequate.
307          */
308         gnutls_priority_set_direct(session,
309                                    "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ECDHE-ECDSA:+CURVE-ALL",
310                                    NULL);
311
312         gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
313
314         gnutls_transport_set_int(session, fd);
315
316         do {
317                 ret = gnutls_handshake(session);
318         } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
319         if (ret < 0) {
320                 close(fd);
321                 gnutls_deinit(session);
322                 fail("server: Handshake has failed (%s)\n\n",
323                      gnutls_strerror(ret));
324                 terminate();
325         }
326         if (debug)
327                 success("server: Handshake was completed\n");
328
329         if (debug)
330                 success("server: TLS version is: %s\n",
331                         gnutls_protocol_get_name
332                         (gnutls_protocol_get_version(session)));
333
334         if (do_thread)
335                 do_thread_stuff(session);
336         else
337                 do_reflect_stuff(session);
338
339
340         close(fd);
341         gnutls_deinit(session);
342
343         gnutls_certificate_free_credentials(x509_cred);
344
345         gnutls_global_deinit();
346
347         if (debug)
348                 success("server: finished\n");
349 }
350
351 static
352 void run(unsigned do_thread)
353 {
354         int fd[2];
355         int ret;
356
357         ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
358         if (ret < 0) {
359                 perror("socketpair");
360                 exit(1);
361         }
362
363         child = fork();
364         if (child < 0) {
365                 perror("fork");
366                 fail("fork");
367                 exit(1);
368         }
369
370         if (child) {
371                 int status;
372                 /* parent */
373
374                 close(fd[1]);
375                 client(fd[0], do_thread);
376                 wait(&status);
377                 if (WEXITSTATUS(status) != 0)
378                         fail("Child died with status %d\n",
379                              WEXITSTATUS(status));
380         } else {
381                 close(fd[0]);
382                 server(fd[1], 1-do_thread);
383                 exit(0);
384         }
385 }
386
387 void doit(void)
388 {
389         signal(SIGPIPE, SIG_IGN);
390         run(0);
391         run(1);
392 }
393 #endif                          /* _WIN32 */