Refactoring BIO: add a test, using test/ssltest
This adds a couple of simple tests to see that SSL traffic using the reimplemented BIO_s_accept() and BIO_s_connect() works as expected, both on IPv4 and on IPv6. Reviewed-by: Kurt Roeckx <kurt@openssl.org>
This commit is contained in:
@@ -329,7 +329,7 @@ sub testssl {
|
||||
|
||||
subtest 'standard SSL tests' => sub {
|
||||
######################################################################
|
||||
plan tests => 27;
|
||||
plan tests => 29;
|
||||
|
||||
SKIP: {
|
||||
skip "SSLv3 is not supported by this OpenSSL build", 4
|
||||
@@ -410,7 +410,7 @@ sub testssl {
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip "Neither SSLv3 nor any TLS version are supported by this OpenSSL build", 6
|
||||
skip "Neither SSLv3 nor any TLS version are supported by this OpenSSL build", 8
|
||||
if $no_anytls;
|
||||
|
||||
SKIP: {
|
||||
@@ -430,6 +430,12 @@ sub testssl {
|
||||
'test sslv2/sslv3 with both client and server authentication via BIO pair');
|
||||
ok(run(test([@ssltest, "-bio_pair", "-server_auth", "-client_auth", "-app_verify", @CA, @extra])),
|
||||
'test sslv2/sslv3 with both client and server authentication via BIO pair and app verify');
|
||||
|
||||
ok(run(test([@ssltest, "-ipv4", @extra])),
|
||||
'test TLS via IPv4');
|
||||
ok(run(test([@ssltest, "-ipv6", @extra])),
|
||||
'test TLS via IPv6');
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
317
test/ssltest.c
317
test/ssltest.c
@@ -732,6 +732,8 @@ static int debug = 0;
|
||||
static const char rnd_seed[] =
|
||||
"string to make the random number generator think it has entropy";
|
||||
|
||||
int doit_localhost(SSL *s_ssl, SSL *c_ssl, int family,
|
||||
long bytes, clock_t *s_time, clock_t *c_time);
|
||||
int doit_biopair(SSL *s_ssl, SSL *c_ssl, long bytes, clock_t *s_time,
|
||||
clock_t *c_time);
|
||||
int doit(SSL *s_ssl, SSL *c_ssl, long bytes);
|
||||
@@ -800,6 +802,8 @@ static void sv_usage(void)
|
||||
" -c_key arg - Client key file (default: same as -c_cert)\n");
|
||||
fprintf(stderr, " -cipher arg - The cipher list\n");
|
||||
fprintf(stderr, " -bio_pair - Use BIO pairs\n");
|
||||
fprintf(stderr, " -ipv4 - Use IPv4 connection on localhost\n");
|
||||
fprintf(stderr, " -ipv6 - Use IPv6 connection on localhost\n");
|
||||
fprintf(stderr, " -f - Test even cases that can't work\n");
|
||||
fprintf(stderr,
|
||||
" -time - measure processor time used by client and server\n");
|
||||
@@ -1007,7 +1011,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
char *CApath = NULL, *CAfile = NULL;
|
||||
int badop = 0;
|
||||
int bio_pair = 0;
|
||||
enum { BIO_MEM, BIO_PAIR, BIO_IPV4, BIO_IPV6 } bio_type = BIO_MEM;
|
||||
int force = 0;
|
||||
int dtls1 = 0, dtls12 = 0, dtls = 0, tls1 = 0, ssl3 = 0, ret = 1;
|
||||
int client_auth = 0;
|
||||
@@ -1215,7 +1219,11 @@ int main(int argc, char *argv[])
|
||||
goto bad;
|
||||
CAfile = *(++argv);
|
||||
} else if (strcmp(*argv, "-bio_pair") == 0) {
|
||||
bio_pair = 1;
|
||||
bio_type = BIO_PAIR;
|
||||
} else if (strcmp(*argv, "-ipv4") == 0) {
|
||||
bio_type = BIO_IPV4;
|
||||
} else if (strcmp(*argv, "-ipv6") == 0) {
|
||||
bio_type = BIO_IPV6;
|
||||
} else if (strcmp(*argv, "-f") == 0) {
|
||||
force = 1;
|
||||
} else if (strcmp(*argv, "-time") == 0) {
|
||||
@@ -1411,9 +1419,9 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
if (print_time) {
|
||||
if (!bio_pair) {
|
||||
if (bio_type != BIO_PAIR) {
|
||||
fprintf(stderr, "Using BIO pair (-bio_pair)\n");
|
||||
bio_pair = 1;
|
||||
bio_type = BIO_PAIR;
|
||||
}
|
||||
if (number < 50 && !force)
|
||||
fprintf(stderr,
|
||||
@@ -1777,11 +1785,23 @@ int main(int argc, char *argv[])
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if (bio_pair)
|
||||
ret = doit_biopair(s_ssl, c_ssl, bytes, &s_time, &c_time);
|
||||
else
|
||||
switch (bio_type) {
|
||||
case BIO_MEM:
|
||||
ret = doit(s_ssl, c_ssl, bytes);
|
||||
if (ret) break;
|
||||
break;
|
||||
case BIO_PAIR:
|
||||
ret = doit_biopair(s_ssl, c_ssl, bytes, &s_time, &c_time);
|
||||
break;
|
||||
case BIO_IPV4:
|
||||
ret = doit_localhost(s_ssl, c_ssl, BIO_FAMILY_IPV4,
|
||||
bytes, &s_time, &c_time);
|
||||
break;
|
||||
case BIO_IPV6:
|
||||
ret = doit_localhost(s_ssl, c_ssl, BIO_FAMILY_IPV6,
|
||||
bytes, &s_time, &c_time);
|
||||
break;
|
||||
}
|
||||
if (ret) break;
|
||||
}
|
||||
|
||||
if (should_negotiate && ret == 0 &&
|
||||
@@ -1851,6 +1871,287 @@ int main(int argc, char *argv[])
|
||||
EXIT(ret);
|
||||
}
|
||||
|
||||
int doit_localhost(SSL *s_ssl, SSL *c_ssl, int family, long count,
|
||||
clock_t *s_time, clock_t *c_time)
|
||||
{
|
||||
long cw_num = count, cr_num = count, sw_num = count, sr_num = count;
|
||||
BIO *s_ssl_bio = NULL, *c_ssl_bio = NULL;
|
||||
BIO *acpt = NULL, *server = NULL, *client = NULL;
|
||||
char addr_str[40];
|
||||
int ret = 1;
|
||||
int err_in_client = 0;
|
||||
int err_in_server = 0;
|
||||
|
||||
acpt = BIO_new_accept("0");
|
||||
if (acpt == NULL)
|
||||
goto err;
|
||||
BIO_set_accept_ip_family(acpt, family);
|
||||
BIO_set_bind_mode(acpt, BIO_SOCK_NONBLOCK | BIO_SOCK_REUSEADDR);
|
||||
if (BIO_do_accept(acpt) <= 0)
|
||||
goto err;
|
||||
|
||||
snprintf(addr_str, sizeof(addr_str), ":%s", BIO_get_accept_port(acpt));
|
||||
|
||||
client = BIO_new_connect(addr_str);
|
||||
BIO_set_conn_ip_family(client, family);
|
||||
if (!client)
|
||||
goto err;
|
||||
|
||||
if (BIO_set_nbio(client, 1) <= 0)
|
||||
goto err;
|
||||
if (BIO_set_nbio(acpt, 1) <= 0)
|
||||
goto err;
|
||||
|
||||
{
|
||||
int st_connect = 0, st_accept = 0;
|
||||
|
||||
while(!st_connect || !st_accept) {
|
||||
if (!st_connect) {
|
||||
if (BIO_do_connect(client) <= 0) {
|
||||
if (!BIO_should_retry(client))
|
||||
goto err;
|
||||
} else {
|
||||
st_connect = 1;
|
||||
}
|
||||
}
|
||||
if (!st_accept) {
|
||||
if (BIO_do_accept(acpt) <= 0) {
|
||||
if (!BIO_should_retry(acpt))
|
||||
goto err;
|
||||
} else {
|
||||
st_accept = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* We're not interested in accepting further connects */
|
||||
server = BIO_pop(acpt);
|
||||
BIO_free_all(acpt);
|
||||
acpt = NULL;
|
||||
|
||||
s_ssl_bio = BIO_new(BIO_f_ssl());
|
||||
if (!s_ssl_bio)
|
||||
goto err;
|
||||
|
||||
c_ssl_bio = BIO_new(BIO_f_ssl());
|
||||
if (!c_ssl_bio)
|
||||
goto err;
|
||||
|
||||
SSL_set_connect_state(c_ssl);
|
||||
SSL_set_bio(c_ssl, client, client);
|
||||
(void)BIO_set_ssl(c_ssl_bio, c_ssl, BIO_NOCLOSE);
|
||||
|
||||
SSL_set_accept_state(s_ssl);
|
||||
SSL_set_bio(s_ssl, server, server);
|
||||
(void)BIO_set_ssl(s_ssl_bio, s_ssl, BIO_NOCLOSE);
|
||||
|
||||
do {
|
||||
/*-
|
||||
* c_ssl_bio: SSL filter BIO
|
||||
*
|
||||
* client: I/O for SSL library
|
||||
*
|
||||
*
|
||||
* server: I/O for SSL library
|
||||
*
|
||||
* s_ssl_bio: SSL filter BIO
|
||||
*/
|
||||
|
||||
/*
|
||||
* We have non-blocking behaviour throughout this test program, but
|
||||
* can be sure that there is *some* progress in each iteration; so we
|
||||
* don't have to worry about ..._SHOULD_READ or ..._SHOULD_WRITE --
|
||||
* we just try everything in each iteration
|
||||
*/
|
||||
|
||||
{
|
||||
/* CLIENT */
|
||||
|
||||
char cbuf[1024 * 8];
|
||||
int i, r;
|
||||
clock_t c_clock = clock();
|
||||
|
||||
memset(cbuf, 0, sizeof(cbuf));
|
||||
|
||||
if (debug)
|
||||
if (SSL_in_init(c_ssl))
|
||||
printf("client waiting in SSL_connect - %s\n",
|
||||
SSL_state_string_long(c_ssl));
|
||||
|
||||
if (cw_num > 0) {
|
||||
/* Write to server. */
|
||||
|
||||
if (cw_num > (long)sizeof cbuf)
|
||||
i = sizeof cbuf;
|
||||
else
|
||||
i = (int)cw_num;
|
||||
r = BIO_write(c_ssl_bio, cbuf, i);
|
||||
if (r < 0) {
|
||||
if (!BIO_should_retry(c_ssl_bio)) {
|
||||
fprintf(stderr, "ERROR in CLIENT\n");
|
||||
err_in_client = 1;
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
* BIO_should_retry(...) can just be ignored here. The
|
||||
* library expects us to call BIO_write with the same
|
||||
* arguments again, and that's what we will do in the
|
||||
* next iteration.
|
||||
*/
|
||||
} else if (r == 0) {
|
||||
fprintf(stderr, "SSL CLIENT STARTUP FAILED\n");
|
||||
goto err;
|
||||
} else {
|
||||
if (debug)
|
||||
printf("client wrote %d\n", r);
|
||||
cw_num -= r;
|
||||
}
|
||||
}
|
||||
|
||||
if (cr_num > 0) {
|
||||
/* Read from server. */
|
||||
|
||||
r = BIO_read(c_ssl_bio, cbuf, sizeof(cbuf));
|
||||
if (r < 0) {
|
||||
if (!BIO_should_retry(c_ssl_bio)) {
|
||||
fprintf(stderr, "ERROR in CLIENT\n");
|
||||
err_in_client = 1;
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
* Again, "BIO_should_retry" can be ignored.
|
||||
*/
|
||||
} else if (r == 0) {
|
||||
fprintf(stderr, "SSL CLIENT STARTUP FAILED\n");
|
||||
goto err;
|
||||
} else {
|
||||
if (debug)
|
||||
printf("client read %d\n", r);
|
||||
cr_num -= r;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* c_time and s_time increments will typically be very small
|
||||
* (depending on machine speed and clock tick intervals), but
|
||||
* sampling over a large number of connections should result in
|
||||
* fairly accurate figures. We cannot guarantee a lot, however
|
||||
* -- if each connection lasts for exactly one clock tick, it
|
||||
* will be counted only for the client or only for the server or
|
||||
* even not at all.
|
||||
*/
|
||||
*c_time += (clock() - c_clock);
|
||||
}
|
||||
|
||||
{
|
||||
/* SERVER */
|
||||
|
||||
char sbuf[1024 * 8];
|
||||
int i, r;
|
||||
clock_t s_clock = clock();
|
||||
|
||||
memset(sbuf, 0, sizeof(sbuf));
|
||||
|
||||
if (debug)
|
||||
if (SSL_in_init(s_ssl))
|
||||
printf("server waiting in SSL_accept - %s\n",
|
||||
SSL_state_string_long(s_ssl));
|
||||
|
||||
if (sw_num > 0) {
|
||||
/* Write to client. */
|
||||
|
||||
if (sw_num > (long)sizeof sbuf)
|
||||
i = sizeof sbuf;
|
||||
else
|
||||
i = (int)sw_num;
|
||||
r = BIO_write(s_ssl_bio, sbuf, i);
|
||||
if (r < 0) {
|
||||
if (!BIO_should_retry(s_ssl_bio)) {
|
||||
fprintf(stderr, "ERROR in SERVER\n");
|
||||
err_in_server = 1;
|
||||
goto err;
|
||||
}
|
||||
/* Ignore "BIO_should_retry". */
|
||||
} else if (r == 0) {
|
||||
fprintf(stderr, "SSL SERVER STARTUP FAILED\n");
|
||||
goto err;
|
||||
} else {
|
||||
if (debug)
|
||||
printf("server wrote %d\n", r);
|
||||
sw_num -= r;
|
||||
}
|
||||
}
|
||||
|
||||
if (sr_num > 0) {
|
||||
/* Read from client. */
|
||||
|
||||
r = BIO_read(s_ssl_bio, sbuf, sizeof(sbuf));
|
||||
if (r < 0) {
|
||||
if (!BIO_should_retry(s_ssl_bio)) {
|
||||
fprintf(stderr, "ERROR in SERVER\n");
|
||||
err_in_server = 1;
|
||||
goto err;
|
||||
}
|
||||
/* blah, blah */
|
||||
} else if (r == 0) {
|
||||
fprintf(stderr, "SSL SERVER STARTUP FAILED\n");
|
||||
goto err;
|
||||
} else {
|
||||
if (debug)
|
||||
printf("server read %d\n", r);
|
||||
sr_num -= r;
|
||||
}
|
||||
}
|
||||
|
||||
*s_time += (clock() - s_clock);
|
||||
}
|
||||
}
|
||||
while (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0);
|
||||
|
||||
if (verbose)
|
||||
print_details(c_ssl, "DONE via TCP connect: ");
|
||||
#ifndef OPENSSL_NO_NEXTPROTONEG
|
||||
if (verify_npn(c_ssl, s_ssl) < 0) {
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
#endif
|
||||
if (verify_serverinfo() < 0) {
|
||||
fprintf(stderr, "Server info verify error\n");
|
||||
ret = 1;
|
||||
goto err;
|
||||
}
|
||||
if (verify_alpn(c_ssl, s_ssl) < 0) {
|
||||
ret = 1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (custom_ext_error) {
|
||||
fprintf(stderr, "Custom extension error\n");
|
||||
ret = 1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
end:
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
ERR_print_errors(bio_err);
|
||||
|
||||
BIO_free_all(acpt);
|
||||
BIO_free(server);
|
||||
BIO_free(client);
|
||||
BIO_free(s_ssl_bio);
|
||||
BIO_free(c_ssl_bio);
|
||||
|
||||
if (should_negotiate != NULL && strcmp(should_negotiate, "fail-client") == 0)
|
||||
ret = (err_in_client != 0) ? 0 : 1;
|
||||
else if (should_negotiate != NULL && strcmp(should_negotiate, "fail-server") == 0)
|
||||
ret = (err_in_server != 0) ? 0 : 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count,
|
||||
clock_t *s_time, clock_t *c_time)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user