root/trunk/lib/Danga/Socket/SSL.pm

Revision 4, 6.3 kB (checked in by bradfitz, 3 years ago)

debug

Line 
1package Danga::Socket::SSL;
2use strict;
3use Danga::Socket 1.54;
4use base 'Danga::Socket';
5use Net::SSLeay;
6
7Net::SSLeay::load_error_strings();
8Net::SSLeay::SSLeay_add_ssl_algorithms();
9Net::SSLeay::randomize();
10
11use constant SSL_ERROR_WANT_READ     => 2;
12use constant SSL_ERROR_WANT_WRITE    => 3;
13
14use constant POLLIN        => 1;
15use constant POLLOUT       => 4;
16
17use fields (
18            'ssl_state',  # our SSL state
19            'ssl_write_when_readable',
20            );
21
22# optionally override this
23sub get_ssl_context {
24    my Danga::Socket::SSL $self = shift;
25
26    my $ctx = Net::SSLeay::CTX_new()
27        or die("Failed to create SSL_CTX $!");
28
29    #$Net::SSLeay::ssl_version = 10; # Insist on TLSv1
30    #$Net::SSLeay::ssl_version = 3; # Insist on SSLv3
31
32    Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL)
33        and Net::SSLeay::die_if_ssl_error("ssl ctx set options");
34
35    Net::SSLeay::CTX_set_mode($ctx, 1)  # enable partial writes
36        and Net::SSLeay::die_if_ssl_error("ssl ctx set options");
37
38    # Following will ask password unless private key is not encrypted
39    Net::SSLeay::CTX_use_RSAPrivateKey_file ($ctx,  $self->ssl_private_key_file,
40                                             &Net::SSLeay::FILETYPE_PEM);
41    Net::SSLeay::die_if_ssl_error("private key");
42
43    Net::SSLeay::CTX_use_certificate_file ($ctx, $self->ssl_cert_file,
44                                           &Net::SSLeay::FILETYPE_PEM);
45    Net::SSLeay::die_if_ssl_error("certificate");
46    return $ctx;
47}
48
49# override this:
50sub ssl_private_key_file {
51    my Danga::Socket::SSL $self = shift;
52    return "/dev/null";
53}
54
55# override this:
56sub ssl_cert_file {
57    my Danga::Socket::SSL $self = shift;
58    return "/dev/null";
59}
60
61sub start_ssl {
62    my Danga::Socket::SSL $self = shift;
63
64    my $ctx = $self->get_ssl_context
65        or die "No SSL context returned";
66
67    my $ssl = Net::SSLeay::new($ctx) or die_now("Failed to create SSL $!");
68    $self->{ssl_state} = $ssl;
69
70#    Net::SSLeay::set_verify($ssl, Net::SSLeay::VERIFY_PEER(), 0);
71
72    my $fileno = $self->{sock}->fileno;
73    warn "setting ssl ($ssl) fileno to $fileno\n";
74    Net::SSLeay::set_fd($ssl, $fileno);
75
76    #$Net::SSLeay::trace = 2;
77    #Net::SSLeay::connect($ssl) or Net::SSLeay::die_now("Failed SSL connect ($!)");
78
79    my $rv = Net::SSLeay::accept($ssl);
80    if (!$rv) {
81        warn "SSL accept error on $self\n";
82        $self->close("ssl_accept_error");
83        return;
84    }
85
86    warn "$self:  Cipher `" . Net::SSLeay::get_cipher($ssl) . "'\n";
87
88    $self->set_writer_func(_danga_socket_writerfunc($self));
89}
90
91sub _danga_socket_writerfunc {
92    my Danga::Socket::SSL $conn = shift;
93    my $ssl = $conn->{ssl_state};
94    return sub {
95        my ($bref, $to_write, $offset) = @_;
96
97        # unless our event_read has been called, we don't want to try
98        # to do any work now.  and probably we should complain.
99        if ($conn->{ssl_write_when_readable}) {
100            warn "writer func called when we're waiting for readability first.\n";
101            return 0;
102        }
103
104        # we can't write a lot or we get some SSL non-blocking error.
105        # NO LONGER RELEVANT?
106        # $to_write = 4096 if $to_write > 4096;
107
108        my $str = substr($$bref, $offset, $to_write);
109        my $written = Net::SSLeay::write($ssl, $str);
110
111        if ($written == -1) {
112            my $err = Net::SSLeay::get_error($ssl, $written);
113
114            if ($err == SSL_ERROR_WANT_READ) {
115                $conn->write_when_readable;
116                return 0;
117            }
118            if ($err == SSL_ERROR_WANT_WRITE) {
119                # unclear here.  it just wants to write some more?  okay.
120                # easy enough.  do nothing?
121                return 0;
122            }
123
124            my $errstr = Net::SSLeay::ERR_error_string($err);
125            warn " SSL write err = $err, $errstr\n";
126            Net::SSLeay::print_errs("SSL_write");
127            $conn->close('ssl_write_error');
128            return 0;
129        }
130
131        return $written;
132    };
133}
134
135sub is_ssl {
136    my Danga::Socket::SSL $self = shift;
137    return $self->{ssl_state} ? 1 : 0;
138}
139
140# return SSL state object.  more useful as a boolean if conn is in SSL mode.
141sub ssl_state {
142    my Danga::Socket::SSL $self = shift;
143    return $self->{ssl_state};
144}
145
146# called by SSL machinery to let us know a write is stalled on readability.
147# so we need to (at least temporarily) go readable and then process writes.
148sub write_when_readable {
149    my $self = shift;
150
151    # enable readability, but remember old value so we can pop it back
152    my $prev_readable = ($self->{event_watch} & POLLIN)  ? 1 : 0;
153    $self->watch_read(1);
154    $self->{ssl_write_when_readable} = [ $prev_readable ];
155
156    # don't need to push/pop its state because Danga::Socket->write, called later,
157    # will do the one final write, or if not all written, will turn on watch_write
158    $self->watch_write(0);
159}
160
161# called by Danga::Socket when a write doesn't fully go through.  by default it
162# enables writability.  but we want to do nothing if we're waiting for a read for SSL
163sub on_incomplete_write {
164    my $self = shift;
165    return if $self->{ssl_write_when_readable};
166    $self->SUPER::on_incomplete_write;
167}
168
169# overloaded read...
170sub read {
171    my Danga::Socket::SSL $self = $_[0];
172    my $amount = $_[1];
173
174    my $ssl = $self->{ssl_state};
175    return $self->SUPER::read($amount) unless $ssl;
176
177    my $data = Net::SSLeay::read($ssl);
178    my $errs = Net::SSLeay::print_errs('SSL_read');
179    if ($errs) {
180        warn "SSL Read error: $errs\n";
181        $self->close;
182        return;
183    }
184
185    return undef if defined $data && ! length $data;
186
187    # Net::SSLeays buffers internally, so if we didn't read anything, it's
188    # in its buffer
189    $data = "" unless $data && length $data;
190    return \$data;
191}
192
193# if returns true, continue to call event read, else skip event_read.
194sub cond_event_read {
195    my Danga::Socket::SSL $self = shift;
196
197    # for async SSL:  if a session renegotation is in progress,
198    # our previous write wants us to become readable first.
199    # we then go back into the write path (by flushing the write
200    # buffer) and it then does a read on this socket.
201    if (my $ar = $self->{ssl_write_when_readable}) {
202        $self->{ssl_write_when_readable} = 0;
203        $self->watch_read($ar->[0]);  # restore previous readability state
204        $self->watch_write(1);
205        return 0;
206    }
207
208    $self->event_read;
209}
210
211
2121;
Note: See TracBrowser for help on using the browser.