File Coverage

blib/lib/XAS/Lib/SSH/Server.pm
Criterion Covered Total %
statement 12 96 12.5
branch 0 6 0.0
condition n/a
subroutine 4 19 21.0
pod 7 7 100.0
total 23 128 17.9


line stmt bran cond sub pod time code
1             package XAS::Lib::SSH::Server;
2              
3             our $VERSION = '0.01';
4              
5 1     1   832 use POE;
  1         2  
  1         8  
6 1     1   257 use POE::Filter::Line;
  1         2  
  1         18  
7 1     1   3 use POE::Wheel::ReadWrite;
  1         2  
  1         52  
8              
9             use XAS::Class
10 1         9 debug => 0,
11             version => $VERSION,
12             base => 'XAS::Lib::POE::Session',
13             accessors => 'client peerhost peerport',
14             utils => ':validation trim',
15             vars => {
16             PARAMS => {
17             -filter => { optional => 1, default => undef },
18             -eol => { optional => 1, default => "\015\012" },
19             }
20             }
21 1     1   4 ;
  1         1  
22              
23             #use Data::Dumper;
24              
25             # ----------------------------------------------------------------------
26             # Public Methods
27             # ----------------------------------------------------------------------
28              
29             sub session_initialize {
30 0     0 1   my $self = shift;
31              
32 0           my $alias = $self->alias;
33              
34 0           $self->log->debug("$alias: entering session_initialize()");
35              
36             # public events
37              
38             # private events
39              
40 0           $poe_kernel->state('client_error', $self, '_client_error');
41 0           $poe_kernel->state('client_input', $self, '_client_input');
42 0           $poe_kernel->state('client_output', $self, '_client_output');
43 0           $poe_kernel->state('client_connection', $self, '_client_connection');
44              
45 0           $poe_kernel->state('process_errors', $self, '_process_errors');
46 0           $poe_kernel->state('process_request', $self, '_process_request');
47 0           $poe_kernel->state('process_response', $self, '_process_response');
48 0           $poe_kernel->state('handle_connection', $self, '_handle_connection');
49              
50             # Find the remote host and port.
51              
52 0           my ($rhost, $rport, $lhost, $lport) = split(' ', $ENV{'SSH_CONNECTION'});
53              
54 0           $self->{'peerhost'} = $rhost;
55 0           $self->{'peerport'} = $rport;
56              
57             # walk the chain
58              
59 0           $self->SUPER::session_initialize();
60              
61 0           $self->log->debug("$alias: leaving session_initialize()");
62              
63             }
64              
65             sub session_startup {
66 0     0 1   my $self = shift;
67              
68 0           my $alias = $self->alias;
69              
70             # start listening for connections
71              
72 0           $self->log->debug("$alias: entering session_startup()");
73              
74 0           $poe_kernel->post($alias, 'client_connection');
75              
76             # walk the chain
77              
78 0           $self->SUPER::session_startup();
79              
80 0           $self->log->debug("$alias: leaving session_startup()");
81              
82             }
83              
84             sub process_request {
85 0     0 1   my $self = shift;
86 0           my ($input, $ctx) = validate_params(\@_, [1,1]);
87              
88 0           return $input;
89              
90             }
91              
92             sub process_response {
93 0     0 1   my $self = shift;
94 0           my ($output, $ctx) = validate_params(\@_, [1,1]);
95              
96 0           return $output;
97              
98             }
99              
100             sub process_errors {
101 0     0 1   my $self = shift;
102 0           my ($output, $ctx) = validate_params(\@_, [1,1]);
103              
104 0           return $output;
105              
106             }
107              
108             sub handle_connection {
109 0     0 1   my $self = shift;
110 0           my ($wheel) = validate_params(\@_, [1]);
111              
112             }
113              
114             # ----------------------------------------------------------------------
115             # Public Events
116             # ----------------------------------------------------------------------
117              
118             # ----------------------------------------------------------------------
119             # Private Events
120             # ----------------------------------------------------------------------
121              
122             sub _process_request {
123 0     0     my ($self, $input, $ctx) = @_[OBJECT,ARG0,ARG1];
124              
125 0           my $alias = $self->alias;
126 0           my $data = $self->process_request($input, $ctx);
127              
128 0           $self->log->debug("$alias: process_request()");
129 0           $poe_kernel->post($alias, 'process_response', $data, $ctx);
130              
131             }
132              
133             sub _process_response {
134 0     0     my ($self, $output, $ctx) = @_[OBJECT,ARG0,ARG1];
135              
136 0           my $alias = $self->alias;
137 0           my $data = $self->process_response($output, $ctx);
138              
139 0           $self->log->debug("$alias: process_response()");
140 0           $poe_kernel->post($alias, 'client_output', $data, $ctx);
141              
142             }
143              
144             sub _process_errors {
145 0     0     my ($self, $output, $ctx) = @_[OBJECT,ARG0,ARG1];
146              
147 0           my $alias = $self->alias;
148 0           my $data = $self->process_errors($output, $ctx);
149              
150 0           $self->log->debug("$alias: process_errors()");
151 0           $poe_kernel->post($alias, 'client_output', $data, $ctx);
152              
153             }
154              
155             sub _handle_connection {
156 0     0     my ($self, $wheel) = @_[OBJECT,ARG0];
157            
158 0           my $alias = $self->alias;
159              
160 0           $self->log->debug("$alias: handle_connection()");
161 0           $self->handle_connection($wheel);
162              
163             }
164              
165             sub _client_connection {
166 0     0     my ($self) = $_[OBJECT];
167              
168 0           my $alias = $self->alias;
169              
170 0           $self->log->debug("$alias: _client_connection()");
171              
172             # Start listening on stdin.
173              
174 0           $self->{'client'} = POE::Wheel::ReadWrite->new(
175             InputHandle => \*STDIN,
176             OutputHandle => \*STDOUT,
177             Filter => $self->filter,
178             InputEvent => 'client_input',
179             ErrorEvent => 'client_error'
180             );
181              
182 0           $poe_kernel->post($alias, 'handle_connection', $self->client->ID);
183              
184             }
185              
186             sub _client_input {
187 0     0     my ($self, $input, $wheel) = @_[OBJECT,ARG0,ARG1];
188              
189 0           my $alias = $self->alias;
190 0           my $ctx = {
191             wheel => $wheel
192             };
193              
194 0           $self->log->debug("$alias: _client_input()");
195              
196 0           $poe_kernel->post($alias, 'process_request', $input, $ctx);
197              
198             }
199              
200             sub _client_output {
201 0     0     my ($self, $output, $ctx) = @_[OBJECT,ARG0,ARG1];
202              
203 0           my @buffer;
204 0           my $alias = $self->alias;
205              
206 0           $self->log->debug("$alias: _client_output()");
207              
208 0 0         if (my $wheel = $self->client) {
209              
210 0           push(@buffer, $output);
211 0           $wheel->put(@buffer);
212              
213             } else {
214              
215 0           $self->log->error_msg('net_server_nowheel', $alias);
216              
217             }
218              
219             }
220              
221             sub _client_error {
222 0     0     my ($self, $syscall, $errnum, $errstr, $wheel) = @_[OBJECT,ARG0 .. ARG3];
223              
224 0           my $alias = $self->alias;
225              
226 0           $self->log->debug("$alias: _client_error()");
227 0           $self->log->debug(sprintf("%s: syscall: %s, errnum: %s, errstr: %s", $alias, $syscall, $errnum, $errstr));
228              
229 0 0         if ($errnum == 0) {
230              
231             # EOF detected.
232              
233 0           $self->log->info_msg('net_server_disconnect', $alias, $self->peerhost, $self->peerport);
234              
235 0           delete $self->{'client'};
236 0           $poe_kernel->post($alias, 'session_shutdown');
237              
238             } else {
239              
240 0           $self->log->error_msg('net_server_error', $alias, $errnum, $errstr);
241              
242             }
243              
244             }
245              
246             # ----------------------------------------------------------------------
247             # Private Methods
248             # ----------------------------------------------------------------------
249              
250             sub init {
251 0     0 1   my $class = shift;
252              
253 0           my $self = $class->SUPER::init(@_);
254              
255 0 0         unless (defined($self->filter)) {
256              
257 0           $self->{'filter'} = POE::Filter::Line->new(
258             InputLiteral => $self->eol,
259             OutputLiteral => $self->eol
260             );
261              
262             }
263              
264 0           return $self;
265              
266             }
267              
268             1;
269              
270             __END__
271              
272             =head1 NAME
273              
274             XAS::Lib::SSH::Server - A SSH Subsystem based server
275              
276             =head1 SYNOPSIS
277              
278             use XAS::Lib::SSH::Server;
279              
280             my $server = XAS::Lib::SSH::Server->new(
281             -filter => POE::Filter::Line->new(),
282             -eol => "\015\012",
283             );
284              
285             $server->run();
286              
287             =head1 DESCRIPTION
288              
289             The module provides a POE based framework for a SSH subsystem. A SSH subsystem
290             reads from stdin, writes to stdout or stderr. This modules emulates
291             L<XAS::Lib::Net::Server|XAS::Lib::Net::Server> to provide a consistent
292             interface.
293              
294             =head1 METHODS
295              
296             =head2 new
297              
298             This initializes the module and starts listening for requests. The following
299             parametrs are used:
300              
301             =over 4
302              
303             =item B<-alias>
304              
305             The name of the POE session.
306              
307             =item B<-filter>
308              
309             An optional filter to use, defaults to POE::Filter::Line
310              
311             =item B<-eol>
312              
313             An optional EOL, defaults to "\015\012";
314              
315             =back
316              
317             =head2 process_request($input, $ctx)
318              
319             This method will process the input from the client. It takes the
320             following parameters:
321              
322             =over 4
323              
324             =item B<$input>
325              
326             The input received from the socket.
327              
328             =item B<$ctx>
329              
330             A hash variable to maintain context. This will be initialized with a "wheel"
331             field. Others fields may be added as needed.
332              
333             =back
334              
335             =head2 process_response($output, $ctx)
336              
337             This method will process the output from the client. It takes the
338             following parameters:
339              
340             =over 4
341              
342             =item B<$output>
343              
344             The output to be sent to the socket.
345              
346             =item B<$ctx>
347              
348             A hash variable to maintain context. This uses the "wheel" field to direct output
349             to the correct socket. Others fields may have been added as needed.
350              
351             =back
352              
353             =head2 process_errors($error, $ctx)
354              
355             This method will process the error output from the client. It takes the
356             following parameters:
357              
358             =over 4
359              
360             =item B<$error>
361              
362             The output to be sent to the socket.
363              
364             =item B<$ctx>
365              
366             A hash variable to maintain context. This uses the "wheel" field to direct output
367             to the correct socket. Others fields may have been added as needed.
368              
369             =back
370              
371             =head2 handle_connection($wheel)
372              
373             This method is called after the client has connected. This is for additional
374             post connection processing as needed. It takes the following parameters:
375              
376             =over 4
377              
378             =item B<$wheel>
379              
380             The id of the clients wheel.
381              
382             =back
383              
384             =head1 ACCESSORS
385              
386             =head2 peerport
387              
388             This returns the peers port number.
389              
390             =head2 peerhost
391              
392             This returns the peers host name.
393              
394             =head1 SEE ALSO
395              
396             =over 4
397              
398             =item L<XAS::Lib::SSH::Client|XAS::Lib::SSH::Client>
399              
400             =item L<XAS|XAS>
401              
402             =back
403              
404             =head1 AUTHOR
405              
406             Kevin L. Esteb, E<lt>kevin@kesteb.usE<gt>
407              
408             =head1 COPYRIGHT AND LICENSE
409              
410             Copyright (c) 2012-2015 Kevin L. Esteb
411              
412             This is free software; you can redistribute it and/or modify it under
413             the terms of the Artistic License 2.0. For details, see the full text
414             of the license at http://www.perlfoundation.org/artistic_license_2_0.
415              
416             =cut