sslexample

sslexample.git
git clone git://git.lenczewski.org/sslexample.git
Log | Files | Refs

common.h (5503B)


      1 #define _GNU_SOURCE 1
      2 #define _XOPEN_SOURCE 700
      3 
      4 #include <assert.h>
      5 #include <stddef.h>
      6 #include <stdint.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <unistd.h>
     11 
     12 #include <sys/socket.h>
     13 #include <netinet/ip.h>
     14 #include <netdb.h>
     15 
     16 #include <fcntl.h>
     17 
     18 #include <liburing.h>
     19 
     20 #include <openssl/ssl.h>
     21 #include <openssl/err.h>
     22 
     23 #ifndef NDEBUG
     24 # define DBGLOG(...) fprintf(stderr, __VA_ARGS__)
     25 #else
     26 # define DBGLOG(...)
     27 #endif
     28 
     29 #define TO_PARENT_PTR(ptr, T, member) \
     30 	((T *) (((uintptr_t) (ptr)) - offsetof(T, member)))
     31 
     32 // ioreq : a helper to make multiplexing operations on an io_uring easier
     33 // ===========================================================================
     34 //
     35 
     36 enum ioreq_type {
     37 	IOREQ_ACCEPT, IOREQ_CONNECT, IOREQ_RECV, IOREQ_SEND, IOREQ_CLOSE,
     38 };
     39 
     40 struct ioreq_accept {
     41 	int fd;
     42 	struct sockaddr *addr;
     43 	socklen_t *addrlen;
     44 	int flags;
     45 };
     46 
     47 struct ioreq_connect {
     48 	int fd;
     49 	struct sockaddr *addr;
     50 	socklen_t addrlen;
     51 };
     52 
     53 struct ioreq_recv {
     54 	int fd;
     55 	void *buf;
     56 	size_t len;
     57 	int flags;
     58 };
     59 
     60 struct ioreq_send {
     61 	int fd;
     62 	void *buf;
     63 	size_t len;
     64 	int flags;
     65 };
     66 
     67 struct ioreq_close {
     68 	int fd;
     69 };
     70 
     71 union ioreq_tag {
     72 	struct ioreq_accept accept;
     73 	struct ioreq_connect connect;
     74 	struct ioreq_recv recv;
     75 	struct ioreq_send send;
     76 	struct ioreq_close close;
     77 };
     78 
     79 struct ioreq {
     80 	enum ioreq_type type;
     81 	union ioreq_tag tag;
     82 };
     83 
     84 static int
     85 queue_ioreqs(struct io_uring *uring, struct ioreq *reqs, size_t len)
     86 {
     87 	for (size_t i = 0; i < len; i++) {
     88 		struct ioreq *req = &reqs[i];
     89 
     90 		struct io_uring_sqe *sqe = io_uring_get_sqe(uring);
     91 		if (!sqe) { /* out of sqes, submit and try again */
     92 			io_uring_submit(uring);
     93 			sqe = io_uring_get_sqe(uring);
     94 		}
     95 
     96 		assert(sqe);
     97 
     98 		io_uring_sqe_set_data(sqe, req);
     99 
    100 		switch (req->type) {
    101 		case IOREQ_ACCEPT:
    102 			io_uring_prep_accept(sqe,
    103 					     req->tag.accept.fd,
    104 					     req->tag.accept.addr,
    105 					     req->tag.accept.addrlen,
    106 					     req->tag.accept.flags);
    107 			break;
    108 
    109 		case IOREQ_CONNECT:
    110 			io_uring_prep_connect(sqe,
    111 					      req->tag.connect.fd,
    112 					      req->tag.connect.addr,
    113 					      req->tag.connect.addrlen);
    114 			break;
    115 
    116 		case IOREQ_RECV:
    117 			io_uring_prep_recv(sqe,
    118 					   req->tag.recv.fd,
    119 					   req->tag.recv.buf,
    120 					   req->tag.recv.len,
    121 					   req->tag.recv.flags);
    122 			break;
    123 
    124 		case IOREQ_SEND:
    125 			io_uring_prep_send(sqe,
    126 					   req->tag.send.fd,
    127 					   req->tag.send.buf,
    128 					   req->tag.send.len,
    129 					   req->tag.send.flags);
    130 			break;
    131 
    132 
    133 		case IOREQ_CLOSE:
    134 			io_uring_prep_close(sqe,
    135 					    req->tag.close.fd);
    136 			break;
    137 		}
    138 	}
    139 
    140 	return 0;
    141 }
    142 
    143 // connection : an abstraction over a socket and an ssl stream
    144 // ===========================================================================
    145 //
    146 
    147 struct connection {
    148 	int socket;
    149 
    150 	struct ioreq ioreq;
    151 
    152 	SSL *ssl;
    153 	BIO *ssl_bio, *net_bio;
    154 
    155 	size_t cur, len, cap;
    156 	unsigned char *buf;
    157 };
    158 
    159 static void
    160 conn_init(struct connection *conn, int fd, SSL *ssl, void *buf, size_t cap)
    161 {
    162 	conn->socket = fd;
    163 	conn->ssl = ssl;
    164 
    165 	BIO_new_bio_pair(&conn->ssl_bio, 0, &conn->net_bio, 0);
    166 	SSL_set_bio(conn->ssl, conn->ssl_bio, conn->ssl_bio);
    167 
    168 	conn->buf = buf;
    169 	conn->cap = cap;
    170 	conn->cur = conn->len = 0;
    171 }
    172 
    173 static void
    174 conn_free(struct connection *conn)
    175 {
    176 	SSL_free(conn->ssl);
    177 	BIO_free(conn->net_bio);
    178 }
    179 
    180 static int
    181 conn_prep_recv(struct connection *conn, struct io_uring *uring)
    182 {
    183 	char *buf;
    184 	int len = BIO_nwrite0(conn->net_bio, &buf);
    185 
    186 	DBGLOG("[conn:%d] receiving %d bytes\n", conn->socket, len);
    187 
    188 	conn->ioreq.type = IOREQ_RECV;
    189 	conn->ioreq.tag.recv.fd = conn->socket;
    190 	conn->ioreq.tag.recv.buf = buf;
    191 	conn->ioreq.tag.recv.len = len;
    192 	conn->ioreq.tag.recv.flags = 0;
    193 
    194 	return queue_ioreqs(uring, &conn->ioreq, 1);
    195 }
    196 
    197 static void
    198 conn_finish_recv(struct connection *conn, int res)
    199 {
    200 	DBGLOG("[conn:%d] recieved %d bytes\n", conn->socket, res);
    201 	BIO_nwrite(conn->net_bio, NULL, res);
    202 }
    203 
    204 static int
    205 conn_prep_send(struct connection *conn, struct io_uring *uring)
    206 {
    207 	char *buf;
    208 	int len = BIO_nread0(conn->net_bio, &buf);
    209 
    210 	DBGLOG("[conn:%d] sending %d bytes\n", conn->socket, len);
    211 
    212 	conn->ioreq.type = IOREQ_SEND;
    213 	conn->ioreq.tag.send.fd = conn->socket;
    214 	conn->ioreq.tag.send.buf = buf;
    215 	conn->ioreq.tag.send.len = len;
    216 	conn->ioreq.tag.send.flags = 0;
    217 
    218 	return queue_ioreqs(uring, &conn->ioreq, 1);
    219 }
    220 
    221 static void
    222 conn_finish_send(struct connection *conn, int res)
    223 {
    224 	DBGLOG("[conn:%d] sent %d bytes\n", conn->socket, res);
    225 	BIO_nread(conn->net_bio, NULL, res);
    226 }
    227 
    228 static int
    229 conn_prep_close(struct connection *conn, struct io_uring *uring)
    230 {
    231 	DBGLOG("[conn:%d] closing connection\n", conn->socket);
    232 
    233 	conn->ioreq.type = IOREQ_CLOSE;
    234 	conn->ioreq.tag.close.fd = conn->socket;
    235 
    236 	return queue_ioreqs(uring, &conn->ioreq, 1);
    237 }
    238 
    239 extern int
    240 do_tls_connect(struct io_uring *uring, struct connection *conn, int res);
    241 
    242 static int
    243 conn_do_tls_handshake(struct connection *conn, struct io_uring *uring,
    244 		      int transferred)
    245 {
    246 	int res = SSL_do_handshake(conn->ssl);
    247 	if (res == 1) /* completed handshake */
    248 		return do_tls_connect(uring, conn, transferred);
    249 
    250 	if (res == 0) /* disconnected */
    251 		return -1;
    252 
    253 	int err = SSL_get_error(conn->ssl, res);
    254 	if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
    255 		fprintf(stderr, "unexpected ssl error: %d\n", err);
    256 		ERR_print_errors_fp(stderr);
    257 		return -1;
    258 	}
    259 
    260 	int pending = BIO_ctrl_pending(conn->net_bio);
    261 	if (pending)
    262 		return conn_prep_send(conn, uring);
    263 
    264 	int expecting = BIO_ctrl_get_read_request(conn->net_bio);
    265 	if (expecting)
    266 		return conn_prep_recv(conn, uring);
    267 
    268 	return -1;
    269 }