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