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