sslexample

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

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 }