ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Transport.pm
(Generate patch)

Comparing AnyEvent-MP/MP/Transport.pm (file contents):
Revision 1.13 by root, Mon Aug 3 22:05:55 2009 UTC vs.
Revision 1.24 by root, Wed Aug 5 23:50:46 2009 UTC

26 26
27use Scalar::Util; 27use Scalar::Util;
28use MIME::Base64 (); 28use MIME::Base64 ();
29use Storable (); 29use Storable ();
30use JSON::XS (); 30use JSON::XS ();
31
32use Digest::MD6 ();
33use Digest::HMAC_MD6 ();
31 34
32use AE (); 35use AE ();
33use AnyEvent::Socket (); 36use AnyEvent::Socket ();
34use AnyEvent::Handle (); 37use AnyEvent::Handle ();
35 38
126 $self->{queue} = []; 129 $self->{queue} = [];
127 130
128 { 131 {
129 Scalar::Util::weaken (my $self = $self); 132 Scalar::Util::weaken (my $self = $self);
130 133
131 $arg{tls_ctx_disabled} ||= {
132 sslv2 => 0,
133 sslv3 => 0,
134 tlsv1 => 1,
135 verify => 1,
136 cert_file => "secret.pem",
137 ca_file => "secret.pem",
138 verify_require_client_cert => 1,
139 };
140
141 $arg{secret} = AnyEvent::MP::Base::default_secret () 134 $arg{secret} = AnyEvent::MP::Base::default_secret ()
142 unless exists $arg{secret}; 135 unless exists $arg{secret};
136
137 $arg{timeout} = 30
138 unless exists $arg{timeout};
139
140 my $keepalive = (int $arg{timeout} * 0.75) || 1;
141
142 my $secret = $arg{secret};
143
144 if ($secret =~ /-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----.*-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/s) {
145 # assume TLS mode
146 $arg{tls_ctx} = {
147 sslv2 => 0,
148 sslv3 => 0,
149 tlsv1 => 1,
150 verify => 1,
151 cert => $secret,
152 ca_cert => $secret,
153 verify_require_client_cert => 1,
154 };
155 }
143 156
144 $self->{hdl} = new AnyEvent::Handle 157 $self->{hdl} = new AnyEvent::Handle
145 fh => delete $arg{fh}, 158 fh => delete $arg{fh},
146 autocork => 1, 159 autocork => 1,
147 no_delay => 1, 160 no_delay => 1,
148 on_error => sub { 161 on_error => sub {
149 $self->error ($_[2]); 162 $self->error ($_[2]);
150 }, 163 },
164 timeout => $AnyEvent::MP::Base::CONNECT_TIMEOUT,
151 peername => delete $arg{peername}, 165 peername => delete $arg{peername},
152 ; 166 ;
153 167
154 my $secret = $arg{secret};
155 my $greeting_kv = $self->{greeting} ||= {}; 168 my $greeting_kv = $self->{greeting} ||= {};
169
170 $self->{local_node} = $AnyEvent::MP::Base::NODE;
171
156 $greeting_kv->{"tls"} = "1.0" 172 $greeting_kv->{"tls"} = "1.0" if $arg{tls_ctx};
157 if $arg{tls_ctx};
158 $greeting_kv->{provider} = "AE-$VERSION"; 173 $greeting_kv->{provider} = "AE-$VERSION";
159 $greeting_kv->{peeraddr} = AnyEvent::Socket::format_hostport $self->{peerhost}, $self->{peerport}; 174 $greeting_kv->{peeraddr} = AnyEvent::Socket::format_hostport $self->{peerhost}, $self->{peerport};
175 $greeting_kv->{maxidle} = $keepalive;
160 176
161 # send greeting 177 # send greeting
162 my $lgreeting1 = "aemp;$PROTOCOL_VERSION" 178 my $lgreeting1 = "aemp;$PROTOCOL_VERSION"
163 . ";$AnyEvent::MP::Base::UNIQ" 179 . ";$AnyEvent::MP::Base::UNIQ"
164 . ";$AnyEvent::MP::Base::NODE" 180 . ";$self->{local_node}"
165 . ";" . (join ",", @AUTH_RCV) 181 . ";" . (join ",", @AUTH_RCV)
166 . ";" . (join ",", @FRAMINGS) 182 . ";" . (join ",", @FRAMINGS)
167 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv); 183 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv);
168 184
169 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), ""; 185 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), "";
217 233
218 # read nonce 234 # read nonce
219 $self->{hdl}->push_read (line => sub { 235 $self->{hdl}->push_read (line => sub {
220 my $rgreeting2 = $_[1]; 236 my $rgreeting2 = $_[1];
221 237
238 "$lgreeting1\012$lgreeting2" ne "$rgreeting1\012$rgreeting2" # echo attack?
239 or return $self->error ("authentication error, echo attack?");
240
241 my $key = Digest::MD6::md6 $secret;
242 my $lauth;
243
222 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) { 244 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) {
223 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept"; 245 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept";
224 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx}); 246 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx});
247 $s_auth = "tls";
248 $lauth = "";
249 } else {
250 # we currently only support hmac_md6_64_256
251 $lauth = Digest::HMAC_MD6::hmac_md6_hex $key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256;
225 } 252 }
226
227 # auth
228 require Digest::MD6;
229 require Digest::HMAC_MD6;
230
231 my $key = Digest::MD6::md6 ($secret);
232 my $lauth = Digest::HMAC_MD6::hmac_md6_hex ($key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256);
233
234 my $rauth =
235 $s_auth eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_hex ($key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256)
236 : $s_auth eq "cleartext" ? unpack "H*", $secret
237 : die;
238
239 $lauth ne $rauth # echo attack?
240 or return $self->error ("authentication error");
241 253
242 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012"); 254 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012");
243 255
244 # reasd the authentication response 256 # read the authentication response
245 $self->{hdl}->push_read (line => sub { 257 $self->{hdl}->push_read (line => sub {
246 my ($hdl, $rline) = @_; 258 my ($hdl, $rline) = @_;
247 259
248 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline; 260 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline;
261
262 my $rauth =
263 $auth_method eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_hex $key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256
264 : $auth_method eq "cleartext" ? unpack "H*", $secret
265 : $auth_method eq "tls" ? ($self->{tls} ? "" : "\012\012") # \012\012 never matches
266 : return $self->error ("$auth_method: fatal, selected unsupported auth method");
249 267
250 if ($rauth2 ne $rauth) { 268 if ($rauth2 ne $rauth) {
251 return $self->error ("authentication failure/shared secret mismatch"); 269 return $self->error ("authentication failure/shared secret mismatch");
252 } 270 }
253 271
254 $self->{s_framing} = $s_framing; 272 $self->{s_framing} = $s_framing;
255 273
256 $hdl->rbuf_max (undef); 274 $hdl->rbuf_max (undef);
257 my $queue = delete $self->{queue}; # we are connected 275 my $queue = delete $self->{queue}; # we are connected
258 276
277# $self->{hdl}->timeout ($self->{remote_greeting}{keepalive} + 5)
278# if $self->{remote_greeting}{keepalive};
279
259 $self->connected; 280 $self->connected;
260 281
261 my $src_node = $self->{node}; 282 my $src_node = $self->{node};
262 283
263 $hdl->push_write ($self->{s_framing} => $_) 284 $self->send ($_)
264 for @$queue; 285 for @$queue;
265 286
266 my $rmsg; $rmsg = sub { 287 my $rmsg; $rmsg = sub {
267 $_[0]->push_read ($r_framing => $rmsg); 288 $_[0]->push_read ($r_framing => $rmsg);
268 289
269 local $AnyEvent::MP::Base::SRCNODE = $src_node; 290 local $AnyEvent::MP::Base::SRCNODE = $src_node;
270 AnyEvent::MP::Base::_inject (@{ $_[1] }); 291 AnyEvent::MP::Base::_inject (@{ $_[1] });
271 }; 292 };
280 301
281sub error { 302sub error {
282 my ($self, $msg) = @_; 303 my ($self, $msg) = @_;
283 304
284 if ($self->{node} && $self->{node}{transport} == $self) { 305 if ($self->{node} && $self->{node}{transport} == $self) {
306 #TODO: store error, but do not instantly fail
307 $self->{node}->fail (transport_error => $self->{node}{noderef}, $msg);
285 $self->{node}->clr_transport; 308 $self->{node}->clr_transport;
286 } 309 }
287 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg"); 310 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg");
288 $self->destroy; 311 $self->destroy;
289} 312}
290 313
291sub connected { 314sub connected {
292 my ($self) = @_; 315 my ($self) = @_;
293 316
317 if (ref $AnyEvent::MP::Base::SLAVE) {
318 # first connect with a master node
319 my $via = $self->{remote_node};
320 $via =~ s/,/!/g;
321 $AnyEvent::MP::Base::NODE .= "\@$via";
322 $AnyEvent::MP::Base::NODE{$AnyEvent::MP::Base::NODE} = $AnyEvent::MP::Base::NODE{""};
323 $AnyEvent::MP::Base::SLAVE->();
324 }
325
326 if ($self->{local_node} ne $AnyEvent::MP::Base::NODE) {
327 # node changed its name since first greeting
328 $self->send (["", iam => $AnyEvent::MP::Base::NODE]);
329 }
330
294 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node}); 331 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node});
295 Scalar::Util::weaken ($self->{node} = $node); 332 Scalar::Util::weaken ($self->{node} = $node);
296 $node->set_transport ($self); 333 $node->set_transport ($self);
297} 334}
298 335
325 362
326The greeting consists of two text lines that are ended by either an ASCII 363The greeting consists of two text lines that are ended by either an ASCII
327CR LF pair, or a single ASCII LF (recommended). 364CR LF pair, or a single ASCII LF (recommended).
328 365
329=head2 GREETING 366=head2 GREETING
367
368All the lines until after authentication must not exceed 4kb in length,
369including delimiter. Afterwards there is no limit on the packet size that
370can be received.
371
372=head3 First Greeting Line
373
374Example:
375
376 aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0
330 377
331The first line contains strings separated (not ended) by C<;> 378The first line contains strings separated (not ended) by C<;>
332characters. The first even ixtrings are fixed by the protocol, the 379characters. The first even ixtrings are fixed by the protocol, the
333remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;> 380remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;>
334characters themselves. 381characters themselves.
335 382
336All the lines until after authentication must not exceed 4kb in length, including delimiter.
337
338The fixed strings are: 383The fixed strings are:
339 384
340=over 4 385=over 4
341 386
342=item C<aemp> 387=item protocol identification
343 388
344The constant C<aemp> to identify the protocol. 389The constant C<aemp> to identify the protocol.
345 390
346=item protocol version 391=item protocol version
347 392
348The protocol version supported by this end, currently C<0>. If the 393The protocol version supported by this end, currently C<0>. If the
349versions don't match then no communication is possible. Minor extensions 394versions don't match then no communication is possible. Minor extensions
350are supposed to be handled by addign additional key-value pairs. 395are supposed to be handled through additional key-value pairs.
351 396
352=item a token uniquely identifying the current node instance 397=item a token uniquely identifying the current node instance
353 398
354This is a string that must change between restarts. It usually contains 399This is a string that must change between restarts. It usually contains
355things like the current time, the (OS) process id or similar values, but 400things like the current time, the (OS) process id or similar values, but
395=item tls=<major>.<minor> 440=item tls=<major>.<minor>
396 441
397Indicates that the other side supports TLS (version should be 1.0) and 442Indicates that the other side supports TLS (version should be 1.0) and
398wishes to do a TLS handshake. 443wishes to do a TLS handshake.
399 444
445=item maxidle=<seconds>
446
447The maximum amount of time the node will not sent data, i.e., idle. This
448can be used to close the conenction when no data has been received for a
449too-long time (say, maxidle + 5 seconds).
450
400=back 451=back
452
453=head3 Second Greeting Line
401 454
402After this greeting line there will be a second line containing a 455After this greeting line there will be a second line containing a
403cryptographic nonce, i.e. random data of high quality. To keep the 456cryptographic nonce, i.e. random data of high quality. To keep the
404protocol text-only, these are usually 32 base64-encoded octets, but 457protocol text-only, these are usually 32 base64-encoded octets, but
405it could be anything that doesn't contain any ASCII CR or ASCII LF 458it could be anything that doesn't contain any ASCII CR or ASCII LF
406characters. 459characters.
407 460
408Example of the two lines of greeting: 461I<< The two nonces B<must> be different, and an aemp implementation
462B<must> check and fail when they are identical >>.
409 463
410 aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0 464Example of a nonce line:
465
411 p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4 466 p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4
412 467
413=head2 TLS handshake 468=head2 TLS handshake
414 469
415If, after the handshake, both sides indicate interest in TLS, then the 470I<< If, after the handshake, both sides indicate interest in TLS, then the
416connection I<must> use TLS, or fail. 471connection B<must> use TLS, or fail. >>
417 472
418Both sides compare their nonces, and the side who sent the lower nonce 473Both sides compare their nonces, and the side who sent the lower nonce
419value ("string" comparison on the raw octet values) becomes the client, 474value ("string" comparison on the raw octet values) becomes the client,
420and the one with the higher nonce the server. 475and the one with the higher nonce the server.
421 476
466 521
467 rauth = HMAC_MD6 key, "rgreeting1\012rgreeting2\012lgreeting1\012lgreeting2\012" 522 rauth = HMAC_MD6 key, "rgreeting1\012rgreeting2\012lgreeting1\012lgreeting2\012"
468 523
469This is the token that is expected from the other side. 524This is the token that is expected from the other side.
470 525
526=item tls
527
528This type is only valid iff TLS was enabled and the TLS handshake
529was successful. It has no authentication data, as the server/client
530certificate was successfully verified.
531
532Implementations supporting TLS I<must> accept this authentication type.
533
471=back 534=back
472 535
473=item the authentication data 536=item the authentication data
474 537
475The authentication data itself, usually base64 or hex-encoded data, see 538The authentication data itself, usually base64 or hex-encoded data, see
480This must be one of the framing protocols offered by the other side in the 543This must be one of the framing protocols offered by the other side in the
481greeting. Each side must accept the choice of the other side. 544greeting. Each side must accept the choice of the other side.
482 545
483=back 546=back
484 547
485Example: 548Example of an authentication reply:
486 549
487 hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json 550 hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json
488 551
489=head2 DATA PHASE 552=head2 DATA PHASE
490 553
491After this, packets get exchanged using the chosen framing protocol. It is 554After this, packets get exchanged using the chosen framing protocol. It is
492quite possible that both sides use a different framing protocol. 555quite possible that both sides use a different framing protocol.
493 556
557=head2 FULL EXAMPLE
558
559This is an actual protocol dump of a handshake, followed by a single data
560packet. The greater than/less than lines indicate the direction of the
561transfer only.
562
563 > aemp;0;nndKd+gn;10.0.0.1:4040;hmac_md6_64_256,cleartext;json,storable;provider=AE-0.0;peeraddr=127.0.0.1:1235
564 > sRG8bbc4TDbkpvH8FTP4HBs87OhepH6VuApoZqXXskuG
565 < aemp;0;nmpKd+gh;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256,cleartext;json,storable;provider=AE-0.0;peeraddr=127.0.0.1:58760
566 < dCEUcL/LJVSTJcx8byEsOzrwhzJYOq+L3YcopA5T6EAo
567 > hmac_md6_64_256;9513d4b258975accfcb2ab7532b83690e9c119a502c612203332a591c7237788;json
568 < hmac_md6_64_256;0298d6ba2240faabb2b2e881cf86b97d70a113ca74a87dc006f9f1e9d3010f90;json
569 > ["","lookup","pinger","10.0.0.1:4040#nndKd+gn.a","resolved"]
570
494=head1 SEE ALSO 571=head1 SEE ALSO
495 572
496L<AnyEvent>. 573L<AnyEvent>.
497 574
498=head1 AUTHOR 575=head1 AUTHOR

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines