VirtualBox

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