]>
Commit | Line | Data |
---|---|---|
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 | |
25 | static const int server_port = 4433; | |
26 | ||
27 | typedef 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 | */ | |
35 | static volatile bool server_running = true; | |
36 | ||
7a7fbeb9 | 37 | static 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 | 75 | static 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 | 95 | static 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 | 109 | static 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 | 127 | static 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 | ||
136 | int 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 | 348 | exit: |
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 | } |