ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Transport.pm
Revision: 1.22
Committed: Wed Aug 5 19:55:58 2009 UTC (14 years, 10 months ago) by root
Branch: MAIN
Changes since 1.21: +1 -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 # send greeting
169 my $lgreeting1 = "aemp;$PROTOCOL_VERSION"
170 . ";$AnyEvent::MP::Base::UNIQ"
171 . ";$AnyEvent::MP::Base::NODE"
172 . ";" . (join ",", @AUTH_RCV)
173 . ";" . (join ",", @FRAMINGS)
174 . (join "", map ";$_=$greeting_kv->{$_}", keys %$greeting_kv);
175
176 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), "";
177
178 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012");
179
180 # expect greeting
181 $self->{hdl}->rbuf_max (4 * 1024);
182 $self->{hdl}->push_read (line => sub {
183 my $rgreeting1 = $_[1];
184
185 my ($aemp, $version, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1;
186
187 if ($aemp ne "aemp") {
188 return $self->error ("unparsable greeting");
189 } elsif ($version != $PROTOCOL_VERSION) {
190 return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version)");
191 }
192
193 my $s_auth;
194 for my $auth_ (split /,/, $auths) {
195 if (grep $auth_ eq $_, @AUTH_SND) {
196 $s_auth = $auth_;
197 last;
198 }
199 }
200
201 defined $s_auth
202 or return $self->error ("$auths: no common auth type supported");
203
204 die unless $s_auth eq "hmac_md6_64_256"; # hardcoded atm.
205
206 my $s_framing;
207 for my $framing_ (split /,/, $framings) {
208 if (grep $framing_ eq $_, @FRAMINGS) {
209 $s_framing = $framing_;
210 last;
211 }
212 }
213
214 defined $s_framing
215 or return $self->error ("$framings: no common framing method supported");
216
217 $self->{remote_uniq} = $uniq;
218 $self->{remote_node} = $rnode;
219
220 $self->{remote_greeting} = {
221 map /^([^=]+)(?:=(.*))?/ ? ($1 => $2) : (),
222 @kv
223 };
224
225 # read nonce
226 $self->{hdl}->push_read (line => sub {
227 my $rgreeting2 = $_[1];
228
229 "$lgreeting1\012$lgreeting2" ne "$rgreeting1\012$rgreeting2" # echo attack?
230 or return $self->error ("authentication error, echo attack?");
231
232 my $key = Digest::MD6::md6 $secret;
233 my $lauth;
234
235 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) {
236 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept";
237 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx});
238 $s_auth = "tls";
239 $lauth = "";
240 } else {
241 # we currently only support hmac_md6_64_256
242 $lauth = Digest::HMAC_MD6::hmac_md6_hex $key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256;
243 }
244
245 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012");
246
247 # read the authentication response
248 $self->{hdl}->push_read (line => sub {
249 my ($hdl, $rline) = @_;
250
251 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline;
252
253 my $rauth =
254 $auth_method eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_hex $key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256
255 : $auth_method eq "cleartext" ? unpack "H*", $secret
256 : $auth_method eq "tls" ? ($self->{tls} ? "" : "\012\012") # \012\012 never matches
257 : return $self->error ("$auth_method: fatal, selected unsupported auth method");
258
259 if ($rauth2 ne $rauth) {
260 return $self->error ("authentication failure/shared secret mismatch");
261 }
262
263 $self->{s_framing} = $s_framing;
264
265 $hdl->rbuf_max (undef);
266 my $queue = delete $self->{queue}; # we are connected
267
268 $self->connected;
269
270 my $src_node = $self->{node};
271
272 $hdl->push_write ($self->{s_framing} => $_)
273 for @$queue;
274
275 my $rmsg; $rmsg = sub {
276 $_[0]->push_read ($r_framing => $rmsg);
277
278 local $AnyEvent::MP::Base::SRCNODE = $src_node;
279 AnyEvent::MP::Base::_inject (@{ $_[1] });
280 };
281 $hdl->push_read ($r_framing => $rmsg);
282 });
283 });
284 });
285 }
286
287 $self
288 }
289
290 sub error {
291 my ($self, $msg) = @_;
292
293 if ($self->{node} && $self->{node}{transport} == $self) {
294 #TODO: store error, but do not instantly fail
295 $self->{node}->fail (transport_error => $self->{node}{noderef}, $msg);
296 $self->{node}->clr_transport;
297 }
298 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg");
299 $self->destroy;
300 }
301
302 sub connected {
303 my ($self) = @_;
304
305 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node});
306 Scalar::Util::weaken ($self->{node} = $node);
307 $node->set_transport ($self);
308 }
309
310 sub send {
311 $_[0]{hdl}->push_write ($_[0]{s_framing} => $_[1]);
312 }
313
314 sub destroy {
315 my ($self) = @_;
316
317 $self->{hdl}->destroy
318 if $self->{hdl};
319 }
320
321 sub DESTROY {
322 my ($self) = @_;
323
324 $self->destroy;
325 }
326
327 =back
328
329 =head1 PROTOCOL
330
331 The protocol is relatively simple, and consists of three phases which are
332 symmetrical for both sides: greeting (followed by optionally switching to
333 TLS mode), authentication and packet exchange.
334
335 the protocol is designed to allow both full-text and binary streams.
336
337 The greeting consists of two text lines that are ended by either an ASCII
338 CR LF pair, or a single ASCII LF (recommended).
339
340 =head2 GREETING
341
342 All the lines until after authentication must not exceed 4kb in length,
343 including delimiter. Afterwards there is no limit on the packet size that
344 can be received.
345
346 =head3 First Greeting Line
347
348 Example:
349
350 aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0
351
352 The first line contains strings separated (not ended) by C<;>
353 characters. The first even ixtrings are fixed by the protocol, the
354 remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;>
355 characters themselves.
356
357 The fixed strings are:
358
359 =over 4
360
361 =item protocol identification
362
363 The constant C<aemp> to identify the protocol.
364
365 =item protocol version
366
367 The protocol version supported by this end, currently C<0>. If the
368 versions don't match then no communication is possible. Minor extensions
369 are supposed to be handled through additional key-value pairs.
370
371 =item a token uniquely identifying the current node instance
372
373 This is a string that must change between restarts. It usually contains
374 things like the current time, the (OS) process id or similar values, but
375 no meaning of the contents are assumed.
376
377 =item the node endpoint descriptors
378
379 for public nodes, this is a comma-separated list of protocol endpoints,
380 i.e., the noderef. For slave nodes, this is a unique identifier.
381
382 =item the acceptable authentication methods
383
384 A comma-separated list of authentication methods supported by the
385 node. Note that AnyEvent::MP supports a C<hex_secret> authentication
386 method that accepts a cleartext password (hex-encoded), but will not use
387 this auth method itself.
388
389 The receiving side should choose the first auth method it supports.
390
391 =item the acceptable framing formats
392
393 A comma-separated list of packet encoding/framign formats understood. The
394 receiving side should choose the first framing format it supports for
395 sending packets (which might be different from the format it has to accept).
396
397 =back
398
399 The remaining arguments are C<KEY=VALUE> pairs. The following key-value
400 pairs are known at this time:
401
402 =over 4
403
404 =item provider=<module-version>
405
406 The software provider for this implementation. For AnyEvent::MP, this is
407 C<AE-0.0> or whatever version it currently is at.
408
409 =item peeraddr=<host>:<port>
410
411 The peer address (socket address of the other side) as seen locally, in the same format
412 as noderef endpoints.
413
414 =item tls=<major>.<minor>
415
416 Indicates that the other side supports TLS (version should be 1.0) and
417 wishes to do a TLS handshake.
418
419 =back
420
421 =head3 Second Greeting Line
422
423 After this greeting line there will be a second line containing a
424 cryptographic nonce, i.e. random data of high quality. To keep the
425 protocol text-only, these are usually 32 base64-encoded octets, but
426 it could be anything that doesn't contain any ASCII CR or ASCII LF
427 characters.
428
429 I<< The two nonces B<must> be different, and an aemp implementation
430 B<must> check and fail when they are identical >>.
431
432 Example of a nonce line:
433
434 p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4
435
436 =head2 TLS handshake
437
438 I<< If, after the handshake, both sides indicate interest in TLS, then the
439 connection B<must> use TLS, or fail. >>
440
441 Both sides compare their nonces, and the side who sent the lower nonce
442 value ("string" comparison on the raw octet values) becomes the client,
443 and the one with the higher nonce the server.
444
445 =head2 AUTHENTICATION PHASE
446
447 After the greeting is received (and the optional TLS handshake),
448 the authentication phase begins, which consists of sending a single
449 C<;>-separated line with three fixed strings and any number of
450 C<KEY=VALUE> pairs.
451
452 The three fixed strings are:
453
454 =over 4
455
456 =item the authentication method chosen
457
458 This must be one of the methods offered by the other side in the greeting.
459
460 The currently supported authentication methods are:
461
462 =over 4
463
464 =item cleartext
465
466 This is simply the shared secret, lowercase-hex-encoded. This method is of
467 course very insecure, unless TLS is used, which is why this module will
468 accept, but not generate, cleartext auth replies.
469
470 =item hmac_md6_64_256
471
472 This method uses an MD6 HMAC with 64 bit blocksize and 256 bit hash. First, the shared secret
473 is hashed with MD6:
474
475 key = MD6 (secret)
476
477 This secret is then used to generate the "local auth reply", by taking
478 the two local greeting lines and the two remote greeting lines (without
479 line endings), appending \012 to all of them, concatenating them and
480 calculating the MD6 HMAC with the key.
481
482 lauth = HMAC_MD6 key, "lgreeting1\012lgreeting2\012rgreeting1\012rgreeting2\012"
483
484 This authentication token is then lowercase-hex-encoded and sent to the
485 other side.
486
487 Then the remote auth reply is generated using the same method, but local
488 and remote greeting lines swapped:
489
490 rauth = HMAC_MD6 key, "rgreeting1\012rgreeting2\012lgreeting1\012lgreeting2\012"
491
492 This is the token that is expected from the other side.
493
494 =item tls
495
496 This type is only valid iff TLS was enabled and the TLS handshake
497 was successful. It has no authentication data, as the server/client
498 certificate was successfully verified.
499
500 Implementations supporting TLS I<must> accept this authentication type.
501
502 =back
503
504 =item the authentication data
505
506 The authentication data itself, usually base64 or hex-encoded data, see
507 above.
508
509 =item the framing protocol chosen
510
511 This must be one of the framing protocols offered by the other side in the
512 greeting. Each side must accept the choice of the other side.
513
514 =back
515
516 Example of an authentication reply:
517
518 hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json
519
520 =head2 DATA PHASE
521
522 After this, packets get exchanged using the chosen framing protocol. It is
523 quite possible that both sides use a different framing protocol.
524
525 =head2 FULL EXAMPLE
526
527 This is an actual protocol dump of a handshake, followed by a single data
528 packet. The greater than/less than lines indicate the direction of the
529 transfer only.
530
531 > 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
532 > sRG8bbc4TDbkpvH8FTP4HBs87OhepH6VuApoZqXXskuG
533 < 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
534 < dCEUcL/LJVSTJcx8byEsOzrwhzJYOq+L3YcopA5T6EAo
535 > hmac_md6_64_256;9513d4b258975accfcb2ab7532b83690e9c119a502c612203332a591c7237788;json
536 < hmac_md6_64_256;0298d6ba2240faabb2b2e881cf86b97d70a113ca74a87dc006f9f1e9d3010f90;json
537 > ["","lookup","pinger","10.0.0.1:4040#nndKd+gn.a","resolved"]
538
539 =head1 SEE ALSO
540
541 L<AnyEvent>.
542
543 =head1 AUTHOR
544
545 Marc Lehmann <schmorp@schmorp.de>
546 http://home.schmorp.de/
547
548 =cut
549
550 1
551