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