VirtualBox

source: vbox/trunk/src/libs/openssl-3.3.2/doc/designs/ddd/ddd-06-mem-uv.c

Last change on this file was 108206, checked in by vboxsync, 3 months ago

openssl-3.3.2: Exported all files to OSE and removed .scm-settings ​bugref:10757

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1#include <sys/poll.h>
2#include <openssl/ssl.h>
3#include <uv.h>
4#include <assert.h>
5#ifdef USE_QUIC
6# include <sys/time.h>
7#endif
8
9typedef struct app_conn_st APP_CONN;
10typedef struct upper_write_op_st UPPER_WRITE_OP;
11typedef struct lower_write_op_st LOWER_WRITE_OP;
12
13typedef void (app_connect_cb)(APP_CONN *conn, int status, void *arg);
14typedef void (app_write_cb)(APP_CONN *conn, int status, void *arg);
15typedef void (app_read_cb)(APP_CONN *conn, void *buf, size_t buf_len, void *arg);
16
17#ifdef USE_QUIC
18static void set_timer(APP_CONN *conn);
19#else
20static void tcp_connect_done(uv_connect_t *tcp_connect, int status);
21#endif
22static void net_connect_fail_close_done(uv_handle_t *handle);
23static int handshake_ssl(APP_CONN *conn);
24static void flush_write_buf(APP_CONN *conn);
25static void set_rx(APP_CONN *conn);
26static int try_write(APP_CONN *conn, UPPER_WRITE_OP *op);
27static void handle_pending_writes(APP_CONN *conn);
28static int write_deferred(APP_CONN *conn, const void *buf, size_t buf_len, app_write_cb *cb, void *arg);
29static void teardown_continued(uv_handle_t *handle);
30static int setup_ssl(APP_CONN *conn, const char *hostname);
31
32#ifdef USE_QUIC
33static inline int timeval_to_ms(const struct timeval *t)
34{
35 return t->tv_sec*1000 + t->tv_usec/1000;
36}
37#endif
38
39/*
40 * Structure to track an application-level write request. Only created
41 * if SSL_write does not accept the data immediately, typically because
42 * it is in WANT_READ.
43 */
44struct upper_write_op_st {
45 struct upper_write_op_st *prev, *next;
46 const uint8_t *buf;
47 size_t buf_len, written;
48 APP_CONN *conn;
49 app_write_cb *cb;
50 void *cb_arg;
51};
52
53/*
54 * Structure to track a network-level write request.
55 */
56struct lower_write_op_st {
57#ifdef USE_QUIC
58 uv_udp_send_t w;
59#else
60 uv_write_t w;
61#endif
62 uv_buf_t b;
63 uint8_t *buf;
64 APP_CONN *conn;
65};
66
67/*
68 * Application connection object.
69 */
70struct app_conn_st {
71 SSL_CTX *ctx;
72 SSL *ssl;
73 BIO *net_bio;
74#ifdef USE_QUIC
75 uv_udp_t udp;
76 uv_timer_t timer;
77#else
78 uv_stream_t *stream;
79 uv_tcp_t tcp;
80 uv_connect_t tcp_connect;
81#endif
82 app_connect_cb *app_connect_cb; /* called once handshake is done */
83 void *app_connect_arg;
84 app_read_cb *app_read_cb; /* application's on-RX callback */
85 void *app_read_arg;
86 const char *hostname;
87 char init_handshake, done_handshake, closed;
88 char *teardown_done;
89
90 UPPER_WRITE_OP *pending_upper_write_head, *pending_upper_write_tail;
91};
92
93/*
94 * The application is initializing and wants an SSL_CTX which it will use for
95 * some number of outgoing connections, which it creates in subsequent calls to
96 * new_conn. The application may also call this function multiple times to
97 * create multiple SSL_CTX.
98 */
99SSL_CTX *create_ssl_ctx(void)
100{
101 SSL_CTX *ctx;
102
103#ifdef USE_QUIC
104 ctx = SSL_CTX_new(OSSL_QUIC_client_method());
105#else
106 ctx = SSL_CTX_new(TLS_client_method());
107#endif
108 if (ctx == NULL)
109 return NULL;
110
111 /* Enable trust chain verification. */
112 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
113
114 /* Load default root CA store. */
115 if (SSL_CTX_set_default_verify_paths(ctx) == 0) {
116 SSL_CTX_free(ctx);
117 return NULL;
118 }
119
120 return ctx;
121}
122
123/*
124 * The application wants to create a new outgoing connection using a given
125 * SSL_CTX. An outgoing TCP connection is started and the callback is called
126 * asynchronously when the TLS handshake is complete.
127 *
128 * hostname is a string like "openssl.org" used for certificate validation.
129 */
130
131APP_CONN *new_conn(SSL_CTX *ctx, const char *hostname,
132 struct sockaddr *sa, socklen_t sa_len,
133 app_connect_cb *cb, void *arg)
134{
135 int rc;
136 APP_CONN *conn = NULL;
137
138 conn = calloc(1, sizeof(APP_CONN));
139 if (!conn)
140 return NULL;
141
142#ifdef USE_QUIC
143 uv_udp_init(uv_default_loop(), &conn->udp);
144 conn->udp.data = conn;
145
146 uv_timer_init(uv_default_loop(), &conn->timer);
147 conn->timer.data = conn;
148#else
149 uv_tcp_init(uv_default_loop(), &conn->tcp);
150 conn->tcp.data = conn;
151
152 conn->stream = (uv_stream_t *)&conn->tcp;
153#endif
154
155 conn->app_connect_cb = cb;
156 conn->app_connect_arg = arg;
157#ifdef USE_QUIC
158 rc = uv_udp_connect(&conn->udp, sa);
159#else
160 conn->tcp_connect.data = conn;
161 rc = uv_tcp_connect(&conn->tcp_connect, &conn->tcp, sa, tcp_connect_done);
162#endif
163 if (rc < 0) {
164#ifdef USE_QUIC
165 uv_close((uv_handle_t *)&conn->udp, net_connect_fail_close_done);
166#else
167 uv_close((uv_handle_t *)&conn->tcp, net_connect_fail_close_done);
168#endif
169 return NULL;
170 }
171
172 conn->ctx = ctx;
173 conn->hostname = hostname;
174
175#ifdef USE_QUIC
176 rc = setup_ssl(conn, hostname);
177 if (rc < 0) {
178 uv_close((uv_handle_t *)&conn->udp, net_connect_fail_close_done);
179 return NULL;
180 }
181#endif
182
183 return conn;
184}
185
186/*
187 * The application wants to start reading from the SSL stream.
188 * The callback is called whenever data is available.
189 */
190int app_read_start(APP_CONN *conn, app_read_cb *cb, void *arg)
191{
192 conn->app_read_cb = cb;
193 conn->app_read_arg = arg;
194 set_rx(conn);
195 return 0;
196}
197
198/*
199 * The application wants to write. The callback is called once the
200 * write is complete. The callback should free the buffer.
201 */
202int app_write(APP_CONN *conn, const void *buf, size_t buf_len, app_write_cb *cb, void *arg)
203{
204 write_deferred(conn, buf, buf_len, cb, arg);
205 handle_pending_writes(conn);
206 return buf_len;
207}
208
209/*
210 * The application wants to close the connection and free bookkeeping
211 * structures.
212 */
213void teardown(APP_CONN *conn)
214{
215 char teardown_done = 0;
216
217 if (conn == NULL)
218 return;
219
220 BIO_free_all(conn->net_bio);
221 SSL_free(conn->ssl);
222
223#ifndef USE_QUIC
224 uv_cancel((uv_req_t *)&conn->tcp_connect);
225#endif
226
227 conn->teardown_done = &teardown_done;
228#ifdef USE_QUIC
229 uv_close((uv_handle_t *)&conn->udp, teardown_continued);
230 uv_close((uv_handle_t *)&conn->timer, teardown_continued);
231#else
232 uv_close((uv_handle_t *)conn->stream, teardown_continued);
233#endif
234
235 /* Just wait synchronously until teardown completes. */
236#ifdef USE_QUIC
237 while (teardown_done < 2)
238#else
239 while (!teardown_done)
240#endif
241 uv_run(uv_default_loop(), UV_RUN_DEFAULT);
242}
243
244/*
245 * The application is shutting down and wants to free a previously
246 * created SSL_CTX.
247 */
248void teardown_ctx(SSL_CTX *ctx)
249{
250 SSL_CTX_free(ctx);
251}
252
253/*
254 * ============================================================================
255 * Internal implementation functions.
256 */
257static void enqueue_upper_write_op(APP_CONN *conn, UPPER_WRITE_OP *op)
258{
259 op->prev = conn->pending_upper_write_tail;
260 if (op->prev)
261 op->prev->next = op;
262
263 conn->pending_upper_write_tail = op;
264 if (conn->pending_upper_write_head == NULL)
265 conn->pending_upper_write_head = op;
266}
267
268static void dequeue_upper_write_op(APP_CONN *conn)
269{
270 if (conn->pending_upper_write_head == NULL)
271 return;
272
273 if (conn->pending_upper_write_head->next == NULL) {
274 conn->pending_upper_write_head = NULL;
275 conn->pending_upper_write_tail = NULL;
276 } else {
277 conn->pending_upper_write_head = conn->pending_upper_write_head->next;
278 conn->pending_upper_write_head->prev = NULL;
279 }
280}
281
282static void net_read_alloc(uv_handle_t *handle,
283 size_t suggested_size, uv_buf_t *buf)
284{
285#ifdef USE_QUIC
286 if (suggested_size < 1472)
287 suggested_size = 1472;
288#endif
289
290 buf->base = malloc(suggested_size);
291 buf->len = suggested_size;
292}
293
294static void on_rx_push(APP_CONN *conn)
295{
296 int srd, rc;
297 int buf_len = 4096;
298
299 do {
300 if (!conn->app_read_cb)
301 return;
302
303 void *buf = malloc(buf_len);
304 if (!buf)
305 return;
306
307 srd = SSL_read(conn->ssl, buf, buf_len);
308 flush_write_buf(conn);
309 if (srd <= 0) {
310 rc = SSL_get_error(conn->ssl, srd);
311 if (rc == SSL_ERROR_WANT_READ) {
312 free(buf);
313 return;
314 }
315 }
316
317 conn->app_read_cb(conn, buf, srd, conn->app_read_arg);
318 } while (srd == buf_len);
319}
320
321static void net_error(APP_CONN *conn)
322{
323 conn->closed = 1;
324 set_rx(conn);
325
326 if (conn->app_read_cb)
327 conn->app_read_cb(conn, NULL, 0, conn->app_read_arg);
328}
329
330static void handle_pending_writes(APP_CONN *conn)
331{
332 int rc;
333
334 if (conn->pending_upper_write_head == NULL)
335 return;
336
337 do {
338 UPPER_WRITE_OP *op = conn->pending_upper_write_head;
339 rc = try_write(conn, op);
340 if (rc <= 0)
341 break;
342
343 dequeue_upper_write_op(conn);
344 free(op);
345 } while (conn->pending_upper_write_head != NULL);
346
347 set_rx(conn);
348}
349
350#ifdef USE_QUIC
351static void net_read_done(uv_udp_t *stream, ssize_t nr, const uv_buf_t *buf,
352 const struct sockaddr *addr, unsigned int flags)
353#else
354static void net_read_done(uv_stream_t *stream, ssize_t nr, const uv_buf_t *buf)
355#endif
356{
357 int rc;
358 APP_CONN *conn = (APP_CONN *)stream->data;
359
360 if (nr < 0) {
361 free(buf->base);
362 net_error(conn);
363 return;
364 }
365
366 if (nr > 0) {
367 int wr = BIO_write(conn->net_bio, buf->base, nr);
368 assert(wr == nr);
369 }
370
371 free(buf->base);
372
373 if (!conn->done_handshake) {
374 rc = handshake_ssl(conn);
375 if (rc < 0) {
376 fprintf(stderr, "handshake error: %d\n", rc);
377 return;
378 }
379
380 if (!conn->done_handshake)
381 return;
382 }
383
384 handle_pending_writes(conn);
385 on_rx_push(conn);
386}
387
388static void set_rx(APP_CONN *conn)
389{
390#ifdef USE_QUIC
391 if (!conn->closed)
392 uv_udp_recv_start(&conn->udp, net_read_alloc, net_read_done);
393 else
394 uv_udp_recv_stop(&conn->udp);
395#else
396 if (!conn->closed && (conn->app_read_cb || (!conn->done_handshake && conn->init_handshake) || conn->pending_upper_write_head != NULL))
397 uv_read_start(conn->stream, net_read_alloc, net_read_done);
398 else
399 uv_read_stop(conn->stream);
400#endif
401}
402
403#ifdef USE_QUIC
404static void net_write_done(uv_udp_send_t *req, int status)
405#else
406static void net_write_done(uv_write_t *req, int status)
407#endif
408{
409 LOWER_WRITE_OP *op = (LOWER_WRITE_OP *)req->data;
410 APP_CONN *conn = op->conn;
411
412 if (status < 0) {
413 fprintf(stderr, "UV write failed %d\n", status);
414 return;
415 }
416
417 free(op->buf);
418 free(op);
419
420 flush_write_buf(conn);
421}
422
423static void flush_write_buf(APP_CONN *conn)
424{
425 int rc, rd;
426 LOWER_WRITE_OP *op;
427 uint8_t *buf;
428
429 buf = malloc(4096);
430 if (!buf)
431 return;
432
433 rd = BIO_read(conn->net_bio, buf, 4096);
434 if (rd <= 0) {
435 free(buf);
436 return;
437 }
438
439 op = calloc(1, sizeof(LOWER_WRITE_OP));
440 if (!op)
441 return;
442
443 op->buf = buf;
444 op->conn = conn;
445 op->w.data = op;
446 op->b.base = (char *)buf;
447 op->b.len = rd;
448
449#ifdef USE_QUIC
450 rc = uv_udp_send(&op->w, &conn->udp, &op->b, 1, NULL, net_write_done);
451#else
452 rc = uv_write(&op->w, conn->stream, &op->b, 1, net_write_done);
453#endif
454 if (rc < 0) {
455 free(buf);
456 free(op);
457 fprintf(stderr, "UV write failed\n");
458 return;
459 }
460}
461
462static void handshake_done_ssl(APP_CONN *conn)
463{
464#ifdef USE_QUIC
465 set_timer(conn);
466#endif
467 conn->app_connect_cb(conn, 0, conn->app_connect_arg);
468}
469
470static int handshake_ssl(APP_CONN *conn)
471{
472 int rc, rcx;
473
474 conn->init_handshake = 1;
475
476 rc = SSL_do_handshake(conn->ssl);
477 if (rc > 0) {
478 conn->done_handshake = 1;
479 handshake_done_ssl(conn);
480 set_rx(conn);
481 return 0;
482 }
483
484 flush_write_buf(conn);
485 rcx = SSL_get_error(conn->ssl, rc);
486 if (rcx == SSL_ERROR_WANT_READ) {
487 set_rx(conn);
488 return 0;
489 }
490
491 fprintf(stderr, "Handshake error: %d\n", rcx);
492 return -rcx;
493}
494
495static int setup_ssl(APP_CONN *conn, const char *hostname)
496{
497 BIO *internal_bio = NULL, *net_bio = NULL;
498 SSL *ssl = NULL;
499#ifdef USE_QUIC
500 static const unsigned char alpn[] = {5, 'd', 'u', 'm', 'm', 'y'};
501#endif
502
503 ssl = SSL_new(conn->ctx);
504 if (!ssl)
505 return -1;
506
507 SSL_set_connect_state(ssl);
508
509#ifdef USE_QUIC
510 if (BIO_new_bio_dgram_pair(&internal_bio, 0, &net_bio, 0) <= 0) {
511 SSL_free(ssl);
512 return -1;
513 }
514#else
515 if (BIO_new_bio_pair(&internal_bio, 0, &net_bio, 0) <= 0) {
516 SSL_free(ssl);
517 return -1;
518 }
519#endif
520
521 SSL_set_bio(ssl, internal_bio, internal_bio);
522
523 if (SSL_set1_host(ssl, hostname) <= 0) {
524 SSL_free(ssl);
525 return -1;
526 }
527
528 if (SSL_set_tlsext_host_name(ssl, hostname) <= 0) {
529 SSL_free(ssl);
530 return -1;
531 }
532
533#ifdef USE_QUIC
534 /* Configure ALPN, which is required for QUIC. */
535 if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn))) {
536 /* Note: SSL_set_alpn_protos returns 1 for failure. */
537 SSL_free(ssl);
538 return -1;
539 }
540#endif
541
542 conn->net_bio = net_bio;
543 conn->ssl = ssl;
544 return handshake_ssl(conn);
545}
546
547#ifndef USE_QUIC
548static void tcp_connect_done(uv_connect_t *tcp_connect, int status)
549{
550 int rc;
551 APP_CONN *conn = (APP_CONN *)tcp_connect->data;
552
553 if (status < 0) {
554 uv_stop(uv_default_loop());
555 return;
556 }
557
558 rc = setup_ssl(conn, conn->hostname);
559 if (rc < 0) {
560 fprintf(stderr, "cannot init SSL\n");
561 uv_stop(uv_default_loop());
562 return;
563 }
564}
565#endif
566
567static void net_connect_fail_close_done(uv_handle_t *handle)
568{
569 APP_CONN *conn = (APP_CONN *)handle->data;
570
571 free(conn);
572}
573
574#ifdef USE_QUIC
575
576static void timer_done(uv_timer_t *timer)
577{
578 APP_CONN *conn = (APP_CONN *)timer->data;
579
580 SSL_handle_events(conn->ssl);
581 handle_pending_writes(conn);
582 flush_write_buf(conn);
583 set_rx(conn);
584 set_timer(conn); /* repeat timer */
585}
586
587static void set_timer(APP_CONN *conn)
588{
589 struct timeval tv;
590 int ms, is_infinite;
591
592 if (!SSL_get_event_timeout(conn->ssl, &tv, &is_infinite))
593 return;
594
595 ms = is_infinite ? -1 : timeval_to_ms(&tv);
596 if (ms > 0)
597 uv_timer_start(&conn->timer, timer_done, ms, 0);
598}
599
600#endif
601
602static int try_write(APP_CONN *conn, UPPER_WRITE_OP *op)
603{
604 int rc, rcx;
605 size_t written = op->written;
606
607 while (written < op->buf_len) {
608 rc = SSL_write(conn->ssl, op->buf + written, op->buf_len - written);
609 if (rc <= 0) {
610 rcx = SSL_get_error(conn->ssl, rc);
611 if (rcx == SSL_ERROR_WANT_READ) {
612 op->written = written;
613 return 0;
614 } else {
615 if (op->cb != NULL)
616 op->cb(conn, -rcx, op->cb_arg);
617 return 1; /* op should be freed */
618 }
619 }
620
621 written += rc;
622 }
623
624 if (op->cb != NULL)
625 op->cb(conn, 0, op->cb_arg);
626
627 flush_write_buf(conn);
628 return 1; /* op should be freed */
629}
630
631static int write_deferred(APP_CONN *conn, const void *buf, size_t buf_len, app_write_cb *cb, void *arg)
632{
633 UPPER_WRITE_OP *op = calloc(1, sizeof(UPPER_WRITE_OP));
634 if (!op)
635 return -1;
636
637 op->buf = buf;
638 op->buf_len = buf_len;
639 op->conn = conn;
640 op->cb = cb;
641 op->cb_arg = arg;
642
643 enqueue_upper_write_op(conn, op);
644 set_rx(conn);
645 flush_write_buf(conn);
646 return buf_len;
647}
648
649static void teardown_continued(uv_handle_t *handle)
650{
651 APP_CONN *conn = (APP_CONN *)handle->data;
652 UPPER_WRITE_OP *op, *next_op;
653 char *teardown_done = conn->teardown_done;
654
655#ifdef USE_QUIC
656 if (++*teardown_done < 2)
657 return;
658#endif
659
660 for (op=conn->pending_upper_write_head; op; op=next_op) {
661 next_op = op->next;
662 free(op);
663 }
664
665 free(conn);
666#ifndef USE_QUIC
667 *teardown_done = 1;
668#endif
669}
670
671/*
672 * ============================================================================
673 * Example driver for the above code. This is just to demonstrate that the code
674 * works and is not intended to be representative of a real application.
675 */
676static void post_read(APP_CONN *conn, void *buf, size_t buf_len, void *arg)
677{
678 if (!buf_len) {
679 free(buf);
680 uv_stop(uv_default_loop());
681 return;
682 }
683
684 fwrite(buf, 1, buf_len, stdout);
685 free(buf);
686}
687
688static void post_write_get(APP_CONN *conn, int status, void *arg)
689{
690 if (status < 0) {
691 fprintf(stderr, "write failed: %d\n", status);
692 return;
693 }
694
695 app_read_start(conn, post_read, NULL);
696}
697
698char tx_msg[300];
699int mlen;
700
701static void post_connect(APP_CONN *conn, int status, void *arg)
702{
703 int wr;
704
705 if (status < 0) {
706 fprintf(stderr, "failed to connect: %d\n", status);
707 uv_stop(uv_default_loop());
708 return;
709 }
710
711 wr = app_write(conn, tx_msg, mlen, post_write_get, NULL);
712 if (wr < mlen) {
713 fprintf(stderr, "error writing request");
714 return;
715 }
716}
717
718int main(int argc, char **argv)
719{
720 int rc = 1;
721 SSL_CTX *ctx = NULL;
722 APP_CONN *conn = NULL;
723 struct addrinfo hints = {0}, *result = NULL;
724
725 if (argc < 3) {
726 fprintf(stderr, "usage: %s host port\n", argv[0]);
727 goto fail;
728 }
729
730 mlen = snprintf(tx_msg, sizeof(tx_msg),
731 "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", argv[1]);
732
733 ctx = create_ssl_ctx();
734 if (!ctx)
735 goto fail;
736
737 hints.ai_family = AF_INET;
738 hints.ai_socktype = SOCK_STREAM;
739 hints.ai_flags = AI_PASSIVE;
740 rc = getaddrinfo(argv[1], argv[2], &hints, &result);
741 if (rc < 0) {
742 fprintf(stderr, "cannot resolve\n");
743 goto fail;
744 }
745
746 conn = new_conn(ctx, argv[1], result->ai_addr, result->ai_addrlen, post_connect, NULL);
747 if (!conn)
748 goto fail;
749
750 uv_run(uv_default_loop(), UV_RUN_DEFAULT);
751
752 rc = 0;
753fail:
754 teardown(conn);
755 freeaddrinfo(result);
756 uv_loop_close(uv_default_loop());
757 teardown_ctx(ctx);
758}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette