BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file BSSLConnection.c |
||
3 | * @author Ambroz Bizjak <ambrop7@gmail.com> |
||
4 | * |
||
5 | * @section LICENSE |
||
6 | * |
||
7 | * Redistribution and use in source and binary forms, with or without |
||
8 | * modification, are permitted provided that the following conditions are met: |
||
9 | * 1. Redistributions of source code must retain the above copyright |
||
10 | * notice, this list of conditions and the following disclaimer. |
||
11 | * 2. Redistributions in binary form must reproduce the above copyright |
||
12 | * notice, this list of conditions and the following disclaimer in the |
||
13 | * documentation and/or other materials provided with the distribution. |
||
14 | * 3. Neither the name of the author nor the |
||
15 | * names of its contributors may be used to endorse or promote products |
||
16 | * derived from this software without specific prior written permission. |
||
17 | * |
||
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
||
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
21 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||
22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
28 | */ |
||
29 | |||
30 | #include <prerror.h> |
||
31 | #include <nss/ssl.h> |
||
32 | |||
33 | #include <string.h> |
||
34 | #include <stdlib.h> |
||
35 | |||
36 | #include <misc/print_macros.h> |
||
37 | #include <base/BLog.h> |
||
38 | |||
39 | #include "BSSLConnection.h" |
||
40 | |||
41 | #include <generated/blog_channel_BSSLConnection.h> |
||
42 | |||
43 | #define THREADWORK_STATE_NONE 0 |
||
44 | #define THREADWORK_STATE_HANDSHAKE 1 |
||
45 | #define THREADWORK_STATE_READ 2 |
||
46 | #define THREADWORK_STATE_WRITE 3 |
||
47 | |||
48 | static void backend_threadwork_start (struct BSSLConnection_backend *b, int op); |
||
49 | static int backend_threadwork_do_io (struct BSSLConnection_backend *b); |
||
50 | static void connection_init_job_handler (BSSLConnection *o); |
||
51 | static void connection_init_up (BSSLConnection *o); |
||
52 | static void connection_try_io (BSSLConnection *o); |
||
53 | static void connection_threadwork_func_work (void *user); |
||
54 | static void connection_threadwork_handler_done (void *user); |
||
55 | static void connection_recv_job_handler (BSSLConnection *o); |
||
56 | static void connection_try_handshake (BSSLConnection *o); |
||
57 | static void connection_try_send (BSSLConnection *o); |
||
58 | static void connection_try_recv (BSSLConnection *o); |
||
59 | static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len); |
||
60 | static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len); |
||
61 | |||
62 | int bprconnection_initialized = 0; |
||
63 | PRDescIdentity bprconnection_identity; |
||
64 | |||
65 | static PRFileDesc * get_bottom (PRFileDesc *layer) |
||
66 | { |
||
67 | while (layer->lower) { |
||
68 | layer = layer->lower; |
||
69 | } |
||
70 | |||
71 | return layer; |
||
72 | } |
||
73 | |||
74 | static PRStatus method_close (PRFileDesc *fd) |
||
75 | { |
||
76 | struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; |
||
77 | ASSERT(!b->con) |
||
78 | ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) |
||
79 | |||
80 | // free mutexes |
||
81 | if ((b->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { |
||
82 | BMutex_Free(&b->recv_buf_mutex); |
||
83 | BMutex_Free(&b->send_buf_mutex); |
||
84 | } |
||
85 | |||
86 | // free backend |
||
87 | free(b); |
||
88 | |||
89 | // set no secret |
||
90 | fd->secret = NULL; |
||
91 | |||
92 | return PR_SUCCESS; |
||
93 | } |
||
94 | |||
95 | static PRInt32 method_read (PRFileDesc *fd, void *buf, PRInt32 amount) |
||
96 | { |
||
97 | struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; |
||
98 | ASSERT(amount > 0) |
||
99 | |||
100 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
101 | BMutex_Lock(&b->recv_buf_mutex); |
||
102 | } |
||
103 | |||
104 | // if we are receiving into buffer or buffer has no data left, refuse recv |
||
105 | if (b->recv_busy || b->recv_pos == b->recv_len) { |
||
106 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
107 | b->threadwork_want_recv = 1; |
||
108 | BMutex_Unlock(&b->recv_buf_mutex); |
||
109 | } else { |
||
110 | // start receiving if not already |
||
111 | if (!b->recv_busy) { |
||
112 | // set recv busy |
||
113 | b->recv_busy = 1; |
||
114 | |||
115 | // receive into buffer |
||
116 | StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE); |
||
117 | } |
||
118 | } |
||
119 | PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
||
120 | return -1; |
||
121 | } |
||
122 | |||
123 | // limit amount to available data |
||
124 | if (amount > b->recv_len - b->recv_pos) { |
||
125 | amount = b->recv_len - b->recv_pos; |
||
126 | } |
||
127 | |||
128 | // copy data |
||
129 | memcpy(buf, b->recv_buf + b->recv_pos, amount); |
||
130 | |||
131 | // update buffer |
||
132 | b->recv_pos += amount; |
||
133 | |||
134 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
135 | BMutex_Unlock(&b->recv_buf_mutex); |
||
136 | } |
||
137 | |||
138 | return amount; |
||
139 | } |
||
140 | |||
141 | static PRInt32 method_write (PRFileDesc *fd, const void *buf, PRInt32 amount) |
||
142 | { |
||
143 | struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; |
||
144 | ASSERT(amount > 0) |
||
145 | |||
146 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
147 | BMutex_Lock(&b->send_buf_mutex); |
||
148 | } |
||
149 | |||
150 | ASSERT(!b->send_busy || b->send_pos < b->send_len) |
||
151 | |||
152 | // if there is data in buffer, refuse send |
||
153 | if (b->send_pos < b->send_len) { |
||
154 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
155 | b->threadwork_want_send = 1; |
||
156 | BMutex_Unlock(&b->send_buf_mutex); |
||
157 | } |
||
158 | PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
||
159 | return -1; |
||
160 | } |
||
161 | |||
162 | // limit amount to buffer size |
||
163 | if (amount > BSSLCONNECTION_BUF_SIZE) { |
||
164 | amount = BSSLCONNECTION_BUF_SIZE; |
||
165 | } |
||
166 | |||
167 | // init buffer |
||
168 | memcpy(b->send_buf, buf, amount); |
||
169 | b->send_pos = 0; |
||
170 | b->send_len = amount; |
||
171 | |||
172 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
173 | BMutex_Unlock(&b->send_buf_mutex); |
||
174 | } else { |
||
175 | // start sending |
||
176 | b->send_busy = 1; |
||
177 | StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); |
||
178 | } |
||
179 | |||
180 | return amount; |
||
181 | } |
||
182 | |||
183 | static PRStatus method_shutdown (PRFileDesc *fd, PRIntn how) |
||
184 | { |
||
185 | PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
||
186 | return PR_FAILURE; |
||
187 | } |
||
188 | |||
189 | static PRInt32 method_recv (PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) |
||
190 | { |
||
191 | ASSERT(flags == 0) |
||
192 | |||
193 | return method_read(fd, buf, amount); |
||
194 | } |
||
195 | |||
196 | static PRInt32 method_send (PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) |
||
197 | { |
||
198 | ASSERT(flags == 0) |
||
199 | |||
200 | return method_write(fd, buf, amount); |
||
201 | } |
||
202 | |||
203 | static PRInt16 method_poll (PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) |
||
204 | { |
||
205 | *out_flags = 0; |
||
206 | return in_flags; |
||
207 | } |
||
208 | |||
209 | static PRStatus method_getpeername (PRFileDesc *fd, PRNetAddr *addr) |
||
210 | { |
||
211 | memset(addr, 0, sizeof(*addr)); |
||
212 | addr->raw.family = PR_AF_INET; |
||
213 | return PR_SUCCESS; |
||
214 | } |
||
215 | |||
216 | static PRStatus method_getsocketoption (PRFileDesc *fd, PRSocketOptionData *data) |
||
217 | { |
||
218 | switch (data->option) { |
||
219 | case PR_SockOpt_Nonblocking: |
||
220 | data->value.non_blocking = PR_TRUE; |
||
221 | return PR_SUCCESS; |
||
222 | } |
||
223 | |||
224 | PR_SetError(PR_UNKNOWN_ERROR, 0); |
||
225 | return PR_FAILURE; |
||
226 | } |
||
227 | |||
228 | static PRStatus method_setsocketoption (PRFileDesc *fd, const PRSocketOptionData *data) |
||
229 | { |
||
230 | PR_SetError(PR_UNKNOWN_ERROR, 0); |
||
231 | return PR_FAILURE; |
||
232 | } |
||
233 | |||
234 | static PRIntn _PR_InvalidIntn (void) |
||
235 | { |
||
236 | ASSERT(0) |
||
237 | PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
||
238 | return -1; |
||
239 | } |
||
240 | |||
241 | static PRInt32 _PR_InvalidInt32 (void) |
||
242 | { |
||
243 | ASSERT(0) |
||
244 | PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
||
245 | return -1; |
||
246 | } |
||
247 | |||
248 | static PRInt64 _PR_InvalidInt64 (void) |
||
249 | { |
||
250 | ASSERT(0) |
||
251 | PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
||
252 | return -1; |
||
253 | } |
||
254 | |||
255 | static PROffset32 _PR_InvalidOffset32 (void) |
||
256 | { |
||
257 | ASSERT(0) |
||
258 | PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
||
259 | return -1; |
||
260 | } |
||
261 | |||
262 | static PROffset64 _PR_InvalidOffset64 (void) |
||
263 | { |
||
264 | ASSERT(0) |
||
265 | PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
||
266 | return -1; |
||
267 | } |
||
268 | |||
269 | static PRStatus _PR_InvalidStatus (void) |
||
270 | { |
||
271 | ASSERT(0) |
||
272 | PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
||
273 | return PR_FAILURE; |
||
274 | } |
||
275 | |||
276 | static PRFileDesc *_PR_InvalidDesc (void) |
||
277 | { |
||
278 | ASSERT(0) |
||
279 | PR_SetError(PR_INVALID_METHOD_ERROR, 0); |
||
280 | return NULL; |
||
281 | } |
||
282 | |||
283 | static PRIOMethods methods = { |
||
284 | (PRDescType)0, |
||
285 | method_close, |
||
286 | method_read, |
||
287 | method_write, |
||
288 | (PRAvailableFN)_PR_InvalidInt32, |
||
289 | (PRAvailable64FN)_PR_InvalidInt64, |
||
290 | (PRFsyncFN)_PR_InvalidStatus, |
||
291 | (PRSeekFN)_PR_InvalidOffset32, |
||
292 | (PRSeek64FN)_PR_InvalidOffset64, |
||
293 | (PRFileInfoFN)_PR_InvalidStatus, |
||
294 | (PRFileInfo64FN)_PR_InvalidStatus, |
||
295 | (PRWritevFN)_PR_InvalidInt32, |
||
296 | (PRConnectFN)_PR_InvalidStatus, |
||
297 | (PRAcceptFN)_PR_InvalidDesc, |
||
298 | (PRBindFN)_PR_InvalidStatus, |
||
299 | (PRListenFN)_PR_InvalidStatus, |
||
300 | method_shutdown, |
||
301 | method_recv, |
||
302 | method_send, |
||
303 | (PRRecvfromFN)_PR_InvalidInt32, |
||
304 | (PRSendtoFN)_PR_InvalidInt32, |
||
305 | method_poll, |
||
306 | (PRAcceptreadFN)_PR_InvalidInt32, |
||
307 | (PRTransmitfileFN)_PR_InvalidInt32, |
||
308 | (PRGetsocknameFN)_PR_InvalidStatus, |
||
309 | method_getpeername, |
||
310 | (PRReservedFN)_PR_InvalidIntn, |
||
311 | (PRReservedFN)_PR_InvalidIntn, |
||
312 | method_getsocketoption, |
||
313 | method_setsocketoption, |
||
314 | (PRSendfileFN)_PR_InvalidInt32, |
||
315 | (PRConnectcontinueFN)_PR_InvalidStatus, |
||
316 | (PRReservedFN)_PR_InvalidIntn, |
||
317 | (PRReservedFN)_PR_InvalidIntn, |
||
318 | (PRReservedFN)_PR_InvalidIntn, |
||
319 | (PRReservedFN)_PR_InvalidIntn |
||
320 | }; |
||
321 | |||
322 | static void backend_send_if_handler_done (struct BSSLConnection_backend *b, int data_len) |
||
323 | { |
||
324 | ASSERT(b->send_busy) |
||
325 | ASSERT(b->send_len > 0) |
||
326 | ASSERT(b->send_pos < b->send_len) |
||
327 | ASSERT(data_len > 0) |
||
328 | ASSERT(data_len <= b->send_len - b->send_pos) |
||
329 | |||
330 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
331 | BMutex_Lock(&b->send_buf_mutex); |
||
332 | } |
||
333 | |||
334 | // update buffer |
||
335 | b->send_pos += data_len; |
||
336 | |||
337 | // send more if needed |
||
338 | if (b->send_pos < b->send_len) { |
||
339 | StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); |
||
340 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
341 | BMutex_Unlock(&b->send_buf_mutex); |
||
342 | } |
||
343 | return; |
||
344 | } |
||
345 | |||
346 | // set send not busy |
||
347 | b->send_busy = 0; |
||
348 | |||
349 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
350 | BMutex_Unlock(&b->send_buf_mutex); |
||
351 | } |
||
352 | |||
353 | // notify connection |
||
354 | if (b->con && !b->con->have_error) { |
||
355 | connection_try_io(b->con); |
||
356 | return; |
||
357 | } |
||
358 | } |
||
359 | |||
360 | static void backend_recv_if_handler_done (struct BSSLConnection_backend *b, int data_len) |
||
361 | { |
||
362 | ASSERT(b->recv_busy) |
||
363 | ASSERT(data_len > 0) |
||
364 | ASSERT(data_len <= BSSLCONNECTION_BUF_SIZE) |
||
365 | |||
366 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
367 | BMutex_Lock(&b->recv_buf_mutex); |
||
368 | } |
||
369 | |||
370 | // init buffer |
||
371 | b->recv_busy = 0; |
||
372 | b->recv_pos = 0; |
||
373 | b->recv_len = data_len; |
||
374 | |||
375 | if (b->threadwork_state != THREADWORK_STATE_NONE) { |
||
376 | BMutex_Unlock(&b->recv_buf_mutex); |
||
377 | } |
||
378 | |||
379 | // notify connection |
||
380 | if (b->con && !b->con->have_error) { |
||
381 | connection_try_io(b->con); |
||
382 | return; |
||
383 | } |
||
384 | } |
||
385 | |||
386 | static void backend_threadwork_start (struct BSSLConnection_backend *b, int op) |
||
387 | { |
||
388 | ASSERT(b->con) |
||
389 | ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) |
||
390 | ASSERT(op == THREADWORK_STATE_HANDSHAKE || op == THREADWORK_STATE_READ || op == THREADWORK_STATE_WRITE) |
||
391 | |||
392 | b->threadwork_state = op; |
||
393 | b->threadwork_want_recv = 0; |
||
394 | b->threadwork_want_send = 0; |
||
395 | BThreadWork_Init(&b->threadwork, b->twd, connection_threadwork_handler_done, b->con, connection_threadwork_func_work, b->con); |
||
396 | } |
||
397 | |||
398 | static int backend_threadwork_do_io (struct BSSLConnection_backend *b) |
||
399 | { |
||
400 | ASSERT(b->con) |
||
401 | ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) |
||
402 | |||
403 | int io_ready = (b->threadwork_want_recv && !b->recv_busy && b->recv_pos < b->recv_len) || |
||
404 | (b->threadwork_want_send && b->send_pos == b->send_len); |
||
405 | |||
406 | if (b->threadwork_want_recv && b->recv_pos == b->recv_len && !b->recv_busy) { |
||
407 | b->recv_busy = 1; |
||
408 | StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE); |
||
409 | } |
||
410 | |||
411 | if (b->send_pos < b->send_len && !b->send_busy) { |
||
412 | b->send_busy = 1; |
||
413 | StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); |
||
414 | } |
||
415 | |||
416 | return io_ready; |
||
417 | } |
||
418 | |||
419 | static void connection_report_error (BSSLConnection *o) |
||
420 | { |
||
421 | ASSERT(!o->have_error) |
||
422 | |||
423 | // set error |
||
424 | o->have_error = 1; |
||
425 | |||
426 | // report error |
||
427 | DEBUGERROR(&o->d_err, o->handler(o->user, BSSLCONNECTION_EVENT_ERROR)); |
||
428 | } |
||
429 | |||
430 | static void connection_init_job_handler (BSSLConnection *o) |
||
431 | { |
||
432 | DebugObject_Access(&o->d_obj); |
||
433 | ASSERT(!o->have_error) |
||
434 | ASSERT(!o->up) |
||
435 | |||
436 | connection_try_handshake(o); |
||
437 | } |
||
438 | |||
439 | static void connection_init_up (BSSLConnection *o) |
||
440 | { |
||
441 | // unset init job |
||
442 | // (just in the impossible case that handshake completed before the init job executed) |
||
443 | BPending_Unset(&o->init_job); |
||
444 | |||
445 | // init send interface |
||
446 | StreamPassInterface_Init(&o->send_if, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, o->pg); |
||
447 | |||
448 | // init recv interface |
||
449 | StreamRecvInterface_Init(&o->recv_if, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, o->pg); |
||
450 | |||
451 | // init recv job |
||
452 | BPending_Init(&o->recv_job, o->pg, (BPending_handler)connection_recv_job_handler, o); |
||
453 | |||
454 | // set no send data |
||
455 | o->send_len = -1; |
||
456 | |||
457 | // set no recv data |
||
458 | o->recv_avail = -1; |
||
459 | |||
460 | // set up |
||
461 | o->up = 1; |
||
462 | } |
||
463 | |||
464 | static void connection_try_io (BSSLConnection *o) |
||
465 | { |
||
466 | DebugObject_Access(&o->d_obj); |
||
467 | ASSERT(!o->have_error) |
||
468 | |||
469 | if (!o->up) { |
||
470 | connection_try_handshake(o); |
||
471 | return; |
||
472 | } |
||
473 | |||
474 | if (o->send_len > 0) { |
||
475 | if (o->recv_avail > 0) { |
||
476 | BPending_Set(&o->recv_job); |
||
477 | } |
||
478 | |||
479 | connection_try_send(o); |
||
480 | return; |
||
481 | } |
||
482 | |||
483 | if (o->recv_avail > 0) { |
||
484 | connection_try_recv(o); |
||
485 | return; |
||
486 | } |
||
487 | } |
||
488 | |||
489 | static void connection_threadwork_func_work (void *user) |
||
490 | { |
||
491 | BSSLConnection *o = (BSSLConnection *)user; |
||
492 | struct BSSLConnection_backend *b = o->backend; |
||
493 | ASSERT(b->threadwork_state != THREADWORK_STATE_NONE) |
||
494 | |||
495 | switch (b->threadwork_state) { |
||
496 | case THREADWORK_STATE_HANDSHAKE: |
||
497 | b->threadwork_result_sec = SSL_ForceHandshake(o->prfd); |
||
498 | break; |
||
499 | case THREADWORK_STATE_WRITE: |
||
500 | b->threadwork_result_pr = PR_Write(o->prfd, o->send_data, o->send_len); |
||
501 | break; |
||
502 | case THREADWORK_STATE_READ: |
||
503 | b->threadwork_result_pr = PR_Read(o->prfd, o->recv_data, o->recv_avail); |
||
504 | break; |
||
505 | default: |
||
506 | ASSERT(0); |
||
507 | } |
||
508 | |||
509 | b->threadwork_error = PR_GetError(); |
||
510 | } |
||
511 | |||
512 | static void connection_threadwork_handler_done (void *user) |
||
513 | { |
||
514 | BSSLConnection *o = (BSSLConnection *)user; |
||
515 | struct BSSLConnection_backend *b = o->backend; |
||
516 | ASSERT(b->threadwork_state != THREADWORK_STATE_NONE) |
||
517 | |||
518 | // remember what operation the threadwork was performing |
||
519 | int op = b->threadwork_state; |
||
520 | |||
521 | // free threadwork |
||
522 | BThreadWork_Free(&b->threadwork); |
||
523 | b->threadwork_state = THREADWORK_STATE_NONE; |
||
524 | |||
525 | // start any necessary backend I/O operations, and determine if any of the requested |
||
526 | // backend I/O that was not available at the time is now available |
||
527 | int io_ready = backend_threadwork_do_io(b); |
||
528 | |||
529 | switch (op) { |
||
530 | case THREADWORK_STATE_HANDSHAKE: { |
||
531 | ASSERT(!o->up) |
||
532 | ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE)) |
||
533 | |||
534 | if (b->threadwork_result_sec == SECFailure) { |
||
535 | if (b->threadwork_error == PR_WOULD_BLOCK_ERROR) { |
||
536 | if (io_ready) { |
||
537 | // requested backend I/O got ready, try again |
||
538 | backend_threadwork_start(o->backend, THREADWORK_STATE_HANDSHAKE); |
||
539 | } |
||
540 | return; |
||
541 | } |
||
542 | BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", b->threadwork_error); |
||
543 | connection_report_error(o); |
||
544 | return; |
||
545 | } |
||
546 | |||
547 | // init up |
||
548 | connection_init_up(o); |
||
549 | |||
550 | // report up |
||
551 | o->handler(o->user, BSSLCONNECTION_EVENT_UP); |
||
552 | return; |
||
553 | } break; |
||
554 | |||
555 | case THREADWORK_STATE_WRITE: { |
||
556 | ASSERT(o->up) |
||
557 | ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) |
||
558 | ASSERT(o->send_len > 0) |
||
559 | |||
560 | PRInt32 result = b->threadwork_result_pr; |
||
561 | PRErrorCode error = b->threadwork_error; |
||
562 | |||
563 | if (result < 0) { |
||
564 | if (error == PR_WOULD_BLOCK_ERROR) { |
||
565 | if (io_ready) { |
||
566 | // requested backend I/O got ready, try again |
||
567 | backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); |
||
568 | } else if (o->recv_avail > 0) { |
||
569 | // don't forget about receiving |
||
570 | backend_threadwork_start(o->backend, THREADWORK_STATE_READ); |
||
571 | } |
||
572 | return; |
||
573 | } |
||
574 | BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error); |
||
575 | connection_report_error(o); |
||
576 | return; |
||
577 | } |
||
578 | |||
579 | ASSERT(result > 0) |
||
580 | ASSERT(result <= o->send_len) |
||
581 | |||
582 | // set no send data |
||
583 | o->send_len = -1; |
||
584 | |||
585 | // don't forget about receiving |
||
586 | if (o->recv_avail > 0) { |
||
587 | backend_threadwork_start(o->backend, THREADWORK_STATE_READ); |
||
588 | } |
||
589 | |||
590 | // finish send operation |
||
591 | StreamPassInterface_Done(&o->send_if, result); |
||
592 | } break; |
||
593 | |||
594 | case THREADWORK_STATE_READ: { |
||
595 | ASSERT(o->up) |
||
596 | ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) |
||
597 | ASSERT(o->recv_avail > 0) |
||
598 | |||
599 | PRInt32 result = b->threadwork_result_pr; |
||
600 | PRErrorCode error = b->threadwork_error; |
||
601 | |||
602 | if (result < 0) { |
||
603 | if (error == PR_WOULD_BLOCK_ERROR) { |
||
604 | if (io_ready) { |
||
605 | // requested backend I/O got ready, try again |
||
606 | backend_threadwork_start(o->backend, THREADWORK_STATE_READ); |
||
607 | } else if (o->send_len > 0) { |
||
608 | // don't forget about sending |
||
609 | backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); |
||
610 | } |
||
611 | return; |
||
612 | } |
||
613 | BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error); |
||
614 | connection_report_error(o); |
||
615 | return; |
||
616 | } |
||
617 | |||
618 | if (result == 0) { |
||
619 | BLog(BLOG_ERROR, "PR_Read returned 0"); |
||
620 | connection_report_error(o); |
||
621 | return; |
||
622 | } |
||
623 | |||
624 | ASSERT(result > 0) |
||
625 | ASSERT(result <= o->recv_avail) |
||
626 | |||
627 | // set no recv data |
||
628 | o->recv_avail = -1; |
||
629 | |||
630 | // don't forget about sending |
||
631 | if (o->send_len > 0) { |
||
632 | backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); |
||
633 | } |
||
634 | |||
635 | // finish receive operation |
||
636 | StreamRecvInterface_Done(&o->recv_if, result); |
||
637 | } break; |
||
638 | |||
639 | default: |
||
640 | ASSERT(0); |
||
641 | } |
||
642 | |||
643 | return; |
||
644 | } |
||
645 | |||
646 | static void connection_recv_job_handler (BSSLConnection *o) |
||
647 | { |
||
648 | DebugObject_Access(&o->d_obj); |
||
649 | ASSERT(!o->have_error) |
||
650 | ASSERT(o->up) |
||
651 | ASSERT(o->recv_avail > 0) |
||
652 | |||
653 | connection_try_recv(o); |
||
654 | return; |
||
655 | } |
||
656 | |||
657 | static void connection_try_handshake (BSSLConnection *o) |
||
658 | { |
||
659 | ASSERT(!o->have_error) |
||
660 | ASSERT(!o->up) |
||
661 | |||
662 | // continue in threadwork if requested |
||
663 | if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE)) { |
||
664 | if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { |
||
665 | backend_threadwork_start(o->backend, THREADWORK_STATE_HANDSHAKE); |
||
666 | } |
||
667 | return; |
||
668 | } |
||
669 | |||
670 | // try handshake |
||
671 | SECStatus res = SSL_ForceHandshake(o->prfd); |
||
672 | if (res == SECFailure) { |
||
673 | PRErrorCode error = PR_GetError(); |
||
674 | if (error == PR_WOULD_BLOCK_ERROR) { |
||
675 | return; |
||
676 | } |
||
677 | BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", error); |
||
678 | connection_report_error(o); |
||
679 | return; |
||
680 | } |
||
681 | |||
682 | // init up |
||
683 | connection_init_up(o); |
||
684 | |||
685 | // report up |
||
686 | o->handler(o->user, BSSLCONNECTION_EVENT_UP); |
||
687 | return; |
||
688 | } |
||
689 | |||
690 | static void connection_try_send (BSSLConnection *o) |
||
691 | { |
||
692 | ASSERT(!o->have_error) |
||
693 | ASSERT(o->up) |
||
694 | ASSERT(o->send_len > 0) |
||
695 | |||
696 | // continue in threadwork if requested |
||
697 | if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { |
||
698 | if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { |
||
699 | backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); |
||
700 | } |
||
701 | return; |
||
702 | } |
||
703 | |||
704 | // send |
||
705 | PRInt32 res = PR_Write(o->prfd, o->send_data, o->send_len); |
||
706 | if (res < 0) { |
||
707 | PRErrorCode error = PR_GetError(); |
||
708 | if (error == PR_WOULD_BLOCK_ERROR) { |
||
709 | return; |
||
710 | } |
||
711 | BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error); |
||
712 | connection_report_error(o); |
||
713 | return; |
||
714 | } |
||
715 | |||
716 | ASSERT(res > 0) |
||
717 | ASSERT(res <= o->send_len) |
||
718 | |||
719 | // set no send data |
||
720 | o->send_len = -1; |
||
721 | |||
722 | // done |
||
723 | StreamPassInterface_Done(&o->send_if, res); |
||
724 | } |
||
725 | |||
726 | static void connection_try_recv (BSSLConnection *o) |
||
727 | { |
||
728 | ASSERT(!o->have_error) |
||
729 | ASSERT(o->up) |
||
730 | ASSERT(o->recv_avail > 0) |
||
731 | |||
732 | // unset recv job |
||
733 | BPending_Unset(&o->recv_job); |
||
734 | |||
735 | // continue in threadwork if requested |
||
736 | if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { |
||
737 | if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { |
||
738 | backend_threadwork_start(o->backend, THREADWORK_STATE_READ); |
||
739 | } |
||
740 | return; |
||
741 | } |
||
742 | |||
743 | // recv |
||
744 | PRInt32 res = PR_Read(o->prfd, o->recv_data, o->recv_avail); |
||
745 | if (res < 0) { |
||
746 | PRErrorCode error = PR_GetError(); |
||
747 | if (error == PR_WOULD_BLOCK_ERROR) { |
||
748 | return; |
||
749 | } |
||
750 | BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error); |
||
751 | connection_report_error(o); |
||
752 | return; |
||
753 | } |
||
754 | |||
755 | if (res == 0) { |
||
756 | BLog(BLOG_ERROR, "PR_Read returned 0"); |
||
757 | connection_report_error(o); |
||
758 | return; |
||
759 | } |
||
760 | |||
761 | ASSERT(res > 0) |
||
762 | ASSERT(res <= o->recv_avail) |
||
763 | |||
764 | // set no recv data |
||
765 | o->recv_avail = -1; |
||
766 | |||
767 | // done |
||
768 | StreamRecvInterface_Done(&o->recv_if, res); |
||
769 | } |
||
770 | |||
771 | static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len) |
||
772 | { |
||
773 | DebugObject_Access(&o->d_obj); |
||
774 | ASSERT(!o->have_error) |
||
775 | ASSERT(o->up) |
||
776 | ASSERT(o->send_len == -1) |
||
777 | ASSERT(data_len > 0) |
||
778 | |||
779 | #ifndef NDEBUG |
||
780 | ASSERT(!o->releasebuffers_called) |
||
781 | o->user_io_started = 1; |
||
782 | #endif |
||
783 | |||
784 | // limit amount for PR_Write |
||
785 | if (data_len > INT32_MAX) { |
||
786 | data_len = INT32_MAX; |
||
787 | } |
||
788 | |||
789 | // set send data |
||
790 | o->send_data = data; |
||
791 | o->send_len = data_len; |
||
792 | |||
793 | // start sending |
||
794 | connection_try_send(o); |
||
795 | } |
||
796 | |||
797 | static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len) |
||
798 | { |
||
799 | DebugObject_Access(&o->d_obj); |
||
800 | ASSERT(!o->have_error) |
||
801 | ASSERT(o->up) |
||
802 | ASSERT(o->recv_avail == -1) |
||
803 | ASSERT(data_len > 0) |
||
804 | |||
805 | #ifndef NDEBUG |
||
806 | ASSERT(!o->releasebuffers_called) |
||
807 | o->user_io_started = 1; |
||
808 | #endif |
||
809 | |||
810 | // limit amount for PR_Read |
||
811 | if (data_len > INT32_MAX) { |
||
812 | data_len = INT32_MAX; |
||
813 | } |
||
814 | |||
815 | // set recv data |
||
816 | o->recv_data = data; |
||
817 | o->recv_avail = data_len; |
||
818 | |||
819 | // start receiving |
||
820 | connection_try_recv(o); |
||
821 | } |
||
822 | |||
823 | int BSSLConnection_GlobalInit (void) |
||
824 | { |
||
825 | ASSERT(!bprconnection_initialized) |
||
826 | |||
827 | if ((bprconnection_identity = PR_GetUniqueIdentity("BSSLConnection")) == PR_INVALID_IO_LAYER) { |
||
828 | BLog(BLOG_ERROR, "PR_GetUniqueIdentity failed"); |
||
829 | return 0; |
||
830 | } |
||
831 | |||
832 | bprconnection_initialized = 1; |
||
833 | |||
834 | return 1; |
||
835 | } |
||
836 | |||
837 | int BSSLConnection_MakeBackend (PRFileDesc *prfd, StreamPassInterface *send_if, StreamRecvInterface *recv_if, BThreadWorkDispatcher *twd, int flags) |
||
838 | { |
||
839 | ASSERT(bprconnection_initialized) |
||
840 | ASSERT(!(flags & ~(BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE | BSSLCONNECTION_FLAG_THREADWORK_IO))) |
||
841 | ASSERT(!(flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || twd) |
||
842 | ASSERT(!(flags & BSSLCONNECTION_FLAG_THREADWORK_IO) || twd) |
||
843 | |||
844 | // don't do stuff in threads if threads aren't available |
||
845 | if (((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) && |
||
846 | !BThreadWorkDispatcher_UsingThreads(twd) |
||
847 | ) { |
||
848 | BLog(BLOG_WARNING, "SSL operations in threads requested but threads are not available"); |
||
849 | flags &= ~(BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE | BSSLCONNECTION_FLAG_THREADWORK_IO); |
||
850 | } |
||
851 | |||
852 | // allocate backend |
||
853 | struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)malloc(sizeof(*b)); |
||
854 | if (!b) { |
||
855 | BLog(BLOG_ERROR, "malloc failed"); |
||
856 | goto fail0; |
||
857 | } |
||
858 | |||
859 | // init mutexes |
||
860 | if ((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { |
||
861 | if (!BMutex_Init(&b->send_buf_mutex)) { |
||
862 | BLog(BLOG_ERROR, "BMutex_Init failed"); |
||
863 | goto fail1; |
||
864 | } |
||
865 | |||
866 | if (!BMutex_Init(&b->recv_buf_mutex)) { |
||
867 | BLog(BLOG_ERROR, "BMutex_Init failed"); |
||
868 | goto fail2; |
||
869 | } |
||
870 | } |
||
871 | |||
872 | // init arguments |
||
873 | b->send_if = send_if; |
||
874 | b->recv_if = recv_if; |
||
875 | b->twd = twd; |
||
876 | b->flags = flags; |
||
877 | |||
878 | // init interfaces |
||
879 | StreamPassInterface_Sender_Init(b->send_if, (StreamPassInterface_handler_done)backend_send_if_handler_done, b); |
||
880 | StreamRecvInterface_Receiver_Init(b->recv_if, (StreamRecvInterface_handler_done)backend_recv_if_handler_done, b); |
||
881 | |||
882 | // set no connection |
||
883 | b->con = NULL; |
||
884 | |||
885 | // init send buffer |
||
886 | b->send_busy = 0; |
||
887 | b->send_len = 0; |
||
888 | b->send_pos = 0; |
||
889 | |||
890 | // init recv buffer |
||
891 | b->recv_busy = 0; |
||
892 | b->recv_pos = 0; |
||
893 | b->recv_len = 0; |
||
894 | |||
895 | // set threadwork state |
||
896 | b->threadwork_state = THREADWORK_STATE_NONE; |
||
897 | |||
898 | // init prfd |
||
899 | memset(prfd, 0, sizeof(*prfd)); |
||
900 | prfd->methods = &methods; |
||
901 | prfd->secret = (PRFilePrivate *)b; |
||
902 | prfd->identity = bprconnection_identity; |
||
903 | |||
904 | return 1; |
||
905 | |||
906 | if ((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { |
||
907 | fail2: |
||
908 | BMutex_Free(&b->send_buf_mutex); |
||
909 | } |
||
910 | fail1: |
||
911 | free(b); |
||
912 | fail0: |
||
913 | return 0; |
||
914 | } |
||
915 | |||
916 | void BSSLConnection_Init (BSSLConnection *o, PRFileDesc *prfd, int force_handshake, BPendingGroup *pg, void *user, |
||
917 | BSSLConnection_handler handler) |
||
918 | { |
||
919 | ASSERT(force_handshake == 0 || force_handshake == 1) |
||
920 | ASSERT(handler) |
||
921 | ASSERT(bprconnection_initialized) |
||
922 | ASSERT(get_bottom(prfd)->identity == bprconnection_identity) |
||
923 | ASSERT(!((struct BSSLConnection_backend *)(get_bottom(prfd)->secret))->con) |
||
924 | |||
925 | // init arguments |
||
926 | o->prfd = prfd; |
||
927 | o->pg = pg; |
||
928 | o->user = user; |
||
929 | o->handler = handler; |
||
930 | |||
931 | // set backend |
||
932 | o->backend = (struct BSSLConnection_backend *)(get_bottom(prfd)->secret); |
||
933 | ASSERT(!o->backend->con) |
||
934 | ASSERT(o->backend->threadwork_state == THREADWORK_STATE_NONE) |
||
935 | |||
936 | // set have no error |
||
937 | o->have_error = 0; |
||
938 | |||
939 | // init init job |
||
940 | BPending_Init(&o->init_job, o->pg, (BPending_handler)connection_init_job_handler, o); |
||
941 | |||
942 | if (force_handshake) { |
||
943 | // set not up |
||
944 | o->up = 0; |
||
945 | |||
946 | // set init job |
||
947 | BPending_Set(&o->init_job); |
||
948 | } else { |
||
949 | // init up |
||
950 | connection_init_up(o); |
||
951 | } |
||
952 | |||
953 | // set backend connection |
||
954 | o->backend->con = o; |
||
955 | |||
956 | #ifndef NDEBUG |
||
957 | o->user_io_started = 0; |
||
958 | o->releasebuffers_called = 0; |
||
959 | #endif |
||
960 | |||
961 | DebugError_Init(&o->d_err, o->pg); |
||
962 | DebugObject_Init(&o->d_obj); |
||
963 | } |
||
964 | |||
965 | void BSSLConnection_Free (BSSLConnection *o) |
||
966 | { |
||
967 | DebugObject_Free(&o->d_obj); |
||
968 | DebugError_Free(&o->d_err); |
||
969 | #ifndef NDEBUG |
||
970 | ASSERT(o->releasebuffers_called || !o->user_io_started) |
||
971 | #endif |
||
972 | ASSERT(o->backend->threadwork_state == THREADWORK_STATE_NONE) |
||
973 | |||
974 | if (o->up) { |
||
975 | // free recv job |
||
976 | BPending_Free(&o->recv_job); |
||
977 | |||
978 | // free recv interface |
||
979 | StreamRecvInterface_Free(&o->recv_if); |
||
980 | |||
981 | // free send interface |
||
982 | StreamPassInterface_Free(&o->send_if); |
||
983 | } |
||
984 | |||
985 | // free init job |
||
986 | BPending_Free(&o->init_job); |
||
987 | |||
988 | // unset backend connection |
||
989 | o->backend->con = NULL; |
||
990 | } |
||
991 | |||
992 | void BSSLConnection_ReleaseBuffers (BSSLConnection *o) |
||
993 | { |
||
994 | DebugObject_Access(&o->d_obj); |
||
995 | #ifndef NDEBUG |
||
996 | ASSERT(!o->releasebuffers_called) |
||
997 | #endif |
||
998 | |||
999 | // wait for threadwork to finish |
||
1000 | if (o->backend->threadwork_state != THREADWORK_STATE_NONE) { |
||
1001 | BThreadWork_Free(&o->backend->threadwork); |
||
1002 | o->backend->threadwork_state = THREADWORK_STATE_NONE; |
||
1003 | } |
||
1004 | |||
1005 | #ifndef NDEBUG |
||
1006 | o->releasebuffers_called = 1; |
||
1007 | #endif |
||
1008 | } |
||
1009 | |||
1010 | StreamPassInterface * BSSLConnection_GetSendIf (BSSLConnection *o) |
||
1011 | { |
||
1012 | DebugObject_Access(&o->d_obj); |
||
1013 | ASSERT(o->up) |
||
1014 | |||
1015 | return &o->send_if; |
||
1016 | } |
||
1017 | |||
1018 | StreamRecvInterface * BSSLConnection_GetRecvIf (BSSLConnection *o) |
||
1019 | { |
||
1020 | DebugObject_Access(&o->d_obj); |
||
1021 | ASSERT(o->up) |
||
1022 | |||
1023 | return &o->recv_if; |
||
1024 | } |