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