1/*
2 * Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the OpenSSL license (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10#ifdef _WIN32
11# include <windows.h>
12#endif
13
14#include <stdio.h>
15#include <string.h>
16#include <openssl/async.h>
17#include <openssl/crypto.h>
18
19static int ctr = 0;
20static ASYNC_JOB *currjob = NULL;
21
22static int only_pause(void *args)
23{
24    ASYNC_pause_job();
25
26    return 1;
27}
28
29static int add_two(void *args)
30{
31    ctr++;
32    ASYNC_pause_job();
33    ctr++;
34
35    return 2;
36}
37
38static int save_current(void *args)
39{
40    currjob = ASYNC_get_current_job();
41    ASYNC_pause_job();
42
43    return 1;
44}
45
46#define MAGIC_WAIT_FD   ((OSSL_ASYNC_FD)99)
47static int waitfd(void *args)
48{
49    ASYNC_JOB *job;
50    ASYNC_WAIT_CTX *waitctx;
51    job = ASYNC_get_current_job();
52    if (job == NULL)
53        return 0;
54    waitctx = ASYNC_get_wait_ctx(job);
55    if (waitctx == NULL)
56        return 0;
57
58    /* First case: no fd added or removed */
59    ASYNC_pause_job();
60
61    /* Second case: one fd added */
62    if (!ASYNC_WAIT_CTX_set_wait_fd(waitctx, waitctx, MAGIC_WAIT_FD, NULL, NULL))
63        return 0;
64    ASYNC_pause_job();
65
66    /* Third case: all fd removed */
67    if (!ASYNC_WAIT_CTX_clear_fd(waitctx, waitctx))
68        return 0;
69    ASYNC_pause_job();
70
71    /* Last case: fd added and immediately removed */
72    if (!ASYNC_WAIT_CTX_set_wait_fd(waitctx, waitctx, MAGIC_WAIT_FD, NULL, NULL))
73        return 0;
74    if (!ASYNC_WAIT_CTX_clear_fd(waitctx, waitctx))
75        return 0;
76
77    return 1;
78}
79
80static int blockpause(void *args)
81{
82    ASYNC_block_pause();
83    ASYNC_pause_job();
84    ASYNC_unblock_pause();
85    ASYNC_pause_job();
86
87    return 1;
88}
89
90static int test_ASYNC_init_thread(void)
91{
92    ASYNC_JOB *job1 = NULL, *job2 = NULL, *job3 = NULL;
93    int funcret1, funcret2, funcret3;
94    ASYNC_WAIT_CTX *waitctx = NULL;
95
96    if (       !ASYNC_init_thread(2, 0)
97            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
98            || ASYNC_start_job(&job1, waitctx, &funcret1, only_pause, NULL, 0)
99                != ASYNC_PAUSE
100            || ASYNC_start_job(&job2, waitctx, &funcret2, only_pause, NULL, 0)
101                != ASYNC_PAUSE
102            || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0)
103                != ASYNC_NO_JOBS
104            || ASYNC_start_job(&job1, waitctx, &funcret1, only_pause, NULL, 0)
105                != ASYNC_FINISH
106            || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0)
107                != ASYNC_PAUSE
108            || ASYNC_start_job(&job2, waitctx, &funcret2, only_pause, NULL, 0)
109                != ASYNC_FINISH
110            || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0)
111                != ASYNC_FINISH
112            || funcret1 != 1
113            || funcret2 != 1
114            || funcret3 != 1) {
115        fprintf(stderr, "test_ASYNC_init_thread() failed\n");
116        ASYNC_WAIT_CTX_free(waitctx);
117        ASYNC_cleanup_thread();
118        return 0;
119    }
120
121    ASYNC_WAIT_CTX_free(waitctx);
122    ASYNC_cleanup_thread();
123    return 1;
124}
125
126static int test_ASYNC_start_job(void)
127{
128    ASYNC_JOB *job = NULL;
129    int funcret;
130    ASYNC_WAIT_CTX *waitctx = NULL;
131
132    ctr = 0;
133
134    if (       !ASYNC_init_thread(1, 0)
135            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
136            || ASYNC_start_job(&job, waitctx, &funcret, add_two, NULL, 0)
137               != ASYNC_PAUSE
138            || ctr != 1
139            || ASYNC_start_job(&job, waitctx, &funcret, add_two, NULL, 0)
140               != ASYNC_FINISH
141            || ctr != 2
142            || funcret != 2) {
143        fprintf(stderr, "test_ASYNC_start_job() failed\n");
144        ASYNC_WAIT_CTX_free(waitctx);
145        ASYNC_cleanup_thread();
146        return 0;
147    }
148
149    ASYNC_WAIT_CTX_free(waitctx);
150    ASYNC_cleanup_thread();
151    return 1;
152}
153
154static int test_ASYNC_get_current_job(void)
155{
156    ASYNC_JOB *job = NULL;
157    int funcret;
158    ASYNC_WAIT_CTX *waitctx = NULL;
159
160    currjob = NULL;
161
162    if (       !ASYNC_init_thread(1, 0)
163            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
164            || ASYNC_start_job(&job, waitctx, &funcret, save_current, NULL, 0)
165                != ASYNC_PAUSE
166            || currjob != job
167            || ASYNC_start_job(&job, waitctx, &funcret, save_current, NULL, 0)
168                != ASYNC_FINISH
169            || funcret != 1) {
170        fprintf(stderr, "test_ASYNC_get_current_job() failed\n");
171        ASYNC_WAIT_CTX_free(waitctx);
172        ASYNC_cleanup_thread();
173        return 0;
174    }
175
176    ASYNC_WAIT_CTX_free(waitctx);
177    ASYNC_cleanup_thread();
178    return 1;
179}
180
181static int test_ASYNC_WAIT_CTX_get_all_fds(void)
182{
183    ASYNC_JOB *job = NULL;
184    int funcret;
185    ASYNC_WAIT_CTX *waitctx = NULL;
186    OSSL_ASYNC_FD fd = OSSL_BAD_ASYNC_FD, delfd = OSSL_BAD_ASYNC_FD;
187    size_t numfds, numdelfds;
188
189    if (       !ASYNC_init_thread(1, 0)
190            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
191               /* On first run we're not expecting any wait fds */
192            || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0)
193                != ASYNC_PAUSE
194            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds)
195            || numfds != 0
196            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL,
197                                               &numdelfds)
198            || numfds != 0
199            || numdelfds != 0
200               /* On second run we're expecting one added fd */
201            || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0)
202                != ASYNC_PAUSE
203            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds)
204            || numfds != 1
205            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, &fd, &numfds)
206            || fd != MAGIC_WAIT_FD
207            || (fd = OSSL_BAD_ASYNC_FD, 0) /* Assign to something else */
208            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL,
209                                               &numdelfds)
210            || numfds != 1
211            || numdelfds != 0
212            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, &fd, &numfds, NULL,
213                                               &numdelfds)
214            || fd != MAGIC_WAIT_FD
215               /* On third run we expect one deleted fd */
216            || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0)
217                != ASYNC_PAUSE
218            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds)
219            || numfds != 0
220            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL,
221                                               &numdelfds)
222            || numfds != 0
223            || numdelfds != 1
224            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, &delfd,
225                                               &numdelfds)
226            || delfd != MAGIC_WAIT_FD
227            /* On last run we are not expecting any wait fd */
228            || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0)
229                != ASYNC_FINISH
230            || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds)
231            || numfds != 0
232            || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL,
233                                               &numdelfds)
234            || numfds != 0
235            || numdelfds != 0
236            || funcret != 1) {
237        fprintf(stderr, "test_ASYNC_get_wait_fd() failed\n");
238        ASYNC_WAIT_CTX_free(waitctx);
239        ASYNC_cleanup_thread();
240        return 0;
241    }
242
243    ASYNC_WAIT_CTX_free(waitctx);
244    ASYNC_cleanup_thread();
245    return 1;
246}
247
248static int test_ASYNC_block_pause(void)
249{
250    ASYNC_JOB *job = NULL;
251    int funcret;
252    ASYNC_WAIT_CTX *waitctx = NULL;
253
254    if (       !ASYNC_init_thread(1, 0)
255            || (waitctx = ASYNC_WAIT_CTX_new()) == NULL
256            || ASYNC_start_job(&job, waitctx, &funcret, blockpause, NULL, 0)
257                != ASYNC_PAUSE
258            || ASYNC_start_job(&job, waitctx, &funcret, blockpause, NULL, 0)
259                != ASYNC_FINISH
260            || funcret != 1) {
261        fprintf(stderr, "test_ASYNC_block_pause() failed\n");
262        ASYNC_WAIT_CTX_free(waitctx);
263        ASYNC_cleanup_thread();
264        return 0;
265    }
266
267    ASYNC_WAIT_CTX_free(waitctx);
268    ASYNC_cleanup_thread();
269    return 1;
270}
271
272int main(int argc, char **argv)
273{
274    if (!ASYNC_is_capable()) {
275        fprintf(stderr,
276                "OpenSSL build is not ASYNC capable - skipping async tests\n");
277    } else {
278        CRYPTO_set_mem_debug(1);
279        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
280
281        if (       !test_ASYNC_init_thread()
282                || !test_ASYNC_start_job()
283                || !test_ASYNC_get_current_job()
284                || !test_ASYNC_WAIT_CTX_get_all_fds()
285                || !test_ASYNC_block_pause()) {
286            return 1;
287        }
288    }
289    printf("PASS\n");
290    return 0;
291}
292