ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-Porttracker/Porttracker.pm
(Generate patch)

Comparing AnyEvent-Porttracker/Porttracker.pm (file contents):
Revision 1.2 by root, Mon Nov 15 04:57:39 2010 UTC vs.
Revision 1.3 by root, Mon Nov 15 19:49:36 2010 UTC

21module will be of little value to you. 21module will be of little value to you.
22 22
23This module is an L<AnyEvent> user, you need to make sure that you use and 23This module is an L<AnyEvent> user, you need to make sure that you use and
24run a supported event loop. 24run a supported event loop.
25 25
26To quickly understand how this module works you should read how to
27construct a new connection object and then read about the event/callback
28system.
29
26=head1 THE AnyEvent::Porttracker CLASS 30=head1 THE AnyEvent::Porttracker CLASS
31
32The AnyEvent::Porttracker class represents a single connection.
27 33
28=over 4 34=over 4
29 35
30=cut 36=cut
31 37
52 : ($type = (UNIVERSAL::can $self, $type)) 58 : ($type = (UNIVERSAL::can $self, $type))
53 ? $type->($self, @args) 59 ? $type->($self, @args)
54 : () 60 : ()
55} 61}
56 62
57=item new AnyEvent::Porttracker 63=item new AnyEvent::Porttracker [key => value...]
64
65Creates a new porttracker API connection object and tries to connect to
66the specified host (see below). After the connection has been established,
67the TLS handshake (if requested) will take place, followed by a login
68attempt using either the C<none>, C<login_cram_md6> or C<login> methods,
69in this order of preference (typically, C<login_cram_md6> is used, which
70shields against some man-in-the-middle attacks and avoids transferring the
71password).
72
73It is permissible to send requests immediately after creating the object -
74they will be queued until after successful login.
75
76Possible key-value pairs are:
77
78=over 4
79
80=item host => $hostname [MANDATORY]
81
82The hostname or IP address of the Porttracker box.
83
84=item port => $service
85
86The service (port) to use (default: C<porttracker=55>).
87
88=item user => $string, pass => $string
89
90These are the username and password to use when authentication is required
91(which it is in almost all cases, so these keys are normally mandatory).
92
93=item tls => ...
94
95#TODO#
96
97=item on_XYZ => $coderef
98
99You can specify event callbacks either by subclassing and overriding the
100respective methods or by specifying coderefs as key-value pairs when
101constructing the object.
102
103=back
58 104
59=cut 105=cut
60 106
61sub new { 107sub new {
62 my $class = shift; 108 my $class = shift;
63 109
64 my $self = bless { 110 my $self = bless {
65 id => "a", 111 id => "a",
112 queue => [], # ininitially queue everything
66 @_, 113 @_,
67 }, $class; 114 }, $class;
68 115
69 { 116 {
70 Scalar::Util::weaken (my $self = $self); 117 Scalar::Util::weaken (my $self = $self);
74 on_error => sub { 121 on_error => sub {
75 $self->error (); 122 $self->error ();
76 }, 123 },
77 on_connect => sub { 124 on_connect => sub {
78 if ($self->{tls}) { 125 if ($self->{tls}) {
79 $self->{queue} ||= [];
80 $self->_req (start_tls => sub { 126 $self->_req (start_tls => sub {
81 $_[1] 127 $_[1]
82 or return $self->error ("TLS rejected by server"); 128 or return $self->error ("TLS rejected by server");
83 129
84 $self->unqueue; 130 $self->_login;
85 }); 131 });
86 } 132 }
87 }, 133 },
88 on_read => sub { 134 on_read => sub {
89 while ($_[0]{rbuf} =~ s/^([^\x0a]*)\x0a//) { 135 while ($_[0]{rbuf} =~ s/^([^\x0a]*)\x0a//) {
115} 161}
116 162
117sub error { 163sub error {
118 my ($self, $msg) = @_; 164 my ($self, $msg) = @_;
119 165
120 warn $msg; 166 call on_error => $msg;
121 167
122 () 168 ()
123} 169}
124 170
125sub _req { 171sub _req {
140 $_[0]{queue} 186 $_[0]{queue}
141 ? push @{ $_[0]{queue} }, [@_] 187 ? push @{ $_[0]{queue} }, [@_]
142 : &_req 188 : &_req
143} 189}
144 190
145sub unqueue {
146 my ($self) = @_;
147
148 my $queue = delete $self->{queue}
149 or return;
150
151 _req @$_
152 for @$queue;
153}
154
155sub on_start_tls_notify { 191sub on_start_tls_notify {
156 my ($self) = @_; 192 my ($self) = @_;
157 193
158 $self->{hdl}->starttls ("connect"); 194 $self->{hdl}->starttls ("connect");
195 $self->{tls} ||= 1;
159 196
160 $self->unqueue; 197 $self->_login;
161} 198}
162 199
163sub on_hello_notify { 200sub on_hello_notify {
164 my ($self, $version, $auths, $nonce) = @_; 201 my ($self, $version, $auths, $nonce) = @_;
165 202
166 $version == 1 203 $version == 1
167 or return $self->error ("protocol mismatch, got $version, expected/supported 1"); 204 or return $self->error ("protocol mismatch, got $version, expected/supported 1");
168 205
169 $nonce = MIME::Base64::decode_base64 $nonce; 206 $nonce = MIME::Base64::decode_base64 $nonce;
170 207
208 $self->{hello} = [$auths, $nonce];
209
210 $self->_login
211 unless $self->{tls}; # delay login when trying to handshake tls
212}
213
214sub _login_success {
215 my ($self, $method) = @_;
216
217 _req @$_
218 for @{ delete $self->{queue} };
219
220 call $self, on_login => $method;
221}
222
223sub _login {
224 my ($self) = @_;
225
226 my ($auths, $nonce) = @{ delete $self->{hello} or return };
227
171 if (grep $_ eq "none", @$auths) { 228 if (grep $_ eq "none", @$auths) {
172 call $self, "on_login"; 229 $self->_login_success ("none");
173 230
174 } elsif (grep $_ eq "login_cram_md6", @$auths) { 231 } elsif (grep $_ eq "login_cram_md6", @$auths) {
175 my $cc = join "", map chr 256 * rand, 0..63; 232 my $cc = join "", map chr 256 * rand, 0..63;
176 233
177 my $key = Digest::HMAC_MD6::hmac_md6 $self->{password}, $self->{username}, 64, 256; 234 my $key = Digest::HMAC_MD6::hmac_md6 $self->{pass}, $self->{user}, 64, 256;
178 my $cr = Digest::HMAC_MD6::hmac_md6_base64 $key, "$cc$nonce", 64, 256; 235 my $cr = Digest::HMAC_MD6::hmac_md6_base64 $key, "$cc$nonce", 64, 256;
179 my $sr = Digest::HMAC_MD6::hmac_md6_base64 $key, "$nonce$cc", 64, 256; 236 my $sr = Digest::HMAC_MD6::hmac_md6_base64 $key, "$nonce$cc", 64, 256;
180 237
181 $cc = MIME::Base64::encode_base64 $cc; 238 $cc = MIME::Base64::encode_base64 $cc;
182 239
183 $self->req (login_cram_md6 => $self->{username}, $cr, $cc, sub { 240 $self->_req (login_cram_md6 => $self->{user}, $cr, $cc, sub {
184 my ($self, $ok, $msg) = @_; 241 my ($self, $ok, $msg) = @_;
185 242
186 $ok 243 $ok
187 or return call $self, on_login_failure => $msg; 244 or return call $self, on_login_failure => $msg;
188 245
189 $msg eq $sr 246 $msg eq $sr
190 or return call $self, on_login_failure => "sr and cr mismatch, possible man in the middle attack"; 247 or return call $self, on_login_failure => "sr and cr mismatch, possible man in the middle attack";
191 248
192 call $self, "on_login"; 249 $self->_login_success ("login_cram_md6");
193 }); 250 });
194 } elsif (grep $_ eq "login", @$auths) { 251 } elsif (grep $_ eq "login", @$auths) {
195 $self->req (login => $self->{username}, $self->{password}, sub { 252 $self->_req (login => $self->{user}, $self->{pass}, sub {
196 my ($self, $ok, $msg) = @_; 253 my ($self, $ok, $msg) = @_;
197 254
198 $ok 255 $ok
199 or return call $self, on_login_failure => $msg; 256 or return call $self, on_login_failure => $msg;
200 257
201 call $self, "on_login"; 258 $self->_login_success ("login");
202 }); 259 });
203 } else { 260 } else {
204 call $self, on_login_failure => "no supported auth method (@$auths)"; 261 call $self, on_login_failure => "no supported auth method (@$auths)";
205 } 262 }
263
264 # we no longer need these, make it a bit harder to get them
265 delete $self->{user};
266 delete $self->{pass};
267}
268
269sub on_info_notify {
270 my ($self, $msg) = @_;
271
272 warn $msg;
273}
274
275sub on_error_notify {
276 my ($self, $msg) = @_;
277
278 $self->error ($msg);
279}
280
281sub on_error {
282 my ($self, $msg) = @_;
283
284 warn $msg;
285
286 %$self = ();
206} 287}
207 288
208sub on_login_failure { 289sub on_login_failure {
209 my ($self, $msg) = @_; 290 my ($self, $msg) = @_;
210 291
211 $msg =~ s/\n$//; 292 $msg =~ s/\n$//;
212 $self->error ("login failed: $msg"); 293 $self->error ("login failed: $msg");
213} 294}
214 295
215sub on_error_notify {
216 my ($self, $msg) = @_;
217
218 $self->error ($msg);
219}
220
221=back 296=back
222 297
298=head2 EVENTS
299
300AnyEvent::Porttracker conenctions are fully event-driven, and naturally
301there are a number of events that can occur. All these events have a name
302starting with C<on_> (example: C<on_login_failure>).
303
304Programs can catch these events in two ways: either by providing
305constructor arguments with the event name as key and a coderef as value:
306
307 my $api = new AnyEvent::Porttracker
308 host => ...,
309 user => ..., pass => ...,
310 on_error => sub {
311 my ($api, $msg) = @_;
312 warn $msg;
313 exit 1;
314 },
315 ;
316
317Or by subclassing C<AnyEvent::Porttracker> and overriding methods of the
318same name:
319
320 package MyClass;
321
322 use base AnyEvent::Porttracker;
323
324 sub on_error {
325 my ($api, $msg) = @_;
326 warn $msg;
327 exit 1;
328 }
329
330Event callbacks are not expected to return anything and are always passed
331the API object as first argument. Some might have default implementations
332(for example, C<on_error>), others are ignored unless overriden.
333
334Description of individual events follow:
335
336=over 4
337
338=item on_error $api, $msg
339
340Is called for every (fatal) error, including C<error> notifies. The
341default prints the message and destroys the object, so it is highly
342advisable to override this event.
343
344=item on_login $api, $method
345
346Called after a successful login, after which commands can be send. It is
347permissible to send commands before a successful login: those will be
348queued and sent just before this event is invoked. C<$method> is the auth
349method that was used.
350
351=item on_login_failure $api, $msg
352
353Called when all login attempts have failed - the default raises a fatal
354error with the error message from the server.
355
356=item on_hello_notify $api, $version, $authtypes, $nonce
357
358This protocol notification is used internally by AnyEvent::Porttracker -
359you can override it, but the module will most likely not work.
360
361=item on_info_notify $api, $msg
362
363Called for informational messages from the server - the default
364implementation calls C<warn> but otherwise ignores this notification.
365
366=item on_error_notify $api, $msg
367
368Called for fatal errors from the server - the default implementation calls
369C<warn> and destroys the API object.
370
371=item on_start_tls_notify $api
372
373Called when the server wants to start TLS negotiation. This is used
374internally and - while it is possible to override it - should not be
375overriden.
376
377=item on_XYZ_notify $api, ...
378
379In general, any protocol notification will result in an event of the form
380C<on_NOTIFICATION_notify>.
381
382=back
383
223=head1 SEE ALSO 384=head1 SEE ALSO
224 385
225L<AnyEvent>, L<http://www.porttracker.com/>, L<http://www.infoblox.com/en/products/portiq.html>. 386L<AnyEvent>, L<http://www.porttracker.com/>, L<http://www.infoblox.com/en/products/portiq.html>.
226 387
227=head1 AUTHOR 388=head1 AUTHOR

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines