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 }