1 #include <sys/poll.h>
2 #include <openssl/ssl.h>
3
4 /*
5 * Demo 2: Client — Managed Connection — Asynchronous Nonblocking
6 * ==============================================================
7 *
8 * This is an example of (part of) an application which uses libssl in an
9 * asynchronous, nonblocking fashion. The functions show all interactions with
10 * libssl the application makes, and would hypothetically be linked into a
11 * larger application.
12 *
13 * In this example, libssl still makes syscalls directly using an fd, which is
14 * configured in nonblocking mode. As such, the application can still be
15 * abstracted from the details of what that fd is (is it a TCP socket? is it a
16 * UDP socket?); this code passes the application an fd and the application
17 * simply calls back into this code when poll()/etc. indicates it is ready.
18 */
19 typedef struct app_conn_st {
20 SSL *ssl;
21 BIO *ssl_bio;
22 int rx_need_tx, tx_need_rx;
23 } APP_CONN;
24
25 /*
26 * The application is initializing and wants an SSL_CTX which it will use for
27 * some number of outgoing connections, which it creates in subsequent calls to
28 * new_conn. The application may also call this function multiple times to
29 * create multiple SSL_CTX.
30 */
create_ssl_ctx(void)31 SSL_CTX *create_ssl_ctx(void)
32 {
33 SSL_CTX *ctx;
34
35 ctx = SSL_CTX_new(TLS_client_method());
36 if (ctx == NULL)
37 return NULL;
38
39 /* Enable trust chain verification. */
40 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
41
42 /* Load default root CA store. */
43 if (SSL_CTX_set_default_verify_paths(ctx) == 0) {
44 SSL_CTX_free(ctx);
45 return NULL;
46 }
47
48 return ctx;
49 }
50
51 /*
52 * The application wants to create a new outgoing connection using a given
53 * SSL_CTX.
54 *
55 * hostname is a string like "openssl.org:443" or "[::1]:443".
56 */
new_conn(SSL_CTX * ctx,const char * hostname)57 APP_CONN *new_conn(SSL_CTX *ctx, const char *hostname)
58 {
59 APP_CONN *conn;
60 BIO *out, *buf;
61 SSL *ssl = NULL;
62 const char *bare_hostname;
63
64 conn = calloc(1, sizeof(APP_CONN));
65 if (conn == NULL)
66 return NULL;
67
68 out = BIO_new_ssl_connect(ctx);
69 if (out == NULL) {
70 free(conn);
71 return NULL;
72 }
73
74 if (BIO_get_ssl(out, &ssl) == 0) {
75 BIO_free_all(out);
76 free(conn);
77 return NULL;
78 }
79
80 buf = BIO_new(BIO_f_buffer());
81 if (buf == NULL) {
82 BIO_free_all(out);
83 free(conn);
84 return NULL;
85 }
86
87 BIO_push(out, buf);
88
89 if (BIO_set_conn_hostname(out, hostname) == 0) {
90 BIO_free_all(out);
91 free(conn);
92 return NULL;
93 }
94
95 /* Returns the parsed hostname extracted from the hostname:port string. */
96 bare_hostname = BIO_get_conn_hostname(out);
97 if (bare_hostname == NULL) {
98 BIO_free_all(out);
99 free(conn);
100 return NULL;
101 }
102
103 /* Tell the SSL object the hostname to check certificates against. */
104 if (SSL_set1_host(ssl, bare_hostname) <= 0) {
105 BIO_free_all(out);
106 free(conn);
107 return NULL;
108 }
109
110 /* Make the BIO nonblocking. */
111 BIO_set_nbio(out, 1);
112
113 conn->ssl_bio = out;
114 return conn;
115 }
116
117 /*
118 * Non-blocking transmission.
119 *
120 * Returns -1 on error. Returns -2 if the function would block (corresponds to
121 * EWOULDBLOCK).
122 */
tx(APP_CONN * conn,const void * buf,int buf_len)123 int tx(APP_CONN *conn, const void *buf, int buf_len)
124 {
125 int l;
126
127 conn->tx_need_rx = 0;
128
129 l = BIO_write(conn->ssl_bio, buf, buf_len);
130 if (l <= 0) {
131 if (BIO_should_retry(conn->ssl_bio)) {
132 conn->tx_need_rx = BIO_should_read(conn->ssl_bio);
133 return -2;
134 } else {
135 return -1;
136 }
137 }
138
139 return l;
140 }
141
142 /*
143 * Non-blocking reception.
144 *
145 * Returns -1 on error. Returns -2 if the function would block (corresponds to
146 * EWOULDBLOCK).
147 */
rx(APP_CONN * conn,void * buf,int buf_len)148 int rx(APP_CONN *conn, void *buf, int buf_len)
149 {
150 int l;
151
152 conn->rx_need_tx = 0;
153
154 l = BIO_read(conn->ssl_bio, buf, buf_len);
155 if (l <= 0) {
156 if (BIO_should_retry(conn->ssl_bio)) {
157 conn->rx_need_tx = BIO_should_write(conn->ssl_bio);
158 return -2;
159 } else {
160 return -1;
161 }
162 }
163
164 return l;
165 }
166
167 /*
168 * The application wants to know a fd it can poll on to determine when the
169 * SSL state machine needs to be pumped.
170 */
get_conn_fd(APP_CONN * conn)171 int get_conn_fd(APP_CONN *conn)
172 {
173 return BIO_get_fd(conn->ssl_bio, NULL);
174 }
175
176 /*
177 * These functions returns zero or more of:
178 *
179 * POLLIN: The SSL state machine is interested in socket readability events.
180 *
181 * POLLOUT: The SSL state machine is interested in socket writeability events.
182 *
183 * POLLERR: The SSL state machine is interested in socket error events.
184 *
185 * get_conn_pending_tx returns events which may cause SSL_write to make
186 * progress and get_conn_pending_rx returns events which may cause SSL_read
187 * to make progress.
188 */
get_conn_pending_tx(APP_CONN * conn)189 int get_conn_pending_tx(APP_CONN *conn)
190 {
191 return (conn->tx_need_rx ? POLLIN : 0) | POLLOUT | POLLERR;
192 }
193
get_conn_pending_rx(APP_CONN * conn)194 int get_conn_pending_rx(APP_CONN *conn)
195 {
196 return (conn->rx_need_tx ? POLLOUT : 0) | POLLIN | POLLERR;
197 }
198
199 /*
200 * The application wants to close the connection and free bookkeeping
201 * structures.
202 */
teardown(APP_CONN * conn)203 void teardown(APP_CONN *conn)
204 {
205 BIO_free_all(conn->ssl_bio);
206 free(conn);
207 }
208
209 /*
210 * The application is shutting down and wants to free a previously
211 * created SSL_CTX.
212 */
teardown_ctx(SSL_CTX * ctx)213 void teardown_ctx(SSL_CTX *ctx)
214 {
215 SSL_CTX_free(ctx);
216 }
217
218 /*
219 * ============================================================================
220 * Example driver for the above code. This is just to demonstrate that the code
221 * works and is not intended to be representative of a real application.
222 */
main(int argc,char ** argv)223 int main(int argc, char **argv)
224 {
225 const char tx_msg[] = "GET / HTTP/1.0\r\nHost: www.openssl.org\r\n\r\n";
226 const char *tx_p = tx_msg;
227 char rx_buf[2048];
228 int res = 1, l, tx_len = sizeof(tx_msg)-1;
229 int timeout = 2000 /* ms */;
230 APP_CONN *conn = NULL;
231 SSL_CTX *ctx;
232
233 ctx = create_ssl_ctx();
234 if (ctx == NULL) {
235 fprintf(stderr, "cannot create SSL context\n");
236 goto fail;
237 }
238
239 conn = new_conn(ctx, "www.openssl.org:443");
240 if (conn == NULL) {
241 fprintf(stderr, "cannot establish connection\n");
242 goto fail;
243 }
244
245 /* TX */
246 while (tx_len != 0) {
247 l = tx(conn, tx_p, tx_len);
248 if (l > 0) {
249 tx_p += l;
250 tx_len -= l;
251 } else if (l == -1) {
252 fprintf(stderr, "tx error\n");
253 } else if (l == -2) {
254 struct pollfd pfd = {0};
255 pfd.fd = get_conn_fd(conn);
256 pfd.events = get_conn_pending_tx(conn);
257 if (poll(&pfd, 1, timeout) == 0) {
258 fprintf(stderr, "tx timeout\n");
259 goto fail;
260 }
261 }
262 }
263
264 /* RX */
265 for (;;) {
266 l = rx(conn, rx_buf, sizeof(rx_buf));
267 if (l > 0) {
268 fwrite(rx_buf, 1, l, stdout);
269 } else if (l == -1) {
270 break;
271 } else if (l == -2) {
272 struct pollfd pfd = {0};
273 pfd.fd = get_conn_fd(conn);
274 pfd.events = get_conn_pending_rx(conn);
275 if (poll(&pfd, 1, timeout) == 0) {
276 fprintf(stderr, "rx timeout\n");
277 goto fail;
278 }
279 }
280 }
281
282 res = 0;
283 fail:
284 if (conn != NULL)
285 teardown(conn);
286 if (ctx != NULL)
287 teardown_ctx(ctx);
288 return res;
289 }
290