Path: blob/main/crypto/openssl/doc/designs/ddd/ddd-05-mem-nonblocking.c
34889 views
#include <sys/poll.h>1#include <openssl/ssl.h>23/*4* Demo 5: Client — Client Uses Memory BIO — Nonblocking5* =====================================================6*7* This is an example of (part of) an application which uses libssl in an8* asynchronous, nonblocking fashion. The application passes memory BIOs to9* OpenSSL, meaning that it controls both when data is read/written from an SSL10* object on the decrypted side but also when encrypted data from the network is11* shunted to/from OpenSSL. In this way OpenSSL is used as a pure state machine12* which does not make its own network I/O calls. OpenSSL never sees or creates13* any file descriptor for a network socket. The functions below show all14* interactions with libssl the application makes, and would hypothetically be15* linked into a larger application.16*/17typedef struct app_conn_st {18SSL *ssl;19BIO *ssl_bio, *net_bio;20int rx_need_tx, tx_need_rx;21} APP_CONN;2223/*24* The application is initializing and wants an SSL_CTX which it will use for25* some number of outgoing connections, which it creates in subsequent calls to26* new_conn. The application may also call this function multiple times to27* create multiple SSL_CTX.28*/29SSL_CTX *create_ssl_ctx(void)30{31SSL_CTX *ctx;3233#ifdef USE_QUIC34ctx = SSL_CTX_new(OSSL_QUIC_client_method());35#else36ctx = SSL_CTX_new(TLS_client_method());37#endif38if (ctx == NULL)39return NULL;4041/* Enable trust chain verification. */42SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);4344/* Load default root CA store. */45if (SSL_CTX_set_default_verify_paths(ctx) == 0) {46SSL_CTX_free(ctx);47return NULL;48}4950return ctx;51}5253/*54* The application wants to create a new outgoing connection using a given55* SSL_CTX.56*57* hostname is a string like "openssl.org" used for certificate validation.58*/59APP_CONN *new_conn(SSL_CTX *ctx, const char *bare_hostname)60{61BIO *ssl_bio, *internal_bio, *net_bio;62APP_CONN *conn;63SSL *ssl;64#ifdef USE_QUIC65static const unsigned char alpn[] = {5, 'd', 'u', 'm', 'm', 'y'};66#endif6768conn = calloc(1, sizeof(APP_CONN));69if (conn == NULL)70return NULL;7172ssl = conn->ssl = SSL_new(ctx);73if (ssl == NULL) {74free(conn);75return NULL;76}7778SSL_set_connect_state(ssl); /* cannot fail */7980#ifdef USE_QUIC81if (BIO_new_bio_dgram_pair(&internal_bio, 0, &net_bio, 0) <= 0) {82#else83if (BIO_new_bio_pair(&internal_bio, 0, &net_bio, 0) <= 0) {84#endif85SSL_free(ssl);86free(conn);87return NULL;88}8990SSL_set_bio(ssl, internal_bio, internal_bio);9192if (SSL_set1_host(ssl, bare_hostname) <= 0) {93SSL_free(ssl);94free(conn);95return NULL;96}9798if (SSL_set_tlsext_host_name(ssl, bare_hostname) <= 0) {99SSL_free(ssl);100free(conn);101return NULL;102}103104ssl_bio = BIO_new(BIO_f_ssl());105if (ssl_bio == NULL) {106SSL_free(ssl);107free(conn);108return NULL;109}110111if (BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE) <= 0) {112SSL_free(ssl);113BIO_free(ssl_bio);114return NULL;115}116117#ifdef USE_QUIC118/* Configure ALPN, which is required for QUIC. */119if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn))) {120/* Note: SSL_set_alpn_protos returns 1 for failure. */121SSL_free(ssl);122BIO_free(ssl_bio);123return NULL;124}125#endif126127conn->ssl_bio = ssl_bio;128conn->net_bio = net_bio;129return conn;130}131132/*133* Non-blocking transmission.134*135* Returns -1 on error. Returns -2 if the function would block (corresponds to136* EWOULDBLOCK).137*/138int tx(APP_CONN *conn, const void *buf, int buf_len)139{140int rc, l;141142l = BIO_write(conn->ssl_bio, buf, buf_len);143if (l <= 0) {144rc = SSL_get_error(conn->ssl, l);145switch (rc) {146case SSL_ERROR_WANT_READ:147conn->tx_need_rx = 1;148case SSL_ERROR_WANT_CONNECT:149case SSL_ERROR_WANT_WRITE:150return -2;151default:152return -1;153}154} else {155conn->tx_need_rx = 0;156}157158return l;159}160161/*162* Non-blocking reception.163*164* Returns -1 on error. Returns -2 if the function would block (corresponds to165* EWOULDBLOCK).166*/167int rx(APP_CONN *conn, void *buf, int buf_len)168{169int rc, l;170171l = BIO_read(conn->ssl_bio, buf, buf_len);172if (l <= 0) {173rc = SSL_get_error(conn->ssl, l);174switch (rc) {175case SSL_ERROR_WANT_WRITE:176conn->rx_need_tx = 1;177case SSL_ERROR_WANT_READ:178return -2;179default:180return -1;181}182} else {183conn->rx_need_tx = 0;184}185186return l;187}188189/*190* Called to get data which has been enqueued for transmission to the network191* by OpenSSL. For QUIC, this always outputs a single datagram.192*193* IMPORTANT (QUIC): If buf_len is inadequate to hold the datagram, it is truncated194* (similar to read(2)). A buffer size of at least 1472 must be used by default195* to guarantee this does not occur.196*/197int read_net_tx(APP_CONN *conn, void *buf, int buf_len)198{199return BIO_read(conn->net_bio, buf, buf_len);200}201202/*203* Called to feed data which has been received from the network to OpenSSL.204*205* QUIC: buf must contain the entirety of a single datagram. It will be consumed206* entirely (return value == buf_len) or not at all.207*/208int write_net_rx(APP_CONN *conn, const void *buf, int buf_len)209{210return BIO_write(conn->net_bio, buf, buf_len);211}212213/*214* Determine how much data can be written to the network RX BIO.215*/216size_t net_rx_space(APP_CONN *conn)217{218return BIO_ctrl_get_write_guarantee(conn->net_bio);219}220221/*222* Determine how much data is currently queued for transmission in the network223* TX BIO.224*/225size_t net_tx_avail(APP_CONN *conn)226{227return BIO_ctrl_pending(conn->net_bio);228}229230/*231* These functions returns zero or more of:232*233* POLLIN: The SSL state machine is interested in socket readability events.234*235* POLLOUT: The SSL state machine is interested in socket writeability events.236*237* POLLERR: The SSL state machine is interested in socket error events.238*239* get_conn_pending_tx returns events which may cause SSL_write to make240* progress and get_conn_pending_rx returns events which may cause SSL_read241* to make progress.242*/243int get_conn_pending_tx(APP_CONN *conn)244{245#ifdef USE_QUIC246return (SSL_net_read_desired(conn->ssl) ? POLLIN : 0)247| (SSL_net_write_desired(conn->ssl) ? POLLOUT : 0)248| POLLERR;249#else250return (conn->tx_need_rx ? POLLIN : 0) | POLLOUT | POLLERR;251#endif252}253254int get_conn_pending_rx(APP_CONN *conn)255{256#ifdef USE_QUIC257return get_conn_pending_tx(conn);258#else259return (conn->rx_need_tx ? POLLOUT : 0) | POLLIN | POLLERR;260#endif261}262263/*264* The application wants to close the connection and free bookkeeping265* structures.266*/267void teardown(APP_CONN *conn)268{269BIO_free_all(conn->ssl_bio);270BIO_free_all(conn->net_bio);271free(conn);272}273274/*275* The application is shutting down and wants to free a previously276* created SSL_CTX.277*/278void teardown_ctx(SSL_CTX *ctx)279{280SSL_CTX_free(ctx);281}282283/*284* ============================================================================285* Example driver for the above code. This is just to demonstrate that the code286* works and is not intended to be representative of a real application.287*/288#include <sys/types.h>289#include <sys/socket.h>290#include <sys/signal.h>291#include <netdb.h>292#include <unistd.h>293#include <fcntl.h>294#include <errno.h>295296static int pump(APP_CONN *conn, int fd, int events, int timeout)297{298int l, l2;299char buf[2048]; /* QUIC: would need to be changed if < 1472 */300size_t wspace;301struct pollfd pfd = {0};302303pfd.fd = fd;304pfd.events = (events & (POLLIN | POLLERR));305if (net_rx_space(conn) == 0)306pfd.events &= ~POLLIN;307if (net_tx_avail(conn) > 0)308pfd.events |= POLLOUT;309310if ((pfd.events & (POLLIN|POLLOUT)) == 0)311return 1;312313if (poll(&pfd, 1, timeout) == 0)314return -1;315316if (pfd.revents & POLLIN) {317while ((wspace = net_rx_space(conn)) > 0) {318l = read(fd, buf, wspace > sizeof(buf) ? sizeof(buf) : wspace);319if (l <= 0) {320switch (errno) {321case EAGAIN:322goto stop;323default:324if (l == 0) /* EOF */325goto stop;326327fprintf(stderr, "error on read: %d\n", errno);328return -1;329}330break;331}332l2 = write_net_rx(conn, buf, l);333if (l2 < l)334fprintf(stderr, "short write %d %d\n", l2, l);335} stop:;336}337338if (pfd.revents & POLLOUT) {339for (;;) {340l = read_net_tx(conn, buf, sizeof(buf));341if (l <= 0)342break;343l2 = write(fd, buf, l);344if (l2 < l)345fprintf(stderr, "short read %d %d\n", l2, l);346}347}348349return 1;350}351352int main(int argc, char **argv)353{354int rc, fd = -1, res = 1;355static char tx_msg[300];356const char *tx_p = tx_msg;357char rx_buf[2048];358int l, tx_len;359int timeout = 2000 /* ms */;360APP_CONN *conn = NULL;361struct addrinfo hints = {0}, *result = NULL;362SSL_CTX *ctx = NULL;363364if (argc < 3) {365fprintf(stderr, "usage: %s host port\n", argv[0]);366goto fail;367}368369tx_len = snprintf(tx_msg, sizeof(tx_msg),370"GET / HTTP/1.0\r\nHost: %s\r\n\r\n",371argv[1]);372373ctx = create_ssl_ctx();374if (ctx == NULL) {375fprintf(stderr, "cannot create SSL context\n");376goto fail;377}378379hints.ai_family = AF_INET;380hints.ai_socktype = SOCK_STREAM;381hints.ai_flags = AI_PASSIVE;382rc = getaddrinfo(argv[1], argv[2], &hints, &result);383if (rc < 0) {384fprintf(stderr, "cannot resolve\n");385goto fail;386}387388signal(SIGPIPE, SIG_IGN);389390#ifdef USE_QUIC391fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);392#else393fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);394#endif395if (fd < 0) {396fprintf(stderr, "cannot create socket\n");397goto fail;398}399400rc = connect(fd, result->ai_addr, result->ai_addrlen);401if (rc < 0) {402fprintf(stderr, "cannot connect\n");403goto fail;404}405406rc = fcntl(fd, F_SETFL, O_NONBLOCK);407if (rc < 0) {408fprintf(stderr, "cannot make socket nonblocking\n");409goto fail;410}411412conn = new_conn(ctx, argv[1]);413if (conn == NULL) {414fprintf(stderr, "cannot establish connection\n");415goto fail;416}417418/* TX */419while (tx_len != 0) {420l = tx(conn, tx_p, tx_len);421if (l > 0) {422tx_p += l;423tx_len -= l;424} else if (l == -1) {425fprintf(stderr, "tx error\n");426} else if (l == -2) {427if (pump(conn, fd, get_conn_pending_tx(conn), timeout) != 1) {428fprintf(stderr, "pump error\n");429goto fail;430}431}432}433434/* RX */435for (;;) {436l = rx(conn, rx_buf, sizeof(rx_buf));437if (l > 0) {438fwrite(rx_buf, 1, l, stdout);439} else if (l == -1) {440break;441} else if (l == -2) {442if (pump(conn, fd, get_conn_pending_rx(conn), timeout) != 1) {443fprintf(stderr, "pump error\n");444goto fail;445}446}447}448449res = 0;450fail:451if (conn != NULL)452teardown(conn);453if (ctx != NULL)454teardown_ctx(ctx);455if (result != NULL)456freeaddrinfo(result);457return res;458}459460461