-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdev_gateway
More file actions
executable file
·133 lines (89 loc) · 3.03 KB
/
Copy pathdev_gateway
File metadata and controls
executable file
·133 lines (89 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env perl
use strict;
use warnings;
use Pod::Usage;
use Getopt::Long;
use Memoize;
memoize('list_busy_ports');
use Socket;
#use Smart::Comments;
### process options
my $gateway_host = 'sgn-devel.sgn.cornell.edu';
my $local_port = 3000;
my $user = '';
my $gateway_port;
my @allowed_gateway_ports = 8080..8090;
GetOptions( 'port|p=i' => \$local_port,
'user|u=s' => \$user,
'remote_port|r=i' => \$gateway_port,
)
or pod2usage('invalid options');
$gateway_host = shift @ARGV if @ARGV;
pod2usage() if @ARGV;
$user .= '@' if $user;
### make sure this is a valid hostname before trying the ssh commands
# (the error checking on the ssh commands leaves a bit to be desired)
validate_hostname( $gateway_host );
### find an available port
my $host = "$user$gateway_host";
$gateway_port ||= find_available_port( $host, @allowed_gateway_ports );
is_port_available( $host, $gateway_port )
or die "Remote port $gateway_port already in use, cannot set up gateway.\n";
### open the ssh connection
print "Forwarding local port $local_port to http://$gateway_host:$gateway_port\n";
print "Press Ctrl-c to close the connection.\n";
exec "ssh -N -R $gateway_host:$gateway_port:localhost:$local_port '$user$gateway_host'";
############ subroutines
sub validate_hostname {
my $host = shift;
die "Could not resolve hostname $host\n" unless gethostbyname( $host );
}
sub find_available_port {
my ( $ssh_host, @acceptable_ports ) = @_;
my $busy_ports = list_busy_ports( $ssh_host );
die "Failed to connect to $user$gateway_host\n"
unless %$busy_ports;
for( @acceptable_ports ) {
return $_ unless $busy_ports->{$_};
}
die "All remote ports seem to be busy.\n",
"Tried ports ".join(', ',@acceptable_ports).".\n";
}
sub list_busy_ports {
my ( $ssh_host ) = @_;
### use netstat on the remote host to find ports that are already in use
my %busy_ports;
open( my $netstat, "ssh '$user$gateway_host' 'netstat -ltunp 2>/dev/null' |" ) or die "$! running ssh\n";
while( <$netstat> ) {
#no warnings 'uninitialized';
my $local = ( split )[3];
my ($busy_port) = $local =~ /:(\d+)/
or next;
$busy_ports{$busy_port} = 1;
}
### busy_ports: %busy_ports
# hash of { port_number => 1 }
return \%busy_ports;
}
sub is_port_available {
my ( $ssh_host, $port ) = @_;
my $busy_ports = list_busy_ports( $ssh_host );
return ! $busy_ports->{$port};
}
__END__
=head1 NAME
dev_gateway - use C<ssh> to make a remote machine act as a gateway to a local port
=head1 SYNOPSIS
dev_gateway foo.example.com
dev_gateway -u bob -p 5432 foo.example.com
dev_gateway --user alice --p 3000 foo.example.com
=head1 OPTIONS
=over 8
=item -p --port <num>
Port on the local machine to forward. Default 3000.
=item -u --user <name>
Username to connect as. Defaults to current local user.
=item -r --remote_port <number>
Force the remote port to use. Should usually be in the range 8080 - 8090.
=back
=cut