Added Simple SSL Echo Client/Server to demos.
[openssl.git] / demos / sslecho / main.c
diff --git a/demos/sslecho/main.c b/demos/sslecho/main.c
new file mode 100644 (file)
index 0000000..c6f087c
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ *  Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ *  Licensed under the Apache License 2.0 (the "License").  You may not use
+ *  this file except in compliance with the License.  You can obtain a copy
+ *  in the file LICENSE in the source distribution or at
+ *  https://www.openssl.org/source/license.html
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+static const int server_port = 4433;
+
+typedef unsigned char   bool;
+#define true            1
+#define false           0
+
+/*
+ * This flag won't be useful until both accept/read (TCP & SSL) methods
+ * can be called with a timeout. TBD.
+ */
+static volatile bool    server_running = true;
+
+int create_socket(bool isServer)
+{
+    int s;
+    int optval = 1;
+    struct sockaddr_in addr;
+
+    s = socket(AF_INET, SOCK_STREAM, 0);
+    if (s < 0) {
+        perror("Unable to create socket");
+        exit(EXIT_FAILURE);
+    }
+
+    if (isServer) {
+        addr.sin_family = AF_INET;
+        addr.sin_port = htons(server_port);
+        addr.sin_addr.s_addr = INADDR_ANY;
+
+        /* Reuse the address; good for quick restarts */
+        if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))
+                < 0) {
+            perror("setsockopt(SO_REUSEADDR) failed");
+            exit(EXIT_FAILURE);
+        }
+
+        if (bind(s, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
+            perror("Unable to bind");
+            exit(EXIT_FAILURE);
+        }
+
+        if (listen(s, 1) < 0) {
+            perror("Unable to listen");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    return s;
+}
+
+SSL_CTX* create_context(bool isServer)
+{
+    const SSL_METHOD *method;
+    SSL_CTX *ctx;
+
+    if (isServer)
+        method = TLS_server_method();
+    else
+        method = TLS_client_method();
+
+    ctx = SSL_CTX_new(method);
+    if (ctx == NULL) {
+        perror("Unable to create SSL context");
+        ERR_print_errors_fp(stderr);
+        exit(EXIT_FAILURE);
+    }
+
+    return ctx;
+}
+
+void configure_server_context(SSL_CTX *ctx)
+{
+    /* Set the key and cert */
+    if (SSL_CTX_use_certificate_chain_file(ctx, "cert.pem") <= 0) {
+        ERR_print_errors_fp(stderr);
+        exit(EXIT_FAILURE);
+    }
+
+    if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0) {
+        ERR_print_errors_fp(stderr);
+        exit(EXIT_FAILURE);
+    }
+}
+
+void configure_client_context(SSL_CTX *ctx)
+{
+    /*
+     * Configure the client to abort the handshake if certificate verification
+     * fails
+     */
+    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+    /*
+     * In a real application you would probably just use the default system certificate trust store and call:
+     *     SSL_CTX_set_default_verify_paths(ctx);
+     * In this demo though we are using a self-signed certificate, so the client must trust it directly.
+     */
+    if (!SSL_CTX_load_verify_locations(ctx, "cert.pem", NULL)) {
+        ERR_print_errors_fp(stderr);
+        exit(EXIT_FAILURE);
+    }
+}
+
+void usage()
+{
+    printf("Usage: sslecho s\n");
+    printf("       --or--\n");
+    printf("       sslecho c ip\n");
+    printf("       c=client, s=server, ip=dotted ip of server\n");
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    bool isServer;
+    int result;
+
+    SSL_CTX *ssl_ctx = NULL;
+    SSL *ssl = NULL;
+
+    int server_skt = -1;
+    int client_skt = -1;
+
+    char txbuf[128];
+    size_t txcap = sizeof(txbuf);
+    int txlen;
+
+    char rxbuf[128];
+    size_t rxcap = sizeof(rxbuf);
+    int rxlen;
+
+    char *rem_server_ip = NULL;
+
+    struct sockaddr_in addr;
+    unsigned int addr_len = sizeof(addr);
+
+    /* Splash */
+    printf("\nsslecho : Simple Echo Client/Server (OpenSSL 3.0.1-dev) : %s : %s\n\n", __DATE__,
+    __TIME__);
+
+    /* Need to know if client or server */
+    if (argc < 2) {
+        usage();
+        /* NOTREACHED */
+    }
+    isServer = (argv[1][0] == 's') ? true : false;
+    /* If client get remote server address (could be 127.0.0.1) */
+    if (!isServer) {
+        if (argc != 3) {
+            usage();
+            /* NOTREACHED */
+        }
+        rem_server_ip = argv[2];
+    }
+
+    /* Create context used by both client and server */
+    ssl_ctx = create_context(isServer);
+
+    /* If server */
+    if (isServer) {
+
+        printf("We are the server on port: %d\n\n", server_port);
+
+        /* Configure server context with appropriate key files */
+        configure_server_context(ssl_ctx);
+
+        /* Create server socket; will bind with server port and listen */
+        server_skt = create_socket(true);
+
+        /*
+         * Loop to accept clients.
+         * Need to implement timeouts on TCP & SSL connect/read functions
+         * before we can catch a CTRL-C and kill the server.
+         */
+        while (server_running) {
+            /* Wait for TCP connection from client */
+            client_skt = accept(server_skt, (struct sockaddr*) &addr,
+                    &addr_len);
+            if (client_skt < 0) {
+                perror("Unable to accept");
+                exit(EXIT_FAILURE);
+            }
+
+            printf("Client TCP connection accepted\n");
+
+            /* Create server SSL structure using newly accepted client socket */
+            ssl = SSL_new(ssl_ctx);
+            SSL_set_fd(ssl, client_skt);
+
+            /* Wait for SSL connection from the client */
+            if (SSL_accept(ssl) <= 0) {
+                ERR_print_errors_fp(stderr);
+                server_running = false;
+            } else {
+
+                printf("Client SSL connection accepted\n\n");
+
+                /* Echo loop */
+                while (true) {
+                    /* Get message from client; will fail if client closes connection */
+                    if ((rxlen = SSL_read(ssl, rxbuf, rxcap)) <= 0) {
+                        if (rxlen == 0) {
+                            printf("Client closed connection\n");
+                        }
+                        ERR_print_errors_fp(stderr);
+                        break;
+                    }
+                    /* Insure null terminated input */
+                    rxbuf[rxlen] = 0;
+                    /* Look for kill switch */
+                    if (strcmp(rxbuf, "kill\n") == 0) {
+                        /* Terminate...with extreme prejudice */
+                        printf("Server received 'kill' command\n");
+                        server_running = false;
+                        break;
+                    }
+                    /* Show received message */
+                    printf("Received: %s", rxbuf);
+                    /* Echo it back */
+                    if (SSL_write(ssl, rxbuf, rxlen) <= 0) {
+                        ERR_print_errors_fp(stderr);
+                    }
+                }
+            }
+            if (server_running) {
+                /* Cleanup for next client */
+                SSL_shutdown(ssl);
+                SSL_free(ssl);
+                close(client_skt);
+            }
+        }
+        printf("Server exiting...\n");
+    }
+    /* Else client */
+    else {
+
+        printf("We are the client\n\n");
+
+        /* Configure client context so we verify the server correctly */
+        configure_client_context(ssl_ctx);
+
+        /* Create "bare" socket */
+        client_skt = create_socket(false);
+        /* Set up connect address */
+        addr.sin_family = AF_INET;
+        inet_pton(AF_INET, rem_server_ip, &addr.sin_addr.s_addr);
+        addr.sin_port = htons(server_port);
+        /* Do TCP connect with server */
+        if (connect(client_skt, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
+            perror("Unable to TCP connect to server");
+            goto exit;
+        } else {
+            printf("TCP connection to server successful\n");
+        }
+
+        /* Create client SSL structure using dedicated client socket */
+        ssl = SSL_new(ssl_ctx);
+        SSL_set_fd(ssl, client_skt);
+        /* Set host name for SNI */
+        SSL_set_tlsext_host_name(ssl, rem_server_ip);
+        /* Configure server hostname check */
+        SSL_set1_host(ssl, rem_server_ip);
+
+        /* Now do SSL connect with server */
+        if (SSL_connect(ssl) == 1) {
+
+            printf("SSL connection to server successful\n\n");
+
+            /* Loop to send input from keyboard */
+            while (true) {
+                /* Get a line of input */
+                txlen = getline(&txbuf, &txcap, stdin);
+                /* Exit loop if just a carriage return */
+                if (txbuf[0] == '\n') {
+                    break;
+                }
+
+                /* Send it to the server */
+                if ((result = SSL_write(ssl, txbuf, txlen)) <= 0) {
+                    printf("Server closed connection\n");
+                    ERR_print_errors_fp(stderr);
+                    break;
+                }
+
+                /* Wait for the echo */
+                rxlen = SSL_read(ssl, rxbuf, rxcap);
+                if (rxlen <= 0) {
+                    printf("Server closed connection\n");
+                    ERR_print_errors_fp(stderr);
+                    break;
+                } else {
+                    /* Show it */
+                    rxbuf[rxlen] = 0;
+                    printf("Received: %s", rxbuf);
+                }
+            }
+            printf("Client exiting...\n");
+        } else {
+
+            printf("SSL connection to server failed\n\n");
+
+            ERR_print_errors_fp(stderr);
+        }
+    }
+    exit:
+    /* Close up */
+    if (ssl != NULL) {
+        SSL_shutdown(ssl);
+        SSL_free(ssl);
+    }
+    SSL_CTX_free(ssl_ctx);
+
+    if (client_skt != -1)
+        close(client_skt);
+    if (server_skt != -1)
+        close(server_skt);
+
+    OPENSSL_free(txbuf);
+    OPENSSL_free(rxbuf);
+
+    printf("sslecho exiting\n");
+
+    return 0;
+}