xref: /openssl/demos/sslecho/main.c (revision 3472732c)
1 /*
2  *  Copyright 2022-2024 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   bool;
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 bool    server_running = true;
35 
create_socket(bool isServer)36 static int create_socket(bool 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 
create_context(bool isServer)74 static SSL_CTX* create_context(bool 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 
configure_server_context(SSL_CTX * ctx)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 
configure_client_context(SSL_CTX * ctx)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 
usage(void)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
main(int argc,char ** argv)136 int main(int argc, char **argv)
137 {
138     bool 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         }
269         printf("Server exiting...\n");
270     }
271     /* Else client */
272     else {
273 
274         printf("We are the client\n\n");
275 
276         /* Configure client context so we verify the server correctly */
277         configure_client_context(ssl_ctx);
278 
279         /* Create "bare" socket */
280         client_skt = create_socket(false);
281         /* Set up connect address */
282         addr.sin_family = AF_INET;
283         inet_pton(AF_INET, rem_server_ip, &addr.sin_addr.s_addr);
284         addr.sin_port = htons(server_port);
285         /* Do TCP connect with server */
286         if (connect(client_skt, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
287             perror("Unable to TCP connect to server");
288             goto exit;
289         } else {
290             printf("TCP connection to server successful\n");
291         }
292 
293         /* Create client SSL structure using dedicated client socket */
294         ssl = SSL_new(ssl_ctx);
295         if (!SSL_set_fd(ssl, client_skt)) {
296             ERR_print_errors_fp(stderr);
297             goto exit;
298         }
299         /* Set hostname for SNI */
300         SSL_set_tlsext_host_name(ssl, rem_server_ip);
301         /* Configure server hostname check */
302         if (!SSL_set1_host(ssl, rem_server_ip)) {
303             ERR_print_errors_fp(stderr);
304             goto exit;
305         }
306 
307         /* Now do SSL connect with server */
308         if (SSL_connect(ssl) == 1) {
309 
310             printf("SSL connection to server successful\n\n");
311 
312             /* Loop to send input from keyboard */
313             while (true) {
314                 /* Get a line of input */
315                 memset(buffer, 0, BUFFERSIZE);
316                 txbuf = fgets(buffer, BUFFERSIZE, stdin);
317 
318                 /* Exit loop on error */
319                 if (txbuf == NULL) {
320                     break;
321                 }
322                 /* Exit loop if just a carriage return */
323                 if (txbuf[0] == '\n') {
324                     break;
325                 }
326                 /* Send it to the server */
327                 if ((result = SSL_write(ssl, txbuf, strlen(txbuf))) <= 0) {
328                     printf("Server closed connection\n");
329                     ERR_print_errors_fp(stderr);
330                     break;
331                 }
332 
333                 /* Wait for the echo */
334                 rxlen = SSL_read(ssl, rxbuf, rxcap);
335                 if (rxlen <= 0) {
336                     printf("Server closed connection\n");
337                     ERR_print_errors_fp(stderr);
338                     break;
339                 } else {
340                     /* Show it */
341                     rxbuf[rxlen] = 0;
342                     printf("Received: %s", rxbuf);
343                 }
344             }
345             printf("Client exiting...\n");
346         } else {
347 
348             printf("SSL connection to server failed\n\n");
349 
350             ERR_print_errors_fp(stderr);
351         }
352     }
353 exit:
354     /* Close up */
355     if (ssl != NULL) {
356         SSL_shutdown(ssl);
357         SSL_free(ssl);
358     }
359     SSL_CTX_free(ssl_ctx);
360 
361     if (client_skt != -1)
362         close(client_skt);
363     if (server_skt != -1)
364         close(server_skt);
365 
366     printf("sslecho exiting\n");
367 
368     return EXIT_SUCCESS;
369 }
370