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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines