Adding in curl and openssl repos

This commit is contained in:
2025-08-14 12:09:30 -04:00
parent af2117b574
commit 0ace93e303
21174 changed files with 3607720 additions and 2 deletions

View File

@@ -0,0 +1,42 @@
#
# To run the demos when linked with a shared library (default) ensure that
# libcrypto and libssl are on the library path. For example:
#
# LD_LIBRARY_PATH=../.. ./tls-client-block www.example.com 443
TESTS = tls-client-block \
tls-server-block \
quic-client-block \
quic-multi-stream \
tls-client-non-block \
quic-client-non-block
CFLAGS = -I../../include -g -Wall
LDFLAGS = -L../..
LDLIBS = -lcrypto -lssl
all: $(TESTS) chain
tls-client-block: tls-client-block.o
tls-server-block: tls-server-block.o
quic-client-block: quic-client-block.o
quic-multi-stream: quic-multi-stream.o
tls-client-non-block: tls-client-non-block.o
quic-client-non-block: quic-client-non-block.o
chain: chain.pem
pkey.pem:
openssl genpkey -algorithm rsa -out pkey.pem -pkeyopt rsa_keygen_bits:2048
chain.pem: pkey.pem
openssl req -x509 -new -key pkey.pem -days 36500 -subj / -out chain.pem
$(TESTS):
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS)
clean:
$(RM) $(TESTS) *.o
.PHONY: test chain
test: all
@echo "\nTLS and QUIC tests:"
@echo "skipped"

View File

@@ -0,0 +1,77 @@
The OpenSSL Guide Demos
=======================
The demos in this directory are the complete source code for the applications
developed in the OpenSSL Guide tutorials. Refer to the various tutorial pages in
the [guide] for an extensive discussion on the demos available here.
They must be built before they can be run. An example UNIX style Makefile is
supplied. Just type "make" from this directory on a Linux/UNIX system.
Running the TLS Demos
---------------------
To run the demos when linked with a shared library (default) ensure that
libcrypto and libssl are on the library path. For example, assuming you have
already built OpenSSL from this source and in the default location then to run
the tls-client-block demo do this:
LD_LIBRARY_PATH=../.. ./tls-client-block hostname port
In the above replace "hostname" and "port" with the hostname and the port number
of the server you are connecting to.
The above assumes that your default trusted certificate store containing trusted
CA certificates has been properly setup and configured as described on the
[TLS Introduction] page.
You can run a test server to try out these demos using the "openssl s_server"
command line utility and using the test server certificate and key provided in
this directory. For example:
LD_LIBRARY_PATH=../.. ../../apps/openssl s_server -www -accept localhost:4443 -cert servercert.pem -key serverkey.pem
The test server certificate in this directory will use a CA that will not be in
your default trusted certificate store. The CA certificate to use is also
available in this directory. To use it you can override the default trusted
certificate store like this:
SSL_CERT_FILE=rootcert.pem LD_LIBRARY_PATH=../.. ./tls-client-block localhost 4443
If the above command is successful it will connect to the test "s_server" and
send a simple HTTP request to it. The server will respond with a page of
information giving details about the TLS connection that was used.
Note that the test server certificate used here is only suitable for use on
"localhost".
The tls-client-non-block demo can be run in exactly the same way. Just replace
"tls-client-block" in the above example commands with "tls-client-non-block".
Running the QUIC Demos
----------------------
The QUIC demos can be run in a very similar way to the TLS demos. However, a
different server implementation will need to be used.
The OpenSSL source distribution includes a test QUIC server implementation for
use with the demos. Note that, although this server does get built when building
OpenSSL from source, it does not get installed via "make install". After
building OpenSSL from source you will find the "quicserver" utility in the
"util" sub-directory of the top of the build tree. This server utility is not
suitable for production use and exists for test purposes only. It will be
removed from a future version of OpenSSL.
While in the demos directory the quic server can be run like this:
./../util/quicserver localhost 4443 servercert.pem serverkey.pem
The QUIC demos can then be run in the same was as the TLS demos. For example
to run the quic-client-block demo:
SSL_CERT_FILE=rootcert.pem LD_LIBRARY_PATH=../.. ./quic-client-block localhost 4443
<!-- Links -->
[guide]: https://www.openssl.org/docs/manmaster/man7/ossl-guide-introduction.html
[TLS Introduction]: https://www.openssl.org/docs/manmaster/man7/ossl-guide-tls-introduction.html

View File

@@ -0,0 +1,31 @@
#
# To run the demos when linked with a shared library (default) ensure that
# libcrypto and libssl are on the library path. For example:
#
# LD_LIBRARY_PATH=../.. ./tls-client-block www.example.com 443
PROGRAMS{noinst} = tls-client-block \
quic-client-block \
quic-multi-stream \
tls-client-non-block \
quic-client-non-block
INCLUDE[tls-client-block]=../../include
SOURCE[tls-client-block]=tls-client-block.c
DEPEND[tls-client-block]=../../libcrypto ../../libssl
INCLUDE[quic-client-block]=../../include
SOURCE[quic-client-block]=quic-client-block.c
DEPEND[quic-client-block]=../../libcrypto ../../libssl
INCLUDE[quic-multi-stream]=../../include
SOURCE[quic-multi-stream]=quic-multi-stream.c
DEPEND[quic-multi-stream]=../../libcrypto ../../libssl
INCLUDE[tls-client-non-block]=../../include
SOURCE[tls-client-non-block]=tls-client-non-block.c
DEPEND[tls-client-non-block]=../../libcrypto ../../libssl
INCLUDE[quic-client-non-block]=../../include
SOURCE[quic-client-non-block]=quic-client-non-block.c
DEPEND[quic-client-non-block]=../../libcrypto ../../libssl

View File

@@ -0,0 +1,341 @@
/*
* Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* NB: Changes to this file should also be reflected in
* doc/man7/ossl-guide-quic-client-block.pod
*/
#include <string.h>
/* Include the appropriate header file for SOCK_DGRAM */
#ifdef _WIN32 /* Windows */
# include <winsock2.h>
#else /* Linux/Unix */
# include <sys/socket.h>
#endif
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/* Helper function to create a BIO connected to the server */
static BIO *create_socket_bio(const char *hostname, const char *port,
int family, BIO_ADDR **peer_addr)
{
int sock = -1;
BIO_ADDRINFO *res;
const BIO_ADDRINFO *ai = NULL;
BIO *bio;
/*
* Lookup IP address info for the server.
*/
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0,
&res))
return NULL;
/*
* Loop through all the possible addresses for the server and find one
* we can connect to.
*/
for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
/*
* Create a UDP socket. We could equally use non-OpenSSL calls such
* as "socket" here for this and the subsequent connect and close
* functions. But for portability reasons and also so that we get
* errors on the OpenSSL stack in the event of a failure we use
* OpenSSL's versions of these functions.
*/
sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_DGRAM, 0, 0);
if (sock == -1)
continue;
/* Connect the socket to the server's address */
if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), 0)) {
BIO_closesocket(sock);
sock = -1;
continue;
}
/* Set to nonblocking mode */
if (!BIO_socket_nbio(sock, 1)) {
BIO_closesocket(sock);
sock = -1;
continue;
}
break;
}
if (sock != -1) {
*peer_addr = BIO_ADDR_dup(BIO_ADDRINFO_address(ai));
if (*peer_addr == NULL) {
BIO_closesocket(sock);
return NULL;
}
}
/* Free the address information resources we allocated earlier */
BIO_ADDRINFO_free(res);
/* If sock is -1 then we've been unable to connect to the server */
if (sock == -1)
return NULL;
/* Create a BIO to wrap the socket */
bio = BIO_new(BIO_s_datagram());
if (bio == NULL) {
BIO_closesocket(sock);
return NULL;
}
/*
* Associate the newly created BIO with the underlying socket. By
* passing BIO_CLOSE here the socket will be automatically closed when
* the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
* case you must close the socket explicitly when it is no longer
* needed.
*/
BIO_set_fd(bio, sock, BIO_CLOSE);
return bio;
}
/*
* Simple application to send a basic HTTP/1.0 request to a server and
* print the response on the screen. Note that HTTP/1.0 over QUIC is
* non-standard and will not typically be supported by real world servers. This
* is for demonstration purposes only.
*/
int main(int argc, char *argv[])
{
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
BIO *bio = NULL;
int res = EXIT_FAILURE;
int ret;
unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '0' };
const char *request_start = "GET / HTTP/1.0\r\nConnection: close\r\nHost: ";
const char *request_end = "\r\n\r\n";
size_t written, readbytes;
char buf[160];
BIO_ADDR *peer_addr = NULL;
char *hostname, *port;
int argnext = 1;
int ipv6 = 0;
if (argc < 3) {
printf("Usage: quic-client-block [-6] hostname port\n");
goto end;
}
if (!strcmp(argv[argnext], "-6")) {
if (argc < 4) {
printf("Usage: quic-client-block [-6] hostname port\n");
goto end;
}
ipv6 = 1;
argnext++;
}
hostname = argv[argnext++];
port = argv[argnext];
/*
* Create an SSL_CTX which we can use to create SSL objects from. We
* want an SSL_CTX for creating clients so we use
* OSSL_QUIC_client_method() here.
*/
ctx = SSL_CTX_new(OSSL_QUIC_client_method());
if (ctx == NULL) {
printf("Failed to create the SSL_CTX\n");
goto end;
}
/*
* Configure the client to abort the handshake if certificate
* verification fails. Virtually all clients should do this unless you
* really know what you are doing.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
/* Use the default trusted certificate store */
if (!SSL_CTX_set_default_verify_paths(ctx)) {
printf("Failed to set the default trusted certificate store\n");
goto end;
}
/* Create an SSL object to represent the TLS connection */
ssl = SSL_new(ctx);
if (ssl == NULL) {
printf("Failed to create the SSL object\n");
goto end;
}
/*
* Create the underlying transport socket/BIO and associate it with the
* connection.
*/
bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET, &peer_addr);
if (bio == NULL) {
printf("Failed to crete the BIO\n");
goto end;
}
SSL_set_bio(ssl, bio, bio);
/*
* Tell the server during the handshake which hostname we are attempting
* to connect to in case the server supports multiple hosts.
*/
if (!SSL_set_tlsext_host_name(ssl, hostname)) {
printf("Failed to set the SNI hostname\n");
goto end;
}
/*
* Ensure we check during certificate verification that the server has
* supplied a certificate for the hostname that we were expecting.
* Virtually all clients should do this unless you really know what you
* are doing.
*/
if (!SSL_set1_host(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
/* SSL_set_alpn_protos returns 0 for success! */
if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn)) != 0) {
printf("Failed to set the ALPN for the connection\n");
goto end;
}
/* Set the IP address of the remote peer */
if (!SSL_set1_initial_peer_addr(ssl, peer_addr)) {
printf("Failed to set the initial peer address\n");
goto end;
}
/* Do the handshake with the server */
if (SSL_connect(ssl) < 1) {
printf("Failed to connect to the server\n");
/*
* If the failure is due to a verification error we can get more
* information about it from SSL_get_verify_result().
*/
if (SSL_get_verify_result(ssl) != X509_V_OK)
printf("Verify error: %s\n",
X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
goto end;
}
/* Write an HTTP GET request to the peer */
if (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) {
printf("Failed to write start of HTTP request\n");
goto end;
}
if (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) {
printf("Failed to write hostname in HTTP request\n");
goto end;
}
if (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
printf("Failed to write end of HTTP request\n");
goto end;
}
/*
* Get up to sizeof(buf) bytes of the response. We keep reading until the
* server closes the connection.
*/
while (SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) {
/*
* OpenSSL does not guarantee that the returned data is a string or
* that it is NUL terminated so we use fwrite() to write the exact
* number of bytes that we read. The data could be non-printable or
* have NUL characters in the middle of it. For this simple example
* we're going to print it to stdout anyway.
*/
fwrite(buf, 1, readbytes, stdout);
}
/* In case the response didn't finish with a newline we add one now */
printf("\n");
/*
* Check whether we finished the while loop above normally or as the
* result of an error. The 0 argument to SSL_get_error() is the return
* code we received from the SSL_read_ex() call. It must be 0 in order
* to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN. In
* QUIC terms this means that the peer has sent FIN on the stream to
* indicate that no further data will be sent.
*/
switch (SSL_get_error(ssl, 0)) {
case SSL_ERROR_ZERO_RETURN:
/* Normal completion of the stream */
break;
case SSL_ERROR_SSL:
/*
* Some stream fatal error occurred. This could be because of a stream
* reset - or some failure occurred on the underlying connection.
*/
switch (SSL_get_stream_read_state(ssl)) {
case SSL_STREAM_STATE_RESET_REMOTE:
printf("Stream reset occurred\n");
/* The stream has been reset but the connection is still healthy. */
break;
case SSL_STREAM_STATE_CONN_CLOSED:
printf("Connection closed\n");
/* Connection is already closed. Skip SSL_shutdown() */
goto end;
default:
printf("Unknown stream failure\n");
break;
}
break;
default:
/* Some other unexpected error occurred */
printf ("Failed reading remaining data\n");
break;
}
/*
* Repeatedly call SSL_shutdown() until the connection is fully
* closed.
*/
do {
ret = SSL_shutdown(ssl);
if (ret < 0) {
printf("Error shutting down: %d\n", ret);
goto end;
}
} while (ret != 1);
/* Success! */
res = EXIT_SUCCESS;
end:
/*
* If something bad happened then we will dump the contents of the
* OpenSSL error stack to stderr. There might be some useful diagnostic
* information there.
*/
if (res == EXIT_FAILURE)
ERR_print_errors_fp(stderr);
/*
* Free the resources we allocated. We do not free the BIO object here
* because ownership of it was immediately transferred to the SSL object
* via SSL_set_bio(). The BIO will be freed when we free the SSL object.
*/
SSL_free(ssl);
SSL_CTX_free(ctx);
BIO_ADDR_free(peer_addr);
return res;
}

View File

@@ -0,0 +1,431 @@
/*
* Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* NB: Changes to this file should also be reflected in
* doc/man7/ossl-guide-quic-client-non-block.pod
*/
#include <string.h>
/* Include the appropriate header file for SOCK_DGRAM */
#ifdef _WIN32 /* Windows */
# include <winsock2.h>
#else /* Linux/Unix */
# include <sys/socket.h>
# include <sys/select.h>
#endif
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/* Helper function to create a BIO connected to the server */
static BIO *create_socket_bio(const char *hostname, const char *port,
int family, BIO_ADDR **peer_addr)
{
int sock = -1;
BIO_ADDRINFO *res;
const BIO_ADDRINFO *ai = NULL;
BIO *bio;
/*
* Lookup IP address info for the server.
*/
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0,
&res))
return NULL;
/*
* Loop through all the possible addresses for the server and find one
* we can connect to.
*/
for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
/*
* Create a UDP socket. We could equally use non-OpenSSL calls such
* as "socket" here for this and the subsequent connect and close
* functions. But for portability reasons and also so that we get
* errors on the OpenSSL stack in the event of a failure we use
* OpenSSL's versions of these functions.
*/
sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_DGRAM, 0, 0);
if (sock == -1)
continue;
/* Connect the socket to the server's address */
if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), 0)) {
BIO_closesocket(sock);
sock = -1;
continue;
}
/* Set to nonblocking mode */
if (!BIO_socket_nbio(sock, 1)) {
BIO_closesocket(sock);
sock = -1;
continue;
}
break;
}
if (sock != -1) {
*peer_addr = BIO_ADDR_dup(BIO_ADDRINFO_address(ai));
if (*peer_addr == NULL) {
BIO_closesocket(sock);
return NULL;
}
}
/* Free the address information resources we allocated earlier */
BIO_ADDRINFO_free(res);
/* If sock is -1 then we've been unable to connect to the server */
if (sock == -1)
return NULL;
/* Create a BIO to wrap the socket */
bio = BIO_new(BIO_s_datagram());
if (bio == NULL) {
BIO_closesocket(sock);
return NULL;
}
/*
* Associate the newly created BIO with the underlying socket. By
* passing BIO_CLOSE here the socket will be automatically closed when
* the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
* case you must close the socket explicitly when it is no longer
* needed.
*/
BIO_set_fd(bio, sock, BIO_CLOSE);
return bio;
}
static void wait_for_activity(SSL *ssl)
{
fd_set wfds, rfds;
int width, sock, isinfinite;
struct timeval tv;
struct timeval *tvp = NULL;
/* Get hold of the underlying file descriptor for the socket */
sock = SSL_get_fd(ssl);
FD_ZERO(&wfds);
FD_ZERO(&rfds);
/*
* Find out if we would like to write to the socket, or read from it (or
* both)
*/
if (SSL_net_write_desired(ssl))
FD_SET(sock, &wfds);
if (SSL_net_read_desired(ssl))
FD_SET(sock, &rfds);
width = sock + 1;
/*
* Find out when OpenSSL would next like to be called, regardless of
* whether the state of the underlying socket has changed or not.
*/
if (SSL_get_event_timeout(ssl, &tv, &isinfinite) && !isinfinite)
tvp = &tv;
/*
* Wait until the socket is writeable or readable. We use select here
* for the sake of simplicity and portability, but you could equally use
* poll/epoll or similar functions
*
* NOTE: For the purposes of this demonstration code this effectively
* makes this demo block until it has something more useful to do. In a
* real application you probably want to go and do other work here (e.g.
* update a GUI, or service other connections).
*
* Let's say for example that you want to update the progress counter on
* a GUI every 100ms. One way to do that would be to use the timeout in
* the last parameter to "select" below. If the tvp value is greater
* than 100ms then use 100ms instead. Then, when select returns, you
* check if it did so because of activity on the file descriptors or
* because of the timeout. If the 100ms GUI timeout has expired but the
* tvp timeout has not then go and update the GUI and then restart the
* "select" (with updated timeouts).
*/
select(width, &rfds, &wfds, NULL, tvp);
}
static int handle_io_failure(SSL *ssl, int res)
{
switch (SSL_get_error(ssl, res)) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* Temporary failure. Wait until we can read/write and try again */
wait_for_activity(ssl);
return 1;
case SSL_ERROR_ZERO_RETURN:
/* EOF */
return 0;
case SSL_ERROR_SYSCALL:
return -1;
case SSL_ERROR_SSL:
/*
* Some stream fatal error occurred. This could be because of a
* stream reset - or some failure occurred on the underlying
* connection.
*/
switch (SSL_get_stream_read_state(ssl)) {
case SSL_STREAM_STATE_RESET_REMOTE:
printf("Stream reset occurred\n");
/*
* The stream has been reset but the connection is still
* healthy.
*/
break;
case SSL_STREAM_STATE_CONN_CLOSED:
printf("Connection closed\n");
/* Connection is already closed. */
break;
default:
printf("Unknown stream failure\n");
break;
}
/*
* If the failure is due to a verification error we can get more
* information about it from SSL_get_verify_result().
*/
if (SSL_get_verify_result(ssl) != X509_V_OK)
printf("Verify error: %s\n",
X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
return -1;
default:
return -1;
}
}
/*
* Simple application to send a basic HTTP/1.0 request to a server and
* print the response on the screen. Note that HTTP/1.0 over QUIC is
* non-standard and will not typically be supported by real world servers. This
* is for demonstration purposes only.
*/
int main(int argc, char *argv[])
{
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
BIO *bio = NULL;
int res = EXIT_FAILURE;
int ret;
unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '0' };
const char *request_start = "GET / HTTP/1.0\r\nConnection: close\r\nHost: ";
const char *request_end = "\r\n\r\n";
size_t written, readbytes = 0;
char buf[160];
BIO_ADDR *peer_addr = NULL;
int eof = 0;
char *hostname, *port;
int ipv6 = 0;
int argnext = 1;
if (argc < 3) {
printf("Usage: quic-client-non-block [-6] hostname port\n");
goto end;
}
if (!strcmp(argv[argnext], "-6")) {
if (argc < 4) {
printf("Usage: quic-client-non-block [-6] hostname port\n");
goto end;
}
ipv6 = 1;
argnext++;
}
hostname = argv[argnext++];
port = argv[argnext];
/*
* Create an SSL_CTX which we can use to create SSL objects from. We
* want an SSL_CTX for creating clients so we use
* OSSL_QUIC_client_method() here.
*/
ctx = SSL_CTX_new(OSSL_QUIC_client_method());
if (ctx == NULL) {
printf("Failed to create the SSL_CTX\n");
goto end;
}
/*
* Configure the client to abort the handshake if certificate
* verification fails. Virtually all clients should do this unless you
* really know what you are doing.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
/* Use the default trusted certificate store */
if (!SSL_CTX_set_default_verify_paths(ctx)) {
printf("Failed to set the default trusted certificate store\n");
goto end;
}
/* Create an SSL object to represent the TLS connection */
ssl = SSL_new(ctx);
if (ssl == NULL) {
printf("Failed to create the SSL object\n");
goto end;
}
/*
* Create the underlying transport socket/BIO and associate it with the
* connection.
*/
bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET,
&peer_addr);
if (bio == NULL) {
printf("Failed to crete the BIO\n");
goto end;
}
SSL_set_bio(ssl, bio, bio);
/*
* Tell the server during the handshake which hostname we are attempting
* to connect to in case the server supports multiple hosts.
*/
if (!SSL_set_tlsext_host_name(ssl, hostname)) {
printf("Failed to set the SNI hostname\n");
goto end;
}
/*
* Ensure we check during certificate verification that the server has
* supplied a certificate for the hostname that we were expecting.
* Virtually all clients should do this unless you really know what you
* are doing.
*/
if (!SSL_set1_host(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
/* SSL_set_alpn_protos returns 0 for success! */
if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn)) != 0) {
printf("Failed to set the ALPN for the connection\n");
goto end;
}
/* Set the IP address of the remote peer */
if (!SSL_set1_initial_peer_addr(ssl, peer_addr)) {
printf("Failed to set the initial peer address\n");
goto end;
}
/*
* The underlying socket is always nonblocking with QUIC, but the default
* behaviour of the SSL object is still to block. We set it for nonblocking
* mode in this demo.
*/
if (!SSL_set_blocking_mode(ssl, 0)) {
printf("Failed to turn off blocking mode\n");
goto end;
}
/* Do the handshake with the server */
while ((ret = SSL_connect(ssl)) != 1) {
if (handle_io_failure(ssl, ret) == 1)
continue; /* Retry */
printf("Failed to connect to server\n");
goto end; /* Cannot retry: error */
}
/* Write an HTTP GET request to the peer */
while (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) {
if (handle_io_failure(ssl, 0) == 1)
continue; /* Retry */
printf("Failed to write start of HTTP request\n");
goto end; /* Cannot retry: error */
}
while (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) {
if (handle_io_failure(ssl, 0) == 1)
continue; /* Retry */
printf("Failed to write hostname in HTTP request\n");
goto end; /* Cannot retry: error */
}
while (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
if (handle_io_failure(ssl, 0) == 1)
continue; /* Retry */
printf("Failed to write end of HTTP request\n");
goto end; /* Cannot retry: error */
}
do {
/*
* Get up to sizeof(buf) bytes of the response. We keep reading until
* the server closes the connection.
*/
while (!eof && !SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) {
switch (handle_io_failure(ssl, 0)) {
case 1:
continue; /* Retry */
case 0:
eof = 1;
continue;
case -1:
default:
printf("Failed reading remaining data\n");
goto end; /* Cannot retry: error */
}
}
/*
* OpenSSL does not guarantee that the returned data is a string or
* that it is NUL terminated so we use fwrite() to write the exact
* number of bytes that we read. The data could be non-printable or
* have NUL characters in the middle of it. For this simple example
* we're going to print it to stdout anyway.
*/
if (!eof)
fwrite(buf, 1, readbytes, stdout);
} while (!eof);
/* In case the response didn't finish with a newline we add one now */
printf("\n");
/*
* Repeatedly call SSL_shutdown() until the connection is fully
* closed.
*/
while ((ret = SSL_shutdown(ssl)) != 1) {
if (ret < 0 && handle_io_failure(ssl, ret) == 1)
continue; /* Retry */
}
/* Success! */
res = EXIT_SUCCESS;
end:
/*
* If something bad happened then we will dump the contents of the
* OpenSSL error stack to stderr. There might be some useful diagnostic
* information there.
*/
if (res == EXIT_FAILURE)
ERR_print_errors_fp(stderr);
/*
* Free the resources we allocated. We do not free the BIO object here
* because ownership of it was immediately transferred to the SSL object
* via SSL_set_bio(). The BIO will be freed when we free the SSL object.
*/
SSL_free(ssl);
SSL_CTX_free(ctx);
BIO_ADDR_free(peer_addr);
return res;
}

View File

@@ -0,0 +1,444 @@
/*
* Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* NB: Changes to this file should also be reflected in
* doc/man7/ossl-guide-quic-multi-stream.pod
*/
#include <string.h>
/* Include the appropriate header file for SOCK_DGRAM */
#ifdef _WIN32 /* Windows */
# include <winsock2.h>
#else /* Linux/Unix */
# include <sys/socket.h>
#endif
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/* Helper function to create a BIO connected to the server */
static BIO *create_socket_bio(const char *hostname, const char *port,
int family, BIO_ADDR **peer_addr)
{
int sock = -1;
BIO_ADDRINFO *res;
const BIO_ADDRINFO *ai = NULL;
BIO *bio;
/*
* Lookup IP address info for the server.
*/
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0,
&res))
return NULL;
/*
* Loop through all the possible addresses for the server and find one
* we can connect to.
*/
for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
/*
* Create a UDP socket. We could equally use non-OpenSSL calls such
* as "socket" here for this and the subsequent connect and close
* functions. But for portability reasons and also so that we get
* errors on the OpenSSL stack in the event of a failure we use
* OpenSSL's versions of these functions.
*/
sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_DGRAM, 0, 0);
if (sock == -1)
continue;
/* Connect the socket to the server's address */
if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), 0)) {
BIO_closesocket(sock);
sock = -1;
continue;
}
/* Set to nonblocking mode */
if (!BIO_socket_nbio(sock, 1)) {
BIO_closesocket(sock);
sock = -1;
continue;
}
break;
}
if (sock != -1) {
*peer_addr = BIO_ADDR_dup(BIO_ADDRINFO_address(ai));
if (*peer_addr == NULL) {
BIO_closesocket(sock);
return NULL;
}
}
/* Free the address information resources we allocated earlier */
BIO_ADDRINFO_free(res);
/* If sock is -1 then we've been unable to connect to the server */
if (sock == -1)
return NULL;
/* Create a BIO to wrap the socket */
bio = BIO_new(BIO_s_datagram());
if (bio == NULL) {
BIO_closesocket(sock);
return NULL;
}
/*
* Associate the newly created BIO with the underlying socket. By
* passing BIO_CLOSE here the socket will be automatically closed when
* the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
* case you must close the socket explicitly when it is no longer
* needed.
*/
BIO_set_fd(bio, sock, BIO_CLOSE);
return bio;
}
static int write_a_request(SSL *stream, const char *request_start,
const char *hostname)
{
const char *request_end = "\r\n\r\n";
size_t written;
if (!SSL_write_ex(stream, request_start, strlen(request_start),
&written))
return 0;
if (!SSL_write_ex(stream, hostname, strlen(hostname), &written))
return 0;
if (!SSL_write_ex(stream, request_end, strlen(request_end), &written))
return 0;
return 1;
}
/*
* Simple application to send basic HTTP/1.0 requests to a server and print the
* response on the screen. Note that HTTP/1.0 over QUIC is not a real protocol
* and will not be supported by real world servers. This is for demonstration
* purposes only.
*/
int main(int argc, char *argv[])
{
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
SSL *stream1 = NULL, *stream2 = NULL, *stream3 = NULL;
BIO *bio = NULL;
int res = EXIT_FAILURE;
int ret;
unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '0' };
const char *request1_start =
"GET /request1.html HTTP/1.0\r\nConnection: close\r\nHost: ";
const char *request2_start =
"GET /request2.html HTTP/1.0\r\nConnection: close\r\nHost: ";
size_t readbytes;
char buf[160];
BIO_ADDR *peer_addr = NULL;
char *hostname, *port;
int argnext = 1;
int ipv6 = 0;
if (argc < 3) {
printf("Usage: quic-client-non-block [-6] hostname port\n");
goto end;
}
if (!strcmp(argv[argnext], "-6")) {
if (argc < 4) {
printf("Usage: quic-client-non-block [-6] hostname port\n");
goto end;
}
ipv6 = 1;
argnext++;
}
hostname = argv[argnext++];
port = argv[argnext];
/*
* Create an SSL_CTX which we can use to create SSL objects from. We
* want an SSL_CTX for creating clients so we use
* OSSL_QUIC_client_method() here.
*/
ctx = SSL_CTX_new(OSSL_QUIC_client_method());
if (ctx == NULL) {
printf("Failed to create the SSL_CTX\n");
goto end;
}
/*
* Configure the client to abort the handshake if certificate
* verification fails. Virtually all clients should do this unless you
* really know what you are doing.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
/* Use the default trusted certificate store */
if (!SSL_CTX_set_default_verify_paths(ctx)) {
printf("Failed to set the default trusted certificate store\n");
goto end;
}
/* Create an SSL object to represent the TLS connection */
ssl = SSL_new(ctx);
if (ssl == NULL) {
printf("Failed to create the SSL object\n");
goto end;
}
/*
* We will use multiple streams so we will disable the default stream mode.
* This is not a requirement for using multiple streams but is recommended.
*/
if (!SSL_set_default_stream_mode(ssl, SSL_DEFAULT_STREAM_MODE_NONE)) {
printf("Failed to disable the default stream mode\n");
goto end;
}
/*
* Create the underlying transport socket/BIO and associate it with the
* connection.
*/
bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET, &peer_addr);
if (bio == NULL) {
printf("Failed to crete the BIO\n");
goto end;
}
SSL_set_bio(ssl, bio, bio);
/*
* Tell the server during the handshake which hostname we are attempting
* to connect to in case the server supports multiple hosts.
*/
if (!SSL_set_tlsext_host_name(ssl, hostname)) {
printf("Failed to set the SNI hostname\n");
goto end;
}
/*
* Ensure we check during certificate verification that the server has
* supplied a certificate for the hostname that we were expecting.
* Virtually all clients should do this unless you really know what you
* are doing.
*/
if (!SSL_set1_host(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
/* SSL_set_alpn_protos returns 0 for success! */
if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn)) != 0) {
printf("Failed to set the ALPN for the connection\n");
goto end;
}
/* Set the IP address of the remote peer */
if (!SSL_set1_initial_peer_addr(ssl, peer_addr)) {
printf("Failed to set the initial peer address\n");
goto end;
}
/* Do the handshake with the server */
if (SSL_connect(ssl) < 1) {
printf("Failed to connect to the server\n");
/*
* If the failure is due to a verification error we can get more
* information about it from SSL_get_verify_result().
*/
if (SSL_get_verify_result(ssl) != X509_V_OK)
printf("Verify error: %s\n",
X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
goto end;
}
/*
* We create two new client initiated streams. The first will be
* bi-directional, and the second will be uni-directional.
*/
stream1 = SSL_new_stream(ssl, 0);
stream2 = SSL_new_stream(ssl, SSL_STREAM_FLAG_UNI);
if (stream1 == NULL || stream2 == NULL) {
printf("Failed to create streams\n");
goto end;
}
/* Write an HTTP GET request on each of our streams to the peer */
if (!write_a_request(stream1, request1_start, hostname)) {
printf("Failed to write HTTP request on stream 1\n");
goto end;
}
if (!write_a_request(stream2, request2_start, hostname)) {
printf("Failed to write HTTP request on stream 2\n");
goto end;
}
/*
* In this demo we read all the data from one stream before reading all the
* data from the next stream for simplicity. In practice there is no need to
* do this. We can interleave IO on the different streams if we wish, or
* manage the streams entirely separately on different threads.
*/
printf("Stream 1 data:\n");
/*
* Get up to sizeof(buf) bytes of the response from stream 1 (which is a
* bidirectional stream). We keep reading until the server closes the
* connection.
*/
while (SSL_read_ex(stream1, buf, sizeof(buf), &readbytes)) {
/*
* OpenSSL does not guarantee that the returned data is a string or
* that it is NUL terminated so we use fwrite() to write the exact
* number of bytes that we read. The data could be non-printable or
* have NUL characters in the middle of it. For this simple example
* we're going to print it to stdout anyway.
*/
fwrite(buf, 1, readbytes, stdout);
}
/* In case the response didn't finish with a newline we add one now */
printf("\n");
/*
* Check whether we finished the while loop above normally or as the
* result of an error. The 0 argument to SSL_get_error() is the return
* code we received from the SSL_read_ex() call. It must be 0 in order
* to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN. In
* QUIC terms this means that the peer has sent FIN on the stream to
* indicate that no further data will be sent.
*/
switch (SSL_get_error(stream1, 0)) {
case SSL_ERROR_ZERO_RETURN:
/* Normal completion of the stream */
break;
case SSL_ERROR_SSL:
/*
* Some stream fatal error occurred. This could be because of a stream
* reset - or some failure occurred on the underlying connection.
*/
switch (SSL_get_stream_read_state(stream1)) {
case SSL_STREAM_STATE_RESET_REMOTE:
printf("Stream reset occurred\n");
/* The stream has been reset but the connection is still healthy. */
break;
case SSL_STREAM_STATE_CONN_CLOSED:
printf("Connection closed\n");
/* Connection is already closed. Skip SSL_shutdown() */
goto end;
default:
printf("Unknown stream failure\n");
break;
}
break;
default:
/* Some other unexpected error occurred */
printf ("Failed reading remaining data\n");
break;
}
/*
* In our hypothetical HTTP/1.0 over QUIC protocol that we are using we
* assume that the server will respond with a server initiated stream
* containing the data requested in our uni-directional stream. This doesn't
* really make sense to do in a real protocol, but its just for
* demonstration purposes.
*
* We're using blocking mode so this will block until a stream becomes
* available. We could override this behaviour if we wanted to by setting
* the SSL_ACCEPT_STREAM_NO_BLOCK flag in the second argument below.
*/
stream3 = SSL_accept_stream(ssl, 0);
if (stream3 == NULL) {
printf("Failed to accept a new stream\n");
goto end;
}
printf("Stream 3 data:\n");
/*
* Read the data from stream 3 like we did for stream 1 above. Note that
* stream 2 was uni-directional so there is no data to be read from that
* one.
*/
while (SSL_read_ex(stream3, buf, sizeof(buf), &readbytes))
fwrite(buf, 1, readbytes, stdout);
printf("\n");
/* Check for errors on the stream */
switch (SSL_get_error(stream3, 0)) {
case SSL_ERROR_ZERO_RETURN:
/* Normal completion of the stream */
break;
case SSL_ERROR_SSL:
switch (SSL_get_stream_read_state(stream3)) {
case SSL_STREAM_STATE_RESET_REMOTE:
printf("Stream reset occurred\n");
break;
case SSL_STREAM_STATE_CONN_CLOSED:
printf("Connection closed\n");
goto end;
default:
printf("Unknown stream failure\n");
break;
}
break;
default:
printf ("Failed reading remaining data\n");
break;
}
/*
* Repeatedly call SSL_shutdown() until the connection is fully
* closed.
*/
do {
ret = SSL_shutdown(ssl);
if (ret < 0) {
printf("Error shutting down: %d\n", ret);
goto end;
}
} while (ret != 1);
/* Success! */
res = EXIT_SUCCESS;
end:
/*
* If something bad happened then we will dump the contents of the
* OpenSSL error stack to stderr. There might be some useful diagnostic
* information there.
*/
if (res == EXIT_FAILURE)
ERR_print_errors_fp(stderr);
/*
* Free the resources we allocated. We do not free the BIO object here
* because ownership of it was immediately transferred to the SSL object
* via SSL_set_bio(). The BIO will be freed when we free the SSL object.
*/
SSL_free(ssl);
SSL_free(stream1);
SSL_free(stream2);
SSL_free(stream3);
SSL_CTX_free(ctx);
BIO_ADDR_free(peer_addr);
return res;
}

View File

@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC8TCCAdmgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
IENBMCAXDTE2MDExNDIyMjkwNVoYDzIxMTYwMTE1MjIyOTA1WjASMRAwDgYDVQQD
DAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv5oV1s3N
us7SINg7omu5AxueEgK97mh5PU3hgZpliSFaESmL2qLGeP609oXs/68XDXVW4utU
LCOjLh0np+5Xy3i3GRDXgBZ72QDe23WqqQqqaBlQVVm1WxG+amRtZJEWdSIsiFBt
k+8dBElHh2WQDhDOWqHGHQarQgJPxGB97MRhMSlbTwK1T5KAWOlqi5mJW5L6vNrQ
7Tra/YceH70fU0fJYOXhBxM92NwD1bbVd9GPYFSqrdrVj19bvo63XsxZduex5QHr
RkWqT5w5mgAHaEgCqWrS/64q9TR9UEwrB8kiZZg3k9/im+zBwEULTZu0r8oMEkpj
bTlXLmt8EMBqxwIDAQABo1AwTjAdBgNVHQ4EFgQUcH8uroNoWZgEIyrN6z4XzSTd
AUkwHwYDVR0jBBgwFoAUcH8uroNoWZgEIyrN6z4XzSTdAUkwDAYDVR0TBAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAQEAuiLq2lhcOJHrwUP0txbHk2vy6rmGTPxqmcCo
CUQFZ3KrvUQM+rtRqqQ0+LzU4wSTFogBz9KSMfT03gPegY3b/7L2TOaMmUFRzTdd
c9PNT0lP8V3pNQrxp0IjKir791QkGe2Ux45iMKf/SXpeTWASp4zeMiD6/LXFzzaK
BfNS5IrIWRDev41lFasDzudK5/kmVaMvDOFyW51KkKkqb64VS4UA81JIEzClvz+3
Vp3k1AXup5+XnTvhqu2nRhrLpJR5w8OXQpcn6qjKlVc2BXtb3xwci1/ibHlZy3CZ
n70e2NYihU5yYKccReP+fjLgVFsuhsDs/0hRML1u9bLp9nUbYA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC/mhXWzc26ztIg
2Duia7kDG54SAr3uaHk9TeGBmmWJIVoRKYvaosZ4/rT2hez/rxcNdVbi61QsI6Mu
HSen7lfLeLcZENeAFnvZAN7bdaqpCqpoGVBVWbVbEb5qZG1kkRZ1IiyIUG2T7x0E
SUeHZZAOEM5aocYdBqtCAk/EYH3sxGExKVtPArVPkoBY6WqLmYlbkvq82tDtOtr9
hx4fvR9TR8lg5eEHEz3Y3APVttV30Y9gVKqt2tWPX1u+jrdezFl257HlAetGRapP
nDmaAAdoSAKpatL/rir1NH1QTCsHySJlmDeT3+Kb7MHARQtNm7SvygwSSmNtOVcu
a3wQwGrHAgMBAAECggEBAL4rWle8JuCuHGNbGz1nO9d41tg7fnYdnZAaN6OiMfr8
bl+wY84aV3GKJOS2InfYOcIy340UU5QHvxOq/kwwRVV/uAOZ8rqAFmZY9djOnhdv
rZjq3xAHnPgJ0XvZt7XkR2z1AUw+v7Pf1WYGsYcSZ/t99MKB5Je0odA/aRqZRwLy
YflbsnAJtxdJ6fsiVCSJcU76V8sxfiCimw6ppLMEp3zCjveQ5Lv0eVoL2zNYeh+l
GiSwqTqaR+WJekkDiXRd9KYI19drf7OkTII1DtOd6bgvKX3zv2lNiere4J4k7cAP
rW6fBFgtSq2oklTpWUlXRH7XQAgDtDvldXdlKaj96dkCgYEA8KPSu5ywg8pjCofE
nLtJTfVyD2g9tgNLj9dI3kuSniZU51kOtk5rZZwL0S8piGczL908aV9DIWdXWsND
5hlXquKUTSjxPYEzZvaN+tvf9e0AcY/D/UaK0mKPjEbh7vg6pS77aZZz2EL2inOs
dam8famOOC9RUkxH5JWa3UV4UhsCgYEAy9T0wPQctjuvDkZQTqKEKsHrmrgY2PXT
Re8DDGI8hxjYb8l+NoFQ7eiwTHir/DULupxQoBBUQtS+idQzUu02tzLMnGcjHNwh
Tu+vZ4xlVnXxfgIRjDKkfQjiAC5SLzoNO9Jn8g4eS/1mEPXhQ0TXIsFonZDypp/n
RMp21DkvdMUCgYAIMgwjR5rbYjEtUqJnlBlTBmD0FWDEqigQpgxdRcWgjT2nA2l0
3AbcVwwv+6M2eg1MPASqsgvfP13CQZQ2afaKY10Zo6NTrOrLPupm+MYP4hp5w6Ox
JI3lzGWHKYLYWKvmpEr7tZwMaXtsC7R77WP2A6hMUZA7dU2dg1ra3lrSsQKBgQDA
sPIsUtmtwOBtqzUSEXrGfQqA+larDEGNVDVaiKfVwzwg+aeyWS+rqRS5Rj64L2GG
KW3i020EvN/fplZap9vY9lIN7UZ5avSmDdqRFl1ajiccy1HRarKrbTFRoHibItMN
4YvYfVZQ2h2aHQe2Myb6OULv6e4qbPIRyyDo4aKmTQKBgQCadq2JfICFIP9Q1aQn
93oD7Z4WcYs+KsLYO+/uJxWMrn0/gv90cGrSfstJqDOHnRq4WKUcgK9ErxaE4LkW
sD0mBhRM3SMxnRJZRO+6roRdehtjHkvzKu75KjcsuwefoMs2sFa4CLQ1YU2vO3Tx
dgzpnKS2bH/i5yLwhelRfddZ6Q==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDBTCCAe2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
IENBMCAXDTIzMTAzMDEwMjYxNloYDzIxMjMxMDMxMTAyNjE2WjAUMRIwEAYDVQQD
DAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCk6ujS
x/spB5QEiXy4gNe2ETgXFfWxk4WldQRb+pk6QBNMMLaIRG+bpN7bHnXxMKj5hmoj
gPYUYsUquWkv5RjDCRRty2uV/If8fGWfJnyiRWaiLcXu0GqxvQpISOOnSmAvwJEx
zl5rz8ESATYxGfedqJPXUHNa4CrMpAY5ugsisA0KuaD0xrRD2dQ8Ub8pbibEKey+
M0/uVMSgMbyEthRmRTkv4UufHqwxeKk0LlGi6IeZa+rqCkBObkcFiErc7ms+1IKO
cJJ6ib2uiZt+1we8zIaob/iPekAjCakghY1Ba7ZBLpPxkM2tTfxPnE1KoiuludOq
4tZCoULHl1iJuIYHAgMBAAGjYjBgMB0GA1UdDgQWBBQtMXuC2ST9usgeZ0mpVcdJ
CxRPnjAfBgNVHSMEGDAWgBRwfy6ug2hZmAQjKs3rPhfNJN0BSTAJBgNVHRMEAjAA
MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IBAQCqXUb/TGdB
ARHt/MysFv4Tx3dOpdqR+buSvIfK3eRO3lmZdIVsPGWODZvpW2hAgkHSIevkhMi4
VI97G8fk4SNDfg+O+rTA8/OlP1GBtbKXnAKdii5Y3RciQOtzp54BzM9rx2YhCzTu
vESEPzk3fAtAgKWFCAkj1Oh7fHykwNC7Xj0WgYcNlrIJKb6EQ76AgA0MHntnYZbY
Hr4D/TxjoGxhEisjPdYfagQhTtTeYDQiEDTr79Lnclj5EPLQ3Da8c7DG8TIjCVWf
KPyTW3uKTqCiGRkJPD+sCHSy9OvJFUf9XLpkaYL5UUpsKzswLZFjER+3VeARtbpN
VTr7JU5bxYhK
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCk6ujSx/spB5QE
iXy4gNe2ETgXFfWxk4WldQRb+pk6QBNMMLaIRG+bpN7bHnXxMKj5hmojgPYUYsUq
uWkv5RjDCRRty2uV/If8fGWfJnyiRWaiLcXu0GqxvQpISOOnSmAvwJExzl5rz8ES
ATYxGfedqJPXUHNa4CrMpAY5ugsisA0KuaD0xrRD2dQ8Ub8pbibEKey+M0/uVMSg
MbyEthRmRTkv4UufHqwxeKk0LlGi6IeZa+rqCkBObkcFiErc7ms+1IKOcJJ6ib2u
iZt+1we8zIaob/iPekAjCakghY1Ba7ZBLpPxkM2tTfxPnE1KoiuludOq4tZCoULH
l1iJuIYHAgMBAAECggEACZE/NIs5fOXdpm27eJCw8vUIxf4WJNkkFbc2K4fcP87b
z727uSgPOX5VF20q9nUWOHOd0LV4kTIxsgrTmV23FAmAz+XPNgJSeUlWM+dtr5RL
Ifl51CLvLaSD5jGkU14zOlH3mmYYgDSrRLohRLP284SGHyWhq5H5qamSWuL6Jmbs
YWwgbHdHQvo+Rl7PVg2aUWdTHr+tb1S/SH+iePze2dyZrQ0fCkzK3leoMwaJvUM/
v/FaDYxsZYzSQZJPOVpdzHPkwm4ywaCgztQ5Qyy6Lr7bqyK6M8EzGWoOQtMaqHCg
0t7AWh+VXxl3BIwSjAHgb/5OpAuYfVCT2p38wMhhYQKBgQDTB9r7tFhytxC/yDXT
A0xGnrPR6oTTmZNiQee4ZxlyI9DLa59l16piZInv9nx9sGkRofHm7EUBN/br6G46
+I8TauE3KM2qWk/U+mAJklydVRi5U6+bY3871l6eJE+otxOYa4jKGQ2YTTsFjAWr
22knbTR130OQU25GpSu3easDDwKBgQDID36BHw24Cexpp+HmtEa/wjyRMX10c9na
IxvlGcAyTYjLYghBHyIQOMO6iqyGQidSMzGzRAi3wBA2/yr59tloVjFttwt2WEnn
qblAoEIIrE/aH++KqAxUydRoP9sHLiVlpTJMJLzhKb9UFjMsZ4SrNx/JGbZmU9uC
C/EgIrHtiQKBgB3XB4T9/F/EOQ8VTV8YIUn/GOg+5CdTmP6U2SI/Gd8E53pMLo7l
Dwe4tbSDwxi2wDSpFJ6VnDBO7JBxHl0iVoDlZRE6qNJE0PMJsFjKJGRu6v8RsUwk
ppIcfuaXtdfig1fTJNWG82As04K2SPsDHHxhucBNIK2gzoAYzPS1tJPLAoGBAMBl
NjGS3aypznPlgfhOUuPDNmAjihTd/RotPXxcAVve+LkvM+T8vdN/46uYBUawhAQn
O5q8yO19hZp+VqRBYt9WVP7AVAOh8KEbtg6SkC6rF7gbklB0QDeiSeVf05HaLRjN
f8t+YS7g6SFCoEAJ5aqKvsS1N5a8+pLcTS2scBSRAoGBAKW0Aq5SdKR3RJ6TWFUQ
J02A68ri95EmCKPx14gXwyyWzT/a8mzv4V/BUdTGZY8lPQYL87U0rXrdFbI2lP67
CmZI7qokmFkRNjQW7semwNZixW+R1N7cudhbdEjrho+yY/MK2UDIUD/RdPLYz33y
h/LhOZwTOJijh/Q96qW3OtAX
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,296 @@
/*
* Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* NB: Changes to this file should also be reflected in
* doc/man7/ossl-guide-tls-client-block.pod
*/
#include <string.h>
/* Include the appropriate header file for SOCK_STREAM */
#ifdef _WIN32 /* Windows */
# include <winsock2.h>
#else /* Linux/Unix */
# include <sys/socket.h>
#endif
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/* Helper function to create a BIO connected to the server */
static BIO *create_socket_bio(const char *hostname, const char *port, int family)
{
int sock = -1;
BIO_ADDRINFO *res;
const BIO_ADDRINFO *ai = NULL;
BIO *bio;
/*
* Lookup IP address info for the server.
*/
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_STREAM, 0,
&res))
return NULL;
/*
* Loop through all the possible addresses for the server and find one
* we can connect to.
*/
for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
/*
* Create a TCP socket. We could equally use non-OpenSSL calls such
* as "socket" here for this and the subsequent connect and close
* functions. But for portability reasons and also so that we get
* errors on the OpenSSL stack in the event of a failure we use
* OpenSSL's versions of these functions.
*/
sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_STREAM, 0, 0);
if (sock == -1)
continue;
/* Connect the socket to the server's address */
if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), BIO_SOCK_NODELAY)) {
BIO_closesocket(sock);
sock = -1;
continue;
}
/* We have a connected socket so break out of the loop */
break;
}
/* Free the address information resources we allocated earlier */
BIO_ADDRINFO_free(res);
/* If sock is -1 then we've been unable to connect to the server */
if (sock == -1)
return NULL;
/* Create a BIO to wrap the socket */
bio = BIO_new(BIO_s_socket());
if (bio == NULL) {
BIO_closesocket(sock);
return NULL;
}
/*
* Associate the newly created BIO with the underlying socket. By
* passing BIO_CLOSE here the socket will be automatically closed when
* the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
* case you must close the socket explicitly when it is no longer
* needed.
*/
BIO_set_fd(bio, sock, BIO_CLOSE);
return bio;
}
/*
* Simple application to send a basic HTTP/1.0 request to a server and
* print the response on the screen.
*/
int main(int argc, char *argv[])
{
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
BIO *bio = NULL;
int res = EXIT_FAILURE;
int ret;
const char *request_start = "GET / HTTP/1.0\r\nConnection: close\r\nHost: ";
const char *request_end = "\r\n\r\n";
size_t written, readbytes;
char buf[160];
char *hostname, *port;
int argnext = 1;
int ipv6 = 0;
if (argc < 3) {
printf("Usage: tls-client-block [-6] hostname port\n");
goto end;
}
if (!strcmp(argv[argnext], "-6")) {
if (argc < 4) {
printf("Usage: tls-client-block [-6] hostname port\n");
goto end;
}
ipv6 = 1;
argnext++;
}
hostname = argv[argnext++];
port = argv[argnext];
/*
* Create an SSL_CTX which we can use to create SSL objects from. We
* want an SSL_CTX for creating clients so we use TLS_client_method()
* here.
*/
ctx = SSL_CTX_new(TLS_client_method());
if (ctx == NULL) {
printf("Failed to create the SSL_CTX\n");
goto end;
}
/*
* Configure the client to abort the handshake if certificate
* verification fails. Virtually all clients should do this unless you
* really know what you are doing.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
/* Use the default trusted certificate store */
if (!SSL_CTX_set_default_verify_paths(ctx)) {
printf("Failed to set the default trusted certificate store\n");
goto end;
}
/*
* TLSv1.1 or earlier are deprecated by IETF and are generally to be
* avoided if possible. We require a minimum TLS version of TLSv1.2.
*/
if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) {
printf("Failed to set the minimum TLS protocol version\n");
goto end;
}
/* Create an SSL object to represent the TLS connection */
ssl = SSL_new(ctx);
if (ssl == NULL) {
printf("Failed to create the SSL object\n");
goto end;
}
/*
* Create the underlying transport socket/BIO and associate it with the
* connection.
*/
bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET);
if (bio == NULL) {
printf("Failed to create the BIO\n");
goto end;
}
SSL_set_bio(ssl, bio, bio);
/*
* Tell the server during the handshake which hostname we are attempting
* to connect to in case the server supports multiple hosts.
*/
if (!SSL_set_tlsext_host_name(ssl, hostname)) {
printf("Failed to set the SNI hostname\n");
goto end;
}
/*
* Ensure we check during certificate verification that the server has
* supplied a certificate for the hostname that we were expecting.
* Virtually all clients should do this unless you really know what you
* are doing.
*/
if (!SSL_set1_host(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
/* Do the handshake with the server */
if (SSL_connect(ssl) < 1) {
printf("Failed to connect to the server\n");
/*
* If the failure is due to a verification error we can get more
* information about it from SSL_get_verify_result().
*/
if (SSL_get_verify_result(ssl) != X509_V_OK)
printf("Verify error: %s\n",
X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
goto end;
}
/* Write an HTTP GET request to the peer */
if (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) {
printf("Failed to write start of HTTP request\n");
goto end;
}
if (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) {
printf("Failed to write hostname in HTTP request\n");
goto end;
}
if (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
printf("Failed to write end of HTTP request\n");
goto end;
}
/*
* Get up to sizeof(buf) bytes of the response. We keep reading until the
* server closes the connection.
*/
while (SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) {
/*
* OpenSSL does not guarantee that the returned data is a string or
* that it is NUL terminated so we use fwrite() to write the exact
* number of bytes that we read. The data could be non-printable or
* have NUL characters in the middle of it. For this simple example
* we're going to print it to stdout anyway.
*/
fwrite(buf, 1, readbytes, stdout);
}
/* In case the response didn't finish with a newline we add one now */
printf("\n");
/*
* Check whether we finished the while loop above normally or as the
* result of an error. The 0 argument to SSL_get_error() is the return
* code we received from the SSL_read_ex() call. It must be 0 in order
* to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN.
*/
if (SSL_get_error(ssl, 0) != SSL_ERROR_ZERO_RETURN) {
/*
* Some error occurred other than a graceful close down by the
* peer.
*/
printf ("Failed reading remaining data\n");
goto end;
}
/*
* The peer already shutdown gracefully (we know this because of the
* SSL_ERROR_ZERO_RETURN above). We should do the same back.
*/
ret = SSL_shutdown(ssl);
if (ret < 1) {
/*
* ret < 0 indicates an error. ret == 0 would be unexpected here
* because that means "we've sent a close_notify and we're waiting
* for one back". But we already know we got one from the peer
* because of the SSL_ERROR_ZERO_RETURN above.
*/
printf("Error shutting down\n");
goto end;
}
/* Success! */
res = EXIT_SUCCESS;
end:
/*
* If something bad happened then we will dump the contents of the
* OpenSSL error stack to stderr. There might be some useful diagnostic
* information there.
*/
if (res == EXIT_FAILURE)
ERR_print_errors_fp(stderr);
/*
* Free the resources we allocated. We do not free the BIO object here
* because ownership of it was immediately transferred to the SSL object
* via SSL_set_bio(). The BIO will be freed when we free the SSL object.
*/
SSL_free(ssl);
SSL_CTX_free(ctx);
return res;
}

View File

@@ -0,0 +1,376 @@
/*
* Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* NB: Changes to this file should also be reflected in
* doc/man7/ossl-guide-tls-client-non-block.pod
*/
#include <string.h>
/* Include the appropriate header file for SOCK_STREAM */
#ifdef _WIN32 /* Windows */
# include <winsock2.h>
#else /* Linux/Unix */
# include <sys/socket.h>
# include <sys/select.h>
#endif
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/* Helper function to create a BIO connected to the server */
static BIO *create_socket_bio(const char *hostname, const char *port, int family)
{
int sock = -1;
BIO_ADDRINFO *res;
const BIO_ADDRINFO *ai = NULL;
BIO *bio;
/*
* Lookup IP address info for the server.
*/
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_STREAM, 0,
&res))
return NULL;
/*
* Loop through all the possible addresses for the server and find one
* we can connect to.
*/
for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
/*
* Create a TCP socket. We could equally use non-OpenSSL calls such
* as "socket" here for this and the subsequent connect and close
* functions. But for portability reasons and also so that we get
* errors on the OpenSSL stack in the event of a failure we use
* OpenSSL's versions of these functions.
*/
sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_STREAM, 0, 0);
if (sock == -1)
continue;
/* Connect the socket to the server's address */
if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), BIO_SOCK_NODELAY)) {
BIO_closesocket(sock);
sock = -1;
continue;
}
/* Set to nonblocking mode */
if (!BIO_socket_nbio(sock, 1)) {
sock = -1;
continue;
}
/* We have a connected socket so break out of the loop */
break;
}
/* Free the address information resources we allocated earlier */
BIO_ADDRINFO_free(res);
/* If sock is -1 then we've been unable to connect to the server */
if (sock == -1)
return NULL;
/* Create a BIO to wrap the socket */
bio = BIO_new(BIO_s_socket());
if (bio == NULL) {
BIO_closesocket(sock);
return NULL;
}
/*
* Associate the newly created BIO with the underlying socket. By
* passing BIO_CLOSE here the socket will be automatically closed when
* the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
* case you must close the socket explicitly when it is no longer
* needed.
*/
BIO_set_fd(bio, sock, BIO_CLOSE);
return bio;
}
static void wait_for_activity(SSL *ssl, int write)
{
fd_set fds;
int width, sock;
/* Get hold of the underlying file descriptor for the socket */
sock = SSL_get_fd(ssl);
FD_ZERO(&fds);
FD_SET(sock, &fds);
width = sock + 1;
/*
* Wait until the socket is writeable or readable. We use select here
* for the sake of simplicity and portability, but you could equally use
* poll/epoll or similar functions
*
* NOTE: For the purposes of this demonstration code this effectively
* makes this demo block until it has something more useful to do. In a
* real application you probably want to go and do other work here (e.g.
* update a GUI, or service other connections).
*
* Let's say for example that you want to update the progress counter on
* a GUI every 100ms. One way to do that would be to add a 100ms timeout
* in the last parameter to "select" below. Then, when select returns,
* you check if it did so because of activity on the file descriptors or
* because of the timeout. If it is due to the timeout then update the
* GUI and then restart the "select".
*/
if (write)
select(width, NULL, &fds, NULL, NULL);
else
select(width, &fds, NULL, NULL, NULL);
}
static int handle_io_failure(SSL *ssl, int res)
{
switch (SSL_get_error(ssl, res)) {
case SSL_ERROR_WANT_READ:
/* Temporary failure. Wait until we can read and try again */
wait_for_activity(ssl, 0);
return 1;
case SSL_ERROR_WANT_WRITE:
/* Temporary failure. Wait until we can write and try again */
wait_for_activity(ssl, 1);
return 1;
case SSL_ERROR_ZERO_RETURN:
/* EOF */
return 0;
case SSL_ERROR_SYSCALL:
return -1;
case SSL_ERROR_SSL:
/*
* If the failure is due to a verification error we can get more
* information about it from SSL_get_verify_result().
*/
if (SSL_get_verify_result(ssl) != X509_V_OK)
printf("Verify error: %s\n",
X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
return -1;
default:
return -1;
}
}
/*
* Simple application to send a basic HTTP/1.0 request to a server and
* print the response on the screen.
*/
int main(int argc, char *argv[])
{
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
BIO *bio = NULL;
int res = EXIT_FAILURE;
int ret;
const char *request_start = "GET / HTTP/1.0\r\nConnection: close\r\nHost: ";
const char *request_end = "\r\n\r\n";
size_t written, readbytes = 0;
char buf[160];
int eof = 0;
char *hostname, *port;
int argnext = 1;
int ipv6 = 0;
if (argc < 3) {
printf("Usage: tls-client-non-block [-6] hostname port\n");
goto end;
}
if (!strcmp(argv[argnext], "-6")) {
if (argc < 4) {
printf("Usage: tls-client-non-block [-6] hostname port\n");
goto end;
}
ipv6 = 1;
argnext++;
}
hostname = argv[argnext++];
port = argv[argnext];
/*
* Create an SSL_CTX which we can use to create SSL objects from. We
* want an SSL_CTX for creating clients so we use TLS_client_method()
* here.
*/
ctx = SSL_CTX_new(TLS_client_method());
if (ctx == NULL) {
printf("Failed to create the SSL_CTX\n");
goto end;
}
/*
* Configure the client to abort the handshake if certificate
* verification fails. Virtually all clients should do this unless you
* really know what you are doing.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
/* Use the default trusted certificate store */
if (!SSL_CTX_set_default_verify_paths(ctx)) {
printf("Failed to set the default trusted certificate store\n");
goto end;
}
/*
* TLSv1.1 or earlier are deprecated by IETF and are generally to be
* avoided if possible. We require a minimum TLS version of TLSv1.2.
*/
if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) {
printf("Failed to set the minimum TLS protocol version\n");
goto end;
}
/* Create an SSL object to represent the TLS connection */
ssl = SSL_new(ctx);
if (ssl == NULL) {
printf("Failed to create the SSL object\n");
goto end;
}
/*
* Create the underlying transport socket/BIO and associate it with the
* connection.
*/
bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET);
if (bio == NULL) {
printf("Failed to crete the BIO\n");
goto end;
}
SSL_set_bio(ssl, bio, bio);
/*
* Tell the server during the handshake which hostname we are attempting
* to connect to in case the server supports multiple hosts.
*/
if (!SSL_set_tlsext_host_name(ssl, hostname)) {
printf("Failed to set the SNI hostname\n");
goto end;
}
/*
* Ensure we check during certificate verification that the server has
* supplied a certificate for the hostname that we were expecting.
* Virtually all clients should do this unless you really know what you
* are doing.
*/
if (!SSL_set1_host(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
/* Do the handshake with the server */
while ((ret = SSL_connect(ssl)) != 1) {
if (handle_io_failure(ssl, ret) == 1)
continue; /* Retry */
printf("Failed to connect to server\n");
goto end; /* Cannot retry: error */
}
/* Write an HTTP GET request to the peer */
while (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) {
if (handle_io_failure(ssl, 0) == 1)
continue; /* Retry */
printf("Failed to write start of HTTP request\n");
goto end; /* Cannot retry: error */
}
while (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) {
if (handle_io_failure(ssl, 0) == 1)
continue; /* Retry */
printf("Failed to write hostname in HTTP request\n");
goto end; /* Cannot retry: error */
}
while (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
if (handle_io_failure(ssl, 0) == 1)
continue; /* Retry */
printf("Failed to write end of HTTP request\n");
goto end; /* Cannot retry: error */
}
do {
/*
* Get up to sizeof(buf) bytes of the response. We keep reading until
* the server closes the connection.
*/
while (!eof && !SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) {
switch (handle_io_failure(ssl, 0)) {
case 1:
continue; /* Retry */
case 0:
eof = 1;
continue;
case -1:
default:
printf("Failed reading remaining data\n");
goto end; /* Cannot retry: error */
}
}
/*
* OpenSSL does not guarantee that the returned data is a string or
* that it is NUL terminated so we use fwrite() to write the exact
* number of bytes that we read. The data could be non-printable or
* have NUL characters in the middle of it. For this simple example
* we're going to print it to stdout anyway.
*/
if (!eof)
fwrite(buf, 1, readbytes, stdout);
} while (!eof);
/* In case the response didn't finish with a newline we add one now */
printf("\n");
/*
* The peer already shutdown gracefully (we know this because of the
* SSL_ERROR_ZERO_RETURN (i.e. EOF) above). We should do the same back.
*/
while ((ret = SSL_shutdown(ssl)) != 1) {
if (ret < 0 && handle_io_failure(ssl, ret) == 1)
continue; /* Retry */
/*
* ret == 0 is unexpected here because that means "we've sent a
* close_notify and we're waiting for one back". But we already know
* we got one from the peer because of the SSL_ERROR_ZERO_RETURN
* (i.e. EOF) above.
*/
printf("Error shutting down\n");
goto end; /* Cannot retry: error */
}
/* Success! */
res = EXIT_SUCCESS;
end:
/*
* If something bad happened then we will dump the contents of the
* OpenSSL error stack to stderr. There might be some useful diagnostic
* information there.
*/
if (res == EXIT_FAILURE)
ERR_print_errors_fp(stderr);
/*
* Free the resources we allocated. We do not free the BIO object here
* because ownership of it was immediately transferred to the SSL object
* via SSL_set_bio(). The BIO will be freed when we free the SSL object.
*/
SSL_free(ssl);
SSL_CTX_free(ctx);
return res;
}

View File

@@ -0,0 +1,284 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* NB: Changes to this file should also be reflected in
* doc/man7/ossl-guide-tls-server-block.pod
*/
#include <string.h>
/* Include the appropriate header file for SOCK_STREAM */
#ifdef _WIN32 /* Windows */
# include <stdarg.h>
# include <winsock2.h>
#else /* Linux/Unix */
# include <err.h>
# include <sys/socket.h>
# include <sys/select.h>
#endif
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
static const char cache_id[] = "OpenSSL Demo Server";
#ifdef _WIN32
static const char *progname;
static void vwarnx(const char *fmt, va_list ap)
{
if (progname != NULL)
fprintf(stderr, "%s: ", progname);
vfprintf(stderr, fmt, ap);
putc('\n', stderr);
}
static void errx(int status, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarnx(fmt, ap);
va_end(ap);
exit(status);
}
static void warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarnx(fmt, ap);
va_end(ap);
}
#endif
/* Minimal TLS echo server. */
int main(int argc, char *argv[])
{
int res = EXIT_FAILURE;
long opts;
const char *hostport;
SSL_CTX *ctx = NULL;
BIO *acceptor_bio;
#ifdef _WIN32
progname = argv[0];
#endif
if (argc != 2)
errx(res, "Usage: %s [host:]port", argv[0]);
hostport = argv[1];
/*
* An SSL_CTX holds shared configuration information for multiple
* subsequent per-client SSL connections.
*/
ctx = SSL_CTX_new(TLS_server_method());
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
errx(res, "Failed to create server SSL_CTX");
}
/*
* TLS versions older than TLS 1.2 are deprecated by IETF and SHOULD
* be avoided if possible.
*/
if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) {
SSL_CTX_free(ctx);
ERR_print_errors_fp(stderr);
errx(res, "Failed to set the minimum TLS protocol version");
}
#if 0
/*
* In applications (e.g. SMTP) where most clients are performing
* unauthenticated opportunistic TLS it may make sense to set the security
* level to 0, allowing weaker encryption parameters, which are still
* stronger than a potential cleartext fallback.
*
* The default security level is 2 (as of OpenSSL 3.2), which is roughly
* equivalent to that of 112 bit symmetric keys, or 2048-bit RSA or
* finite-field Diffie-Hellman keys. Notably, non-zero security levels no
* longer allow the use of SHA-1 in certificate signatures, key exchange
* or in the TLS 1.[01] PRF (so TLS 1.0 and 1.1 require security level 0).
*/
SSL_CTX_set_security_level(ctx, 0);
#endif
/*
* Tolerate clients hanging up without a TLS "shutdown". Appropriate in all
* application protocols which perform their own message "framing", and
* don't rely on TLS to defend against "truncation" attacks.
*/
opts = SSL_OP_IGNORE_UNEXPECTED_EOF;
/*
* Block potential CPU-exhaustion attacks by clients that request frequent
* renegotiation. This is of course only effective if there are existing
* limits on initial full TLS handshake or connection rates.
*/
opts |= SSL_OP_NO_RENEGOTIATION;
/*
* Most servers elect to use their own cipher preference rather than that of
* the client.
*/
opts |= SSL_OP_CIPHER_SERVER_PREFERENCE;
/* Apply the selection options */
SSL_CTX_set_options(ctx, opts);
/*
* Load the server's certificate *chain* file (PEM format), which includes
* not only the leaf (end-entity) server certificate, but also any
* intermediate issuer-CA certificates. The leaf certificate must be the
* first certificate in the file.
*
* In advanced use-cases this can be called multiple times, once per public
* key algorithm for which the server has a corresponding certificate.
* However, the corresponding private key (see below) must be loaded first,
* *before* moving on to the next chain file.
*
* The requisite files "chain.pem" and "pkey.pem" can be generated by running
* "make chain" in this directory. If the server will be executed from some
* other directory, move or copy the files there.
*/
if (SSL_CTX_use_certificate_chain_file(ctx, "chain.pem") <= 0) {
SSL_CTX_free(ctx);
ERR_print_errors_fp(stderr);
errx(res, "Failed to load the server certificate chain file");
}
/*
* Load the corresponding private key, this also checks that the private
* key matches the just loaded end-entity certificate. It does not check
* whether the certificate chain is valid, the certificates could be
* expired, or may otherwise fail to form a chain that a client can validate.
*/
if (SSL_CTX_use_PrivateKey_file(ctx, "pkey.pem", SSL_FILETYPE_PEM) <= 0) {
SSL_CTX_free(ctx);
ERR_print_errors_fp(stderr);
errx(res, "Error loading the server private key file, "
"possible key/cert mismatch???");
}
/*
* Servers that want to enable session resumption must specify a cache id
* byte array, that identifies the server application, and reduces the
* chance of inappropriate cache sharing.
*/
SSL_CTX_set_session_id_context(ctx, (void *)cache_id, sizeof(cache_id));
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
/*
* How many client TLS sessions to cache. The default is
* SSL_SESSION_CACHE_MAX_SIZE_DEFAULT (20k in recent OpenSSL versions),
* which may be too small or too large.
*/
SSL_CTX_sess_set_cache_size(ctx, 1024);
/*
* Sessions older than this are considered a cache miss even if still in
* the cache. The default is two hours. Busy servers whose clients make
* many connections in a short burst may want a shorter timeout, on lightly
* loaded servers with sporadic connections from any given client, a longer
* time may be appropriate.
*/
SSL_CTX_set_timeout(ctx, 3600);
/*
* Clients rarely employ certificate-based authentication, and so we don't
* require "mutual" TLS authentication (indeed there's no way to know
* whether or how the client authenticated the server, so the term "mutual"
* is potentially misleading).
*
* Since we're not soliciting or processing client certificates, we don't
* need to configure a trusted-certificate store, so no call to
* SSL_CTX_set_default_verify_paths() is needed. The server's own
* certificate chain is assumed valid.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
/*
* Create a listener socket wrapped in a BIO.
* The first call to BIO_do_accept() initialises the socket
*/
acceptor_bio = BIO_new_accept(hostport);
if (acceptor_bio == NULL) {
SSL_CTX_free(ctx);
ERR_print_errors_fp(stderr);
errx(res, "Error creating acceptor bio");
}
BIO_set_bind_mode(acceptor_bio, BIO_BIND_REUSEADDR);
if (BIO_do_accept(acceptor_bio) <= 0) {
SSL_CTX_free(ctx);
ERR_print_errors_fp(stderr);
errx(res, "Error setting up acceptor socket");
}
/* Wait for incoming connection */
for (;;) {
BIO *client_bio;
SSL *ssl;
unsigned char buf[8192];
size_t nread;
size_t nwritten;
size_t total = 0;
/* Pristine error stack for each new connection */
ERR_clear_error();
/* Wait for the next client to connect */
if (BIO_do_accept(acceptor_bio) <= 0) {
/* Client went away before we accepted the connection */
continue;
}
/* Pop the client connection from the BIO chain */
client_bio = BIO_pop(acceptor_bio);
fprintf(stderr, "New client connection accepted\n");
/* Associate a new SSL handle with the new connection */
if ((ssl = SSL_new(ctx)) == NULL) {
ERR_print_errors_fp(stderr);
warnx("Error creating SSL handle for new connection");
BIO_free(client_bio);
continue;
}
SSL_set_bio(ssl, client_bio, client_bio);
/* Attempt an SSL handshake with the client */
if (SSL_accept(ssl) <= 0) {
ERR_print_errors_fp(stderr);
warnx("Error performing SSL handshake with client");
SSL_free(ssl);
continue;
}
while (SSL_read_ex(ssl, buf, sizeof(buf), &nread) > 0) {
if (SSL_write_ex(ssl, buf, nread, &nwritten) > 0 &&
nwritten == nread) {
total += nwritten;
continue;
}
warnx("Error echoing client input");
break;
}
fprintf(stderr, "Client connection closed, %zu bytes sent\n", total);
SSL_free(ssl);
}
/*
* Unreachable placeholder cleanup code, the above loop runs forever.
*/
SSL_CTX_free(ctx);
return EXIT_SUCCESS;
}