ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Transport.pm
Revision: 1.1
Committed: Thu Jul 30 08:38:50 2009 UTC (14 years, 11 months ago) by root
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 AnyEvent::MP::Transport - actual transport protocol
4
5 =head1 SYNOPSIS
6
7 use AnyEvent::MP::Transport;
8
9 =head1 DESCRIPTION
10
11 This is the superclass for MP transports, most of which is considered an
12 implementation detail.
13
14 Future versions might document the actual protocol.
15
16 =head1 FUNCTIONS/METHODS
17
18 =over 4
19
20 =cut
21
22 package AnyEvent::MP::Transport;
23
24 use common::sense;
25
26 use Scalar::Util;
27 use MIME::Base64 ();
28 use Storable ();
29
30 use AE ();
31 use AnyEvent::Socket ();
32 use AnyEvent::Handle ();
33 use AnyEvent::MP ();
34
35 use base Exporter::;
36
37 our $VERSION = '0.0';
38 our $PROTOCOL_VERSION_MAJOR = 0;
39 our $PROTOCOL_VERSION_MINOR = 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, fh and tls 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 tls => "accept",
64 @args,
65 );
66 }
67 }
68
69 =item new AnyEvent::MP::Transport
70
71 # immediately starts negotiation
72 my $transport = new AnyEvent::MP::Transport
73 # fh OR connect is mandatory
74 fh => $filehandle,
75 connect => [$host, $port],
76
77 # mandatory
78 on_recv => sub { receive-callback },
79 on_error => sub { error-callback },
80
81 # optional
82 local_id => $identifier,
83 secret => "shared secret",
84 on_eof => sub { clean-close-callback },
85 on_connect => sub { successful-connect-callback },
86
87 # tls support
88 tls => "accept|connect",
89 tls_ctx => AnyEvent::TLS,
90 peername => $peername, # for verification
91 ;
92
93 =cut
94
95 sub new {
96 my ($class, %arg) = @_;
97
98 my $self = bless \%arg, $class;
99
100 $self->{queue} = [];
101
102 {
103 Scalar::Util::weaken (my $self = $self);
104
105 if (exists $arg{connect}) {
106 $arg{tls} ||= "connect";
107 $arg{tls_ctx} ||= { sslv2 => 0, sslv3 => 0, tlsv1 => 1, verify => 1, verify_peername => "https" };
108 }
109
110 $self->{hdl} = new AnyEvent::Handle
111 (exists $arg{fh} ? (fh => delete $arg{fh}) : (connect => delete $arg{connect})),
112 on_error => sub {
113 $self->error ($_[2]);
114 },
115 peername => delete $arg{peername},
116 ;
117
118 my $secret = delete $arg{secret} ? delete $arg{secret} : AnyEvent::MP::default_secret;
119
120 # send greeting
121 my $lgreeting = "aemp;$PROTOCOL_VERSION_MAJOR;$PROTOCOL_VERSION_MINOR;AnyEvent::MP;$VERSION;"
122 . (MIME::Base64::encode_base64 AnyEvent::MP::nonce 33, "") . ";"
123 . "hmac_md6_64_256;" # hardcoded atm.
124 . "storable;" # hardcoded atm.
125 . "$self->{local_id};"
126 . (exists $arg{tls} && $arg{tls_ctx} ? "tls1.0=$arg{tls};" : "");
127
128 $self->{hdl}->push_write ("$lgreeting\012");
129
130 # expect greeting
131 $self->{hdl}->push_read (line => sub {
132 my $rgreeting = $_[1];
133
134 my ($aemp, $major, $minor, $provider, $provider_version, $nonce2, $auth, $framing, $rid, @kv) = split /;/, $rgreeting;
135
136 if ($aemp ne "aemp") {
137 return $self->error ("unparsable greeting");
138 } elsif ($major != $PROTOCOL_VERSION_MAJOR) {
139 return $self->error ("major version mismatch ($PROTOCOL_VERSION_MAJOR vs. $major)");
140 } elsif ($auth ne "hmac_md6_64_256") {
141 return $self->error ("unsupported auth method ($auth)");
142 } elsif ($framing ne "storable") {
143 return $self->error ("unsupported auth method ($auth)");
144 }
145
146 $self->{remote_id} = $rid;
147
148 $self->{greeting} = {
149 provider => $provider,
150 provider_version => $provider_version,
151 };
152
153 /^([^=]+)(?:=(.*))?/ and $self->{greeting}{$1} = $2
154 for @kv;
155
156 if (exists $self->{tls} and $self->{tls_ctx} and exists $self->{greeting}{"tls1.0"}) {
157 if ($self->{tls} ne $self->{greeting}{"tls1.0"}) {
158 return $self->error ("TLS server/client mismatch");
159 }
160 $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx});
161 }
162
163 # auth
164 require Digest::MD6;
165 require Digest::HMAC_MD6;
166
167 my $key = Digest::MD6::md6_hex ($secret);
168 my $lauth = Digest::HMAC_MD6::hmac_md6_base64 ($key, "$lgreeting$rgreeting", 64, 256);
169 my $rauth = Digest::HMAC_MD6::hmac_md6_base64 ($key, "$rgreeting$lgreeting", 64, 256);
170 $self->{hdl}->push_write ("$lauth\012");
171
172 $self->{hdl}->push_read (line => sub {
173 my ($hdl, $rauth2) = @_;
174
175 if ($rauth2 ne $rauth) {
176 return $self->error ("authentication failure/shared secret mismatch");
177 }
178
179 my $queue = delete $self->{queue}; # we are connected
180
181 $self->{on_connect}($self)
182 if $self->{on_connect};
183
184 $hdl->push_write (storable => $_)
185 for @$queue;
186
187 my $rmsg; $rmsg = sub {
188 $_[0]->push_read (storable => $rmsg);
189
190 $self->{on_recv}($self, $_[1]);
191 };
192 $hdl->push_read (storable => $rmsg);
193 });
194 });
195 }
196
197 $self
198 }
199
200 sub error {
201 my ($self, $msg) = @_;
202
203 $self->{on_error}($self, $msg);
204 $self->{hdl}->destroy;
205 }
206
207 sub send {
208 my ($self, $msg) = @_;
209
210 exists $self->{queue}
211 ? push @{ $self->{queue} }, $msg
212 : $self->{hdl}->push_write (storable => $msg);
213 }
214
215 sub destroy {
216 my ($self) = @_;
217
218 $self->{hdl}->destroy;
219 }
220
221 sub DESTROY {
222 my ($self) = @_;
223
224 $self->destroy;
225 }
226
227 =back
228
229 =head1 SEE ALSO
230
231 L<AnyEvent>.
232
233 =head1 AUTHOR
234
235 Marc Lehmann <schmorp@schmorp.de>
236 http://home.schmorp.de/
237
238 =cut
239
240 1
241