ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Transport.pm
Revision: 1.23
Committed: Wed Aug 5 22:40:51 2009 UTC (14 years, 11 months ago) by root
Branch: MAIN
Changes since 1.22: +15 -1 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 AnyEvent::MP::Transport - actual transport protocol handler
4
5 =head1 SYNOPSIS
6
7 use AnyEvent::MP::Transport;
8
9 =head1 DESCRIPTION
10
11 This implements the actual transport protocol for MP (it represents a
12 single link), most of which is considered an implementation detail.
13
14 See the "PROTOCOL" section below if you want to write another client for
15 this protocol.
16
17 =head1 FUNCTIONS/METHODS
18
19 =over 4
20
21 =cut
22
23 package AnyEvent::MP::Transport;
24
25 use common::sense;
26
27 use Scalar::Util;
28 use MIME::Base64 ();
29 use Storable ();
30 use JSON::XS ();
31
32 use Digest::MD6 ();
33 use Digest::HMAC_MD6 ();
34
35 use AE ();
36 use AnyEvent::Socket ();
37 use AnyEvent::Handle ();
38
39 use base Exporter::;
40
41 our $VERSION = '0.0';
42 our $PROTOCOL_VERSION = 0;
43
44 =item $listener = mp_listener $host, $port, <constructor-args>, $cb->($transport)
45
46 Creates a listener on the given host/port using
47 C<AnyEvent::Socket::tcp_server>.
48
49 See C<new>, below, for constructor arguments.
50
51 Defaults for peerhost, peerport and fh are provided.
52
53 =cut
54
55 sub mp_server($$@) {
56 my $cb = pop;
57 my ($host, $port, @args) = @_;
58
59 AnyEvent::Socket::tcp_server $host, $port, sub {
60 my ($fh, $host, $port) = @_;
61
62 $cb->(new AnyEvent::MP::Transport
63 fh => $fh,
64 peerhost => $host,
65 peerport => $port,
66 @args,
67 );
68 }
69 }
70
71 =item $guard = mp_connect $host, $port, <constructor-args>, $cb->($transport)
72
73 =cut
74
75 sub mp_connect {
76 my $cb = pop;
77 my ($host, $port, @args) = @_;
78
79 AnyEvent::Socket::tcp_connect $host, $port, sub {
80 my ($fh, $nhost, $nport) = @_;
81
82 return $cb->() unless $fh;
83
84 $cb->(new AnyEvent::MP::Transport
85 fh => $fh,
86 peername => $host,
87 peerhost => $nhost,
88 peerport => $nport,
89 @args,
90 );
91 }
92 }
93
94 =item new AnyEvent::MP::Transport
95
96 # immediately starts negotiation
97 my $transport = new AnyEvent::MP::Transport
98 # mandatory
99 fh => $filehandle,
100 local_id => $identifier,
101 on_recv => sub { receive-callback },
102 on_error => sub { error-callback },
103
104 # optional
105 secret => "shared secret",
106 on_eof => sub { clean-close-callback },
107 on_connect => sub { successful-connect-callback },
108 greeting => { key => value },
109
110 # tls support
111 tls_ctx => AnyEvent::TLS,
112 peername => $peername, # for verification
113 ;
114
115 =cut
116
117 our @FRAMINGS = qw(json storable); # the framing types we accept and send, in order of preference
118 our @AUTH_SND = qw(hmac_md6_64_256); # auth types we send
119 our @AUTH_RCV = (@AUTH_SND, qw(cleartext)); # auth types we accept
120
121 #AnyEvent::Handle::register_write_type mp_record => sub {
122 #};
123
124 sub new {
125 my ($class, %arg) = @_;
126
127 my $self = bless \%arg, $class;
128
129 $self->{queue} = [];
130
131 {
132 Scalar::Util::weaken (my $self = $self);
133
134 $arg{secret} = AnyEvent::MP::Base::default_secret ()
135 unless exists $arg{secret};
136
137 my $secret = $arg{secret};
138
139 if ($secret =~ /-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----.*-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/s) {
140 # assume TLS mode
141 $arg{tls_ctx} = {
142 sslv2 => 0,
143 sslv3 => 0,
144 tlsv1 => 1,
145 verify => 1,
146 cert => $secret,
147 ca_cert => $secret,
148 verify_require_client_cert => 1,
149 };
150 }
151
152 $self->{hdl} = new AnyEvent::Handle
153 fh => delete $arg{fh},
154 autocork => 1,
155 no_delay => 1,
156 on_error => sub {
157 $self->error ($_[2]);
158 },
159 peername => delete $arg{peername},
160 ;
161
162 my $greeting_kv = $self->{greeting} ||= {};
163 $greeting_kv->{"tls"} = "1.0"
164 if $arg{tls_ctx};
165 $greeting_kv->{provider} = "AE-$VERSION";
166 $greeting_kv->{peeraddr} = AnyEvent::Socket::format_hostport $self->{peerhost}, $self->{peerport};
167
168 $self->{local_node} = $AnyEvent::MP::Base::NODE;
169
170 # send greeting
171 my $lgreeting1 = "aemp;$PROTOCOL_VERSION"
172 . ";$AnyEvent::MP::Base::UNIQ"
173 . ";$AnyEvent::MP::Base::NODE"
174 . ";" . (join ",", @AUTH_RCV)
175 . ";" . (join ",", @FRAMINGS)
176 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv);
177
178 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), "";
179
180 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012");
181
182 # expect greeting
183 $self->{hdl}->rbuf_max (4 * 1024);
184 $self->{hdl}->push_read (line => sub {
185 my $rgreeting1 = $_[1];
186
187 my ($aemp, $version, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1;
188
189 if ($aemp ne "aemp") {
190 return $self->error ("unparsable greeting");
191 } elsif ($version != $PROTOCOL_VERSION) {
192 return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version)");
193 }
194
195 my $s_auth;
196 for my $auth_ (split /,/, $auths) {
197 if (grep $auth_ eq $_, @AUTH_SND) {
198 $s_auth = $auth_;
199 last;
200 }
201 }
202
203 defined $s_auth
204 or return $self->error ("$auths: no common auth type supported");
205
206 die unless $s_auth eq "hmac_md6_64_256"; # hardcoded atm.
207
208 my $s_framing;
209 for my $framing_ (split /,/, $framings) {
210 if (grep $framing_ eq $_, @FRAMINGS) {
211 $s_framing = $framing_;
212 last;
213 }
214 }
215
216 defined $s_framing
217 or return $self->error ("$framings: no common framing method supported");
218
219 $self->{remote_uniq} = $uniq;
220 $self->{remote_node} = $rnode;
221
222 $self->{remote_greeting} = {
223 map /^([^=]+)(?:=(.*))?/ ? ($1 => $2) : (),
224 @kv
225 };
226
227 # read nonce
228 $self->{hdl}->push_read (line => sub {
229 my $rgreeting2 = $_[1];
230
231 "$lgreeting1\012$lgreeting2" ne "$rgreeting1\012$rgreeting2" # echo attack?
232 or return $self->error ("authentication error, echo attack?");
233
234 my $key = Digest::MD6::md6 $secret;
235 my $lauth;
236
237 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) {
238 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept";
239 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx});
240 $s_auth = "tls";
241 $lauth = "";
242 } else {
243 # we currently only support hmac_md6_64_256
244 $lauth = Digest::HMAC_MD6::hmac_md6_hex $key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256;
245 }
246
247 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012");
248
249 # read the authentication response
250 $self->{hdl}->push_read (line => sub {
251 my ($hdl, $rline) = @_;
252
253 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline;
254
255 my $rauth =
256 $auth_method eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_hex $key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256
257 : $auth_method eq "cleartext" ? unpack "H*", $secret
258 : $auth_method eq "tls" ? ($self->{tls} ? "" : "\012\012") # \012\012 never matches
259 : return $self->error ("$auth_method: fatal, selected unsupported auth method");
260
261 if ($rauth2 ne $rauth) {
262 return $self->error ("authentication failure/shared secret mismatch");
263 }
264
265 $self->{s_framing} = $s_framing;
266
267 $hdl->rbuf_max (undef);
268 my $queue = delete $self->{queue}; # we are connected
269
270 $self->connected;
271
272 my $src_node = $self->{node};
273
274 $self->send ($_)
275 for @$queue;
276
277 my $rmsg; $rmsg = sub {
278 $_[0]->push_read ($r_framing => $rmsg);
279
280 local $AnyEvent::MP::Base::SRCNODE = $src_node;
281 AnyEvent::MP::Base::_inject (@{ $_[1] });
282 };
283 $hdl->push_read ($r_framing => $rmsg);
284 });
285 });
286 });
287 }
288
289 $self
290 }
291
292 sub error {
293 my ($self, $msg) = @_;
294
295 if ($self->{node} && $self->{node}{transport} == $self) {
296 #TODO: store error, but do not instantly fail
297 $self->{node}->fail (transport_error => $self->{node}{noderef}, $msg);
298 $self->{node}->clr_transport;
299 }
300 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg");
301 $self->destroy;
302 }
303
304 sub connected {
305 my ($self) = @_;
306
307 if (ref $AnyEvent::MP::Base::SLAVE) {
308 # first connect with a master node
309 $AnyEvent::MP::Base::NODE .= "\@$self->{remote_node}";
310 $AnyEvent::MP::Base::NODE{$AnyEvent::MP::Base::NODE} = $AnyEvent::MP::Base::NODE{""};
311 $AnyEvent::MP::Base::SLAVE->();
312 }
313
314 if ($self->{local_node} ne $AnyEvent::MP::Base::NODE) {
315 # node changed its name since first greeting
316 $self->send (["", iam => $AnyEvent::MP::Base::NODE]);
317 }
318
319 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node});
320 Scalar::Util::weaken ($self->{node} = $node);
321 $node->set_transport ($self);
322 }
323
324 sub send {
325 $_[0]{hdl}->push_write ($_[0]{s_framing} => $_[1]);
326 }
327
328 sub destroy {
329 my ($self) = @_;
330
331 $self->{hdl}->destroy
332 if $self->{hdl};
333 }
334
335 sub DESTROY {
336 my ($self) = @_;
337
338 $self->destroy;
339 }
340
341 =back
342
343 =head1 PROTOCOL
344
345 The protocol is relatively simple, and consists of three phases which are
346 symmetrical for both sides: greeting (followed by optionally switching to
347 TLS mode), authentication and packet exchange.
348
349 the protocol is designed to allow both full-text and binary streams.
350
351 The greeting consists of two text lines that are ended by either an ASCII
352 CR LF pair, or a single ASCII LF (recommended).
353
354 =head2 GREETING
355
356 All the lines until after authentication must not exceed 4kb in length,
357 including delimiter. Afterwards there is no limit on the packet size that
358 can be received.
359
360 =head3 First Greeting Line
361
362 Example:
363
364 aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0
365
366 The first line contains strings separated (not ended) by C<;>
367 characters. The first even ixtrings are fixed by the protocol, the
368 remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;>
369 characters themselves.
370
371 The fixed strings are:
372
373 =over 4
374
375 =item protocol identification
376
377 The constant C<aemp> to identify the protocol.
378
379 =item protocol version
380
381 The protocol version supported by this end, currently C<0>. If the
382 versions don't match then no communication is possible. Minor extensions
383 are supposed to be handled through additional key-value pairs.
384
385 =item a token uniquely identifying the current node instance
386
387 This is a string that must change between restarts. It usually contains
388 things like the current time, the (OS) process id or similar values, but
389 no meaning of the contents are assumed.
390
391 =item the node endpoint descriptors
392
393 for public nodes, this is a comma-separated list of protocol endpoints,
394 i.e., the noderef. For slave nodes, this is a unique identifier.
395
396 =item the acceptable authentication methods
397
398 A comma-separated list of authentication methods supported by the
399 node. Note that AnyEvent::MP supports a C<hex_secret> authentication
400 method that accepts a cleartext password (hex-encoded), but will not use
401 this auth method itself.
402
403 The receiving side should choose the first auth method it supports.
404
405 =item the acceptable framing formats
406
407 A comma-separated list of packet encoding/framign formats understood. The
408 receiving side should choose the first framing format it supports for
409 sending packets (which might be different from the format it has to accept).
410
411 =back
412
413 The remaining arguments are C<KEY=VALUE> pairs. The following key-value
414 pairs are known at this time:
415
416 =over 4
417
418 =item provider=<module-version>
419
420 The software provider for this implementation. For AnyEvent::MP, this is
421 C<AE-0.0> or whatever version it currently is at.
422
423 =item peeraddr=<host>:<port>
424
425 The peer address (socket address of the other side) as seen locally, in the same format
426 as noderef endpoints.
427
428 =item tls=<major>.<minor>
429
430 Indicates that the other side supports TLS (version should be 1.0) and
431 wishes to do a TLS handshake.
432
433 =back
434
435 =head3 Second Greeting Line
436
437 After this greeting line there will be a second line containing a
438 cryptographic nonce, i.e. random data of high quality. To keep the
439 protocol text-only, these are usually 32 base64-encoded octets, but
440 it could be anything that doesn't contain any ASCII CR or ASCII LF
441 characters.
442
443 I<< The two nonces B<must> be different, and an aemp implementation
444 B<must> check and fail when they are identical >>.
445
446 Example of a nonce line:
447
448 p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4
449
450 =head2 TLS handshake
451
452 I<< If, after the handshake, both sides indicate interest in TLS, then the
453 connection B<must> use TLS, or fail. >>
454
455 Both sides compare their nonces, and the side who sent the lower nonce
456 value ("string" comparison on the raw octet values) becomes the client,
457 and the one with the higher nonce the server.
458
459 =head2 AUTHENTICATION PHASE
460
461 After the greeting is received (and the optional TLS handshake),
462 the authentication phase begins, which consists of sending a single
463 C<;>-separated line with three fixed strings and any number of
464 C<KEY=VALUE> pairs.
465
466 The three fixed strings are:
467
468 =over 4
469
470 =item the authentication method chosen
471
472 This must be one of the methods offered by the other side in the greeting.
473
474 The currently supported authentication methods are:
475
476 =over 4
477
478 =item cleartext
479
480 This is simply the shared secret, lowercase-hex-encoded. This method is of
481 course very insecure, unless TLS is used, which is why this module will
482 accept, but not generate, cleartext auth replies.
483
484 =item hmac_md6_64_256
485
486 This method uses an MD6 HMAC with 64 bit blocksize and 256 bit hash. First, the shared secret
487 is hashed with MD6:
488
489 key = MD6 (secret)
490
491 This secret is then used to generate the "local auth reply", by taking
492 the two local greeting lines and the two remote greeting lines (without
493 line endings), appending \012 to all of them, concatenating them and
494 calculating the MD6 HMAC with the key.
495
496 lauth = HMAC_MD6 key, "lgreeting1\012lgreeting2\012rgreeting1\012rgreeting2\012"
497
498 This authentication token is then lowercase-hex-encoded and sent to the
499 other side.
500
501 Then the remote auth reply is generated using the same method, but local
502 and remote greeting lines swapped:
503
504 rauth = HMAC_MD6 key, "rgreeting1\012rgreeting2\012lgreeting1\012lgreeting2\012"
505
506 This is the token that is expected from the other side.
507
508 =item tls
509
510 This type is only valid iff TLS was enabled and the TLS handshake
511 was successful. It has no authentication data, as the server/client
512 certificate was successfully verified.
513
514 Implementations supporting TLS I<must> accept this authentication type.
515
516 =back
517
518 =item the authentication data
519
520 The authentication data itself, usually base64 or hex-encoded data, see
521 above.
522
523 =item the framing protocol chosen
524
525 This must be one of the framing protocols offered by the other side in the
526 greeting. Each side must accept the choice of the other side.
527
528 =back
529
530 Example of an authentication reply:
531
532 hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json
533
534 =head2 DATA PHASE
535
536 After this, packets get exchanged using the chosen framing protocol. It is
537 quite possible that both sides use a different framing protocol.
538
539 =head2 FULL EXAMPLE
540
541 This is an actual protocol dump of a handshake, followed by a single data
542 packet. The greater than/less than lines indicate the direction of the
543 transfer only.
544
545 > 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
546 > sRG8bbc4TDbkpvH8FTP4HBs87OhepH6VuApoZqXXskuG
547 < 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
548 < dCEUcL/LJVSTJcx8byEsOzrwhzJYOq+L3YcopA5T6EAo
549 > hmac_md6_64_256;9513d4b258975accfcb2ab7532b83690e9c119a502c612203332a591c7237788;json
550 < hmac_md6_64_256;0298d6ba2240faabb2b2e881cf86b97d70a113ca74a87dc006f9f1e9d3010f90;json
551 > ["","lookup","pinger","10.0.0.1:4040#nndKd+gn.a","resolved"]
552
553 =head1 SEE ALSO
554
555 L<AnyEvent>.
556
557 =head1 AUTHOR
558
559 Marc Lehmann <schmorp@schmorp.de>
560 http://home.schmorp.de/
561
562 =cut
563
564 1
565