File Coverage

blib/lib/Net/SSH/Tunnel.pm
Criterion Covered Total %
statement 21 89 23.6
branch 0 26 0.0
condition 0 12 0.0
subroutine 7 16 43.7
pod 7 7 100.0
total 35 150 23.3


line stmt bran cond sub pod time code
1             package Net::SSH::Tunnel;
2              
3 1     1   29562 use 5.006;
  1         4  
  1         49  
4 1     1   6 use strict;
  1         3  
  1         51  
5 1     1   12 use warnings;
  1         6  
  1         32  
6 1     1   6 use Carp;
  1         1  
  1         198  
7 1     1   1363 use Getopt::Long;
  1         16461  
  1         6  
8 1     1   9423 use Data::Dumper;
  1         31122  
  1         94  
9 1     1   1579 use Log::Log4perl qw(:easy);
  1         57165  
  1         6  
10              
11             =head1 NAME
12              
13             Net::SSH::Tunnel - This is a simple wrapper around ssh to establish a tunnel.
14             Supports both local and remote port forwarding.
15              
16             =head1 VERSION
17              
18             Version 0.04
19              
20             =cut
21              
22             our $VERSION = '0.04';
23              
24              
25             =head1 SYNOPSIS
26              
27             Perl module to setup / destroy a ssh tunnel.
28              
29             create a very short driver script.
30             $ vi driver.pl
31             #!/usr/bin/perl
32              
33             use strict;
34             use warnings;
35             use Net::SSH::Tunnel;
36            
37             Net::SSH::Tunnel->run();
38              
39             run the driver script with options.
40             $ ./driver.pl --host dest.example.com --hostname hostname.example.com
41            
42             the above is equivalent to creating a local port forwarding like this:
43             ssh -f -N -L 10000:dest.example.com:22 @hostname.example.com
44              
45             after the driver script is done, you can then do:
46             ssh -p 10000 user@localhost
47            
48             other usages:
49             Usage: ./driver.pl --port 10000 --host dest.example.com --hostport 22 --hostname hostname.example.com
50             Sets up a ssh tunnel. Works on both local and remote forwarding.
51             In the example above, it will create a tunnel from your host to
52             hostname.example.com, where your local port 10000 is forwarded to
53             dest.example.com's port 22.
54              
55             --hostname specify the host where you create a tunnel from your host
56             --host specify the destination of port forwarding
57             --user user when connecting to . default: effective user
58             --type specify local or remote, for forwarding. default: local
59             --hostport target port on . default: 22
60             --port source port for forwarding. default: 10000
61             --sshport equivalent of -p in ssh client. default: 22
62             --action 'setup' or 'destroy' a tunnel. default: setup
63             --help prints the usage and exits
64             --debug turn on debug messages
65            
66             Notes on testing:
67             This module wraps around ssh and as such, requires authentication.
68             I have included test_deeply.pl that asks for hostnames, runs ssh and establishes a tunnel.
69             If you'd like to test manually, please use the script.
70              
71             =head1 SUBROUTINES/METHODS
72              
73             =head2 new
74              
75             The constructor. Creates an object, invokes init() for argument parsing
76              
77             =cut
78              
79             sub new {
80 0     0 1   my $class = shift;
81 0           my $self = {};
82 0           bless $self, $class;
83 0           $self->init();
84 0           return $self;
85             }
86              
87             =head2 init
88              
89             Arg parser. Sets default values, uses Getopt::Long then do the necessary parsing.
90            
91             =cut
92              
93             sub init {
94 0     0 1   my $self = shift;
95              
96 0           my $opts = {
97             hostport => 22,
98             port => 10000,
99             type => 'local',
100             action => 'setup',
101             help => 0,
102             debug => 0,
103             user => scalar( getpwuid($>) ),
104             sshport => 22,
105             };
106              
107             GetOptions(
108             $opts,
109             'hostname=s',
110             'host=s',
111             'type=s',
112             'hostport=i',
113             'port=i',
114             'user=s',
115             'sshport=i',
116 0     0     'destroy' => sub { $opts->{ action } = 'destroy' },
117             'help' => \$opts->{ help },
118 0     0     'debug' => sub { $opts->{ debug }++ }, # for various debug levels, if needed
119 0           );
120              
121 0 0 0       $self->usage() if ( !$opts->{ hostname } || !$opts->{ host } || $opts->{ type } !~ /local|remote/ || $opts->{ help } );
      0        
122 0 0         Log::Log4perl->easy_init($DEBUG) if $opts->{ debug };
123 0           $self->{ opts } = $opts;
124              
125 0           chomp( $self->{ cmds }->{ ssh } = `which ssh` );
126 0           chomp( $self->{ cmds }->{ ps } = `which ps` );
127 0           chomp( $self->{ cmds }->{ grep } = `which grep` );
128              
129 0 0 0       croak "ssh, ps or grep not found" unless( -x $self->{ cmds }->{ ssh } && -x $self->{ cmds }->{ ps } && -x $self->{ cmds }->{ grep } );
      0        
130             }
131              
132             =head2 run
133              
134             Driver method to do the new()->init() dance, then calls appropriate methods based on the args
135            
136             =cut
137              
138             sub run {
139 0     0 1   my $class = shift;
140 0           my $self = $class->new();
141            
142 0           my $action = $self->{ opts }->{ action };
143            
144 0 0         if ( $action eq 'setup' ) {
    0          
145 0           DEBUG "Setting up tunnel";
146 0           $self->setup_tunnel();
147             }
148             elsif( $action eq 'destroy' ) {
149 0           DEBUG "Destroying tunnel";
150 0           $self->destroy_tunnel();
151             }
152 0           return $self;
153             }
154              
155             =head2 setup_tunnel
156              
157             Establishes a ssh tunnel based on the object info.
158            
159             =cut
160              
161             sub setup_tunnel {
162 0     0 1   my $self = shift;
163            
164             # this will seek for a tunnel according to params. If found, just return
165 0 0         return if ( $self->check_tunnel() );
166              
167 0           my $ssh = $self->{ cmds }->{ ssh };
168 0           my $hostport = $self->{ opts }->{ hostport };
169 0           my $port = $self->{ opts }->{ port };
170 0           my $hostname = $self->{ opts }->{ hostname };
171 0           my $host = $self->{ opts }->{ host };
172 0           my $user = $self->{ opts }->{ user };
173 0           my $type = $self->{ opts }->{ type };
174 0           my $sshport = $self->{ opts }->{ sshport };
175            
176 0           my $command;
177 0 0         if ( $type eq 'local' ) {
    0          
178 0           $command = "$ssh -f -N -L $port:$host:$hostport -p $sshport $user\@$hostname";
179             }
180             elsif ( $type eq 'remote' ) {
181 0           $command = "$ssh -f -N -R $port:$host:$hostport -p $sshport $user\@$hostname";
182             }
183            
184 0           system( $command );
185 0           my $ret = $? >> 8;
186 0 0         croak "something went wrong while setting up a tunnel" if ( $ret );
187             }
188              
189             =head2 check_tunnel
190              
191             Runs ps and finds an existing tunnel, according to the parameters supplied
192            
193             =cut
194              
195             sub check_tunnel {
196 0     0 1   my $self = shift;
197              
198             # kind of redundant but I want to set shorter variables for readability
199 0           my $ssh = $self->{ cmds }->{ ssh };
200 0           my $ps = $self->{ cmds }->{ ps };
201 0           my $grep = $self->{ cmds }->{ grep };
202 0           my $hostport = $self->{ opts }->{ hostport };
203 0           my $port = $self->{ opts }->{ port };
204 0           my $hostname = $self->{ opts }->{ hostname };
205 0           my $host = $self->{ opts }->{ host };
206 0           my $user = $self->{ opts }->{ user };
207 0           my $sshport = $self->{ opts }->{ sshport };
208              
209 0           my $command = "$ps auxw | $grep $ssh | $grep $hostport | $grep $port | $grep $hostname | $grep $host | $grep $user | $grep $sshport | $grep -v grep";
210 0 0         open( my $fh, "-|", $command ) or croak "could not execute $command: $!";
211            
212 0           my $pid;
213 0           while( <$fh> ) {
214 0           chomp;
215 0           $pid = ( split( /\s+/, $_ ) )[1];
216             }
217 0 0         ( $pid ) ? $pid : undef;
218             }
219              
220             =head2 destroy_tunnel
221              
222             Calls check_tunnel() for existing tunnel, and if it exists, kills it.
223            
224             =cut
225              
226             sub destroy_tunnel {
227 0     0 1   my $self = shift;
228            
229 0           my $pid = $self->check_tunnel();
230 0 0         if ( $pid ) {
231 0           my $rc = kill 15, $pid;
232 0 0         croak "could not kill tunnel" unless( $rc );
233             }
234             }
235              
236             =head2 usage
237              
238             The sub to provide help.
239            
240             =cut
241              
242             sub usage {
243 0     0 1   my $self = shift;
244              
245 0           print <
246             Usage: ./driver.pl --port 10000 --host dest.example.com --hostport 22 --hostname hostname.example.com
247             Sets up a ssh tunnel. Works on both local and remote forwarding.
248             In the example above, it will create a tunnel from your host to
249             hostname.example.com, where your local port 10000 is forwarded to
250             dest.example.com's port 22.
251              
252             --hostname specify the host where you create a tunnel from your host
253             --host specify the destination of port forwarding
254             --user user when connecting to . default: effective user
255             --type specify local or remote, for forwarding. default: local
256             --hostport target port on . default: 22
257             --port source port for forwarding. default: 10000
258             --sshport equivalent of -p in ssh client. default: 22
259             --action 'setup' or 'destroy' a tunnel. default: setup
260             --help prints the usage and exits
261             --debug turn on debug messages
262             USAGE
263 0           exit(0);
264             }
265              
266             =head1 AUTHOR
267              
268             Satoshi Yagi, C<< >>
269              
270             =head1 BUGS
271              
272             Please report any bugs or feature requests to C, or through
273             the web interface at L. I will be notified, and then you'll
274             automatically be notified of progress on your bug as I make changes.
275              
276              
277              
278              
279             =head1 SUPPORT
280              
281             You can find documentation for this module with the perldoc command.
282              
283             perldoc Net::SSH::Tunnel
284              
285              
286             You can also look for information at:
287              
288             =over 4
289              
290             =item * RT: CPAN's request tracker (report bugs here)
291              
292             L
293              
294             =item * AnnoCPAN: Annotated CPAN documentation
295              
296             L
297              
298             =item * CPAN Ratings
299              
300             L
301              
302             =item * Search CPAN
303              
304             L
305              
306             =back
307              
308              
309             =head1 ACKNOWLEDGEMENTS
310              
311              
312             =head1 LICENSE AND COPYRIGHT
313              
314             Copyright 2012-2013 Satoshi Yagi.
315              
316             This program is free software; you can redistribute it and/or modify it
317             under the terms of either: the GNU General Public License as published
318             by the Free Software Foundation; or the Artistic License.
319              
320             See http://dev.perl.org/licenses/ for more information.
321              
322              
323             =cut
324              
325             1; # End of Net::SSH::Tunnel