Path: blob/main/crypto/openssl/doc/designs/ddd/ddd-04-fd-nonblocking.c
34889 views
#include <sys/poll.h>1#include <openssl/ssl.h>23/*4* Demo 4: Client — Client Creates FD — Nonblocking5* ================================================6*7* This is an example of (part of) an application which uses libssl in an8* asynchronous, nonblocking fashion. The client is responsible for creating the9* socket and passing it to libssl. The functions show all interactions with10* libssl the application makes, and would hypothetically be linked into a11* larger application.12*/13typedef struct app_conn_st {14SSL *ssl;15int fd;16int rx_need_tx, tx_need_rx;17} APP_CONN;1819/*20* The application is initializing and wants an SSL_CTX which it will use for21* some number of outgoing connections, which it creates in subsequent calls to22* new_conn. The application may also call this function multiple times to23* create multiple SSL_CTX.24*/25SSL_CTX *create_ssl_ctx(void)26{27SSL_CTX *ctx;2829#ifdef USE_QUIC30ctx = SSL_CTX_new(OSSL_QUIC_client_method());31#else32ctx = SSL_CTX_new(TLS_client_method());33#endif34if (ctx == NULL)35return NULL;3637/* Enable trust chain verification. */38SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);3940/* Load default root CA store. */41if (SSL_CTX_set_default_verify_paths(ctx) == 0) {42SSL_CTX_free(ctx);43return NULL;44}4546return ctx;47}4849/*50* The application wants to create a new outgoing connection using a given51* SSL_CTX.52*53* hostname is a string like "openssl.org" used for certificate validation.54*/55APP_CONN *new_conn(SSL_CTX *ctx, int fd, const char *bare_hostname)56{57APP_CONN *conn;58SSL *ssl;59#ifdef USE_QUIC60static const unsigned char alpn[] = {5, 'd', 'u', 'm', 'm', 'y'};61#endif6263conn = calloc(1, sizeof(APP_CONN));64if (conn == NULL)65return NULL;6667ssl = conn->ssl = SSL_new(ctx);68if (ssl == NULL) {69free(conn);70return NULL;71}7273SSL_set_connect_state(ssl); /* cannot fail */7475if (SSL_set_fd(ssl, fd) <= 0) {76SSL_free(ssl);77free(conn);78return NULL;79}8081if (SSL_set1_host(ssl, bare_hostname) <= 0) {82SSL_free(ssl);83free(conn);84return NULL;85}8687if (SSL_set_tlsext_host_name(ssl, bare_hostname) <= 0) {88SSL_free(ssl);89free(conn);90return NULL;91}9293#ifdef USE_QUIC94/* Configure ALPN, which is required for QUIC. */95if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn))) {96/* Note: SSL_set_alpn_protos returns 1 for failure. */97SSL_free(ssl);98free(conn);99return NULL;100}101#endif102103conn->fd = fd;104return conn;105}106107/*108* Non-blocking transmission.109*110* Returns -1 on error. Returns -2 if the function would block (corresponds to111* EWOULDBLOCK).112*/113int tx(APP_CONN *conn, const void *buf, int buf_len)114{115int rc, l;116117conn->tx_need_rx = 0;118119l = SSL_write(conn->ssl, buf, buf_len);120if (l <= 0) {121rc = SSL_get_error(conn->ssl, l);122switch (rc) {123case SSL_ERROR_WANT_READ:124conn->tx_need_rx = 1;125case SSL_ERROR_WANT_CONNECT:126case SSL_ERROR_WANT_WRITE:127return -2;128default:129return -1;130}131}132133return l;134}135136/*137* Non-blocking reception.138*139* Returns -1 on error. Returns -2 if the function would block (corresponds to140* EWOULDBLOCK).141*/142int rx(APP_CONN *conn, void *buf, int buf_len)143{144int rc, l;145146conn->rx_need_tx = 0;147148l = SSL_read(conn->ssl, buf, buf_len);149if (l <= 0) {150rc = SSL_get_error(conn->ssl, l);151switch (rc) {152case SSL_ERROR_WANT_WRITE:153conn->rx_need_tx = 1;154case SSL_ERROR_WANT_READ:155return -2;156default:157return -1;158}159}160161return l;162}163164/*165* The application wants to know a fd it can poll on to determine when the166* SSL state machine needs to be pumped.167*168* If the fd returned has:169*170* POLLIN: SSL_read *may* return data;171* if application does not want to read yet, it should call pump().172*173* POLLOUT: SSL_write *may* accept data174*175* POLLERR: An application should call pump() if it is not likely to call176* SSL_read or SSL_write soon.177*178*/179int get_conn_fd(APP_CONN *conn)180{181return conn->fd;182}183184/*185* These functions returns zero or more of:186*187* POLLIN: The SSL state machine is interested in socket readability events.188*189* POLLOUT: The SSL state machine is interested in socket writeability events.190*191* POLLERR: The SSL state machine is interested in socket error events.192*193* get_conn_pending_tx returns events which may cause SSL_write to make194* progress and get_conn_pending_rx returns events which may cause SSL_read195* to make progress.196*/197int get_conn_pending_tx(APP_CONN *conn)198{199#ifdef USE_QUIC200return (SSL_net_read_desired(conn->ssl) ? POLLIN : 0)201| (SSL_net_write_desired(conn->ssl) ? POLLOUT : 0)202| POLLERR;203#else204return (conn->tx_need_rx ? POLLIN : 0) | POLLOUT | POLLERR;205#endif206}207208int get_conn_pending_rx(APP_CONN *conn)209{210return get_conn_pending_tx(conn);211}212213#ifdef USE_QUIC214/*215* Returns the number of milliseconds after which some call to libssl must be216* made. Any call (SSL_read/SSL_write/SSL_pump) will do. Returns -1 if there is217* no need for such a call. This may change after the next call218* to libssl.219*/220static inline int timeval_to_ms(const struct timeval *t);221222int get_conn_pump_timeout(APP_CONN *conn)223{224struct timeval tv;225int is_infinite;226227if (!SSL_get_event_timeout(conn->ssl, &tv, &is_infinite))228return -1;229230return is_infinite ? -1 : timeval_to_ms(&tv);231}232233/*234* Called to advance internals of libssl state machines without having to235* perform an application-level read/write.236*/237void pump(APP_CONN *conn)238{239SSL_handle_events(conn->ssl);240}241#endif242243/*244* The application wants to close the connection and free bookkeeping245* structures.246*/247void teardown(APP_CONN *conn)248{249SSL_shutdown(conn->ssl);250SSL_free(conn->ssl);251free(conn);252}253254/*255* The application is shutting down and wants to free a previously256* created SSL_CTX.257*/258void teardown_ctx(SSL_CTX *ctx)259{260SSL_CTX_free(ctx);261}262263/*264* ============================================================================265* Example driver for the above code. This is just to demonstrate that the code266* works and is not intended to be representative of a real application.267*/268#include <sys/types.h>269#include <sys/socket.h>270#include <sys/signal.h>271#ifdef USE_QUIC272# include <sys/time.h>273#endif274#include <netdb.h>275#include <unistd.h>276#include <fcntl.h>277278#ifdef USE_QUIC279280static inline void ms_to_timeval(struct timeval *t, int ms)281{282t->tv_sec = ms < 0 ? -1 : ms/1000;283t->tv_usec = ms < 0 ? 0 : (ms%1000)*1000;284}285286static inline int timeval_to_ms(const struct timeval *t)287{288return t->tv_sec*1000 + t->tv_usec/1000;289}290291#endif292293int main(int argc, char **argv)294{295int rc, fd = -1, res = 1;296static char tx_msg[300];297const char *tx_p = tx_msg;298char rx_buf[2048];299int l, tx_len;300#ifdef USE_QUIC301struct timeval timeout;302#else303int timeout = 2000 /* ms */;304#endif305APP_CONN *conn = NULL;306struct addrinfo hints = {0}, *result = NULL;307SSL_CTX *ctx = NULL;308309#ifdef USE_QUIC310ms_to_timeval(&timeout, 2000);311#endif312313if (argc < 3) {314fprintf(stderr, "usage: %s host port\n", argv[0]);315goto fail;316}317318tx_len = snprintf(tx_msg, sizeof(tx_msg),319"GET / HTTP/1.0\r\nHost: %s\r\n\r\n", argv[1]);320321ctx = create_ssl_ctx();322if (ctx == NULL) {323fprintf(stderr, "cannot create SSL context\n");324goto fail;325}326327hints.ai_family = AF_INET;328hints.ai_socktype = SOCK_STREAM;329hints.ai_flags = AI_PASSIVE;330rc = getaddrinfo(argv[1], argv[2], &hints, &result);331if (rc < 0) {332fprintf(stderr, "cannot resolve\n");333goto fail;334}335336signal(SIGPIPE, SIG_IGN);337338#ifdef USE_QUIC339fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);340#else341fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);342#endif343if (fd < 0) {344fprintf(stderr, "cannot create socket\n");345goto fail;346}347348rc = connect(fd, result->ai_addr, result->ai_addrlen);349if (rc < 0) {350fprintf(stderr, "cannot connect\n");351goto fail;352}353354rc = fcntl(fd, F_SETFL, O_NONBLOCK);355if (rc < 0) {356fprintf(stderr, "cannot make socket nonblocking\n");357goto fail;358}359360conn = new_conn(ctx, fd, argv[1]);361if (conn == NULL) {362fprintf(stderr, "cannot establish connection\n");363goto fail;364}365366/* TX */367while (tx_len != 0) {368l = tx(conn, tx_p, tx_len);369if (l > 0) {370tx_p += l;371tx_len -= l;372} else if (l == -1) {373fprintf(stderr, "tx error\n");374goto fail;375} else if (l == -2) {376#ifdef USE_QUIC377struct timeval start, now, deadline, t;378#endif379struct pollfd pfd = {0};380381#ifdef USE_QUIC382ms_to_timeval(&t, get_conn_pump_timeout(conn));383if (t.tv_sec < 0 || timercmp(&t, &timeout, >))384t = timeout;385386gettimeofday(&start, NULL);387timeradd(&start, &timeout, &deadline);388#endif389390pfd.fd = get_conn_fd(conn);391pfd.events = get_conn_pending_tx(conn);392#ifdef USE_QUIC393if (poll(&pfd, 1, timeval_to_ms(&t)) == 0)394#else395if (poll(&pfd, 1, timeout) == 0)396#endif397{398#ifdef USE_QUIC399pump(conn);400401gettimeofday(&now, NULL);402if (timercmp(&now, &deadline, >=))403#endif404{405fprintf(stderr, "tx timeout\n");406goto fail;407}408}409}410}411412/* RX */413for (;;) {414l = rx(conn, rx_buf, sizeof(rx_buf));415if (l > 0) {416fwrite(rx_buf, 1, l, stdout);417} else if (l == -1) {418break;419} else if (l == -2) {420#ifdef USE_QUIC421struct timeval start, now, deadline, t;422#endif423struct pollfd pfd = {0};424425#ifdef USE_QUIC426ms_to_timeval(&t, get_conn_pump_timeout(conn));427if (t.tv_sec < 0 || timercmp(&t, &timeout, >))428t = timeout;429430gettimeofday(&start, NULL);431timeradd(&start, &timeout, &deadline);432#endif433434pfd.fd = get_conn_fd(conn);435pfd.events = get_conn_pending_rx(conn);436#ifdef USE_QUIC437if (poll(&pfd, 1, timeval_to_ms(&t)) == 0)438#else439if (poll(&pfd, 1, timeout) == 0)440#endif441{442#ifdef USE_QUIC443pump(conn);444gettimeofday(&now, NULL);445if (timercmp(&now, &deadline, >=))446#endif447{448fprintf(stderr, "rx timeout\n");449goto fail;450}451}452}453}454455res = 0;456fail:457if (conn != NULL)458teardown(conn);459if (ctx != NULL)460teardown_ctx(ctx);461if (result != NULL)462freeaddrinfo(result);463return res;464}465466467