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