]>
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> | |
01eaf203 | 15 | #include <netinet/in.h> |
801c638c | 16 | #include <openssl/ssl.h> |
17 | #include <openssl/err.h> | |
f309b3f6 | 18 | #include <signal.h> |
801c638c | 19 | |
20 | static const int server_port = 4433; | |
21 | ||
22 | typedef 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 | */ | |
30 | static volatile bool server_running = true; | |
31 | ||
32 | int 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 | ||
70 | SSL_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 | ||
90 | void 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 | ||
104 | void 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 | 122 | void 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 | ||
131 | int 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 | 332 | exit: |
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 | } |