Path: blob/main/crypto/openssl/demos/sslecho/main.c
34889 views
/*1* Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#include <stdio.h>10#include <string.h>11#include <signal.h>12#include <openssl/ssl.h>13#include <openssl/err.h>14#if !defined(OPENSSL_SYS_WINDOWS)15#include <unistd.h>16#include <sys/socket.h>17#include <arpa/inet.h>18#include <netinet/in.h>19#else20#include <winsock.h>21#endif2223static const int server_port = 4433;2425typedef unsigned char flag;26#define true 127#define false 02829/*30* This flag won't be useful until both accept/read (TCP & SSL) methods31* can be called with a timeout. TBD.32*/33static volatile flag server_running = true;3435static int create_socket(flag isServer)36{37int s;38int optval = 1;39struct sockaddr_in addr;4041s = socket(AF_INET, SOCK_STREAM, 0);42if (s < 0) {43perror("Unable to create socket");44exit(EXIT_FAILURE);45}4647if (isServer) {48addr.sin_family = AF_INET;49addr.sin_port = htons(server_port);50addr.sin_addr.s_addr = INADDR_ANY;5152/* Reuse the address; good for quick restarts */53if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))54< 0) {55perror("setsockopt(SO_REUSEADDR) failed");56exit(EXIT_FAILURE);57}5859if (bind(s, (struct sockaddr*) &addr, sizeof(addr)) < 0) {60perror("Unable to bind");61exit(EXIT_FAILURE);62}6364if (listen(s, 1) < 0) {65perror("Unable to listen");66exit(EXIT_FAILURE);67}68}6970return s;71}7273static SSL_CTX *create_context(flag isServer)74{75const SSL_METHOD *method;76SSL_CTX *ctx;7778if (isServer)79method = TLS_server_method();80else81method = TLS_client_method();8283ctx = SSL_CTX_new(method);84if (ctx == NULL) {85perror("Unable to create SSL context");86ERR_print_errors_fp(stderr);87exit(EXIT_FAILURE);88}8990return ctx;91}9293static void configure_server_context(SSL_CTX *ctx)94{95/* Set the key and cert */96if (SSL_CTX_use_certificate_chain_file(ctx, "cert.pem") <= 0) {97ERR_print_errors_fp(stderr);98exit(EXIT_FAILURE);99}100101if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0) {102ERR_print_errors_fp(stderr);103exit(EXIT_FAILURE);104}105}106107static void configure_client_context(SSL_CTX *ctx)108{109/*110* Configure the client to abort the handshake if certificate verification111* fails112*/113SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);114/*115* In a real application you would probably just use the default system certificate trust store and call:116* SSL_CTX_set_default_verify_paths(ctx);117* In this demo though we are using a self-signed certificate, so the client must trust it directly.118*/119if (!SSL_CTX_load_verify_locations(ctx, "cert.pem", NULL)) {120ERR_print_errors_fp(stderr);121exit(EXIT_FAILURE);122}123}124125static void usage(void)126{127printf("Usage: sslecho s\n");128printf(" --or--\n");129printf(" sslecho c ip\n");130printf(" c=client, s=server, ip=dotted ip of server\n");131exit(EXIT_FAILURE);132}133134#define BUFFERSIZE 1024135int main(int argc, char **argv)136{137flag isServer;138int result;139140SSL_CTX *ssl_ctx = NULL;141SSL *ssl = NULL;142143int server_skt = -1;144int client_skt = -1;145146/* used by fgets */147char buffer[BUFFERSIZE];148char *txbuf;149150char rxbuf[128];151size_t rxcap = sizeof(rxbuf);152int rxlen;153154char *rem_server_ip = NULL;155156struct sockaddr_in addr;157#if defined(OPENSSL_SYS_CYGWIN) || defined(OPENSSL_SYS_WINDOWS)158int addr_len = sizeof(addr);159#else160unsigned int addr_len = sizeof(addr);161#endif162163#if !defined (OPENSSL_SYS_WINDOWS)164/* ignore SIGPIPE so that server can continue running when client pipe closes abruptly */165signal(SIGPIPE, SIG_IGN);166#endif167168/* Splash */169printf("\nsslecho : Simple Echo Client/Server : %s : %s\n\n", __DATE__,170__TIME__);171172/* Need to know if client or server */173if (argc < 2) {174usage();175/* NOTREACHED */176}177isServer = (argv[1][0] == 's') ? true : false;178/* If client get remote server address (could be 127.0.0.1) */179if (!isServer) {180if (argc != 3) {181usage();182/* NOTREACHED */183}184rem_server_ip = argv[2];185}186187/* Create context used by both client and server */188ssl_ctx = create_context(isServer);189190/* If server */191if (isServer) {192193printf("We are the server on port: %d\n\n", server_port);194195/* Configure server context with appropriate key files */196configure_server_context(ssl_ctx);197198/* Create server socket; will bind with server port and listen */199server_skt = create_socket(true);200201/*202* Loop to accept clients.203* Need to implement timeouts on TCP & SSL connect/read functions204* before we can catch a CTRL-C and kill the server.205*/206while (server_running) {207/* Wait for TCP connection from client */208client_skt = accept(server_skt, (struct sockaddr*) &addr,209&addr_len);210if (client_skt < 0) {211perror("Unable to accept");212exit(EXIT_FAILURE);213}214215printf("Client TCP connection accepted\n");216217/* Create server SSL structure using newly accepted client socket */218ssl = SSL_new(ssl_ctx);219if (!SSL_set_fd(ssl, client_skt)) {220ERR_print_errors_fp(stderr);221exit(EXIT_FAILURE);222}223224/* Wait for SSL connection from the client */225if (SSL_accept(ssl) <= 0) {226ERR_print_errors_fp(stderr);227server_running = false;228} else {229230printf("Client SSL connection accepted\n\n");231232/* Echo loop */233while (true) {234/* Get message from client; will fail if client closes connection */235if ((rxlen = SSL_read(ssl, rxbuf, rxcap)) <= 0) {236if (rxlen == 0) {237printf("Client closed connection\n");238} else {239printf("SSL_read returned %d\n", rxlen);240}241ERR_print_errors_fp(stderr);242break;243}244/* Insure null terminated input */245rxbuf[rxlen] = 0;246/* Look for kill switch */247if (strcmp(rxbuf, "kill\n") == 0) {248/* Terminate...with extreme prejudice */249printf("Server received 'kill' command\n");250server_running = false;251break;252}253/* Show received message */254printf("Received: %s", rxbuf);255/* Echo it back */256if (SSL_write(ssl, rxbuf, rxlen) <= 0) {257ERR_print_errors_fp(stderr);258}259}260}261if (server_running) {262/* Cleanup for next client */263SSL_shutdown(ssl);264SSL_free(ssl);265close(client_skt);266/*267* Set client_skt to -1 to avoid double close when268* server_running become false before next accept269*/270client_skt = -1;271}272}273printf("Server exiting...\n");274}275/* Else client */276else {277278printf("We are the client\n\n");279280/* Configure client context so we verify the server correctly */281configure_client_context(ssl_ctx);282283/* Create "bare" socket */284client_skt = create_socket(false);285/* Set up connect address */286addr.sin_family = AF_INET;287inet_pton(AF_INET, rem_server_ip, &addr.sin_addr.s_addr);288addr.sin_port = htons(server_port);289/* Do TCP connect with server */290if (connect(client_skt, (struct sockaddr*) &addr, sizeof(addr)) != 0) {291perror("Unable to TCP connect to server");292goto exit;293} else {294printf("TCP connection to server successful\n");295}296297/* Create client SSL structure using dedicated client socket */298ssl = SSL_new(ssl_ctx);299if (!SSL_set_fd(ssl, client_skt)) {300ERR_print_errors_fp(stderr);301goto exit;302}303/* Set hostname for SNI */304SSL_set_tlsext_host_name(ssl, rem_server_ip);305/* Configure server hostname check */306if (!SSL_set1_host(ssl, rem_server_ip)) {307ERR_print_errors_fp(stderr);308goto exit;309}310311/* Now do SSL connect with server */312if (SSL_connect(ssl) == 1) {313314printf("SSL connection to server successful\n\n");315316/* Loop to send input from keyboard */317while (true) {318/* Get a line of input */319memset(buffer, 0, BUFFERSIZE);320txbuf = fgets(buffer, BUFFERSIZE, stdin);321322/* Exit loop on error */323if (txbuf == NULL) {324break;325}326/* Exit loop if just a carriage return */327if (txbuf[0] == '\n') {328break;329}330/* Send it to the server */331if ((result = SSL_write(ssl, txbuf, strlen(txbuf))) <= 0) {332printf("Server closed connection\n");333ERR_print_errors_fp(stderr);334break;335}336337/* Wait for the echo */338rxlen = SSL_read(ssl, rxbuf, rxcap);339if (rxlen <= 0) {340printf("Server closed connection\n");341ERR_print_errors_fp(stderr);342break;343} else {344/* Show it */345rxbuf[rxlen] = 0;346printf("Received: %s", rxbuf);347}348}349printf("Client exiting...\n");350} else {351352printf("SSL connection to server failed\n\n");353354ERR_print_errors_fp(stderr);355}356}357exit:358/* Close up */359if (ssl != NULL) {360SSL_shutdown(ssl);361SSL_free(ssl);362}363SSL_CTX_free(ssl_ctx);364365if (client_skt != -1)366close(client_skt);367if (server_skt != -1)368close(server_skt);369370printf("sslecho exiting\n");371372return EXIT_SUCCESS;373}374375376