Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/openssl/doc/designs/ddd/ddd-06-mem-uv.c
34889 views
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
9
typedef struct app_conn_st APP_CONN;
10
typedef struct upper_write_op_st UPPER_WRITE_OP;
11
typedef struct lower_write_op_st LOWER_WRITE_OP;
12
13
typedef void (app_connect_cb)(APP_CONN *conn, int status, void *arg);
14
typedef void (app_write_cb)(APP_CONN *conn, int status, void *arg);
15
typedef void (app_read_cb)(APP_CONN *conn, void *buf, size_t buf_len, void *arg);
16
17
#ifdef USE_QUIC
18
static void set_timer(APP_CONN *conn);
19
#else
20
static void tcp_connect_done(uv_connect_t *tcp_connect, int status);
21
#endif
22
static void net_connect_fail_close_done(uv_handle_t *handle);
23
static int handshake_ssl(APP_CONN *conn);
24
static void flush_write_buf(APP_CONN *conn);
25
static void set_rx(APP_CONN *conn);
26
static int try_write(APP_CONN *conn, UPPER_WRITE_OP *op);
27
static void handle_pending_writes(APP_CONN *conn);
28
static int write_deferred(APP_CONN *conn, const void *buf, size_t buf_len, app_write_cb *cb, void *arg);
29
static void teardown_continued(uv_handle_t *handle);
30
static int setup_ssl(APP_CONN *conn, const char *hostname);
31
32
#ifdef USE_QUIC
33
static 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
*/
44
struct 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
*/
56
struct 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
*/
70
struct 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
*/
99
SSL_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
131
APP_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
*/
190
int 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
*/
202
int 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
*/
213
void 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
*/
248
void teardown_ctx(SSL_CTX *ctx)
249
{
250
SSL_CTX_free(ctx);
251
}
252
253
/*
254
* ============================================================================
255
* Internal implementation functions.
256
*/
257
static 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
268
static 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
282
static 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
294
static 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
321
static 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
330
static 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
351
static 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
354
static 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
388
static 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
404
static void net_write_done(uv_udp_send_t *req, int status)
405
#else
406
static 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
423
static 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
462
static 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
470
static 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
495
static 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
548
static 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
567
static 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
576
static 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
587
static 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
602
static 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
631
static 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
649
static 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
*/
676
static 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
688
static 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
698
char tx_msg[300];
699
int mlen;
700
701
static 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
718
int 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;
753
fail:
754
teardown(conn);
755
freeaddrinfo(result);
756
uv_loop_close(uv_default_loop());
757
teardown_ctx(ctx);
758
}
759
760