Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/openssl/demos/guide/tls-client-non-block.c
34876 views
1
/*
2
* Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
3
*
4
* Licensed under the Apache License 2.0 (the "License"). You may not use
5
* this file except in compliance with the License. You can obtain a copy
6
* in the file LICENSE in the source distribution or at
7
* https://www.openssl.org/source/license.html
8
*/
9
10
/*
11
* NB: Changes to this file should also be reflected in
12
* doc/man7/ossl-guide-tls-client-non-block.pod
13
*/
14
15
#include <string.h>
16
17
/* Include the appropriate header file for SOCK_STREAM */
18
#ifdef _WIN32 /* Windows */
19
# include <winsock2.h>
20
#else /* Linux/Unix */
21
# include <sys/socket.h>
22
# include <sys/select.h>
23
#endif
24
25
#include <openssl/bio.h>
26
#include <openssl/ssl.h>
27
#include <openssl/err.h>
28
29
/* Helper function to create a BIO connected to the server */
30
static BIO *create_socket_bio(const char *hostname, const char *port, int family)
31
{
32
int sock = -1;
33
BIO_ADDRINFO *res;
34
const BIO_ADDRINFO *ai = NULL;
35
BIO *bio;
36
37
/*
38
* Lookup IP address info for the server.
39
*/
40
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_STREAM, 0,
41
&res))
42
return NULL;
43
44
/*
45
* Loop through all the possible addresses for the server and find one
46
* we can connect to.
47
*/
48
for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
49
/*
50
* Create a TCP socket. We could equally use non-OpenSSL calls such
51
* as "socket" here for this and the subsequent connect and close
52
* functions. But for portability reasons and also so that we get
53
* errors on the OpenSSL stack in the event of a failure we use
54
* OpenSSL's versions of these functions.
55
*/
56
sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_STREAM, 0, 0);
57
if (sock == -1)
58
continue;
59
60
/* Connect the socket to the server's address */
61
if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), BIO_SOCK_NODELAY)) {
62
BIO_closesocket(sock);
63
sock = -1;
64
continue;
65
}
66
67
/* Set to nonblocking mode */
68
if (!BIO_socket_nbio(sock, 1)) {
69
sock = -1;
70
continue;
71
}
72
73
/* We have a connected socket so break out of the loop */
74
break;
75
}
76
77
/* Free the address information resources we allocated earlier */
78
BIO_ADDRINFO_free(res);
79
80
/* If sock is -1 then we've been unable to connect to the server */
81
if (sock == -1)
82
return NULL;
83
84
/* Create a BIO to wrap the socket */
85
bio = BIO_new(BIO_s_socket());
86
if (bio == NULL) {
87
BIO_closesocket(sock);
88
return NULL;
89
}
90
91
/*
92
* Associate the newly created BIO with the underlying socket. By
93
* passing BIO_CLOSE here the socket will be automatically closed when
94
* the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
95
* case you must close the socket explicitly when it is no longer
96
* needed.
97
*/
98
BIO_set_fd(bio, sock, BIO_CLOSE);
99
100
return bio;
101
}
102
103
static void wait_for_activity(SSL *ssl, int write)
104
{
105
fd_set fds;
106
int width, sock;
107
108
/* Get hold of the underlying file descriptor for the socket */
109
sock = SSL_get_fd(ssl);
110
111
FD_ZERO(&fds);
112
FD_SET(sock, &fds);
113
width = sock + 1;
114
115
/*
116
* Wait until the socket is writeable or readable. We use select here
117
* for the sake of simplicity and portability, but you could equally use
118
* poll/epoll or similar functions
119
*
120
* NOTE: For the purposes of this demonstration code this effectively
121
* makes this demo block until it has something more useful to do. In a
122
* real application you probably want to go and do other work here (e.g.
123
* update a GUI, or service other connections).
124
*
125
* Let's say for example that you want to update the progress counter on
126
* a GUI every 100ms. One way to do that would be to add a 100ms timeout
127
* in the last parameter to "select" below. Then, when select returns,
128
* you check if it did so because of activity on the file descriptors or
129
* because of the timeout. If it is due to the timeout then update the
130
* GUI and then restart the "select".
131
*/
132
if (write)
133
select(width, NULL, &fds, NULL, NULL);
134
else
135
select(width, &fds, NULL, NULL, NULL);
136
}
137
138
static int handle_io_failure(SSL *ssl, int res)
139
{
140
switch (SSL_get_error(ssl, res)) {
141
case SSL_ERROR_WANT_READ:
142
/* Temporary failure. Wait until we can read and try again */
143
wait_for_activity(ssl, 0);
144
return 1;
145
146
case SSL_ERROR_WANT_WRITE:
147
/* Temporary failure. Wait until we can write and try again */
148
wait_for_activity(ssl, 1);
149
return 1;
150
151
case SSL_ERROR_ZERO_RETURN:
152
/* EOF */
153
return 0;
154
155
case SSL_ERROR_SYSCALL:
156
return -1;
157
158
case SSL_ERROR_SSL:
159
/*
160
* If the failure is due to a verification error we can get more
161
* information about it from SSL_get_verify_result().
162
*/
163
if (SSL_get_verify_result(ssl) != X509_V_OK)
164
printf("Verify error: %s\n",
165
X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
166
return -1;
167
168
default:
169
return -1;
170
}
171
}
172
173
/*
174
* Simple application to send a basic HTTP/1.0 request to a server and
175
* print the response on the screen.
176
*/
177
int main(int argc, char *argv[])
178
{
179
SSL_CTX *ctx = NULL;
180
SSL *ssl = NULL;
181
BIO *bio = NULL;
182
int res = EXIT_FAILURE;
183
int ret;
184
const char *request_start = "GET / HTTP/1.0\r\nConnection: close\r\nHost: ";
185
const char *request_end = "\r\n\r\n";
186
size_t written, readbytes = 0;
187
char buf[160];
188
int eof = 0;
189
char *hostname, *port;
190
int argnext = 1;
191
int ipv6 = 0;
192
193
if (argc < 3) {
194
printf("Usage: tls-client-non-block [-6] hostname port\n");
195
goto end;
196
}
197
198
if (!strcmp(argv[argnext], "-6")) {
199
if (argc < 4) {
200
printf("Usage: tls-client-non-block [-6] hostname port\n");
201
goto end;
202
}
203
ipv6 = 1;
204
argnext++;
205
}
206
207
hostname = argv[argnext++];
208
port = argv[argnext];
209
210
/*
211
* Create an SSL_CTX which we can use to create SSL objects from. We
212
* want an SSL_CTX for creating clients so we use TLS_client_method()
213
* here.
214
*/
215
ctx = SSL_CTX_new(TLS_client_method());
216
if (ctx == NULL) {
217
printf("Failed to create the SSL_CTX\n");
218
goto end;
219
}
220
221
/*
222
* Configure the client to abort the handshake if certificate
223
* verification fails. Virtually all clients should do this unless you
224
* really know what you are doing.
225
*/
226
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
227
228
/* Use the default trusted certificate store */
229
if (!SSL_CTX_set_default_verify_paths(ctx)) {
230
printf("Failed to set the default trusted certificate store\n");
231
goto end;
232
}
233
234
/*
235
* TLSv1.1 or earlier are deprecated by IETF and are generally to be
236
* avoided if possible. We require a minimum TLS version of TLSv1.2.
237
*/
238
if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) {
239
printf("Failed to set the minimum TLS protocol version\n");
240
goto end;
241
}
242
243
/* Create an SSL object to represent the TLS connection */
244
ssl = SSL_new(ctx);
245
if (ssl == NULL) {
246
printf("Failed to create the SSL object\n");
247
goto end;
248
}
249
250
/*
251
* Create the underlying transport socket/BIO and associate it with the
252
* connection.
253
*/
254
bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET);
255
if (bio == NULL) {
256
printf("Failed to crete the BIO\n");
257
goto end;
258
}
259
SSL_set_bio(ssl, bio, bio);
260
261
/*
262
* Tell the server during the handshake which hostname we are attempting
263
* to connect to in case the server supports multiple hosts.
264
*/
265
if (!SSL_set_tlsext_host_name(ssl, hostname)) {
266
printf("Failed to set the SNI hostname\n");
267
goto end;
268
}
269
270
/*
271
* Ensure we check during certificate verification that the server has
272
* supplied a certificate for the hostname that we were expecting.
273
* Virtually all clients should do this unless you really know what you
274
* are doing.
275
*/
276
if (!SSL_set1_host(ssl, hostname)) {
277
printf("Failed to set the certificate verification hostname");
278
goto end;
279
}
280
281
/* Do the handshake with the server */
282
while ((ret = SSL_connect(ssl)) != 1) {
283
if (handle_io_failure(ssl, ret) == 1)
284
continue; /* Retry */
285
printf("Failed to connect to server\n");
286
goto end; /* Cannot retry: error */
287
}
288
289
/* Write an HTTP GET request to the peer */
290
while (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) {
291
if (handle_io_failure(ssl, 0) == 1)
292
continue; /* Retry */
293
printf("Failed to write start of HTTP request\n");
294
goto end; /* Cannot retry: error */
295
}
296
while (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) {
297
if (handle_io_failure(ssl, 0) == 1)
298
continue; /* Retry */
299
printf("Failed to write hostname in HTTP request\n");
300
goto end; /* Cannot retry: error */
301
}
302
while (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
303
if (handle_io_failure(ssl, 0) == 1)
304
continue; /* Retry */
305
printf("Failed to write end of HTTP request\n");
306
goto end; /* Cannot retry: error */
307
}
308
309
do {
310
/*
311
* Get up to sizeof(buf) bytes of the response. We keep reading until
312
* the server closes the connection.
313
*/
314
while (!eof && !SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) {
315
switch (handle_io_failure(ssl, 0)) {
316
case 1:
317
continue; /* Retry */
318
case 0:
319
eof = 1;
320
continue;
321
case -1:
322
default:
323
printf("Failed reading remaining data\n");
324
goto end; /* Cannot retry: error */
325
}
326
}
327
/*
328
* OpenSSL does not guarantee that the returned data is a string or
329
* that it is NUL terminated so we use fwrite() to write the exact
330
* number of bytes that we read. The data could be non-printable or
331
* have NUL characters in the middle of it. For this simple example
332
* we're going to print it to stdout anyway.
333
*/
334
if (!eof)
335
fwrite(buf, 1, readbytes, stdout);
336
} while (!eof);
337
/* In case the response didn't finish with a newline we add one now */
338
printf("\n");
339
340
/*
341
* The peer already shutdown gracefully (we know this because of the
342
* SSL_ERROR_ZERO_RETURN (i.e. EOF) above). We should do the same back.
343
*/
344
while ((ret = SSL_shutdown(ssl)) != 1) {
345
if (ret < 0 && handle_io_failure(ssl, ret) == 1)
346
continue; /* Retry */
347
/*
348
* ret == 0 is unexpected here because that means "we've sent a
349
* close_notify and we're waiting for one back". But we already know
350
* we got one from the peer because of the SSL_ERROR_ZERO_RETURN
351
* (i.e. EOF) above.
352
*/
353
printf("Error shutting down\n");
354
goto end; /* Cannot retry: error */
355
}
356
357
/* Success! */
358
res = EXIT_SUCCESS;
359
end:
360
/*
361
* If something bad happened then we will dump the contents of the
362
* OpenSSL error stack to stderr. There might be some useful diagnostic
363
* information there.
364
*/
365
if (res == EXIT_FAILURE)
366
ERR_print_errors_fp(stderr);
367
368
/*
369
* Free the resources we allocated. We do not free the BIO object here
370
* because ownership of it was immediately transferred to the SSL object
371
* via SSL_set_bio(). The BIO will be freed when we free the SSL object.
372
*/
373
SSL_free(ssl);
374
SSL_CTX_free(ctx);
375
return res;
376
}
377
378