1 1.1 christos /* 2 1.1 christos * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * 4 1.1 christos * Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos * this file except in compliance with the License. You can obtain a copy 6 1.1 christos * in the file LICENSE in the source distribution or at 7 1.1 christos * https://www.openssl.org/source/license.html 8 1.1 christos */ 9 1.1 christos 10 1.1 christos #include <stdio.h> 11 1.1 christos #include <string.h> 12 1.1 christos #include <signal.h> 13 1.1 christos #include <openssl/ssl.h> 14 1.1 christos #include <openssl/err.h> 15 1.1 christos #if !defined(OPENSSL_SYS_WINDOWS) 16 1.1 christos #include <unistd.h> 17 1.1 christos #include <sys/socket.h> 18 1.1 christos #include <arpa/inet.h> 19 1.1 christos #include <netinet/in.h> 20 1.1 christos #else 21 1.1 christos #include <winsock.h> 22 1.1 christos #endif 23 1.1 christos 24 1.1 christos static const int server_port = 4433; 25 1.1 christos 26 1.1.1.2 christos typedef unsigned char flag; 27 1.1.1.2 christos #define true 1 28 1.1.1.2 christos #define false 0 29 1.1 christos 30 1.1 christos /* 31 1.1 christos * This flag won't be useful until both accept/read (TCP & SSL) methods 32 1.1 christos * can be called with a timeout. TBD. 33 1.1 christos */ 34 1.1 christos static volatile flag server_running = true; 35 1.1 christos 36 1.1 christos static int create_socket(flag isServer) 37 1.1 christos { 38 1.1 christos int s; 39 1.1 christos int optval = 1; 40 1.1 christos struct sockaddr_in addr; 41 1.1 christos 42 1.1 christos s = socket(AF_INET, SOCK_STREAM, 0); 43 1.1 christos if (s < 0) { 44 1.1 christos perror("Unable to create socket"); 45 1.1 christos exit(EXIT_FAILURE); 46 1.1 christos } 47 1.1 christos 48 1.1 christos if (isServer) { 49 1.1 christos addr.sin_family = AF_INET; 50 1.1 christos addr.sin_port = htons(server_port); 51 1.1 christos addr.sin_addr.s_addr = INADDR_ANY; 52 1.1 christos 53 1.1 christos /* Reuse the address; good for quick restarts */ 54 1.1 christos if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) 55 1.1.1.2 christos < 0) { 56 1.1 christos perror("setsockopt(SO_REUSEADDR) failed"); 57 1.1 christos exit(EXIT_FAILURE); 58 1.1 christos } 59 1.1 christos 60 1.1.1.2 christos if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 61 1.1 christos perror("Unable to bind"); 62 1.1 christos exit(EXIT_FAILURE); 63 1.1 christos } 64 1.1 christos 65 1.1 christos if (listen(s, 1) < 0) { 66 1.1 christos perror("Unable to listen"); 67 1.1 christos exit(EXIT_FAILURE); 68 1.1 christos } 69 1.1 christos } 70 1.1 christos 71 1.1 christos return s; 72 1.1 christos } 73 1.1 christos 74 1.1 christos static SSL_CTX *create_context(flag isServer) 75 1.1 christos { 76 1.1 christos const SSL_METHOD *method; 77 1.1 christos SSL_CTX *ctx; 78 1.1 christos 79 1.1 christos if (isServer) 80 1.1 christos method = TLS_server_method(); 81 1.1 christos else 82 1.1 christos method = TLS_client_method(); 83 1.1 christos 84 1.1 christos ctx = SSL_CTX_new(method); 85 1.1 christos if (ctx == NULL) { 86 1.1 christos perror("Unable to create SSL context"); 87 1.1 christos ERR_print_errors_fp(stderr); 88 1.1 christos exit(EXIT_FAILURE); 89 1.1 christos } 90 1.1 christos 91 1.1 christos return ctx; 92 1.1 christos } 93 1.1 christos 94 1.1 christos static void configure_server_context(SSL_CTX *ctx) 95 1.1 christos { 96 1.1 christos /* Set the key and cert */ 97 1.1 christos if (SSL_CTX_use_certificate_chain_file(ctx, "cert.pem") <= 0) { 98 1.1 christos ERR_print_errors_fp(stderr); 99 1.1 christos exit(EXIT_FAILURE); 100 1.1 christos } 101 1.1 christos 102 1.1 christos if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0) { 103 1.1 christos ERR_print_errors_fp(stderr); 104 1.1 christos exit(EXIT_FAILURE); 105 1.1 christos } 106 1.1 christos } 107 1.1 christos 108 1.1 christos static void configure_client_context(SSL_CTX *ctx) 109 1.1 christos { 110 1.1 christos /* 111 1.1 christos * Configure the client to abort the handshake if certificate verification 112 1.1 christos * fails 113 1.1 christos */ 114 1.1 christos SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); 115 1.1 christos /* 116 1.1 christos * In a real application you would probably just use the default system certificate trust store and call: 117 1.1 christos * SSL_CTX_set_default_verify_paths(ctx); 118 1.1 christos * In this demo though we are using a self-signed certificate, so the client must trust it directly. 119 1.1 christos */ 120 1.1 christos if (!SSL_CTX_load_verify_locations(ctx, "cert.pem", NULL)) { 121 1.1 christos ERR_print_errors_fp(stderr); 122 1.1 christos exit(EXIT_FAILURE); 123 1.1 christos } 124 1.1 christos } 125 1.1 christos 126 1.1 christos static void usage(void) 127 1.1 christos { 128 1.1 christos printf("Usage: sslecho s\n"); 129 1.1 christos printf(" --or--\n"); 130 1.1 christos printf(" sslecho c ip\n"); 131 1.1 christos printf(" c=client, s=server, ip=dotted ip of server\n"); 132 1.1 christos exit(EXIT_FAILURE); 133 1.1 christos } 134 1.1 christos 135 1.1 christos #define BUFFERSIZE 1024 136 1.1 christos int main(int argc, char **argv) 137 1.1 christos { 138 1.1 christos flag isServer; 139 1.1 christos int result; 140 1.1 christos 141 1.1 christos SSL_CTX *ssl_ctx = NULL; 142 1.1 christos SSL *ssl = NULL; 143 1.1 christos 144 1.1 christos int server_skt = -1; 145 1.1 christos int client_skt = -1; 146 1.1 christos 147 1.1 christos /* used by fgets */ 148 1.1 christos char buffer[BUFFERSIZE]; 149 1.1 christos char *txbuf; 150 1.1 christos 151 1.1 christos char rxbuf[128]; 152 1.1 christos size_t rxcap = sizeof(rxbuf); 153 1.1 christos int rxlen; 154 1.1 christos 155 1.1 christos char *rem_server_ip = NULL; 156 1.1 christos 157 1.1 christos struct sockaddr_in addr; 158 1.1 christos #if defined(OPENSSL_SYS_CYGWIN) || defined(OPENSSL_SYS_WINDOWS) 159 1.1 christos int addr_len = sizeof(addr); 160 1.1 christos #else 161 1.1 christos unsigned int addr_len = sizeof(addr); 162 1.1 christos #endif 163 1.1 christos 164 1.1.1.2 christos #if !defined(OPENSSL_SYS_WINDOWS) 165 1.1 christos /* ignore SIGPIPE so that server can continue running when client pipe closes abruptly */ 166 1.1 christos signal(SIGPIPE, SIG_IGN); 167 1.1 christos #endif 168 1.1 christos 169 1.1 christos /* Splash */ 170 1.1 christos printf("\nsslecho : Simple Echo Client/Server : %s : %s\n\n", __DATE__, 171 1.1.1.2 christos __TIME__); 172 1.1 christos 173 1.1 christos /* Need to know if client or server */ 174 1.1 christos if (argc < 2) { 175 1.1 christos usage(); 176 1.1 christos /* NOTREACHED */ 177 1.1 christos } 178 1.1 christos isServer = (argv[1][0] == 's') ? true : false; 179 1.1 christos /* If client get remote server address (could be 127.0.0.1) */ 180 1.1 christos if (!isServer) { 181 1.1 christos if (argc != 3) { 182 1.1 christos usage(); 183 1.1 christos /* NOTREACHED */ 184 1.1 christos } 185 1.1 christos rem_server_ip = argv[2]; 186 1.1 christos } 187 1.1 christos 188 1.1 christos /* Create context used by both client and server */ 189 1.1 christos ssl_ctx = create_context(isServer); 190 1.1 christos 191 1.1 christos /* If server */ 192 1.1 christos if (isServer) { 193 1.1 christos 194 1.1 christos printf("We are the server on port: %d\n\n", server_port); 195 1.1 christos 196 1.1 christos /* Configure server context with appropriate key files */ 197 1.1 christos configure_server_context(ssl_ctx); 198 1.1 christos 199 1.1 christos /* Create server socket; will bind with server port and listen */ 200 1.1 christos server_skt = create_socket(true); 201 1.1 christos 202 1.1 christos /* 203 1.1 christos * Loop to accept clients. 204 1.1 christos * Need to implement timeouts on TCP & SSL connect/read functions 205 1.1 christos * before we can catch a CTRL-C and kill the server. 206 1.1 christos */ 207 1.1 christos while (server_running) { 208 1.1 christos /* Wait for TCP connection from client */ 209 1.1.1.2 christos client_skt = accept(server_skt, (struct sockaddr *)&addr, 210 1.1.1.2 christos &addr_len); 211 1.1 christos if (client_skt < 0) { 212 1.1 christos perror("Unable to accept"); 213 1.1 christos exit(EXIT_FAILURE); 214 1.1 christos } 215 1.1 christos 216 1.1 christos printf("Client TCP connection accepted\n"); 217 1.1 christos 218 1.1 christos /* Create server SSL structure using newly accepted client socket */ 219 1.1 christos ssl = SSL_new(ssl_ctx); 220 1.1 christos if (!SSL_set_fd(ssl, client_skt)) { 221 1.1 christos ERR_print_errors_fp(stderr); 222 1.1 christos exit(EXIT_FAILURE); 223 1.1 christos } 224 1.1 christos 225 1.1 christos /* Wait for SSL connection from the client */ 226 1.1 christos if (SSL_accept(ssl) <= 0) { 227 1.1 christos ERR_print_errors_fp(stderr); 228 1.1 christos server_running = false; 229 1.1 christos } else { 230 1.1 christos 231 1.1 christos printf("Client SSL connection accepted\n\n"); 232 1.1 christos 233 1.1 christos /* Echo loop */ 234 1.1 christos while (true) { 235 1.1 christos /* Get message from client; will fail if client closes connection */ 236 1.1 christos if ((rxlen = SSL_read(ssl, rxbuf, rxcap)) <= 0) { 237 1.1 christos if (rxlen == 0) { 238 1.1 christos printf("Client closed connection\n"); 239 1.1 christos } else { 240 1.1 christos printf("SSL_read returned %d\n", rxlen); 241 1.1 christos } 242 1.1 christos ERR_print_errors_fp(stderr); 243 1.1 christos break; 244 1.1 christos } 245 1.1 christos /* Insure null terminated input */ 246 1.1 christos rxbuf[rxlen] = 0; 247 1.1 christos /* Look for kill switch */ 248 1.1 christos if (strcmp(rxbuf, "kill\n") == 0) { 249 1.1 christos /* Terminate...with extreme prejudice */ 250 1.1 christos printf("Server received 'kill' command\n"); 251 1.1 christos server_running = false; 252 1.1 christos break; 253 1.1 christos } 254 1.1 christos /* Show received message */ 255 1.1 christos printf("Received: %s", rxbuf); 256 1.1 christos /* Echo it back */ 257 1.1 christos if (SSL_write(ssl, rxbuf, rxlen) <= 0) { 258 1.1 christos ERR_print_errors_fp(stderr); 259 1.1 christos } 260 1.1 christos } 261 1.1 christos } 262 1.1 christos if (server_running) { 263 1.1 christos /* Cleanup for next client */ 264 1.1 christos SSL_shutdown(ssl); 265 1.1 christos SSL_free(ssl); 266 1.1 christos close(client_skt); 267 1.1 christos /* 268 1.1 christos * Set client_skt to -1 to avoid double close when 269 1.1 christos * server_running become false before next accept 270 1.1 christos */ 271 1.1 christos client_skt = -1; 272 1.1 christos } 273 1.1 christos } 274 1.1 christos printf("Server exiting...\n"); 275 1.1 christos } 276 1.1 christos /* Else client */ 277 1.1 christos else { 278 1.1 christos 279 1.1 christos printf("We are the client\n\n"); 280 1.1 christos 281 1.1 christos /* Configure client context so we verify the server correctly */ 282 1.1 christos configure_client_context(ssl_ctx); 283 1.1 christos 284 1.1 christos /* Create "bare" socket */ 285 1.1 christos client_skt = create_socket(false); 286 1.1 christos /* Set up connect address */ 287 1.1 christos addr.sin_family = AF_INET; 288 1.1 christos inet_pton(AF_INET, rem_server_ip, &addr.sin_addr.s_addr); 289 1.1 christos addr.sin_port = htons(server_port); 290 1.1 christos /* Do TCP connect with server */ 291 1.1.1.2 christos if (connect(client_skt, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 292 1.1 christos perror("Unable to TCP connect to server"); 293 1.1 christos goto exit; 294 1.1 christos } else { 295 1.1 christos printf("TCP connection to server successful\n"); 296 1.1 christos } 297 1.1 christos 298 1.1 christos /* Create client SSL structure using dedicated client socket */ 299 1.1 christos ssl = SSL_new(ssl_ctx); 300 1.1 christos if (!SSL_set_fd(ssl, client_skt)) { 301 1.1 christos ERR_print_errors_fp(stderr); 302 1.1 christos goto exit; 303 1.1 christos } 304 1.1 christos /* Set hostname for SNI */ 305 1.1 christos SSL_set_tlsext_host_name(ssl, rem_server_ip); 306 1.1 christos /* Configure server hostname check */ 307 1.1 christos if (!SSL_set1_host(ssl, rem_server_ip)) { 308 1.1 christos ERR_print_errors_fp(stderr); 309 1.1 christos goto exit; 310 1.1 christos } 311 1.1 christos 312 1.1 christos /* Now do SSL connect with server */ 313 1.1 christos if (SSL_connect(ssl) == 1) { 314 1.1 christos 315 1.1 christos printf("SSL connection to server successful\n\n"); 316 1.1 christos 317 1.1 christos /* Loop to send input from keyboard */ 318 1.1 christos while (true) { 319 1.1 christos /* Get a line of input */ 320 1.1 christos memset(buffer, 0, BUFFERSIZE); 321 1.1 christos txbuf = fgets(buffer, BUFFERSIZE, stdin); 322 1.1 christos 323 1.1 christos /* Exit loop on error */ 324 1.1 christos if (txbuf == NULL) { 325 1.1 christos break; 326 1.1 christos } 327 1.1 christos /* Exit loop if just a carriage return */ 328 1.1 christos if (txbuf[0] == '\n') { 329 1.1 christos break; 330 1.1 christos } 331 1.1 christos /* Send it to the server */ 332 1.1 christos if ((result = SSL_write(ssl, txbuf, strlen(txbuf))) <= 0) { 333 1.1 christos printf("Server closed connection\n"); 334 1.1 christos ERR_print_errors_fp(stderr); 335 1.1 christos break; 336 1.1 christos } 337 1.1 christos 338 1.1 christos /* Wait for the echo */ 339 1.1 christos rxlen = SSL_read(ssl, rxbuf, rxcap); 340 1.1 christos if (rxlen <= 0) { 341 1.1 christos printf("Server closed connection\n"); 342 1.1 christos ERR_print_errors_fp(stderr); 343 1.1 christos break; 344 1.1 christos } else { 345 1.1 christos /* Show it */ 346 1.1 christos rxbuf[rxlen] = 0; 347 1.1 christos printf("Received: %s", rxbuf); 348 1.1 christos } 349 1.1 christos } 350 1.1 christos printf("Client exiting...\n"); 351 1.1 christos } else { 352 1.1 christos 353 1.1 christos printf("SSL connection to server failed\n\n"); 354 1.1 christos 355 1.1 christos ERR_print_errors_fp(stderr); 356 1.1 christos } 357 1.1 christos } 358 1.1 christos exit: 359 1.1 christos /* Close up */ 360 1.1 christos if (ssl != NULL) { 361 1.1 christos SSL_shutdown(ssl); 362 1.1 christos SSL_free(ssl); 363 1.1 christos } 364 1.1 christos SSL_CTX_free(ssl_ctx); 365 1.1 christos 366 1.1 christos if (client_skt != -1) 367 1.1 christos close(client_skt); 368 1.1 christos if (server_skt != -1) 369 1.1 christos close(server_skt); 370 1.1 christos 371 1.1 christos printf("sslecho exiting\n"); 372 1.1 christos 373 1.1 christos return EXIT_SUCCESS; 374 1.1 christos } 375