File: plain_upgrade_downgrade.t

package info (click to toggle)
libio-socket-ssl-perl 2.002-2%2Bdeb8u3
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 1,348 kB
  • sloc: perl: 14,412; makefile: 4
file content (153 lines) | stat: -rw-r--r-- 4,693 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use strict;
use warnings;
use IO::Socket::SSL;
use IO::Socket::SSL::Utils;
use Test::More;
do './testlib.pl' || do './t/testlib.pl' || die "no testlib";

# create listener
IO::Socket::SSL::default_ca('certs/my-ca.pem');
my $server = IO::Socket::SSL->new(
    LocalAddr => '127.0.0.1',
    LocalPort => 0,
    Listen => 2,
    SSL_cert_file => 'certs/server-cert.pem',
    SSL_key_file => 'certs/server-key.pem',
    # start as plain and upgrade later
    SSL_startHandshake => 0,
) || die "not ok #tcp listen failed: $!\n";
my $saddr = $server->sockhost.':'.$server->sockport;
#diag("listen at $saddr");

# fork child for server
defined( my $pid = fork() ) || die $!;
if ( ! $pid ) {
    $SIG{ALRM} = sub { die "server timed out" };
    while (1) {
	alarm(30);
	my $cl = $server->accept;
	diag("server accepted new client");
	#${*$cl}{_SSL_ctx} or die "accepted socket has no SSL context";
	${*$cl}{_SSL_object} and die "accepted socket is already SSL";

	# try to find out if we start with TLS immediatly (peek gets data from
	# client hello) or have some plain data initially (peek gets these
	# plain data)
	diag("wait for initial data from client");
	my $buf = '';
	while (length($buf)<3) {
	    vec(my $rin='',fileno($cl),1) = 1;
	    my $rv = select($rin,undef,undef,10);
	    die "timeout waiting for data from client" if ! $rv;
	    die "something wrong: $!" if $rv<0;
	    $cl->peek($buf,3);
	    $buf eq '' and die "eof from client";
	    diag("got 0x".unpack("H*",$buf)." from client");
	}

	if ($buf eq "end") {
	    # done
	    diag("client requested end of tests");
	    exit(0);
	}

	if ($buf eq 'foo') {
	    # initial plain dialog
	    diag("server: got plain data at start of connection");
	    read($cl,$buf,3) or die "failed to read";
	    $buf eq 'foo' or die "read($buf) different from peek";
	    print $cl "bar"; # reply
	}

	# now we upgrade to TLS
	diag("server: TLS upgrade");
	$cl->accept_SSL or die "failed to SSL upgrade server side: $SSL_ERROR";
	${*$cl}{_SSL_object} or die "no SSL object after accept_SSL";
	read($cl,$buf,6) or die "failed to ssl read";
	$buf eq 'sslfoo' or die "wrong data received from client '$buf'";
	print $cl "sslbar";

	# now we downgrade from TLS to plain and try to exchange some data
	diag("server: TLS downgrade");
	$cl->stop_SSL or die "failed to stop SSL";
	${*$cl}{_SSL_object} and die "still SSL object after stop_SSL";
	read($cl,$buf,3);
	$buf eq 'foo' or die "wrong data received from client '$buf'";
	print $cl "bar";

	# now we upgrade again to TLS
	diag("server: TLS upgrade#2");
	$cl->accept_SSL or die "failed to SSL upgrade server side";
	${*$cl}{_SSL_object} or die "no SSL object after accept_SSL";
	read($cl,$buf,6) or die "failed to ssl read";
	$buf eq 'sslfoo' or die "wrong data received from client '$buf'";
	print $cl "sslbar";
    }
}

# client
close($server); # close server in client
$SIG{ALRM} = sub { die "client timed out" };

plan tests => 15;

for my $test (
    [qw(newINET start_SSL stop_SSL start_SSL)],
    [qw(newSSL stop_SSL connect_SSL)],
    [qw(newSSL:0 connect_SSL stop_SSL connect_SSL)],
    [qw(newSSL:0 start_SSL stop_SSL connect_SSL)],
) {
    my $cl;
    diag("-- test: @$test");
    for my $act (@$test) {
	if (eval {
	    if ($act =~m{newSSL(?::(.*))?$} ) {
		$cl = IO::Socket::SSL->new(
		    PeerAddr => $saddr,
		    defined($1) ? (SSL_startHandshake => $1):(),
		) or die "failed to connect: $!|$SSL_ERROR";
		if ( ! defined($1) || $1 ) {
		    ${*$cl}{_SSL_object} or die "no SSL object";
		} else {
		    ${*$cl}{_SSL_object} and die "have SSL object";
		}
	    } elsif ($act eq 'newINET') {
		$cl = IO::Socket::INET->new($saddr)
		    or die "failed to connect: $!";
	    } elsif ($act eq 'stop_SSL') {
		$cl->stop_SSL or die "stop_SSL failed: $SSL_ERROR";
		${*$cl}{_SSL_object} and
		    die "still having SSL object after stop_SSL";
	    } elsif ($act eq 'connect_SSL') {
		$cl->connect_SSL or die "connect_SSL failed: $SSL_ERROR";
		${*$cl}{_SSL_object} or die "no SSL object after connect_SSL";
	    } elsif ($act eq 'start_SSL') {
		IO::Socket::SSL->start_SSL($cl) or
		    die "start_SSL failed: $SSL_ERROR";
		${*$cl}{_SSL_object} or die "no SSL object after start_SSL";
	    } else {
		die "unknown action $act"
	    }
	    if (${*$cl}{_SSL_object}) {
		print $cl "sslfoo";
		read($cl, my $buf,6);
		$buf eq 'sslbar' or die "wrong response with ssl: $buf";
	    } else {
		print $cl "foo";
		read($cl, my $buf,3);
		$buf eq 'bar' or die "wrong response without ssl: $buf";
	    }
	}) {
	    pass($act);
	} else {
	    fail("$act: $@");
	    last; # slip rest
	}
    }
}

# make server exit
alarm(10);
my $cl = IO::Socket::INET->new($saddr);
print $cl "end" if $cl;
wait;