Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

UDP hole punching for partly firewalled servers #287

Open
wants to merge 1 commit into from

3 participants

@klenze

Hi Keith, I finally managed to get github to generate a pull request.

Most of the changes should already be described in my mail, I also added a string fw_hole_command which is read from some environment variable and meant to contain a shell line to get the server to dispatch another hole punching packet after client IP changes. (It is not used yet -- I'm not sure if it should be automatically run on some conditions or bound to a keyboard shortcut.)

I did not adapt the perl script yet but just wrote a minimal wrapper shell script (also included in the mail) to use the new hole-punching features.

@brettviren

I've attempted to bring Klenze's patch up to date with the latest head.

https://github.com/brettviren/mosh/tree/udp-hole-punch-redux

One difference is to pass the client's port to mosh-client via the command line instead of an environment variable.

I added a README to discuss some of the issues with this idea.

https://github.com/brettviren/mosh/blob/udp-hole-punch-redux/README_udp_hole_punch.org

More work is needed including teaching the "mosh" script what to do and to try to hande roaming.

@gordon-morehouse

Would this be a good place to beg for better UPnP-style UDP port mapping for NAT'd servers? :) I know it's a feature request, just not certain if it's tracked anywhere else, and it'd be really, really useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 13, 2012
  1. @klenze
This page is out of date. Refresh to see the latest.
View
13 src/frontend/mosh-client.cc
@@ -111,7 +111,16 @@ int main( int argc, char *argv[] )
/* Read prediction preference */
char *predict_mode = getenv( "MOSH_PREDICTION_DISPLAY" );
/* can be NULL */
-
+
+ int cport=0;
+ char *client_port = getenv( "MOSH_CLIENT_PORT" );
+ if ( client_port )
+ cport=myatoi( client_port );
+
+ char* fw_hole_command = getenv( "MOSH_FW_HOLE_COMMAND" );
+ //turns out std::string handles NULL really badly
+ if (!fw_hole_command)
+ fw_hole_command="";
char *key = strdup( env_key );
if ( key == NULL ) {
perror( "strdup" );
@@ -127,7 +136,7 @@ int main( int argc, char *argv[] )
set_native_locale();
try {
- STMClient client( ip, port, key, predict_mode );
+ STMClient client( ip, port, key, predict_mode, cport, fw_hole_command );
client.init();
try {
View
59 src/frontend/mosh-server.cc
@@ -39,6 +39,7 @@
#include <getopt.h>
#include <time.h>
#include <sys/stat.h>
+#include <fcntl.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
@@ -71,7 +72,8 @@ typedef Network::Transport< Terminal::Complete, Network::UserStream > ServerConn
void serve( int host_fd,
Terminal::Complete &terminal,
- ServerConnection &network );
+ ServerConnection &network,
+ const char* pipename);
int run_server( const char *desired_ip, const char *desired_port,
const string &command_path, char *command_argv[],
@@ -436,8 +438,13 @@ int run_server( const char *desired_ip, const char *desired_port,
utempter_add_record( master, tmp );
#endif
+ // open a named pipe for hole punching requests
+ char pipename[64];
+ snprintf(pipename, 64, "/tmp/.mosh_%d", getpid());
+ mknod(pipename, S_IFIFO | 0600, 0);
+
try {
- serve( master, terminal, *network );
+ serve( master, terminal, *network, pipename );
} catch ( const Network::NetworkException& e ) {
fprintf( stderr, "Network exception: %s: %s\n",
e.function.c_str(), strerror( e.the_errno ) );
@@ -450,6 +457,8 @@ int run_server( const char *desired_ip, const char *desired_port,
utempter_remove_record( master );
#endif
+ unlink(pipename);
+
if ( close( master ) < 0 ) {
perror( "close" );
exit( 1 );
@@ -463,12 +472,48 @@ int run_server( const char *desired_ip, const char *desired_port,
return 0;
}
-void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network )
+void handle_fw_hole_request(ServerConnection &network, int pipe_fd)
+{
+ /* udp hole punching for firewall */
+ /* if we encounter an error, we just continue on*/
+ char buf[512];
+ char ip[512];
+ int port;
+ int num=read(pipe_fd, buf, 512);
+
+ if (num<0)
+ return; /* read failed */
+ buf[num]='\0';
+
+ if (sscanf(buf, "%s %d", ip, &port)!=2)
+ return; /* bad format */
+
+ struct sockaddr_in addr;
+ addr.sin_family=AF_INET;
+ if (inet_aton( ip, &(addr.sin_addr) ) == 0 )
+ return; /* bad format, again */
+
+ addr.sin_port=htons((short)port);
+
+ int s=network.fd();
+ /* try to send an empty dummy packet to the desired host/port to trick a
+ stateful firewall into forwarding packets from said host/port to us. */
+ int r=sendto(s, NULL, 0, MSG_EOR, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
+ if (r<0)
+ return;
+}
+
+
+
+void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network, const char* pipename )
{
+ int pipe_fd=open(pipename, O_RDONLY| O_NDELAY);
+
/* prepare to poll for events */
Select &sel = Select::get_instance();
sel.add_fd( network.fd() );
sel.add_fd( host_fd );
+ sel.add_fd( pipe_fd );
sel.add_signal( SIGTERM );
sel.add_signal( SIGINT );
@@ -620,6 +665,13 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network
}
}
+ if ( sel.read( pipe_fd ) )
+ {
+ handle_fw_hole_request(network, pipe_fd);
+ close(pipe_fd);
+ pipe_fd = open(pipename, O_RDONLY| O_NDELAY);
+ }
+
/* quit if our shutdown has been acknowledged */
if ( network.shutdown_in_progress() && network.shutdown_acknowledged() ) {
break;
@@ -676,6 +728,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network
}
}
}
+ close(pipe_fd);
}
/* OpenSSH prints the motd on startup, so we will too */
View
2  src/frontend/stmclient.cc
@@ -151,7 +151,7 @@ void STMClient::main_init( void )
Network::UserStream blank;
Terminal::Complete local_terminal( window_size.ws_col, window_size.ws_row );
network = new Network::Transport< Network::UserStream, Terminal::Complete >( blank, local_terminal,
- key.c_str(), ip.c_str(), port );
+ key.c_str(), ip.c_str(), port, client_port );
network->set_send_delay( 1 ); /* minimal delay on outgoing keystrokes */
View
7 src/frontend/stmclient.h
@@ -33,7 +33,8 @@ class STMClient {
std::string ip;
int port;
std::string key;
-
+ int client_port;
+ std::string fw_hole_command;
struct termios saved_termios, raw_termios;
struct winsize window_size;
@@ -61,8 +62,10 @@ class STMClient {
}
public:
- STMClient( const char *s_ip, int s_port, const char *s_key, const char *predict_mode )
+STMClient( const char *s_ip, int s_port, const char *s_key, const char *predict_mode,
+ int s_client_port, char* s_fw_hole_command )
: ip( s_ip ), port( s_port ), key( s_key ),
+ client_port( s_client_port ), fw_hole_command( s_fw_hole_command ),
saved_termios(), raw_termios(),
window_size(),
local_framebuffer( NULL ),
View
7 src/network/network.cc
@@ -122,6 +122,7 @@ void Connection::setup( void )
}
}
+
Connection::Connection( const char *desired_ip, const char *desired_port ) /* server */
: sock( -1 ),
has_remote_addr( false ),
@@ -231,7 +232,7 @@ bool Connection::try_bind( int socket, uint32_t s_addr, int port )
return false;
}
-Connection::Connection( const char *key_str, const char *ip, int port ) /* client */
+Connection::Connection( const char *key_str, const char *ip, int port, int client_port ) /* client */
: sock( -1 ),
has_remote_addr( false ),
remote_addr(),
@@ -262,6 +263,10 @@ Connection::Connection( const char *key_str, const char *ip, int port ) /* clien
throw NetworkException( buffer, saved_errno );
}
+ if ( client_port ) {
+ try_bind( sock, INADDR_ANY, client_port );
+ }
+
has_remote_addr = true;
}
View
8 src/network/network.h
@@ -50,6 +50,8 @@ namespace Network {
TO_CLIENT = 1
};
+ void make_firewall_hole(int signum);
+
class Packet {
public:
uint64_t seq;
@@ -76,7 +78,7 @@ namespace Network {
static const int PORT_RANGE_LOW = 60001;
static const int PORT_RANGE_HIGH = 60999;
-
+
static bool try_bind( int socket, uint32_t s_addr, int port );
int sock;
@@ -111,7 +113,7 @@ namespace Network {
public:
Connection( const char *desired_ip, const char *desired_port ); /* server */
- Connection( const char *key_str, const char *ip, int port ); /* client */
+ Connection( const char *key_str, const char *ip, int port, int client_port ); /* client */
~Connection();
void send( string s );
@@ -125,7 +127,7 @@ namespace Network {
uint64_t timeout( void ) const;
double get_SRTT( void ) const { return SRTT; }
-
+
const struct in_addr & get_remote_ip( void ) const { return remote_addr.sin_addr; }
const NetworkException *get_send_exception( void ) const
View
5 src/network/networktransport.cc
@@ -40,8 +40,9 @@ Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState
template <class MyState, class RemoteState>
Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState &initial_remote,
- const char *key_str, const char *ip, int port )
- : connection( key_str, ip, port ),
+ const char *key_str, const char *ip, int port,
+ int client_port )
+ : connection( key_str, ip, port, client_port ),
sender( &connection, initial_state ),
received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ),
last_receiver_state( initial_remote ),
View
2  src/network/networktransport.h
@@ -54,7 +54,7 @@ namespace Network {
Transport( MyState &initial_state, RemoteState &initial_remote,
const char *desired_ip, const char *desired_port );
Transport( MyState &initial_state, RemoteState &initial_remote,
- const char *key_str, const char *ip, int port );
+ const char *key_str, const char *ip, int port, int client_port );
/* Send data or an ack if necessary. */
void tick( void ) { sender.tick(); }
Something went wrong with that request. Please try again.