]>
Commit | Line | Data |
---|---|---|
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 | |
19 | static const int server_port = 4433; | |
20 | ||
21 | typedef 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 | */ | |
29 | static volatile bool server_running = true; | |
30 | ||
31 | int 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 | ||
69 | SSL_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 | ||
89 | void 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 | ||
103 | void 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 | 121 | void 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 | ||
130 | int 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 */ |
159 | printf("\nsslecho : Simple Echo Client/Server (OpenSSL 3.0.1-dev) : %s : %s\n\n", __DATE__, | |
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 | 331 | exit: |
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 | } |