diff --git a/man/mosh-server.1 b/man/mosh-server.1 index 16d65d4..39fbb34 100644 --- a/man/mosh-server.1 +++ b/man/mosh-server.1 @@ -2,7 +2,7 @@ .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) -.TH MOSH 1 "February 2012" +.TH MOSH 1 "October 2012" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -22,9 +22,9 @@ mosh-server \- server-side helper for mosh new [\-s] [\-v] -[\-i IP] -[\-p port] -[\-c colors] +[\-i \fIIP\fP] +[\-p \fIPORT\fP[:\fIPORT2\fP]] +[\-c \fICOLORS\fP] [\-\- command...] .br .SH DESCRIPTION @@ -69,8 +69,8 @@ Print some debugging information even after detaching. IP address of the local interface to bind (for multihomed hosts) .TP -.B \-p \fIPORT\fP -UDP port number to bind +.B \-p \fIPORT\fP[:\fIPORT2\fP] +UDP port number or port-range to bind .TP .B \-c \fICOLORS\fP diff --git a/man/mosh.1 b/man/mosh.1 index 0292ab0..ea63ef2 100644 --- a/man/mosh.1 +++ b/man/mosh.1 @@ -2,7 +2,7 @@ .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) -.TH MOSH 1 "February 2012" +.TH MOSH 1 "October 2012" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -121,8 +121,9 @@ Synonym for \-\-predict=always Synonym for \-\-predict=never .TP -.B \-p \fINUM\fP, \-\-port=\fINUM\fP -Use a particular server-side UDP port, for example, if this is the +.B \-p \fIPORT\fP[:\fIPORT2\fP], \-\-port=\fIPORT\fP[:\fIPORT2\fP] +Use a particular server-side UDP port or port range, +for example, if this is the only port that is forwarded through a firewall to the server. Otherwise, \fBmosh\fP will choose a port between 60000 and 61000. diff --git a/scripts/mosh b/scripts/mosh index a6d6584..5e91417 100755 --- a/scripts/mosh +++ b/scripts/mosh @@ -66,7 +66,8 @@ qq{Usage: $0 [options] [--] [user@]host [command...] -n --predict=never never use local echo --predict=experimental aggressively echo even when incorrect --p NUM --port=NUM server-side UDP port +-p PORT[:PORT2] + --port=PORT[:PORT2] server-side UDP port or range --ssh=COMMAND ssh command to run when setting up session (example: "ssh -p 2222") @@ -99,10 +100,10 @@ sub predict_check { GetOptions( 'client=s' => \$client, 'server=s' => \$server, 'predict=s' => \$predict, - 'port=i' => \$port_request, + 'port=s' => \$port_request, 'a' => sub { $predict = 'always' }, 'n' => sub { $predict = 'never' }, - 'p=i' => \$port_request, + 'p=s' => \$port_request, 'ssh=s' => \$ssh, 'help' => \$help, 'version' => \$version, @@ -122,12 +123,21 @@ if ( defined $predict ) { } if ( defined $port_request ) { - if ( $port_request =~ m{^[0-9]+$} - and $port_request >= 0 - and $port_request <= 65535 ) { - # good port + if ( $port_request =~ m{^(?\d+)(:(?\d+))?$} ) { + # good port or port-range + if ( $+{low} <= 0 or $+{low} >= 65535 ) { + die "$0: Server-side (low) port ($+{low}) must be within valid range [0..65535].\n"; + } + if ( defined $+{high} ) { + if ( $+{high} <= 0 or $+{high} >= 65535 ) { + die "$0: Server-side high port ($+{high}) must be within valid range [0..65535].\n"; + } + if ( $+{low} > $+{high} ) { + die "$0: Server-side port range ($port_request): low port greater than high port.\n"; + } + } } else { - die "$0: Server-side port ($port_request) must be within valid range [0..65535].\n"; + die "$0: Server-side port ($port_request) not valid.\n"; } } diff --git a/src/frontend/mosh-server.cc b/src/frontend/mosh-server.cc index de6d6bf..4f7a5eb 100644 --- a/src/frontend/mosh-server.cc +++ b/src/frontend/mosh-server.cc @@ -103,7 +103,7 @@ using namespace std; void print_usage( const char *argv0 ) { - fprintf( stderr, "Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT] [-c COLORS] [-l NAME=VALUE] [-- COMMAND...]\n", argv0 ); + fprintf( stderr, "Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT[:PORT2]] [-c COLORS] [-l NAME=VALUE] [-- COMMAND...]\n", argv0 ); } void print_motd( void ); @@ -235,9 +235,9 @@ int main( int argc, char *argv[] ) exit( 1 ); } - if ( desired_port - && ( strspn( desired_port, "0123456789" ) != strlen( desired_port ) ) ) { - fprintf( stderr, "%s: Bad UDP port (%s)\n", argv[ 0 ], desired_port ); + int dpl, dph; + if ( desired_port && ! Connection::parse_portrange( desired_port, dpl, dph ) ) { + fprintf( stderr, "%s: Bad UDP port range (%s)\n", argv[ 0 ], desired_port ); print_usage( argv[ 0 ] ); exit( 1 ); } diff --git a/src/network/network.cc b/src/network/network.cc index d257188..d9d42d7 100644 --- a/src/network/network.cc +++ b/src/network/network.cc @@ -223,20 +223,12 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se /* If an IP request is given, we try to bind to that IP, but we also try INADDR_ANY. If a port request is given, we bind only to that port. */ - /* convert port number */ - long int desired_port_no = 0; + /* convert port numbers */ + int desired_port_low = 0; + int desired_port_high = 0; - if ( desired_port ) { - char *end; - errno = 0; - desired_port_no = strtol( desired_port, &end, 10 ); - if ( (errno != 0) || (end != desired_port + strlen( desired_port )) ) { - throw NetworkException( "Invalid port number", errno ); - } - } - - if ( (desired_port_no < 0) || (desired_port_no > 65535) ) { - throw NetworkException( "Port number outside valid range [0..65535]", 0 ); + if ( desired_port && !parse_portrange( desired_port, desired_port_low, desired_port_high ) ) { + throw NetworkException("Invalid port range", 0); } /* convert desired IP */ @@ -253,7 +245,7 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se /* try to bind to desired IP first */ if ( desired_ip_addr != INADDR_ANY ) { try { - if ( try_bind( sock(), desired_ip_addr, desired_port_no ) ) { return; } + if ( try_bind( sock(), desired_ip_addr, desired_port_low, desired_port_high ) ) { return; } } catch ( const NetworkException& e ) { struct in_addr sin_addr; sin_addr.s_addr = desired_ip_addr; @@ -265,7 +257,7 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se /* now try any local interface */ try { - if ( try_bind( sock(), INADDR_ANY, desired_port_no ) ) { return; } + if ( try_bind( sock(), INADDR_ANY, desired_port_low, desired_port_high ) ) { return; } } catch ( const NetworkException& e ) { fprintf( stderr, "Error binding to any interface: %s: %s\n", e.function.c_str(), strerror( e.the_errno ) ); @@ -276,7 +268,7 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se throw NetworkException( "Could not bind", errno ); } -bool Connection::try_bind( int socket, uint32_t addr, int port ) +bool Connection::try_bind( int socket, uint32_t addr, int port_low, int port_high ) { struct sockaddr_in local_addr; local_addr.sin_family = AF_INET; @@ -284,8 +276,11 @@ bool Connection::try_bind( int socket, uint32_t addr, int port ) int search_low = PORT_RANGE_LOW, search_high = PORT_RANGE_HIGH; - if ( port != 0 ) { /* port preference */ - search_low = search_high = port; + if ( port_low != 0 ) { /* low port preference */ + search_low = port_low; + } + if ( port_high != 0 ) { /* high port preference */ + search_high = port_high; } for ( int i = search_low; i <= search_high; i++ ) { @@ -597,3 +592,50 @@ const Connection::Socket & Connection::Socket::operator=( const Socket & other ) return *this; } + +bool Connection::parse_portrange( const char * desired_port, int & desired_port_low, int & desired_port_high ) +{ + /* parse "port" or "portlow:porthigh" */ + desired_port_low = desired_port_high = 0; + char *end; + long value; + + /* parse first (only?) port */ + errno = 0; + value = strtol( desired_port, &end, 10 ); + if ( (errno != 0) || (*end != '\0' && *end != ':') ) { + fprintf( stderr, "Invalid (low) port number (%s)\n", desired_port ); + return false; + } + if ( (value < 0) || (value > 65535) ) { + fprintf( stderr, "(Low) port number %ld outside valid range [0..65535]\n", value ); + return false; + } + + desired_port_low = (int)value; + if (*end == '\0') { /* not a port range */ + desired_port_high = desired_port_low; + return true; + } + + /* port range; parse high port */ + const char * cp = end + 1; + errno = 0; + value = strtol( cp, &end, 10 ); + if ( (errno != 0) || (*end != '\0') ) { + fprintf( stderr, "Invalid high port number (%s)\n", cp ); + return false; + } + if ( (value < 0) || (value > 65535) ) { + fprintf( stderr, "High port number %ld outside valid range [0..65535]\n", value ); + return false; + } + + desired_port_high = (int)value; + if ( desired_port_low > desired_port_high ) { + fprintf( stderr, "Low port %d greater than high port %d\n", desired_port_low, desired_port_high ); + return false; + } + + return true; +} diff --git a/src/network/network.h b/src/network/network.h index f98da24..8db4bd8 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -101,7 +101,7 @@ namespace Network { static const int CONGESTION_TIMESTAMP_PENALTY = 500; /* ms */ - static bool try_bind( int socket, uint32_t addr, int port ); + static bool try_bind( int socket, uint32_t addr, int port_low, int port_high ); class Socket { @@ -185,6 +185,8 @@ namespace Network { } void set_last_roundtrip_success( uint64_t s_success ) { last_roundtrip_success = s_success; } + + static bool parse_portrange( const char * desired_port_range, int & desired_port_low, int & desired_port_high ); }; }