Applied vnc-via.patch from FC3 package.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@183 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/network/TcpSocket.cxx b/network/TcpSocket.cxx
index 4c4e8d3..3fe4a65 100644
--- a/network/TcpSocket.cxx
+++ b/network/TcpSocket.cxx
@@ -54,6 +54,29 @@
 
 static rfb::LogWriter vlog("TcpSocket");
 
+/* Tunnelling support. */
+int network::findFreeTcpPort (void)
+{
+  int sock, port;
+  struct sockaddr_in addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = INADDR_ANY;
+
+  if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+    throw SocketException ("unable to create socket", errorNumber);
+
+  for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) {
+    addr.sin_port = htons ((unsigned short) port);
+    if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == 0) {
+      close (sock);
+      return port;
+    }
+  }
+  throw SocketException ("no free port in range", 0);
+  return 0;
+}
+
 
 void
 TcpSocket::initTcpSockets() {
diff --git a/network/TcpSocket.h b/network/TcpSocket.h
index 9533340..7469782 100644
--- a/network/TcpSocket.h
+++ b/network/TcpSocket.h
@@ -32,8 +32,14 @@
 
 #include <list>
 
+/* Tunnelling support. */
+#define TUNNEL_PORT_OFFSET 5500
+
 namespace network {
 
+  /* Tunnelling support. */
+  int findFreeTcpPort (void);
+
   class TcpSocket : public Socket {
   public:
     TcpSocket(int sock, bool close=true);
diff --git a/vncviewer_unix/vncviewer.cxx b/vncviewer_unix/vncviewer.cxx
index f0c5c98..a3d2fd3 100644
--- a/vncviewer_unix/vncviewer.cxx
+++ b/vncviewer_unix/vncviewer.cxx
@@ -44,6 +44,7 @@
 
 using namespace network;
 using namespace rfb;
+using namespace std;
 
 IntParameter pointerEventInterval("PointerEventInterval",
                                   "Time in milliseconds to rate-limit"
@@ -99,6 +100,8 @@
 StringParameter geometry("geometry", "X geometry specification", "");
 StringParameter displayname("display", "The X display", "");
 
+StringParameter via("via", "Gateway to tunnel via", "");
+
 BoolParameter customCompressLevel("CustomCompressLevel",
 				 "Use custom compression level. "
 				 "Default if CompressLevel is specified.", false);
@@ -179,6 +182,61 @@
   exit(1);
 }
 
+/* Tunnelling support. */
+static void
+interpretViaParam (char **gatewayHost, char **remoteHost,
+		   int *remotePort, char **vncServerName,
+		   int localPort)
+{
+  const int SERVER_PORT_OFFSET = 5900;
+  char *pos = strchr (*vncServerName, ':');
+  if (pos == NULL)
+    *remotePort = SERVER_PORT_OFFSET;
+  else {
+    int portOffset = SERVER_PORT_OFFSET;
+    size_t len;
+    *pos++ = '\0';
+    len = strlen (pos);
+    if (*pos == ':') {
+      /* Two colons is an absolute port number, not an offset. */
+      pos++;
+      len--;
+      portOffset = 0;
+    }
+    if (!len || strspn (pos, "-0123456789") != len )
+      usage ();
+    *remotePort = atoi (pos) + portOffset;
+  }
+
+  if (**vncServerName != '\0')
+    *remoteHost = *vncServerName;
+
+  *gatewayHost = strDup (via.getValueStr ());
+  *vncServerName = new char[50];
+  sprintf (*vncServerName, "localhost::%d", localPort);
+}
+
+static void
+createTunnel (const char *gatewayHost, const char *remoteHost,
+	      int remotePort, int localPort)
+{
+  char *cmd = getenv ("VNC_VIA_CMD");
+  char *percent;
+  char lport[10], rport[10];
+  sprintf (lport, "%d", localPort);
+  sprintf (rport, "%d", remotePort);
+  setenv ("G", gatewayHost, 1);
+  setenv ("H", remoteHost, 1);
+  setenv ("R", rport, 1);
+  setenv ("L", lport, 1);
+  if (!cmd)
+    cmd = "/usr/bin/ssh -f -L \"$L\":\"$H\":\"$R\" \"$G\" sleep 20";
+  /* Compatibility with TightVNC's method. */
+  while ((percent = strchr (cmd, '%')) != NULL)
+    *percent = '$';
+  system (cmd);
+}
+
 int main(int argc, char** argv)
 {
   setlocale(LC_ALL, "");
@@ -221,8 +279,6 @@
       usage();
     }
 
-    if (vncServerName)
-      usage();
     vncServerName = argv[i];
   }
 
@@ -240,6 +296,19 @@
   try {
     TcpSocket::initTcpSockets();
 
+    /* Tunnelling support. */
+    if (strlen (via.getValueStr ()) > 0) {
+      char *gatewayHost = "";
+      char *remoteHost = "localhost";
+      int localPort = findFreeTcpPort ();
+      int remotePort;
+      if (!vncServerName)
+        usage();
+      interpretViaParam (&gatewayHost, &remoteHost, &remotePort,
+			 &vncServerName, localPort);
+      createTunnel (gatewayHost, remoteHost, remotePort, localPort);
+    }
+
     Socket* sock = 0;
 
     if (listenMode) {
diff --git a/vncviewer_unix/vncviewer.man b/vncviewer_unix/vncviewer.man
index fc7439c..b567f0a4 100644
--- a/vncviewer_unix/vncviewer.man
+++ b/vncviewer_unix/vncviewer.man
@@ -178,6 +178,23 @@
 specified as an X11 keysym name (these can be obtained by removing the XK_
 prefix from the entries in "/usr/include/X11/keysymdef.h").  Default is F8.
 
+.TP
+\fB\-via\fR \fIgateway\fR
+Automatically create encrypted TCP tunnel to the \fIgateway\fR machine
+before connection, connect to the \fIhost\fR through that tunnel
+(TightVNC\-specific). By default, this option invokes SSH local port
+forwarding, assuming that SSH client binary can be accessed as
+/usr/bin/ssh. Note that when using the \fB\-via\fR option, the host
+machine name should be specified as known to the gateway machine, e.g. 
+"localhost" denotes the \fIgateway\fR, not the machine where vncviewer
+was launched. The environment variable \fIVNC_VIA_CMD\fR can override
+the default tunnel command of
+\fB/usr/bin/ssh\ -f\ -L\ "$L":"$H":"$R"\ "$G"\ sleep\ 20\fR.  The tunnel
+command is executed with the environment variables \fIL\fR, \fIH\fR,
+\fIR\fR, and \fIG\fR taken the values of the local port number, the remote
+host, the port number on the remote host, and the gateway machine
+respectively.
+
 .SH SEE ALSO
 .BR Xvnc (1)
 .BR vncconfig (1),