dialog: use new API
[vlc.git] / test / modules / misc / tls.c
1 /*****************************************************************************
2  * tls.c: Transport Layer Security module test
3  *****************************************************************************
4  * Copyright © 2016 Rémi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #undef NDEBUG
26 #include <assert.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <poll.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36
37 #include <vlc_common.h>
38 #include <vlc_modules.h>
39 #include <vlc_tls.h>
40 #include <vlc_dialog.h>
41 #include "../../../lib/libvlc_internal.h"
42
43 #include <vlc/vlc.h>
44
45 static int tlspair(int fds[2])
46 {
47     return vlc_socketpair(PF_LOCAL, SOCK_STREAM, 0, fds, true);
48 }
49
50 static void
51 dialog_display_question_cb(vlc_dialog_id *p_id, const char *psz_title,
52                            const char *psz_text, vlc_dialog_question_type i_type,
53                            const char *psz_cancel, const char *psz_action1,
54                            const char *psz_action2, void *p_data)
55 {
56     (void) psz_title;
57     (void) psz_text;
58     (void) i_type;
59     (void) psz_cancel;
60     (void) psz_action1;
61     (void) psz_action2;
62     int *value = p_data;
63     vlc_dialog_id_post_action(p_id, *value);
64 }
65
66 static void dialog_cancel_cb(vlc_dialog_id *id, void *data)
67 {
68     (void)data;
69     vlc_dialog_id_dismiss(id);
70 }
71
72 static libvlc_instance_t *vlc;
73 static vlc_object_t *obj;
74 static vlc_tls_creds_t *server_creds;
75 static vlc_tls_creds_t *client_creds;
76
77 static void *tls_echo(void *data)
78 {
79     vlc_tls_t *tls = data;
80     struct pollfd ufd;
81     ssize_t val;
82     char buf[256];
83
84     ufd.fd = vlc_tls_GetFD(tls);
85
86     while ((val = vlc_tls_SessionHandshake(server_creds, tls)) > 0)
87     {
88         switch (val)
89         {
90             case 1:  ufd.events = POLLIN;  break;
91             case 2:  ufd.events = POLLOUT; break;
92             default: vlc_assert_unreachable();
93         }
94         poll(&ufd, 1, -1);
95     }
96
97     if (val < 0)
98         goto error;
99
100     while ((val = vlc_tls_Read(tls, buf, sizeof (buf), false)) > 0)
101         if (vlc_tls_Write(tls, buf, val) < val)
102             goto error;
103
104     if (val < 0 || vlc_tls_Shutdown(tls, false))
105         goto error;
106
107     vlc_tls_Close(tls);
108     return tls;
109 error:
110     vlc_tls_Close(tls);
111     return NULL;
112 }
113
114 static int securepair(vlc_thread_t *th, vlc_tls_t **restrict client,
115                       const char *const *alpnv[2], char **restrict alp)
116 {
117     vlc_tls_t *server;
118     int val;
119     int insecurev[2];
120
121     val = tlspair(insecurev);
122     assert(val == 0);
123
124     server = vlc_tls_ServerSessionCreate(server_creds, insecurev[0], alpnv[0]);
125     assert(server != NULL);
126
127     val = vlc_clone(th, tls_echo, server, VLC_THREAD_PRIORITY_LOW);
128     assert(val == 0);
129
130     *client = vlc_tls_ClientSessionCreateFD(client_creds, insecurev[1],
131                                           "localhost", "vlc-tls-test",
132                                           alpnv[1], alp);
133     if (*client == NULL)
134     {
135         val = close(insecurev[1]);
136         assert(val == 0);
137         vlc_join(*th, NULL);
138         return -1;
139     }
140     return 0;
141 }
142
143 static const char certpath[] = SRCDIR"/modules/misc/certkey.pem";
144 static const char *const alpn[] = { "foo", "bar", NULL };
145
146 int main(void)
147 {
148     int val;
149     int answer = 0;
150
151     /* Create fake home for stored keys */
152     char homedir[] = "/tmp/vlc-test-XXXXXX";
153     if (mkdtemp(homedir) != homedir)
154     {
155         perror("Temporary directory");
156         return 77;
157     }
158
159     assert(!strncmp(homedir, "/tmp/vlc-test-", 14));
160     setenv("HOME", homedir, 1);
161     setenv("VLC_PLUGIN_PATH", "../modules", 1);
162
163     vlc = libvlc_new(0, NULL);
164     assert(vlc != NULL);
165     obj = VLC_OBJECT(vlc->p_libvlc_int);
166
167     server_creds = vlc_tls_ServerCreate(obj, SRCDIR"/nonexistent", NULL);
168     assert(server_creds == NULL);
169     server_creds = vlc_tls_ServerCreate(obj, SRCDIR"/samples/empty.voc", NULL);
170     assert(server_creds == NULL);
171     server_creds = vlc_tls_ServerCreate(obj, certpath, SRCDIR"/nonexistent");
172     assert(server_creds == NULL);
173     server_creds = vlc_tls_ServerCreate(obj, certpath, NULL);
174     if (server_creds == NULL)
175     {
176         libvlc_release(vlc);
177         return 77;
178     }
179
180     client_creds = vlc_tls_ClientCreate(obj);
181     assert(client_creds != NULL);
182
183     vlc_dialog_cbs cbs = {
184         .pf_display_question = dialog_display_question_cb,
185         .pf_cancel = dialog_cancel_cb,
186     };
187     vlc_dialog_provider_set_callbacks(obj, &cbs, &answer);
188
189     vlc_thread_t th;
190     vlc_tls_t *tls;
191     const char *const *alpnv[2] = { alpn + 1, alpn };
192     char *alp;
193     void *p;
194
195     /* Test unknown certificate */
196     answer = 0;
197     val = securepair(&th, &tls, alpnv, &alp);
198     assert(val == -1);
199
200     /* Accept unknown certificate */
201     answer = 1;
202     val = securepair(&th, &tls, alpnv, &alp);
203     assert(val == 0);
204     assert(alp != NULL);
205     assert(!strcmp(alp, "bar"));
206     free(alp);
207
208     /* Do some I/O */
209     char buf[12];
210     struct iovec iov;
211
212     iov.iov_base = buf;
213     iov.iov_len = sizeof (buf);
214     val = tls->readv(tls, &iov, 1);
215     assert(val == -1 && errno == EAGAIN);
216
217     val = vlc_tls_Write(tls, "Hello ", 6);
218     assert(val == 6);
219     val = vlc_tls_Write(tls, "world!", 6);
220     assert(val == 6);
221
222     val = vlc_tls_Read(tls, buf, sizeof (buf), true);
223     assert(val == 12);
224     assert(!memcmp(buf, "Hello world!", 12));
225
226     val = vlc_tls_Shutdown(tls, false);
227     assert(val == 0);
228     vlc_join(th, &p);
229     assert(p != NULL);
230     val = vlc_tls_Read(tls, buf, sizeof (buf), false);
231     assert(val == 0);
232     vlc_tls_Close(tls);
233
234     /* Test known certificate, ignore ALPN result */
235     answer = 0;
236     val = securepair(&th, &tls, alpnv, NULL);
237     assert(val == 0);
238
239     /* Do a lot of I/O, test congestion handling */
240     static unsigned char data[16184];
241     size_t bytes = 0;
242     unsigned seed = 0;
243
244     iov.iov_base = data;
245     iov.iov_len = sizeof (data);
246
247     do
248     {
249         for (size_t i = 0; i < sizeof (data); i++)
250             data[i] = rand_r(&seed);
251         bytes += sizeof (data);
252     }
253     while ((val = tls->writev(tls, &iov, 1)) == sizeof (data));
254
255     bytes -= sizeof (data);
256     if (val > 0)
257         bytes += val;
258
259     fprintf(stderr, "Sent %zu bytes.\n", bytes);
260     seed = 0;
261
262     while (bytes > 0)
263     {
264         unsigned char c = rand_r(&seed);
265
266         val = vlc_tls_Read(tls, buf, 1, false);
267         assert(val == 1);
268         assert(c == (unsigned char)buf[0]);
269         bytes--;
270     }
271
272     vlc_tls_Close(tls);
273     vlc_join(th, NULL);
274
275     /* Test known certificate, no ALPN */
276     alpnv[0] = alpnv[1] = NULL;
277     val = securepair(&th, &tls, alpnv, NULL);
278     assert(val == 0);
279     vlc_tls_Close(tls);
280     vlc_join(th, NULL);
281
282     vlc_dialog_provider_set_callbacks(obj, NULL, NULL);
283     vlc_tls_Delete(client_creds);
284     vlc_tls_Delete(server_creds);
285     libvlc_release(vlc);
286
287     if (fork() == 0)
288         execlp("rm", "rm", "-rf", homedir, (char *)NULL);
289     return 0;
290 }