VirtualBox

source: vbox/trunk/src/libs/openssl-3.3.2/doc/designs/ddd/ddd-05-mem-nonblocking.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: 11.5 KB
Line 
1#include <sys/poll.h>
2#include <openssl/ssl.h>
3
4/*
5 * Demo 5: Client — Client Uses Memory BIO — Nonblocking
6 * =====================================================
7 *
8 * This is an example of (part of) an application which uses libssl in an
9 * asynchronous, nonblocking fashion. The application passes memory BIOs to
10 * OpenSSL, meaning that it controls both when data is read/written from an SSL
11 * object on the decrypted side but also when encrypted data from the network is
12 * shunted to/from OpenSSL. In this way OpenSSL is used as a pure state machine
13 * which does not make its own network I/O calls. OpenSSL never sees or creates
14 * any file descriptor for a network socket. The functions below show all
15 * interactions with libssl the application makes, and would hypothetically be
16 * linked into a larger application.
17 */
18typedef struct app_conn_st {
19 SSL *ssl;
20 BIO *ssl_bio, *net_bio;
21 int rx_need_tx, tx_need_rx;
22} APP_CONN;
23
24/*
25 * The application is initializing and wants an SSL_CTX which it will use for
26 * some number of outgoing connections, which it creates in subsequent calls to
27 * new_conn. The application may also call this function multiple times to
28 * create multiple SSL_CTX.
29 */
30SSL_CTX *create_ssl_ctx(void)
31{
32 SSL_CTX *ctx;
33
34#ifdef USE_QUIC
35 ctx = SSL_CTX_new(OSSL_QUIC_client_method());
36#else
37 ctx = SSL_CTX_new(TLS_client_method());
38#endif
39 if (ctx == NULL)
40 return NULL;
41
42 /* Enable trust chain verification. */
43 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
44
45 /* Load default root CA store. */
46 if (SSL_CTX_set_default_verify_paths(ctx) == 0) {
47 SSL_CTX_free(ctx);
48 return NULL;
49 }
50
51 return ctx;
52}
53
54/*
55 * The application wants to create a new outgoing connection using a given
56 * SSL_CTX.
57 *
58 * hostname is a string like "openssl.org" used for certificate validation.
59 */
60APP_CONN *new_conn(SSL_CTX *ctx, const char *bare_hostname)
61{
62 BIO *ssl_bio, *internal_bio, *net_bio;
63 APP_CONN *conn;
64 SSL *ssl;
65#ifdef USE_QUIC
66 static const unsigned char alpn[] = {5, 'd', 'u', 'm', 'm', 'y'};
67#endif
68
69 conn = calloc(1, sizeof(APP_CONN));
70 if (conn == NULL)
71 return NULL;
72
73 ssl = conn->ssl = SSL_new(ctx);
74 if (ssl == NULL) {
75 free(conn);
76 return NULL;
77 }
78
79 SSL_set_connect_state(ssl); /* cannot fail */
80
81#ifdef USE_QUIC
82 if (BIO_new_bio_dgram_pair(&internal_bio, 0, &net_bio, 0) <= 0) {
83#else
84 if (BIO_new_bio_pair(&internal_bio, 0, &net_bio, 0) <= 0) {
85#endif
86 SSL_free(ssl);
87 free(conn);
88 return NULL;
89 }
90
91 SSL_set_bio(ssl, internal_bio, internal_bio);
92
93 if (SSL_set1_host(ssl, bare_hostname) <= 0) {
94 SSL_free(ssl);
95 free(conn);
96 return NULL;
97 }
98
99 if (SSL_set_tlsext_host_name(ssl, bare_hostname) <= 0) {
100 SSL_free(ssl);
101 free(conn);
102 return NULL;
103 }
104
105 ssl_bio = BIO_new(BIO_f_ssl());
106 if (ssl_bio == NULL) {
107 SSL_free(ssl);
108 free(conn);
109 return NULL;
110 }
111
112 if (BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE) <= 0) {
113 SSL_free(ssl);
114 BIO_free(ssl_bio);
115 return NULL;
116 }
117
118#ifdef USE_QUIC
119 /* Configure ALPN, which is required for QUIC. */
120 if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn))) {
121 /* Note: SSL_set_alpn_protos returns 1 for failure. */
122 SSL_free(ssl);
123 BIO_free(ssl_bio);
124 return NULL;
125 }
126#endif
127
128 conn->ssl_bio = ssl_bio;
129 conn->net_bio = net_bio;
130 return conn;
131}
132
133/*
134 * Non-blocking transmission.
135 *
136 * Returns -1 on error. Returns -2 if the function would block (corresponds to
137 * EWOULDBLOCK).
138 */
139int tx(APP_CONN *conn, const void *buf, int buf_len)
140{
141 int rc, l;
142
143 l = BIO_write(conn->ssl_bio, buf, buf_len);
144 if (l <= 0) {
145 rc = SSL_get_error(conn->ssl, l);
146 switch (rc) {
147 case SSL_ERROR_WANT_READ:
148 conn->tx_need_rx = 1;
149 case SSL_ERROR_WANT_CONNECT:
150 case SSL_ERROR_WANT_WRITE:
151 return -2;
152 default:
153 return -1;
154 }
155 } else {
156 conn->tx_need_rx = 0;
157 }
158
159 return l;
160}
161
162/*
163 * Non-blocking reception.
164 *
165 * Returns -1 on error. Returns -2 if the function would block (corresponds to
166 * EWOULDBLOCK).
167 */
168int rx(APP_CONN *conn, void *buf, int buf_len)
169{
170 int rc, l;
171
172 l = BIO_read(conn->ssl_bio, buf, buf_len);
173 if (l <= 0) {
174 rc = SSL_get_error(conn->ssl, l);
175 switch (rc) {
176 case SSL_ERROR_WANT_WRITE:
177 conn->rx_need_tx = 1;
178 case SSL_ERROR_WANT_READ:
179 return -2;
180 default:
181 return -1;
182 }
183 } else {
184 conn->rx_need_tx = 0;
185 }
186
187 return l;
188}
189
190/*
191 * Called to get data which has been enqueued for transmission to the network
192 * by OpenSSL. For QUIC, this always outputs a single datagram.
193 *
194 * IMPORTANT (QUIC): If buf_len is inadequate to hold the datagram, it is truncated
195 * (similar to read(2)). A buffer size of at least 1472 must be used by default
196 * to guarantee this does not occur.
197 */
198int read_net_tx(APP_CONN *conn, void *buf, int buf_len)
199{
200 return BIO_read(conn->net_bio, buf, buf_len);
201}
202
203/*
204 * Called to feed data which has been received from the network to OpenSSL.
205 *
206 * QUIC: buf must contain the entirety of a single datagram. It will be consumed
207 * entirely (return value == buf_len) or not at all.
208 */
209int write_net_rx(APP_CONN *conn, const void *buf, int buf_len)
210{
211 return BIO_write(conn->net_bio, buf, buf_len);
212}
213
214/*
215 * Determine how much data can be written to the network RX BIO.
216 */
217size_t net_rx_space(APP_CONN *conn)
218{
219 return BIO_ctrl_get_write_guarantee(conn->net_bio);
220}
221
222/*
223 * Determine how much data is currently queued for transmission in the network
224 * TX BIO.
225 */
226size_t net_tx_avail(APP_CONN *conn)
227{
228 return BIO_ctrl_pending(conn->net_bio);
229}
230
231/*
232 * These functions returns zero or more of:
233 *
234 * POLLIN: The SSL state machine is interested in socket readability events.
235 *
236 * POLLOUT: The SSL state machine is interested in socket writeability events.
237 *
238 * POLLERR: The SSL state machine is interested in socket error events.
239 *
240 * get_conn_pending_tx returns events which may cause SSL_write to make
241 * progress and get_conn_pending_rx returns events which may cause SSL_read
242 * to make progress.
243 */
244int get_conn_pending_tx(APP_CONN *conn)
245{
246#ifdef USE_QUIC
247 return (SSL_net_read_desired(conn->ssl) ? POLLIN : 0)
248 | (SSL_net_write_desired(conn->ssl) ? POLLOUT : 0)
249 | POLLERR;
250#else
251 return (conn->tx_need_rx ? POLLIN : 0) | POLLOUT | POLLERR;
252#endif
253}
254
255int get_conn_pending_rx(APP_CONN *conn)
256{
257#ifdef USE_QUIC
258 return get_conn_pending_tx(conn);
259#else
260 return (conn->rx_need_tx ? POLLOUT : 0) | POLLIN | POLLERR;
261#endif
262}
263
264/*
265 * The application wants to close the connection and free bookkeeping
266 * structures.
267 */
268void teardown(APP_CONN *conn)
269{
270 BIO_free_all(conn->ssl_bio);
271 BIO_free_all(conn->net_bio);
272 free(conn);
273}
274
275/*
276 * The application is shutting down and wants to free a previously
277 * created SSL_CTX.
278 */
279void teardown_ctx(SSL_CTX *ctx)
280{
281 SSL_CTX_free(ctx);
282}
283
284/*
285 * ============================================================================
286 * Example driver for the above code. This is just to demonstrate that the code
287 * works and is not intended to be representative of a real application.
288 */
289#include <sys/types.h>
290#include <sys/socket.h>
291#include <sys/signal.h>
292#include <netdb.h>
293#include <unistd.h>
294#include <fcntl.h>
295#include <errno.h>
296
297static int pump(APP_CONN *conn, int fd, int events, int timeout)
298{
299 int l, l2;
300 char buf[2048]; /* QUIC: would need to be changed if < 1472 */
301 size_t wspace;
302 struct pollfd pfd = {0};
303
304 pfd.fd = fd;
305 pfd.events = (events & (POLLIN | POLLERR));
306 if (net_rx_space(conn) == 0)
307 pfd.events &= ~POLLIN;
308 if (net_tx_avail(conn) > 0)
309 pfd.events |= POLLOUT;
310
311 if ((pfd.events & (POLLIN|POLLOUT)) == 0)
312 return 1;
313
314 if (poll(&pfd, 1, timeout) == 0)
315 return -1;
316
317 if (pfd.revents & POLLIN) {
318 while ((wspace = net_rx_space(conn)) > 0) {
319 l = read(fd, buf, wspace > sizeof(buf) ? sizeof(buf) : wspace);
320 if (l <= 0) {
321 switch (errno) {
322 case EAGAIN:
323 goto stop;
324 default:
325 if (l == 0) /* EOF */
326 goto stop;
327
328 fprintf(stderr, "error on read: %d\n", errno);
329 return -1;
330 }
331 break;
332 }
333 l2 = write_net_rx(conn, buf, l);
334 if (l2 < l)
335 fprintf(stderr, "short write %d %d\n", l2, l);
336 } stop:;
337 }
338
339 if (pfd.revents & POLLOUT) {
340 for (;;) {
341 l = read_net_tx(conn, buf, sizeof(buf));
342 if (l <= 0)
343 break;
344 l2 = write(fd, buf, l);
345 if (l2 < l)
346 fprintf(stderr, "short read %d %d\n", l2, l);
347 }
348 }
349
350 return 1;
351}
352
353int main(int argc, char **argv)
354{
355 int rc, fd = -1, res = 1;
356 static char tx_msg[300];
357 const char *tx_p = tx_msg;
358 char rx_buf[2048];
359 int l, tx_len;
360 int timeout = 2000 /* ms */;
361 APP_CONN *conn = NULL;
362 struct addrinfo hints = {0}, *result = NULL;
363 SSL_CTX *ctx = NULL;
364
365 if (argc < 3) {
366 fprintf(stderr, "usage: %s host port\n", argv[0]);
367 goto fail;
368 }
369
370 tx_len = snprintf(tx_msg, sizeof(tx_msg),
371 "GET / HTTP/1.0\r\nHost: %s\r\n\r\n",
372 argv[1]);
373
374 ctx = create_ssl_ctx();
375 if (ctx == NULL) {
376 fprintf(stderr, "cannot create SSL context\n");
377 goto fail;
378 }
379
380 hints.ai_family = AF_INET;
381 hints.ai_socktype = SOCK_STREAM;
382 hints.ai_flags = AI_PASSIVE;
383 rc = getaddrinfo(argv[1], argv[2], &hints, &result);
384 if (rc < 0) {
385 fprintf(stderr, "cannot resolve\n");
386 goto fail;
387 }
388
389 signal(SIGPIPE, SIG_IGN);
390
391#ifdef USE_QUIC
392 fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
393#else
394 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
395#endif
396 if (fd < 0) {
397 fprintf(stderr, "cannot create socket\n");
398 goto fail;
399 }
400
401 rc = connect(fd, result->ai_addr, result->ai_addrlen);
402 if (rc < 0) {
403 fprintf(stderr, "cannot connect\n");
404 goto fail;
405 }
406
407 rc = fcntl(fd, F_SETFL, O_NONBLOCK);
408 if (rc < 0) {
409 fprintf(stderr, "cannot make socket nonblocking\n");
410 goto fail;
411 }
412
413 conn = new_conn(ctx, argv[1]);
414 if (conn == NULL) {
415 fprintf(stderr, "cannot establish connection\n");
416 goto fail;
417 }
418
419 /* TX */
420 while (tx_len != 0) {
421 l = tx(conn, tx_p, tx_len);
422 if (l > 0) {
423 tx_p += l;
424 tx_len -= l;
425 } else if (l == -1) {
426 fprintf(stderr, "tx error\n");
427 } else if (l == -2) {
428 if (pump(conn, fd, get_conn_pending_tx(conn), timeout) != 1) {
429 fprintf(stderr, "pump error\n");
430 goto fail;
431 }
432 }
433 }
434
435 /* RX */
436 for (;;) {
437 l = rx(conn, rx_buf, sizeof(rx_buf));
438 if (l > 0) {
439 fwrite(rx_buf, 1, l, stdout);
440 } else if (l == -1) {
441 break;
442 } else if (l == -2) {
443 if (pump(conn, fd, get_conn_pending_rx(conn), timeout) != 1) {
444 fprintf(stderr, "pump error\n");
445 goto fail;
446 }
447 }
448 }
449
450 res = 0;
451fail:
452 if (conn != NULL)
453 teardown(conn);
454 if (ctx != NULL)
455 teardown_ctx(ctx);
456 if (result != NULL)
457 freeaddrinfo(result);
458 return res;
459}
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