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