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