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