sslclient.c (5567B)
1 #include "common.h" 2 3 struct ssl_client { 4 struct connection conn; 5 }; 6 7 int 8 do_tls_connect(struct io_uring *uring, struct connection *conn, int res) 9 { 10 DBGLOG("completed tls handshake!\n"); 11 12 #ifndef NDEBUG 13 conn->len = snprintf((char *) conn->buf, conn->cap, "Hello, World!\n"); 14 #else 15 conn->len = conn->cap; 16 #endif 17 18 printf("sending and expecting to receive %zu byte chunks\n", conn->len); 19 20 conn->cur = SSL_write(conn->ssl, conn->buf, conn->len); 21 22 return conn_prep_send(conn, uring); 23 } 24 25 int 26 do_recv(struct io_uring *uring, struct connection *conn, int res) 27 { 28 if (conn->cur < conn->len) { /* have not received entire message */ 29 conn->cur += SSL_read(conn->ssl, conn->buf + conn->cur, conn->len - conn->cur); 30 return conn_prep_recv(conn, uring); 31 } else { /* received entire message */ 32 #ifndef NDEBUG 33 printf("received %zu plaintext bytes\n", conn->cur); 34 35 uint64_t nsec_per_msec = 1000000; 36 struct timespec timeout = { .tv_nsec = nsec_per_msec, }; 37 while (nanosleep(&timeout, &timeout) < 0); 38 #endif 39 40 conn->cur = SSL_write(conn->ssl, conn->buf, conn->len); 41 42 return conn_prep_send(conn, uring); 43 } 44 } 45 46 int 47 do_send(struct io_uring *uring, struct connection *conn, int res) 48 { 49 if (conn->cur < conn->len) { /* have not sent entire message */ 50 conn->cur += SSL_write(conn->ssl, conn->buf + conn->cur, conn->len - conn->cur); 51 return conn_prep_send(conn, uring); 52 } else { /* sent entire message */ 53 #ifndef NDEBUG 54 printf("sent %zu plaintext bytes\n", conn->cur); 55 #endif 56 57 conn->cur = SSL_read(conn->ssl, conn->buf, conn->len); 58 59 return conn_prep_recv(conn, uring); 60 } 61 } 62 63 struct ssl_app { 64 char const *host, *port; 65 66 struct io_uring io_uring; 67 68 SSL_CTX *ssl_ctx; 69 }; 70 71 int 72 start_client(struct ssl_app *app) 73 { 74 struct addrinfo hints = { 75 .ai_family = AF_UNSPEC, 76 .ai_socktype = SOCK_STREAM, 77 .ai_protocol = IPPROTO_TCP, 78 .ai_flags = AI_NUMERICSERV, 79 }, *addrinfo, *ptr; 80 81 int res; 82 if ((res = getaddrinfo(app->host, app->port, &hints, &addrinfo))) { 83 fprintf(stderr, "Failed to get address info: %s\n", 84 gai_strerror(res)); 85 exit(EXIT_FAILURE); 86 } 87 88 int sock; 89 for (ptr = addrinfo; ptr; ptr = ptr->ai_next) { 90 sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); 91 if (sock < 0) 92 continue; 93 94 if (connect(sock, ptr->ai_addr, ptr->ai_addrlen) < 0) { 95 close(sock); 96 continue; 97 } 98 99 break; 100 } 101 102 if (ptr) { 103 char host[NI_MAXHOST], serv[NI_MAXSERV]; 104 getnameinfo(ptr->ai_addr, ptr->ai_addrlen, 105 host, sizeof host, serv, sizeof serv, 106 NI_NUMERICSERV); 107 printf("Connected to %s:%s\n", host, serv); 108 } 109 110 freeaddrinfo(addrinfo); 111 112 if (!ptr) { 113 fprintf(stderr, "Failed to connect to %s:%s\n", 114 app->host, app->port); 115 exit(EXIT_FAILURE); 116 } 117 118 struct ssl_client *client = malloc(sizeof *client); 119 assert(client); 120 121 SSL *ssl = SSL_new(app->ssl_ctx); 122 assert(ssl); 123 124 SSL_set_connect_state(ssl); 125 SSL_set_tlsext_host_name(ssl, app->host); 126 SSL_set1_host(ssl, app->host); 127 128 size_t cap = 4096; 129 void *buf = malloc(cap); 130 assert(buf); 131 132 conn_init(&client->conn, sock, ssl, buf, cap); 133 134 return conn_do_tls_handshake(&client->conn, &app->io_uring, 0); 135 } 136 137 void 138 stop_client(struct ssl_app *app, struct connection *conn) 139 { 140 conn_free(conn); 141 142 struct ssl_client *client = TO_PARENT_PTR(conn, struct ssl_client, conn); 143 144 free(client->conn.buf); 145 free(client); 146 } 147 148 int 149 handle(struct ssl_app *app) 150 { 151 start_client(app); 152 153 int quit = 0; 154 while (!quit) { 155 io_uring_submit_and_wait(&app->io_uring, 1); 156 157 struct io_uring_cqe *cqe; 158 unsigned head, seen_cqes = 0; 159 io_uring_for_each_cqe(&app->io_uring, head, cqe) { 160 struct ioreq *ioreq = io_uring_cqe_get_data(cqe); 161 assert(ioreq); 162 163 struct connection *conn = 164 TO_PARENT_PTR(ioreq, struct connection, ioreq); 165 166 switch (ioreq->type) { 167 case IOREQ_RECV: { 168 if (cqe->res <= 0) { 169 conn_prep_close(conn, &app->io_uring); 170 goto next_cqe; 171 } 172 173 conn_finish_recv(conn, cqe->res); 174 175 if (!SSL_is_init_finished(conn->ssl)) { 176 conn_do_tls_handshake(conn, &app->io_uring, cqe->res); 177 } else { 178 do_recv(&app->io_uring, conn, cqe->res); 179 } 180 } break; 181 182 case IOREQ_SEND: { 183 if (cqe->res <= 0) { 184 conn_prep_close(conn, &app->io_uring); 185 goto next_cqe; 186 } 187 188 conn_finish_send(conn, cqe->res); 189 190 if (!SSL_is_init_finished(conn->ssl)) { 191 conn_do_tls_handshake(conn, &app->io_uring, cqe->res); 192 } else { 193 do_send(&app->io_uring, conn, cqe->res); 194 } 195 } break; 196 197 case IOREQ_CLOSE: { 198 stop_client(app, conn); 199 quit = 1; 200 } break; 201 202 default: 203 break; 204 } 205 206 next_cqe: 207 seen_cqes++; 208 } 209 210 io_uring_cq_advance(&app->io_uring, seen_cqes); 211 } 212 213 return EXIT_SUCCESS; 214 } 215 216 int 217 main(int argc, char **argv) 218 { 219 if (argc < 4) { 220 fprintf(stderr, "Usage: %s <host> <port> <cert>\n", argv[0]); 221 exit(EXIT_FAILURE); 222 } 223 224 char *host = argv[1], *port = argv[2], *cert = argv[3]; 225 226 struct ssl_app app; 227 memset(&app, 0, sizeof app); 228 229 app.host = host; 230 app.port = port; 231 232 // create uring 233 // =================================================================== 234 // 235 236 unsigned entries = 32, flags = 0; 237 io_uring_queue_init(entries, &app.io_uring, flags); 238 239 // setup global ssl context and client ssl context 240 // =================================================================== 241 // 242 243 app.ssl_ctx = SSL_CTX_new(TLS_method()); 244 assert(app.ssl_ctx); 245 246 SSL_CTX_set_min_proto_version(app.ssl_ctx, TLS1_2_VERSION); 247 SSL_CTX_set_verify(app.ssl_ctx, SSL_VERIFY_PEER, NULL); 248 SSL_CTX_load_verify_file(app.ssl_ctx, cert); 249 250 // start client 251 // =================================================================== 252 // 253 254 exit(handle(&app)); 255 }