ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Transport.pm
Revision: 1.15
Committed: Tue Aug 4 07:47:29 2009 UTC (14 years, 10 months ago) by root
Branch: MAIN
Changes since 1.14: +7 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3 root 1.13 AnyEvent::MP::Transport - actual transport protocol handler
4 root 1.1
5     =head1 SYNOPSIS
6    
7     use AnyEvent::MP::Transport;
8    
9     =head1 DESCRIPTION
10    
11 root 1.13 This implements the actual transport protocol for MP (it represents a
12     single link), most of which is considered an implementation detail.
13 root 1.1
14 root 1.7 See the "PROTOCOL" section below if you want to write another client for
15     this protocol.
16 root 1.1
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 root 1.2 use JSON::XS ();
31 root 1.1
32     use AE ();
33     use AnyEvent::Socket ();
34     use AnyEvent::Handle ();
35 root 1.2
36 root 1.1 use base Exporter::;
37    
38     our $VERSION = '0.0';
39 root 1.2 our $PROTOCOL_VERSION = 0;
40 root 1.1
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 root 1.10 Defaults for peerhost, peerport and fh are provided.
49 root 1.1
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 root 1.2 =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 root 1.1 =item new AnyEvent::MP::Transport
92    
93     # immediately starts negotiation
94     my $transport = new AnyEvent::MP::Transport
95 root 1.2 # mandatory
96 root 1.1 fh => $filehandle,
97 root 1.2 local_id => $identifier,
98 root 1.1 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 root 1.2 greeting => { key => value },
106 root 1.1
107     # tls support
108     tls_ctx => AnyEvent::TLS,
109     peername => $peername, # for verification
110     ;
111    
112     =cut
113    
114 root 1.7 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 root 1.13 our @AUTH_RCV = (@AUTH_SND, qw(cleartext)); # auth types we accept
117 root 1.7
118     #AnyEvent::Handle::register_write_type mp_record => sub {
119     #};
120 root 1.4
121 root 1.1 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 root 1.11 $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 root 1.1
141 root 1.5 $arg{secret} = AnyEvent::MP::Base::default_secret ()
142 root 1.2 unless exists $arg{secret};
143    
144 root 1.1 $self->{hdl} = new AnyEvent::Handle
145 root 1.2 fh => delete $arg{fh},
146 root 1.4 autocork => 1,
147     no_delay => 1,
148 root 1.1 on_error => sub {
149     $self->error ($_[2]);
150     },
151     peername => delete $arg{peername},
152     ;
153    
154 root 1.2 my $secret = $arg{secret};
155     my $greeting_kv = $self->{greeting} ||= {};
156 root 1.8 $greeting_kv->{"tls"} = "1.0"
157     if $arg{tls_ctx};
158 root 1.2 $greeting_kv->{provider} = "AE-$VERSION";
159 root 1.7 $greeting_kv->{peeraddr} = AnyEvent::Socket::format_hostport $self->{peerhost}, $self->{peerport};
160 root 1.1
161     # send greeting
162 root 1.12 my $lgreeting1 = "aemp;$PROTOCOL_VERSION"
163 root 1.7 . ";$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 root 1.12
169 root 1.7 my $lgreeting2 = MIME::Base64::encode_base64 AnyEvent::MP::Base::nonce (33), "";
170 root 1.1
171 root 1.7 $self->{hdl}->push_write ("$lgreeting1\012$lgreeting2\012");
172 root 1.1
173     # expect greeting
174 root 1.12 $self->{hdl}->rbuf_max (4 * 1024);
175 root 1.1 $self->{hdl}->push_read (line => sub {
176 root 1.7 my $rgreeting1 = $_[1];
177 root 1.1
178 root 1.12 my ($aemp, $version, $uniq, $rnode, $auths, $framings, @kv) = split /;/, $rgreeting1;
179 root 1.1
180     if ($aemp ne "aemp") {
181     return $self->error ("unparsable greeting");
182 root 1.12 } elsif ($version != $PROTOCOL_VERSION) {
183     return $self->error ("version mismatch (we: $PROTOCOL_VERSION, they: $version)");
184 root 1.1 }
185    
186 root 1.7 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 root 1.2 $self->{remote_uniq} = $uniq;
211     $self->{remote_node} = $rnode;
212 root 1.1
213 root 1.2 $self->{remote_greeting} = {
214     map /^([^=]+)(?:=(.*))?/ ? ($1 => $2) : (),
215     @kv
216 root 1.1 };
217    
218 root 1.7 # read nonce
219     $self->{hdl}->push_read (line => sub {
220     my $rgreeting2 = $_[1];
221    
222 root 1.10 if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) {
223 root 1.8 $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept";
224     $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx});
225     }
226    
227 root 1.7 # auth
228     require Digest::MD6;
229     require Digest::HMAC_MD6;
230 root 1.1
231 root 1.11 my $key = Digest::MD6::md6 ($secret);
232 root 1.13 my $lauth = Digest::HMAC_MD6::hmac_md6_hex ($key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256);
233 root 1.1
234 root 1.7 my $rauth =
235 root 1.13 $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 root 1.7 : die;
238 root 1.1
239 root 1.7 $lauth ne $rauth # echo attack?
240     or return $self->error ("authentication error");
241 root 1.2
242 root 1.7 $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012");
243 root 1.2
244 root 1.12 # reasd the authentication response
245 root 1.7 $self->{hdl}->push_read (line => sub {
246     my ($hdl, $rline) = @_;
247 root 1.2
248 root 1.7 my ($auth_method, $rauth2, $r_framing) = split /;/, $rline;
249 root 1.1
250 root 1.7 if ($rauth2 ne $rauth) {
251     return $self->error ("authentication failure/shared secret mismatch");
252     }
253 root 1.1
254 root 1.7 $self->{s_framing} = $s_framing;
255 root 1.2
256 root 1.7 $hdl->rbuf_max (undef);
257     my $queue = delete $self->{queue}; # we are connected
258 root 1.1
259 root 1.7 $self->connected;
260 root 1.1
261 root 1.12 my $src_node = $self->{node};
262    
263 root 1.7 $hdl->push_write ($self->{s_framing} => $_)
264     for @$queue;
265 root 1.1
266 root 1.7 my $rmsg; $rmsg = sub {
267     $_[0]->push_read ($r_framing => $rmsg);
268 root 1.1
269 root 1.12 local $AnyEvent::MP::Base::SRCNODE = $src_node;
270     AnyEvent::MP::Base::_inject (@{ $_[1] });
271 root 1.7 };
272     $hdl->push_read ($r_framing => $rmsg);
273     });
274 root 1.1 });
275     });
276     }
277    
278     $self
279     }
280    
281     sub error {
282     my ($self, $msg) = @_;
283    
284 root 1.4 if ($self->{node} && $self->{node}{transport} == $self) {
285     $self->{node}->clr_transport;
286     }
287 root 1.7 $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg");
288 root 1.4 $self->destroy;
289 root 1.1 }
290    
291 root 1.2 sub connected {
292     my ($self) = @_;
293    
294 root 1.5 my $node = AnyEvent::MP::Base::add_node ($self->{remote_node});
295 root 1.4 Scalar::Util::weaken ($self->{node} = $node);
296     $node->set_transport ($self);
297 root 1.2 }
298    
299 root 1.1 sub send {
300 root 1.2 $_[0]{hdl}->push_write ($_[0]{s_framing} => $_[1]);
301 root 1.1 }
302    
303     sub destroy {
304     my ($self) = @_;
305    
306 root 1.2 $self->{hdl}->destroy
307     if $self->{hdl};
308 root 1.1 }
309    
310     sub DESTROY {
311     my ($self) = @_;
312    
313     $self->destroy;
314     }
315    
316     =back
317    
318 root 1.7 =head1 PROTOCOL
319    
320     The protocol is relatively simple, and consists of three phases which are
321     symmetrical for both sides: greeting (followed by optionally switching to
322     TLS mode), authentication and packet exchange.
323    
324     the protocol is designed to allow both full-text and binary streams.
325    
326     The greeting consists of two text lines that are ended by either an ASCII
327     CR LF pair, or a single ASCII LF (recommended).
328    
329     =head2 GREETING
330    
331 root 1.8 The first line contains strings separated (not ended) by C<;>
332 root 1.12 characters. The first even ixtrings are fixed by the protocol, the
333 root 1.7 remaining strings are C<KEY=VALUE> pairs. None of them may contain C<;>
334     characters themselves.
335    
336 root 1.15 All the lines until after authentication must not exceed 4kb in length,
337     including delimiter. Afterwards there is no limit on the packet size that
338     can be received.
339    
340     =head3 First Greeting Line
341 root 1.12
342     The fixed strings are:
343 root 1.7
344     =over 4
345    
346     =item C<aemp>
347    
348     The constant C<aemp> to identify the protocol.
349    
350     =item protocol version
351    
352 root 1.12 The protocol version supported by this end, currently C<0>. If the
353     versions don't match then no communication is possible. Minor extensions
354     are supposed to be handled by addign additional key-value pairs.
355 root 1.7
356     =item a token uniquely identifying the current node instance
357    
358     This is a string that must change between restarts. It usually contains
359     things like the current time, the (OS) process id or similar values, but
360     no meaning of the contents are assumed.
361    
362     =item the node endpoint descriptors
363    
364     for public nodes, this is a comma-separated list of protocol endpoints,
365     i.e., the noderef. For slave nodes, this is a unique identifier.
366    
367     =item the acceptable authentication methods
368    
369     A comma-separated list of authentication methods supported by the
370     node. Note that AnyEvent::MP supports a C<hex_secret> authentication
371     method that accepts a cleartext password (hex-encoded), but will not use
372     this auth method itself.
373    
374     The receiving side should choose the first auth method it supports.
375    
376     =item the acceptable framing formats
377    
378     A comma-separated list of packet encoding/framign formats understood. The
379     receiving side should choose the first framing format it supports for
380     sending packets (which might be different from the format it has to accept).
381    
382 root 1.10 =back
383 root 1.8
384     The remaining arguments are C<KEY=VALUE> pairs. The following key-value
385     pairs are known at this time:
386    
387     =over 4
388    
389     =item provider=<module-version>
390    
391     The software provider for this implementation. For AnyEvent::MP, this is
392     C<AE-0.0> or whatever version it currently is at.
393    
394     =item peeraddr=<host>:<port>
395    
396     The peer address (socket address of the other side) as seen locally, in the same format
397     as noderef endpoints.
398    
399     =item tls=<major>.<minor>
400    
401     Indicates that the other side supports TLS (version should be 1.0) and
402     wishes to do a TLS handshake.
403    
404     =back
405    
406 root 1.15 =head3 Second Greeting Line
407    
408 root 1.8 After this greeting line there will be a second line containing a
409     cryptographic nonce, i.e. random data of high quality. To keep the
410     protocol text-only, these are usually 32 base64-encoded octets, but
411     it could be anything that doesn't contain any ASCII CR or ASCII LF
412     characters.
413    
414 root 1.14 I<< The two nonces B<must> be different, and an aemp implementation
415     B<must> check and fail when they are identical >>.
416    
417 root 1.8 Example of the two lines of greeting:
418    
419 root 1.12 aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0
420     p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4
421 root 1.8
422     =head2 TLS handshake
423    
424 root 1.14 I<< If, after the handshake, both sides indicate interest in TLS, then the
425     connection B<must> use TLS, or fail.>>
426 root 1.8
427     Both sides compare their nonces, and the side who sent the lower nonce
428     value ("string" comparison on the raw octet values) becomes the client,
429     and the one with the higher nonce the server.
430    
431     =head2 AUTHENTICATION PHASE
432    
433     After the greeting is received (and the optional TLS handshake),
434     the authentication phase begins, which consists of sending a single
435     C<;>-separated line with three fixed strings and any number of
436     C<KEY=VALUE> pairs.
437    
438     The three fixed strings are:
439    
440     =over 4
441    
442     =item the authentication method chosen
443    
444     This must be one of the methods offered by the other side in the greeting.
445    
446 root 1.13 The currently supported authentication methods are:
447    
448     =over 4
449    
450     =item cleartext
451    
452     This is simply the shared secret, lowercase-hex-encoded. This method is of
453     course very insecure, unless TLS is used, which is why this module will
454     accept, but not generate, cleartext auth replies.
455    
456     =item hmac_md6_64_256
457    
458     This method uses an MD6 HMAC with 64 bit blocksize and 256 bit hash. First, the shared secret
459     is hashed with MD6:
460    
461     key = MD6 (secret)
462    
463     This secret is then used to generate the "local auth reply", by taking
464     the two local greeting lines and the two remote greeting lines (without
465     line endings), appending \012 to all of them, concatenating them and
466     calculating the MD6 HMAC with the key.
467    
468     lauth = HMAC_MD6 key, "lgreeting1\012lgreeting2\012rgreeting1\012rgreeting2\012"
469    
470     This authentication token is then lowercase-hex-encoded and sent to the
471     other side.
472    
473     Then the remote auth reply is generated using the same method, but local
474     and remote greeting lines swapped:
475    
476     rauth = HMAC_MD6 key, "rgreeting1\012rgreeting2\012lgreeting1\012lgreeting2\012"
477    
478     This is the token that is expected from the other side.
479    
480     =back
481    
482 root 1.8 =item the authentication data
483    
484 root 1.13 The authentication data itself, usually base64 or hex-encoded data, see
485     above.
486 root 1.8
487     =item the framing protocol chosen
488    
489     This must be one of the framing protocols offered by the other side in the
490     greeting. Each side must accept the choice of the other side.
491    
492     =back
493    
494 root 1.12 Example:
495 root 1.9
496 root 1.13 hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json
497 root 1.9
498 root 1.8 =head2 DATA PHASE
499    
500     After this, packets get exchanged using the chosen framing protocol. It is
501     quite possible that both sides use a different framing protocol.
502    
503 root 1.1 =head1 SEE ALSO
504    
505     L<AnyEvent>.
506    
507     =head1 AUTHOR
508    
509     Marc Lehmann <schmorp@schmorp.de>
510     http://home.schmorp.de/
511    
512     =cut
513    
514     1
515