]> git.ipfire.org Git - thirdparty/openssl.git/blame - demos/sslecho/main.c
Copyright year updates
[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>
01eaf203 15#include <netinet/in.h>
801c638c 16#include <openssl/ssl.h>
17#include <openssl/err.h>
f309b3f6 18#include <signal.h>
801c638c 19
20static const int server_port = 4433;
21
22typedef 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 */
30static volatile bool server_running = true;
31
32int 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
70SSL_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
90void 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
104void 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
e22ebb89 122void usage(void)
801c638c 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");
09ff84bd 128 exit(EXIT_FAILURE);
801c638c 129}
130
131int 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
3c0e8bc4 142 /* used by getline relying on realloc, can't be statically allocated */
143 char *txbuf = NULL;
144 size_t txcap = 0;
801c638c 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
f309b3f6
VP
156 /* ignore SIGPIPE so that server can continue running when client pipe closes abruptly */
157 signal(SIGPIPE, SIG_IGN);
158
801c638c 159 /* Splash */
86db9588 160 printf("\nsslecho : Simple Echo Client/Server : %s : %s\n\n", __DATE__,
801c638c 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 SSL_set_fd(ssl, client_skt);
211
212 /* Wait for SSL connection from the client */
213 if (SSL_accept(ssl) <= 0) {
214 ERR_print_errors_fp(stderr);
215 server_running = false;
216 } else {
217
218 printf("Client SSL connection accepted\n\n");
219
220 /* Echo loop */
221 while (true) {
222 /* Get message from client; will fail if client closes connection */
223 if ((rxlen = SSL_read(ssl, rxbuf, rxcap)) <= 0) {
224 if (rxlen == 0) {
225 printf("Client closed connection\n");
f309b3f6
VP
226 } else {
227 printf("SSL_read returned %d\n", rxlen);
801c638c 228 }
229 ERR_print_errors_fp(stderr);
230 break;
231 }
232 /* Insure null terminated input */
233 rxbuf[rxlen] = 0;
234 /* Look for kill switch */
235 if (strcmp(rxbuf, "kill\n") == 0) {
236 /* Terminate...with extreme prejudice */
237 printf("Server received 'kill' command\n");
238 server_running = false;
239 break;
240 }
241 /* Show received message */
242 printf("Received: %s", rxbuf);
243 /* Echo it back */
244 if (SSL_write(ssl, rxbuf, rxlen) <= 0) {
245 ERR_print_errors_fp(stderr);
246 }
247 }
248 }
249 if (server_running) {
250 /* Cleanup for next client */
251 SSL_shutdown(ssl);
252 SSL_free(ssl);
253 close(client_skt);
254 }
255 }
256 printf("Server exiting...\n");
257 }
258 /* Else client */
259 else {
260
261 printf("We are the client\n\n");
262
263 /* Configure client context so we verify the server correctly */
264 configure_client_context(ssl_ctx);
265
266 /* Create "bare" socket */
267 client_skt = create_socket(false);
268 /* Set up connect address */
269 addr.sin_family = AF_INET;
270 inet_pton(AF_INET, rem_server_ip, &addr.sin_addr.s_addr);
271 addr.sin_port = htons(server_port);
272 /* Do TCP connect with server */
273 if (connect(client_skt, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
274 perror("Unable to TCP connect to server");
275 goto exit;
276 } else {
277 printf("TCP connection to server successful\n");
278 }
279
280 /* Create client SSL structure using dedicated client socket */
281 ssl = SSL_new(ssl_ctx);
282 SSL_set_fd(ssl, client_skt);
9929c817 283 /* Set hostname for SNI */
801c638c 284 SSL_set_tlsext_host_name(ssl, rem_server_ip);
285 /* Configure server hostname check */
286 SSL_set1_host(ssl, rem_server_ip);
287
288 /* Now do SSL connect with server */
289 if (SSL_connect(ssl) == 1) {
290
291 printf("SSL connection to server successful\n\n");
292
293 /* Loop to send input from keyboard */
294 while (true) {
295 /* Get a line of input */
296 txlen = getline(&txbuf, &txcap, stdin);
3c0e8bc4 297 /* Exit loop on error */
298 if (txlen < 0 || txbuf == NULL) {
299 break;
300 }
801c638c 301 /* Exit loop if just a carriage return */
302 if (txbuf[0] == '\n') {
303 break;
304 }
801c638c 305 /* Send it to the server */
306 if ((result = SSL_write(ssl, txbuf, txlen)) <= 0) {
307 printf("Server closed connection\n");
308 ERR_print_errors_fp(stderr);
309 break;
310 }
311
312 /* Wait for the echo */
313 rxlen = SSL_read(ssl, rxbuf, rxcap);
314 if (rxlen <= 0) {
315 printf("Server closed connection\n");
316 ERR_print_errors_fp(stderr);
317 break;
318 } else {
319 /* Show it */
320 rxbuf[rxlen] = 0;
321 printf("Received: %s", rxbuf);
322 }
323 }
324 printf("Client exiting...\n");
325 } else {
326
327 printf("SSL connection to server failed\n\n");
328
329 ERR_print_errors_fp(stderr);
330 }
331 }
09ff84bd 332exit:
801c638c 333 /* Close up */
334 if (ssl != NULL) {
335 SSL_shutdown(ssl);
336 SSL_free(ssl);
337 }
338 SSL_CTX_free(ssl_ctx);
339
340 if (client_skt != -1)
341 close(client_skt);
342 if (server_skt != -1)
343 close(server_skt);
344
3c0e8bc4 345 if (txbuf != NULL && txcap > 0)
346 free(txbuf);
801c638c 347
348 printf("sslecho exiting\n");
349
09ff84bd 350 return EXIT_SUCCESS;
801c638c 351}