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.10 by root, Mon Aug 3 15:02:42 2009 UTC vs.
Revision 1.25 by root, Thu Aug 6 10:21:48 2009 UTC

1=head1 NAME 1=head1 NAME
2 2
3AnyEvent::MP::Transport - actual transport protocol 3AnyEvent::MP::Transport - actual transport protocol handler
4 4
5=head1 SYNOPSIS 5=head1 SYNOPSIS
6 6
7 use AnyEvent::MP::Transport; 7 use AnyEvent::MP::Transport;
8 8
9=head1 DESCRIPTION 9=head1 DESCRIPTION
10 10
11This is the superclass for MP transports, most of which is considered an 11This implements the actual transport protocol for MP (it represents a
12implementation detail. 12single link), most of which is considered an implementation detail.
13 13
14See the "PROTOCOL" section below if you want to write another client for 14See the "PROTOCOL" section below if you want to write another client for
15this protocol. 15this protocol.
16 16
17=head1 FUNCTIONS/METHODS 17=head1 FUNCTIONS/METHODS
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
111 114
112=cut 115=cut
113 116
114our @FRAMINGS = qw(json storable); # the framing types we accept and send, in order of preference 117our @FRAMINGS = qw(json storable); # the framing types we accept and send, in order of preference
115our @AUTH_SND = qw(hmac_md6_64_256); # auth types we send 118our @AUTH_SND = qw(hmac_md6_64_256); # auth types we send
116our @AUTH_RCV = (@AUTH_SND, qw(hex_secret)); # auth types we accept 119our @AUTH_RCV = (@AUTH_SND, qw(cleartext)); # auth types we accept
117 120
118#AnyEvent::Handle::register_write_type mp_record => sub { 121#AnyEvent::Handle::register_write_type mp_record => sub {
119#}; 122#};
120 123
121sub new { 124sub new {
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} ||= { sslv2 => 0, sslv3 => 0, tlsv1 => 1, verify => 1 };
132
133 $arg{secret} = AnyEvent::MP::Base::default_secret () 134 $arg{secret} = AnyEvent::MP::Base::default_secret ()
134 unless exists $arg{secret}; 135 unless exists $arg{secret};
135 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 }
156
136 $self->{hdl} = new AnyEvent::Handle 157 $self->{hdl} = new AnyEvent::Handle
137 fh => delete $arg{fh}, 158 fh => delete $arg{fh},
138 rbuf_max => 64 * 1024,
139 autocork => 1, 159 autocork => 1,
140 no_delay => 1, 160 no_delay => 1,
141 on_error => sub { 161 on_error => sub {
142 $self->error ($_[2]); 162 $self->error ($_[2]);
143 }, 163 },
164 timeout => $AnyEvent::MP::Base::CONNECT_TIMEOUT,
144 peername => delete $arg{peername}, 165 peername => delete $arg{peername},
145 ; 166 ;
146 167
147 my $secret = $arg{secret};
148 my $greeting_kv = $self->{greeting} ||= {}; 168 my $greeting_kv = $self->{greeting} ||= {};
169
170 $self->{local_node} = $AnyEvent::MP::Base::NODE;
171
149 $greeting_kv->{"tls"} = "1.0" 172 $greeting_kv->{"tls"} = "1.0" if $arg{tls_ctx};
150 if $arg{tls_ctx};
151 $greeting_kv->{provider} = "AE-$VERSION"; 173 $greeting_kv->{provider} = "AE-$VERSION";
152 $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;
153 176
154 # send greeting 177 # send greeting
155 my $lgreeting1 = "aemp;$PROTOCOL_VERSION;$PROTOCOL_VERSION" # version, min 178 my $lgreeting1 = "aemp;$PROTOCOL_VERSION"
156 . ";$AnyEvent::MP::Base::UNIQ" 179 . ";$AnyEvent::MP::Base::UNIQ"
157 . ";$AnyEvent::MP::Base::NODE" 180 . ";$self->{local_node}"
158 . ";" . (join ",", @AUTH_RCV) 181 . ";" . (join ",", @AUTH_RCV)
159 . ";" . (join ",", @FRAMINGS) 182 . ";" . (join ",", @FRAMINGS)
160 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv); 183 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv);
184
161 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), ""; 185 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), "";
162 186
163 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012"); 187 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012");
164 188
165 # expect greeting 189 # expect greeting
190 $self->{hdl}->rbuf_max (4 * 1024);
166 $self->{hdl}->push_read (line => sub { 191 $self->{hdl}->push_read (line => sub {
167 my $rgreeting1 = $_[1]; 192 my $rgreeting1 = $_[1];
168 193
169 my ($aemp, $version, $version_min, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1; 194 my ($aemp, $version, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1;
170 195
171 if ($aemp ne "aemp") { 196 if ($aemp ne "aemp") {
172 return $self->error ("unparsable greeting"); 197 return $self->error ("unparsable greeting");
173 } elsif ($version_min > $PROTOCOL_VERSION) { 198 } elsif ($version != $PROTOCOL_VERSION) {
174 return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version_min .. $version)"); 199 return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version)");
175 } 200 }
176 201
177 my $s_auth; 202 my $s_auth;
178 for my $auth_ (split /,/, $auths) { 203 for my $auth_ (split /,/, $auths) {
179 if (grep $auth_ eq $_, @AUTH_SND) { 204 if (grep $auth_ eq $_, @AUTH_SND) {
208 233
209 # read nonce 234 # read nonce
210 $self->{hdl}->push_read (line => sub { 235 $self->{hdl}->push_read (line => sub {
211 my $rgreeting2 = $_[1]; 236 my $rgreeting2 = $_[1];
212 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
213 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) { 244 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) {
214 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept"; 245 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept";
215 $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;
216 } 252 }
217
218 # auth
219 require Digest::MD6;
220 require Digest::HMAC_MD6;
221
222 my $key = Digest::MD6::md6_hex ($secret);
223 my $lauth = Digest::HMAC_MD6::hmac_md6_base64 ($key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256);
224
225 my $rauth =
226 $s_auth eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_base64 ($key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256)
227 : $s_auth eq "hex_secret" ? unpack "H*", $secret
228 : die;
229
230 $lauth ne $rauth # echo attack?
231 or return $self->error ("authentication error");
232 253
233 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012"); 254 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012");
234 255
235 $self->{hdl}->rbuf_max (64); # enough for 44 reply bytes or so 256 # read the authentication response
236 $self->{hdl}->push_read (line => sub { 257 $self->{hdl}->push_read (line => sub {
237 my ($hdl, $rline) = @_; 258 my ($hdl, $rline) = @_;
238 259
239 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");
240 267
241 if ($rauth2 ne $rauth) { 268 if ($rauth2 ne $rauth) {
242 return $self->error ("authentication failure/shared secret mismatch"); 269 return $self->error ("authentication failure/shared secret mismatch");
243 } 270 }
244 271
245 $self->{s_framing} = $s_framing; 272 $self->{s_framing} = $s_framing;
246 273
247 $hdl->rbuf_max (undef); 274 $hdl->rbuf_max (undef);
248 my $queue = delete $self->{queue}; # we are connected 275 my $queue = delete $self->{queue}; # we are connected
249 276
277 $self->{hdl}->timeout ($self->{remote_greeting}{keepalive} + 5)
278 if $self->{remote_greeting}{keepalive};
279
250 $self->connected; 280 $self->connected;
251 281
252 $hdl->push_write ($self->{s_framing} => $_) 282 my $src_node = $self->{node};
283
284 $self->send ($_)
253 for @$queue; 285 for @$queue;
254 286
255 my $rmsg; $rmsg = sub { 287 my $rmsg; $rmsg = sub {
256 $_[0]->push_read ($r_framing => $rmsg); 288 $_[0]->push_read ($r_framing => $rmsg);
257 289
290 local $AnyEvent::MP::Base::SRCNODE = $src_node;
258 AnyEvent::MP::Base::_inject ($_[1]); 291 AnyEvent::MP::Base::_inject (@{ $_[1] });
259 }; 292 };
260 $hdl->push_read ($r_framing => $rmsg); 293 $hdl->push_read ($r_framing => $rmsg);
261 }); 294 });
262 }); 295 });
263 }); 296 });
268 301
269sub error { 302sub error {
270 my ($self, $msg) = @_; 303 my ($self, $msg) = @_;
271 304
272 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);
273 $self->{node}->clr_transport; 308 $self->{node}->clr_transport;
274 } 309 }
275 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg"); 310 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg");
276 $self->destroy; 311 $self->destroy;
277} 312}
278 313
279sub connected { 314sub connected {
280 my ($self) = @_; 315 my ($self) = @_;
281 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
282 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node}); 331 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node});
283 Scalar::Util::weaken ($self->{node} = $node); 332 Scalar::Util::weaken ($self->{node} = $node);
284 $node->set_transport ($self); 333 $node->set_transport ($self);
285} 334}
286 335
314The 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
315CR LF pair, or a single ASCII LF (recommended). 364CR LF pair, or a single ASCII LF (recommended).
316 365
317=head2 GREETING 366=head2 GREETING
318 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
377
319The first line contains strings separated (not ended) by C<;> 378The first line contains strings separated (not ended) by C<;>
320characters. The first seven strings are fixed by the protocol, the 379characters. The first even ixtrings are fixed by the protocol, the
321remaining 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<;>
322characters themselves. 381characters themselves.
323 382
324The seven fixed strings are: 383The fixed strings are:
325 384
326=over 4 385=over 4
327 386
328=item C<aemp> 387=item protocol identification
329 388
330The constant C<aemp> to identify the protocol. 389The constant C<aemp> to identify the protocol.
331 390
332=item protocol version 391=item protocol version
333 392
334The (maximum) protocol version supported by this end, currently C<0>.
335
336=item minimum protocol version
337
338The minimum protocol version supported by this end, currently C<0>. 393The protocol version supported by this end, currently C<0>. If the
394versions don't match then no communication is possible. Minor extensions
395are supposed to be handled through additional key-value pairs.
339 396
340=item a token uniquely identifying the current node instance 397=item a token uniquely identifying the current node instance
341 398
342This is a string that must change between restarts. It usually contains 399This is a string that must change between restarts. It usually contains
343things 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
383=item tls=<major>.<minor> 440=item tls=<major>.<minor>
384 441
385Indicates 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
386wishes to do a TLS handshake. 443wishes to do a TLS handshake.
387 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
388=back 451=back
452
453=head3 Second Greeting Line
389 454
390After this greeting line there will be a second line containing a 455After this greeting line there will be a second line containing a
391cryptographic nonce, i.e. random data of high quality. To keep the 456cryptographic nonce, i.e. random data of high quality. To keep the
392protocol text-only, these are usually 32 base64-encoded octets, but 457protocol text-only, these are usually 32 base64-encoded octets, but
393it 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
394characters. 459characters.
395 460
396Example 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 >>.
397 463
398 aemp;0;0;e7d.4a76f48f;10.0.0.1:4040;hmac_md6_64_256,hex_secret;json,storable;provider=AE-0.0;peeraddr=127.0.0.1:1235 464Example of a nonce line:
399 XntegV2Guvss0qNn7phCPnoU87xqxV+4Mqm/5y4iQm6a 465
466 p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4
400 467
401=head2 TLS handshake 468=head2 TLS handshake
402 469
403If, after the handshake, both sides indicate interest in TLS, then the 470I<< If, after the handshake, both sides indicate interest in TLS, then the
404connection I<must> use TLS, or fail. 471connection B<must> use TLS, or fail. >>
405 472
406Both 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
407value ("string" comparison on the raw octet values) becomes the client, 474value ("string" comparison on the raw octet values) becomes the client,
408and the one with the higher nonce the server. 475and the one with the higher nonce the server.
409 476
420 487
421=item the authentication method chosen 488=item the authentication method chosen
422 489
423This must be one of the methods offered by the other side in the greeting. 490This must be one of the methods offered by the other side in the greeting.
424 491
492The currently supported authentication methods are:
493
494=over 4
495
496=item cleartext
497
498This is simply the shared secret, lowercase-hex-encoded. This method is of
499course very insecure, unless TLS is used, which is why this module will
500accept, but not generate, cleartext auth replies.
501
502=item hmac_md6_64_256
503
504This method uses an MD6 HMAC with 64 bit blocksize and 256 bit hash. First, the shared secret
505is hashed with MD6:
506
507 key = MD6 (secret)
508
509This secret is then used to generate the "local auth reply", by taking
510the two local greeting lines and the two remote greeting lines (without
511line endings), appending \012 to all of them, concatenating them and
512calculating the MD6 HMAC with the key.
513
514 lauth = HMAC_MD6 key, "lgreeting1\012lgreeting2\012rgreeting1\012rgreeting2\012"
515
516This authentication token is then lowercase-hex-encoded and sent to the
517other side.
518
519Then the remote auth reply is generated using the same method, but local
520and remote greeting lines swapped:
521
522 rauth = HMAC_MD6 key, "rgreeting1\012rgreeting2\012lgreeting1\012lgreeting2\012"
523
524This is the token that is expected from the other side.
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
534=back
535
425=item the authentication data 536=item the authentication data
426 537
427The authentication data itself, usually base64 or hex-encoded data. 538The authentication data itself, usually base64 or hex-encoded data, see
539above.
428 540
429=item the framing protocol chosen 541=item the framing protocol chosen
430 542
431This 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
432greeting. Each side must accept the choice of the other side. 544greeting. Each side must accept the choice of the other side.
433 545
434=back 546=back
435 547
436Example (the actual reply matching the previous example): 548Example of an authentication reply:
437 549
438 hmac_md6_64_256;wIlLedBY956UCGSISG9mBZRDTG8xUi73/sVse2DSQp0;json 550 hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json
439 551
440=head2 DATA PHASE 552=head2 DATA PHASE
441 553
442After this, packets get exchanged using the chosen framing protocol. It is 554After this, packets get exchanged using the chosen framing protocol. It is
443quite possible that both sides use a different framing protocol. 555quite possible that both sides use a different framing protocol.
444 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
445=head1 SEE ALSO 571=head1 SEE ALSO
446 572
447L<AnyEvent>. 573L<AnyEvent>.
448 574
449=head1 AUTHOR 575=head1 AUTHOR

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines