blob: 8c1e75e82c4ca9fe24cc385d89d55ffb452e5a68 [file] [log] [blame]
David Benjamin025b3d32014-07-01 19:53:04 -04001/* Copyright (c) 2014, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
Adam Langleyded93582014-07-31 15:23:51 -070015#include <openssl/base.h>
16
17#if !defined(OPENSSL_WINDOWS)
David Benjamin025b3d32014-07-01 19:53:04 -040018#include <arpa/inet.h>
David Benjamin1d5c83e2014-07-22 19:20:02 -040019#include <netinet/in.h>
20#include <signal.h>
21#include <sys/socket.h>
David Benjamin8f2c20e2014-07-09 09:30:38 -040022#include <unistd.h>
Adam Langleyded93582014-07-31 15:23:51 -070023#endif
24
25#include <sys/types.h>
David Benjamin025b3d32014-07-01 19:53:04 -040026
David Benjamin025b3d32014-07-01 19:53:04 -040027#include <openssl/bio.h>
David Benjamin8f2c20e2014-07-09 09:30:38 -040028#include <openssl/bytestring.h>
David Benjamin1d5c83e2014-07-22 19:20:02 -040029#include <openssl/ssl.h>
30
David Benjamin43ec06f2014-08-05 02:28:57 -040031#include "async_bio.h"
David Benjamin6fd297b2014-08-11 18:43:38 -040032#include "packeted_bio.h"
David Benjamin5a593af2014-08-11 19:51:50 -040033#include "test_config.h"
David Benjamin43ec06f2014-08-05 02:28:57 -040034
David Benjamin1d5c83e2014-07-22 19:20:02 -040035static int usage(const char *program) {
David Benjamin5a593af2014-08-11 19:51:50 -040036 fprintf(stderr, "Usage: %s [flags...]\n",
David Benjamin1d5c83e2014-07-22 19:20:02 -040037 program);
38 return 1;
39}
David Benjamin025b3d32014-07-01 19:53:04 -040040
David Benjamin5a593af2014-08-11 19:51:50 -040041static int g_ex_data_index = 0;
42
43static void SetConfigPtr(SSL *ssl, const TestConfig *config) {
44 SSL_set_ex_data(ssl, g_ex_data_index, (void *)config);
45}
46
47static const TestConfig *GetConfigPtr(SSL *ssl) {
48 return (const TestConfig *)SSL_get_ex_data(ssl, g_ex_data_index);
49}
50
David Benjamina08e49d2014-08-24 01:46:07 -040051static EVP_PKEY *LoadPrivateKey(const std::string &file) {
52 BIO *bio = BIO_new(BIO_s_file());
53 if (bio == NULL) {
54 return NULL;
55 }
56 if (!BIO_read_filename(bio, file.c_str())) {
57 BIO_free(bio);
58 return NULL;
59 }
60 EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
61 BIO_free(bio);
62 return pkey;
63}
64
David Benjamin1f5f62b2014-07-12 16:18:02 -040065static int early_callback_called = 0;
David Benjamin8f2c20e2014-07-09 09:30:38 -040066
David Benjamin1f5f62b2014-07-12 16:18:02 -040067static int select_certificate_callback(const struct ssl_early_callback_ctx *ctx) {
David Benjamin8f2c20e2014-07-09 09:30:38 -040068 early_callback_called = 1;
69
David Benjamin5a593af2014-08-11 19:51:50 -040070 const TestConfig *config = GetConfigPtr(ctx->ssl);
71
72 if (config->expected_server_name.empty()) {
David Benjamin7b030512014-07-08 17:30:11 -040073 return 1;
74 }
David Benjamin8f2c20e2014-07-09 09:30:38 -040075
David Benjamin7b030512014-07-08 17:30:11 -040076 const uint8_t *extension_data;
77 size_t extension_len;
78 CBS extension, server_name_list, host_name;
79 uint8_t name_type;
David Benjamin8f2c20e2014-07-09 09:30:38 -040080
David Benjamin7b030512014-07-08 17:30:11 -040081 if (!SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
82 &extension_data,
83 &extension_len)) {
84 fprintf(stderr, "Could not find server_name extension.\n");
85 return -1;
86 }
David Benjamin8f2c20e2014-07-09 09:30:38 -040087
David Benjamin7b030512014-07-08 17:30:11 -040088 CBS_init(&extension, extension_data, extension_len);
89 if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
90 CBS_len(&extension) != 0 ||
91 !CBS_get_u8(&server_name_list, &name_type) ||
92 name_type != TLSEXT_NAMETYPE_host_name ||
93 !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
94 CBS_len(&server_name_list) != 0) {
95 fprintf(stderr, "Could not decode server_name extension.\n");
96 return -1;
97 }
98
David Benjamin5a593af2014-08-11 19:51:50 -040099 if (!CBS_mem_equal(&host_name,
100 (const uint8_t*)config->expected_server_name.data(),
101 config->expected_server_name.size())) {
David Benjamin7b030512014-07-08 17:30:11 -0400102 fprintf(stderr, "Server name mismatch.\n");
David Benjamin8f2c20e2014-07-09 09:30:38 -0400103 }
104
105 return 1;
106}
David Benjamin025b3d32014-07-01 19:53:04 -0400107
David Benjamin1f5f62b2014-07-12 16:18:02 -0400108static int skip_verify(int preverify_ok, X509_STORE_CTX *store_ctx) {
David Benjamin67666e72014-07-12 15:47:52 -0400109 return 1;
110}
111
David Benjamin1f5f62b2014-07-12 16:18:02 -0400112static int next_protos_advertised_callback(SSL *ssl,
David Benjamin7e3305e2014-07-28 14:52:32 -0400113 const uint8_t **out,
114 unsigned int *out_len,
115 void *arg) {
David Benjamin5a593af2014-08-11 19:51:50 -0400116 const TestConfig *config = GetConfigPtr(ssl);
117 if (config->advertise_npn.empty())
David Benjamin1f5f62b2014-07-12 16:18:02 -0400118 return SSL_TLSEXT_ERR_NOACK;
119
120 // TODO(davidben): Support passing byte strings with NULs to the
121 // test shim.
David Benjamin5a593af2014-08-11 19:51:50 -0400122 *out = (const uint8_t*)config->advertise_npn.data();
123 *out_len = config->advertise_npn.size();
David Benjamin1f5f62b2014-07-12 16:18:02 -0400124 return SSL_TLSEXT_ERR_OK;
125}
126
David Benjamin7e3305e2014-07-28 14:52:32 -0400127static int next_proto_select_callback(SSL* ssl,
128 uint8_t** out,
129 uint8_t* outlen,
130 const uint8_t* in,
131 unsigned inlen,
132 void* arg) {
David Benjamin5a593af2014-08-11 19:51:50 -0400133 const TestConfig *config = GetConfigPtr(ssl);
134 if (config->select_next_proto.empty())
David Benjamin7e3305e2014-07-28 14:52:32 -0400135 return SSL_TLSEXT_ERR_NOACK;
136
David Benjamin5a593af2014-08-11 19:51:50 -0400137 *out = (uint8_t*)config->select_next_proto.data();
138 *outlen = config->select_next_proto.size();
David Benjamin7e3305e2014-07-28 14:52:32 -0400139 return SSL_TLSEXT_ERR_OK;
140}
141
David Benjaminfb4ea282014-08-15 13:38:15 -0400142static int cookie_generate_callback(SSL *ssl, uint8_t *cookie, size_t *cookie_len) {
David Benjamin6fd297b2014-08-11 18:43:38 -0400143 *cookie_len = 32;
144 memset(cookie, 42, *cookie_len);
145 return 1;
146}
147
David Benjaminfb4ea282014-08-15 13:38:15 -0400148static int cookie_verify_callback(SSL *ssl, const uint8_t *cookie, size_t cookie_len) {
David Benjamin6fd297b2014-08-11 18:43:38 -0400149 if (cookie_len != 32) {
150 fprintf(stderr, "Cookie length mismatch.\n");
151 return 0;
152 }
153 for (size_t i = 0; i < cookie_len; i++) {
154 if (cookie[i] != 42) {
155 fprintf(stderr, "Cookie mismatch.\n");
156 return 0;
157 }
158 }
159 return 1;
160}
161
David Benjamin5a593af2014-08-11 19:51:50 -0400162static SSL_CTX *setup_ctx(const TestConfig *config) {
David Benjamin025b3d32014-07-01 19:53:04 -0400163 SSL_CTX *ssl_ctx = NULL;
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400164 DH *dh = NULL;
David Benjamin025b3d32014-07-01 19:53:04 -0400165
David Benjamin6fd297b2014-08-11 18:43:38 -0400166 const SSL_METHOD *method;
167 if (config->is_dtls) {
168 // TODO(davidben): Get DTLS 1.2 working and test the version negotiation
169 // codepath. This doesn't currently work because
170 // - Session resumption is broken: https://crbug.com/403378
171 // - DTLS hasn't been updated for EVP_AEAD.
172 if (config->is_server) {
173 method = DTLSv1_server_method();
174 } else {
175 method = DTLSv1_client_method();
176 }
177 } else {
178 if (config->is_server) {
179 method = SSLv23_server_method();
180 } else {
181 method = SSLv23_client_method();
182 }
183 }
184 ssl_ctx = SSL_CTX_new(method);
David Benjamin025b3d32014-07-01 19:53:04 -0400185 if (ssl_ctx == NULL) {
186 goto err;
187 }
188
David Benjamin6fd297b2014-08-11 18:43:38 -0400189 if (config->is_dtls) {
190 // DTLS needs read-ahead to function on a datagram BIO.
191 //
192 // TODO(davidben): this should not be necessary. DTLS code should only
193 // expect a datagram BIO.
194 SSL_CTX_set_read_ahead(ssl_ctx, 1);
195 }
196
David Benjamin025b3d32014-07-01 19:53:04 -0400197 if (!SSL_CTX_set_ecdh_auto(ssl_ctx, 1)) {
198 goto err;
199 }
200
201 if (!SSL_CTX_set_cipher_list(ssl_ctx, "ALL")) {
202 goto err;
203 }
204
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400205 dh = DH_get_2048_256(NULL);
206 if (!SSL_CTX_set_tmp_dh(ssl_ctx, dh)) {
207 goto err;
208 }
209
David Benjamin1d5c83e2014-07-22 19:20:02 -0400210 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
211
David Benjamin8f2c20e2014-07-09 09:30:38 -0400212 ssl_ctx->select_certificate_cb = select_certificate_callback;
213
David Benjamin1f5f62b2014-07-12 16:18:02 -0400214 SSL_CTX_set_next_protos_advertised_cb(
215 ssl_ctx, next_protos_advertised_callback, NULL);
David Benjamin7e3305e2014-07-28 14:52:32 -0400216 SSL_CTX_set_next_proto_select_cb(
217 ssl_ctx, next_proto_select_callback, NULL);
David Benjamin1f5f62b2014-07-12 16:18:02 -0400218
David Benjamin6fd297b2014-08-11 18:43:38 -0400219 SSL_CTX_set_cookie_generate_cb(ssl_ctx, cookie_generate_callback);
220 SSL_CTX_set_cookie_verify_cb(ssl_ctx, cookie_verify_callback);
221
David Benjamina08e49d2014-08-24 01:46:07 -0400222 ssl_ctx->tlsext_channel_id_enabled_new = 1;
223
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400224 DH_free(dh);
David Benjamin1d5c83e2014-07-22 19:20:02 -0400225 return ssl_ctx;
226
227 err:
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400228 if (dh != NULL) {
229 DH_free(dh);
230 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400231 if (ssl_ctx != NULL) {
232 SSL_CTX_free(ssl_ctx);
233 }
234 return NULL;
235}
236
David Benjamin43ec06f2014-08-05 02:28:57 -0400237static int retry_async(SSL *ssl, int ret, BIO *bio) {
238 // No error; don't retry.
239 if (ret >= 0) {
240 return 0;
David Benjamin025b3d32014-07-01 19:53:04 -0400241 }
David Benjamin43ec06f2014-08-05 02:28:57 -0400242 // See if we needed to read or write more. If so, allow one byte through on
243 // the appropriate end to maximally stress the state machine.
244 int err = SSL_get_error(ssl, ret);
245 if (err == SSL_ERROR_WANT_READ) {
246 async_bio_allow_read(bio, 1);
247 return 1;
248 } else if (err == SSL_ERROR_WANT_WRITE) {
249 async_bio_allow_write(bio, 1);
250 return 1;
David Benjamin025b3d32014-07-01 19:53:04 -0400251 }
David Benjamin43ec06f2014-08-05 02:28:57 -0400252 return 0;
David Benjamin025b3d32014-07-01 19:53:04 -0400253}
254
David Benjamin1d5c83e2014-07-22 19:20:02 -0400255static int do_exchange(SSL_SESSION **out_session,
256 SSL_CTX *ssl_ctx,
David Benjamin5a593af2014-08-11 19:51:50 -0400257 const TestConfig *config,
258 bool is_resume,
David Benjamin1d5c83e2014-07-22 19:20:02 -0400259 int fd,
260 SSL_SESSION *session) {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400261 early_callback_called = 0;
David Benjamin025b3d32014-07-01 19:53:04 -0400262
David Benjamin43ec06f2014-08-05 02:28:57 -0400263 SSL *ssl = SSL_new(ssl_ctx);
David Benjamin025b3d32014-07-01 19:53:04 -0400264 if (ssl == NULL) {
265 BIO_print_errors_fp(stdout);
266 return 1;
267 }
268
David Benjamin5a593af2014-08-11 19:51:50 -0400269 SetConfigPtr(ssl, config);
270
271 if (config->fallback_scsv) {
272 if (!SSL_enable_fallback_scsv(ssl)) {
273 BIO_print_errors_fp(stdout);
David Benjamin025b3d32014-07-01 19:53:04 -0400274 return 1;
275 }
276 }
David Benjamin5a593af2014-08-11 19:51:50 -0400277 if (!config->key_file.empty()) {
278 if (!SSL_use_PrivateKey_file(ssl, config->key_file.c_str(),
279 SSL_FILETYPE_PEM)) {
280 BIO_print_errors_fp(stdout);
281 return 1;
282 }
283 }
284 if (!config->cert_file.empty()) {
285 if (!SSL_use_certificate_file(ssl, config->cert_file.c_str(),
286 SSL_FILETYPE_PEM)) {
287 BIO_print_errors_fp(stdout);
288 return 1;
289 }
290 }
291 if (config->require_any_client_certificate) {
292 SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
293 skip_verify);
294 }
295 if (config->false_start) {
296 SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
297 }
298 if (config->cbc_record_splitting) {
299 SSL_set_mode(ssl, SSL_MODE_CBC_RECORD_SPLITTING);
300 }
301 if (config->partial_write) {
302 SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
303 }
304 if (config->no_tls12) {
305 SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
306 }
307 if (config->no_tls11) {
308 SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
309 }
310 if (config->no_tls1) {
311 SSL_set_options(ssl, SSL_OP_NO_TLSv1);
312 }
313 if (config->no_ssl3) {
314 SSL_set_options(ssl, SSL_OP_NO_SSLv3);
315 }
David Benjamin6fd297b2014-08-11 18:43:38 -0400316 if (config->cookie_exchange) {
317 SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);
318 }
David Benjamina08e49d2014-08-24 01:46:07 -0400319 if (!config->expected_channel_id.empty()) {
320 SSL_enable_tls_channel_id(ssl);
321 }
322 if (!config->send_channel_id.empty()) {
323 EVP_PKEY *pkey = LoadPrivateKey(config->send_channel_id);
324 if (pkey == NULL) {
325 BIO_print_errors_fp(stdout);
326 return 1;
327 }
328 SSL_enable_tls_channel_id(ssl);
329 if (!SSL_set1_tls_channel_id(ssl, pkey)) {
330 EVP_PKEY_free(pkey);
331 BIO_print_errors_fp(stdout);
332 return 1;
333 }
334 EVP_PKEY_free(pkey);
335 }
David Benjamin025b3d32014-07-01 19:53:04 -0400336
David Benjamin43ec06f2014-08-05 02:28:57 -0400337 BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
338 if (bio == NULL) {
339 BIO_print_errors_fp(stdout);
340 return 1;
341 }
David Benjamin6fd297b2014-08-11 18:43:38 -0400342 if (config->is_dtls) {
343 BIO *packeted = packeted_bio_create();
344 BIO_push(packeted, bio);
345 bio = packeted;
346 }
David Benjamin5a593af2014-08-11 19:51:50 -0400347 if (config->async) {
David Benjamin6fd297b2014-08-11 18:43:38 -0400348 BIO *async =
349 config->is_dtls ? async_bio_create_datagram() : async_bio_create();
David Benjamin43ec06f2014-08-05 02:28:57 -0400350 BIO_push(async, bio);
351 bio = async;
352 }
353 SSL_set_bio(ssl, bio, bio);
354
David Benjamin1d5c83e2014-07-22 19:20:02 -0400355 if (session != NULL) {
356 if (SSL_set_session(ssl, session) != 1) {
357 fprintf(stderr, "failed to set session\n");
358 return 2;
359 }
360 }
361
362 int ret;
David Benjamin43ec06f2014-08-05 02:28:57 -0400363 do {
David Benjamin5a593af2014-08-11 19:51:50 -0400364 if (config->is_server) {
David Benjamin43ec06f2014-08-05 02:28:57 -0400365 ret = SSL_accept(ssl);
366 } else {
367 ret = SSL_connect(ssl);
368 }
David Benjamin5a593af2014-08-11 19:51:50 -0400369 } while (config->async && retry_async(ssl, ret, bio));
David Benjamin025b3d32014-07-01 19:53:04 -0400370 if (ret != 1) {
371 SSL_free(ssl);
372 BIO_print_errors_fp(stdout);
373 return 2;
374 }
375
David Benjamin1d5c83e2014-07-22 19:20:02 -0400376 if (is_resume && !SSL_session_reused(ssl)) {
377 fprintf(stderr, "session was not reused\n");
378 return 2;
379 }
380
David Benjamin5a593af2014-08-11 19:51:50 -0400381 if (!config->expected_server_name.empty()) {
David Benjamin197b3ab2014-07-02 18:37:33 -0400382 const char *server_name =
383 SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
David Benjamin5a593af2014-08-11 19:51:50 -0400384 if (server_name != config->expected_server_name) {
David Benjamin197b3ab2014-07-02 18:37:33 -0400385 fprintf(stderr, "servername mismatch (got %s; want %s)\n",
David Benjamin5a593af2014-08-11 19:51:50 -0400386 server_name, config->expected_server_name.c_str());
David Benjamin197b3ab2014-07-02 18:37:33 -0400387 return 2;
388 }
David Benjamin8f2c20e2014-07-09 09:30:38 -0400389
390 if (!early_callback_called) {
391 fprintf(stderr, "early callback not called\n");
392 return 2;
393 }
David Benjamin197b3ab2014-07-02 18:37:33 -0400394 }
395
David Benjamin5a593af2014-08-11 19:51:50 -0400396 if (!config->expected_certificate_types.empty()) {
David Benjamin7b030512014-07-08 17:30:11 -0400397 uint8_t *certificate_types;
398 int num_certificate_types =
David Benjamina08e49d2014-08-24 01:46:07 -0400399 SSL_get0_certificate_types(ssl, &certificate_types);
David Benjamin5a593af2014-08-11 19:51:50 -0400400 if (num_certificate_types !=
401 (int)config->expected_certificate_types.size() ||
David Benjamin7b030512014-07-08 17:30:11 -0400402 memcmp(certificate_types,
David Benjamin5a593af2014-08-11 19:51:50 -0400403 config->expected_certificate_types.data(),
David Benjamin7b030512014-07-08 17:30:11 -0400404 num_certificate_types) != 0) {
405 fprintf(stderr, "certificate types mismatch\n");
406 return 2;
407 }
408 }
409
David Benjamin5a593af2014-08-11 19:51:50 -0400410 if (!config->expected_next_proto.empty()) {
David Benjamin1f5f62b2014-07-12 16:18:02 -0400411 const uint8_t *next_proto;
412 unsigned next_proto_len;
413 SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
David Benjamin5a593af2014-08-11 19:51:50 -0400414 if (next_proto_len != config->expected_next_proto.size() ||
415 memcmp(next_proto, config->expected_next_proto.data(),
416 next_proto_len) != 0) {
David Benjamin1f5f62b2014-07-12 16:18:02 -0400417 fprintf(stderr, "negotiated next proto mismatch\n");
418 return 2;
419 }
420 }
421
David Benjamina08e49d2014-08-24 01:46:07 -0400422 if (!config->expected_channel_id.empty()) {
423 uint8_t channel_id[64];
424 if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) {
425 fprintf(stderr, "no channel id negotiated\n");
426 return 2;
427 }
428 if (config->expected_channel_id.size() != 64 ||
429 memcmp(config->expected_channel_id.data(),
430 channel_id, 64) != 0) {
431 fprintf(stderr, "channel id mismatch\n");
432 return 2;
433 }
434 }
435
David Benjamin5a593af2014-08-11 19:51:50 -0400436 if (config->write_different_record_sizes) {
David Benjamin6fd297b2014-08-11 18:43:38 -0400437 if (config->is_dtls) {
438 fprintf(stderr, "write_different_record_sizes not supported for DTLS\n");
439 return 6;
440 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700441 // This mode writes a number of different record sizes in an attempt to
442 // trip up the CBC record splitting code.
443 uint8_t buf[32769];
444 memset(buf, 0x42, sizeof(buf));
445 static const size_t kRecordSizes[] = {
446 0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769};
447 for (size_t i = 0; i < sizeof(kRecordSizes) / sizeof(kRecordSizes[0]);
448 i++) {
David Benjamin43ec06f2014-08-05 02:28:57 -0400449 int w;
Kenny Root7fdeaf12014-08-05 15:23:37 -0700450 const size_t len = kRecordSizes[i];
451 size_t off = 0;
452
453 if (len > sizeof(buf)) {
454 fprintf(stderr, "Bad kRecordSizes value.\n");
455 return 5;
456 }
457
David Benjamin43ec06f2014-08-05 02:28:57 -0400458 do {
Kenny Root7fdeaf12014-08-05 15:23:37 -0700459 w = SSL_write(ssl, buf + off, len - off);
460 if (w > 0) {
461 off += (size_t) w;
462 }
David Benjamin5a593af2014-08-11 19:51:50 -0400463 } while ((config->async && retry_async(ssl, w, bio)) ||
464 (w > 0 && off < len));
Kenny Root7fdeaf12014-08-05 15:23:37 -0700465
466 if (w < 0 || off != len) {
David Benjamin025b3d32014-07-01 19:53:04 -0400467 SSL_free(ssl);
468 BIO_print_errors_fp(stdout);
469 return 4;
470 }
471 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700472 } else {
473 for (;;) {
474 uint8_t buf[512];
475 int n;
476 do {
477 n = SSL_read(ssl, buf, sizeof(buf));
David Benjamin5a593af2014-08-11 19:51:50 -0400478 } while (config->async && retry_async(ssl, n, bio));
Kenny Root7fdeaf12014-08-05 15:23:37 -0700479 if (n < 0) {
480 SSL_free(ssl);
481 BIO_print_errors_fp(stdout);
482 return 3;
483 } else if (n == 0) {
484 break;
485 } else {
486 for (int i = 0; i < n; i++) {
487 buf[i] ^= 0xff;
488 }
489 int w;
490 do {
491 w = SSL_write(ssl, buf, n);
David Benjamin5a593af2014-08-11 19:51:50 -0400492 } while (config->async && retry_async(ssl, w, bio));
Kenny Root7fdeaf12014-08-05 15:23:37 -0700493 if (w != n) {
494 SSL_free(ssl);
495 BIO_print_errors_fp(stdout);
496 return 4;
497 }
498 }
499 }
David Benjamin025b3d32014-07-01 19:53:04 -0400500 }
501
David Benjamin1d5c83e2014-07-22 19:20:02 -0400502 if (out_session) {
503 *out_session = SSL_get1_session(ssl);
504 }
505
506 SSL_shutdown(ssl);
David Benjamin025b3d32014-07-01 19:53:04 -0400507 SSL_free(ssl);
508 return 0;
509}
David Benjamin1d5c83e2014-07-22 19:20:02 -0400510
511int main(int argc, char **argv) {
Adam Langleyded93582014-07-31 15:23:51 -0700512#if !defined(OPENSSL_WINDOWS)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400513 signal(SIGPIPE, SIG_IGN);
Adam Langleyded93582014-07-31 15:23:51 -0700514#endif
David Benjamin1d5c83e2014-07-22 19:20:02 -0400515
David Benjamin5a593af2014-08-11 19:51:50 -0400516 if (!SSL_library_init()) {
517 return 1;
518 }
519 g_ex_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
520
521 TestConfig config;
522 if (!ParseConfig(argc - 1, argv + 1, &config)) {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400523 return usage(argv[0]);
524 }
525
David Benjamin5a593af2014-08-11 19:51:50 -0400526 SSL_CTX *ssl_ctx = setup_ctx(&config);
David Benjamin1d5c83e2014-07-22 19:20:02 -0400527 if (ssl_ctx == NULL) {
528 BIO_print_errors_fp(stdout);
529 return 1;
530 }
531
Adam Langleye7bf2812014-08-19 11:36:45 -0700532 SSL_SESSION *session = NULL;
David Benjamin1d5c83e2014-07-22 19:20:02 -0400533 int ret = do_exchange(&session,
David Benjamin5a593af2014-08-11 19:51:50 -0400534 ssl_ctx, &config,
535 false /* is_resume */,
David Benjamin1d5c83e2014-07-22 19:20:02 -0400536 3 /* fd */, NULL /* session */);
537 if (ret != 0) {
Adam Langleye7bf2812014-08-19 11:36:45 -0700538 goto out;
David Benjamin1d5c83e2014-07-22 19:20:02 -0400539 }
540
David Benjamin5a593af2014-08-11 19:51:50 -0400541 if (config.resume) {
Adam Langleye7bf2812014-08-19 11:36:45 -0700542 ret = do_exchange(NULL,
543 ssl_ctx, &config,
544 true /* is_resume */,
545 4 /* fd */,
546 config.is_server ? NULL : session);
David Benjamin1d5c83e2014-07-22 19:20:02 -0400547 if (ret != 0) {
Adam Langleye7bf2812014-08-19 11:36:45 -0700548 goto out;
David Benjamin1d5c83e2014-07-22 19:20:02 -0400549 }
550 }
551
Adam Langleye7bf2812014-08-19 11:36:45 -0700552 ret = 0;
553
554out:
David Benjamin1d5c83e2014-07-22 19:20:02 -0400555 SSL_SESSION_free(session);
556 SSL_CTX_free(ssl_ctx);
Adam Langleye7bf2812014-08-19 11:36:45 -0700557 return ret;
David Benjamin1d5c83e2014-07-22 19:20:02 -0400558}