Path: blob/main/crypto/openssl/doc/designs/ddd/ddd-05-mem-nonblocking.c
109249 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}336stop:;337}338339if (pfd.revents & POLLOUT) {340for (;;) {341l = read_net_tx(conn, buf, sizeof(buf));342if (l <= 0)343break;344l2 = write(fd, buf, l);345if (l2 < l)346fprintf(stderr, "short read %d %d\n", l2, l);347}348}349350return 1;351}352353int main(int argc, char **argv)354{355int rc, fd = -1, res = 1;356static char tx_msg[300];357const char *tx_p = tx_msg;358char rx_buf[2048];359int l, tx_len;360int timeout = 2000 /* ms */;361APP_CONN *conn = NULL;362struct addrinfo hints = { 0 }, *result = NULL;363SSL_CTX *ctx = NULL;364365if (argc < 3) {366fprintf(stderr, "usage: %s host port\n", argv[0]);367goto fail;368}369370tx_len = snprintf(tx_msg, sizeof(tx_msg),371"GET / HTTP/1.0\r\nHost: %s\r\n\r\n",372argv[1]);373374ctx = create_ssl_ctx();375if (ctx == NULL) {376fprintf(stderr, "cannot create SSL context\n");377goto fail;378}379380hints.ai_family = AF_INET;381hints.ai_socktype = SOCK_STREAM;382hints.ai_flags = AI_PASSIVE;383rc = getaddrinfo(argv[1], argv[2], &hints, &result);384if (rc < 0) {385fprintf(stderr, "cannot resolve\n");386goto fail;387}388389signal(SIGPIPE, SIG_IGN);390391#ifdef USE_QUIC392fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);393#else394fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);395#endif396if (fd < 0) {397fprintf(stderr, "cannot create socket\n");398goto fail;399}400401rc = connect(fd, result->ai_addr, result->ai_addrlen);402if (rc < 0) {403fprintf(stderr, "cannot connect\n");404goto fail;405}406407rc = fcntl(fd, F_SETFL, O_NONBLOCK);408if (rc < 0) {409fprintf(stderr, "cannot make socket nonblocking\n");410goto fail;411}412413conn = new_conn(ctx, argv[1]);414if (conn == NULL) {415fprintf(stderr, "cannot establish connection\n");416goto fail;417}418419/* TX */420while (tx_len != 0) {421l = tx(conn, tx_p, tx_len);422if (l > 0) {423tx_p += l;424tx_len -= l;425} else if (l == -1) {426fprintf(stderr, "tx error\n");427} else if (l == -2) {428if (pump(conn, fd, get_conn_pending_tx(conn), timeout) != 1) {429fprintf(stderr, "pump error\n");430goto fail;431}432}433}434435/* RX */436for (;;) {437l = rx(conn, rx_buf, sizeof(rx_buf));438if (l > 0) {439fwrite(rx_buf, 1, l, stdout);440} else if (l == -1) {441break;442} else if (l == -2) {443if (pump(conn, fd, get_conn_pending_rx(conn), timeout) != 1) {444fprintf(stderr, "pump error\n");445goto fail;446}447}448}449450res = 0;451fail:452if (conn != NULL)453teardown(conn);454if (ctx != NULL)455teardown_ctx(ctx);456if (result != NULL)457freeaddrinfo(result);458return res;459}460461462