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