]> source.dussan.org Git - tigervnc.git/commitdiff
Replace all stream-based IO with non-blocking NIO-based implementation. Still a...
authorBrian Hinz <bphinz@users.sourceforge.net>
Fri, 3 Feb 2012 05:38:23 +0000 (05:38 +0000)
committerBrian Hinz <bphinz@users.sourceforge.net>
Fri, 3 Feb 2012 05:38:23 +0000 (05:38 +0000)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4842 3789f03b-4d11-0410-bbf8-ca57d06f2519

22 files changed:
java/CMakeLists.txt
java/com/tigervnc/network/FileDescriptor.java [new file with mode: 0644]
java/com/tigervnc/network/SSLEngineManager.java [new file with mode: 0644]
java/com/tigervnc/network/Socket.java [new file with mode: 0644]
java/com/tigervnc/network/SocketDescriptor.java [new file with mode: 0644]
java/com/tigervnc/network/SocketListener.java [new file with mode: 0644]
java/com/tigervnc/network/TcpListener.java [new file with mode: 0644]
java/com/tigervnc/network/TcpSocket.java [new file with mode: 0644]
java/com/tigervnc/rdr/FdInStream.java [new file with mode: 0644]
java/com/tigervnc/rdr/FdInStreamBlockCallback.java [new file with mode: 0644]
java/com/tigervnc/rdr/FdOutStream.java [new file with mode: 0644]
java/com/tigervnc/rdr/InStream.java
java/com/tigervnc/rdr/JavaInStream.java [deleted file]
java/com/tigervnc/rdr/JavaOutStream.java [deleted file]
java/com/tigervnc/rdr/OutStream.java
java/com/tigervnc/rdr/TLSException.java [new file with mode: 0644]
java/com/tigervnc/rdr/TLSInStream.java [new file with mode: 0644]
java/com/tigervnc/rdr/TLSOutStream.java [new file with mode: 0644]
java/com/tigervnc/rfb/CConnection.java
java/com/tigervnc/rfb/CSecurityTLS.java
java/com/tigervnc/vncviewer/CConn.java
java/com/tigervnc/vncviewer/VncViewer.java

index f0a884b80875bc531834cf69f35e6dfe80f80e22..f8364fbae3d9b44b2583248dddeaca09da736d9d 100644 (file)
@@ -44,6 +44,7 @@ endforeach()
 file(GLOB DEPEND_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/com/tigervnc/rfb/*.java
   ${CMAKE_CURRENT_SOURCE_DIR}/com/tigervnc/rdr/*.java
+  ${CMAKE_CURRENT_SOURCE_DIR}/com/tigervnc/network/*.java
   ${CMAKE_CURRENT_SOURCE_DIR}/com/jcraft/jzlib/*.java)
 
 string(REGEX REPLACE " " ";" JAVACFLAGS "${JAVACFLAGS}")
@@ -81,6 +82,7 @@ add_custom_command(OUTPUT VncViewer.jar
     com/tigervnc/vncviewer/*.class
     com/tigervnc/rfb/*.class
     com/tigervnc/rdr/*.class
+    com/tigervnc/network/*.class
     com/jcraft/jzlib/*.class
     com/tigervnc/vncviewer/tigervnc.png
     com/tigervnc/vncviewer/tigervnc.ico
diff --git a/java/com/tigervnc/network/FileDescriptor.java b/java/com/tigervnc/network/FileDescriptor.java
new file mode 100644 (file)
index 0000000..e2d04fa
--- /dev/null
@@ -0,0 +1,31 @@
+/* 
+ * Copyright (C) 2012 TigerVNC Team.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+package com.tigervnc.network;
+
+import java.lang.Exception;
+import java.io.IOException;
+
+public interface FileDescriptor {
+    
+  public int read(byte[] buf, int bufPtr, int length) throws java.lang.Exception;
+  public int write(byte[] buf, int bufPtr, int length) throws java.lang.Exception;
+  public int select(int interestOps, int timeout) throws java.lang.Exception;
+  public void close() throws IOException;
+
+}
diff --git a/java/com/tigervnc/network/SSLEngineManager.java b/java/com/tigervnc/network/SSLEngineManager.java
new file mode 100644 (file)
index 0000000..0f5ad66
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (C) 2008  Trustin Heuiseung Lee
+ * Copyright (C) 2012  TigerVNC Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA
+ */
+ package com.tigervnc.network;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.*;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import com.tigervnc.rdr.FdInStream;
+import com.tigervnc.rdr.FdOutStream;
+
+public class SSLEngineManager {
+
+  private SSLEngine engine = null;
+
+  private int applicationBufferSize;
+  private int packetBufferSize;
+
+  private ByteBuffer myAppData;
+  private ByteBuffer myNetData;
+  private ByteBuffer peerAppData;
+  private ByteBuffer peerNetData;
+
+
+  private Executor executor;
+  private FdInStream inStream;
+  private FdOutStream outStream;
+
+  public SSLEngineManager(SSLEngine sslEngine, FdInStream is, 
+                          FdOutStream os) throws IOException {
+
+    inStream = is;
+    outStream = os;
+    engine = sslEngine;
+
+    executor = Executors.newSingleThreadExecutor();
+
+    packetBufferSize = engine.getSession().getPacketBufferSize();
+    applicationBufferSize = engine.getSession().getApplicationBufferSize();
+
+    myAppData = ByteBuffer.allocate(applicationBufferSize);
+    myNetData = ByteBuffer.allocate(packetBufferSize);
+    peerAppData = ByteBuffer.allocate(applicationBufferSize);
+    peerNetData = ByteBuffer.allocate(packetBufferSize);
+  }
+
+  public void doHandshake() throws Exception {
+
+    // Begin handshake
+    engine.beginHandshake();
+    SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+
+    // Process handshaking message
+    while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
+        hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+
+        switch (hs) {
+
+        case NEED_UNWRAP:
+            // Receive handshaking data from peer
+            pull(peerNetData);
+            //if (pull(peerNetData) < 0) {
+                // Handle closed channel
+            //}
+
+            // Process incoming handshaking data
+            peerNetData.flip();
+            SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
+            peerNetData.compact();
+            hs = res.getHandshakeStatus();
+
+            // Check status
+            switch (res.getStatus()) {
+            case OK :
+                // Handle OK status
+                break;
+
+            // Handle other status: BUFFER_UNDERFLOW, BUFFER_OVERFLOW, CLOSED
+            //...
+            }
+            break;
+
+        case NEED_WRAP :
+            // Empty the local network packet buffer.
+            myNetData.clear();
+
+            // Generate handshaking data
+            res = engine.wrap(myAppData, myNetData);
+            hs = res.getHandshakeStatus();
+
+            // Check status
+            switch (res.getStatus()) {
+            case OK :
+                //myNetData.flip();
+
+                push(myNetData);
+                // Send the handshaking data to peer
+                //while (myNetData.hasRemaining()) {
+                //    if (push(myNetData) < 0) {
+                //        // Handle closed channel
+                //    }
+                //}
+                break;
+
+            // Handle other status:  BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED
+            //...
+            }
+            break;
+
+        case NEED_TASK :
+            // Handle blocking tasks
+            executeTasks();
+            break;
+
+        // Handle other status:  // FINISHED or NOT_HANDSHAKING
+        //...
+        }
+      hs = engine.getHandshakeStatus();
+    }
+
+    // Processes after handshaking
+    //...
+}
+
+  private void executeTasks() {
+    Runnable task;
+    while ((task = engine.getDelegatedTask()) != null) {
+      executor.execute(task);
+    }
+  }
+
+  public int read(byte[] data, int dataPtr, int length) throws IOException {
+    // Read SSL/TLS encoded data from peer
+    int bytesRead = 0;
+    SSLEngineResult res;
+    do {
+      // Process incoming data
+      int packetLength = pull(peerNetData);
+      peerNetData.flip();
+      res = engine.unwrap(peerNetData, peerAppData);
+      peerNetData.compact();
+      bytesRead += packetLength;
+    } while (res.getStatus() != SSLEngineResult.Status.OK);
+    peerAppData.flip();
+    int n = Math.min(peerAppData.remaining(), length);
+    peerAppData.get(data, dataPtr, n);
+    peerAppData.compact();
+    return n;
+  }
+
+  public int write(byte[] data, int dataPtr, int length) throws IOException {
+    int n = 0;
+//while (myAppData.hasRemaining()) {
+    // Generate SSL/TLS encoded data (handshake or application data)
+    // FIXME:
+    // Need to make sure myAppData has space for data!
+    myAppData.put(data, dataPtr, length);
+    myAppData.flip();
+    SSLEngineResult res = engine.wrap(myAppData, myNetData);
+    myAppData.compact();
+
+    // Process status of call
+    //if (res.getStatus() == SSLEngineResult.Status.OK) {
+        //myAppData.compact();
+
+        // Send SSL/TLS encoded data to peer
+        //while(myNetData.hasRemaining()) {
+            int num = push(myNetData);
+            if (num == -1) {
+                // handle closed channel
+            } else if (num == 0) {
+                // no bytes written; try again later
+            }
+            //n += num;
+        //}
+    //}
+
+    // Handle other status:  BUFFER_OVERFLOW, CLOSED
+    //...
+  //}
+   return num;
+  }
+
+  private int push(ByteBuffer src) throws IOException {
+    src.flip();
+    int n = src.remaining();
+    byte[] b = new byte[n];
+    src.get(b);
+    src.clear();
+    outStream.writeBytes(b, 0, n);
+    outStream.flush();
+    return n;
+  }
+
+  private int pull(ByteBuffer dst) throws IOException {
+    int packetLength = 0;
+    inStream.checkNoWait(5);
+    //if (!inStream.checkNoWait(5)) {
+    //  return 0;
+    //}
+
+    byte[] header = new byte[5];
+    inStream.readBytes(header, 0, 5);
+
+    boolean tls;
+    int h = header[0] & 0xFF;
+    switch (header[0] & 0xFF) {
+    case 20:  // change_cipher_spec
+    case 21:  // alert
+    case 22:  // handshake
+    case 23:  // application_data
+      tls = true;
+      break;
+    default:
+      //  SSLv2 bad data
+      tls = false;
+    }
+
+    if (tls) {
+      int majorVersion = (int)(header[1] & 0xFF);
+      if (majorVersion >= 3 && majorVersion < 10) {
+        // SSLv3 or TLS
+        packetLength = (int)(((header[3] << 8) | (header[4] & 0xFF)) & 0xFFFF) + 5;
+        if (packetLength <= 5) {
+          // Neither SSLv2 or TLSv1
+          tls = false;
+        }
+      } else {
+        // Neither SSLv2 or TLSv1
+        tls = false;
+      }
+    }
+
+    if (!tls) {
+      boolean sslv2 = true;
+      int headerLength = (int)((header[0] & 0xFF) & 0x80) != 0 ? 2 : 3;
+      int majorVersion = (int)(header[headerLength + 1] & 0xFF);
+      if (majorVersion >= 2 && majorVersion < 10) {
+        // SSLv2
+        if (headerLength == 2) {
+          packetLength = (int)(((header[0] << 8) | (header[1] & 0xFF)) & 0x7FFF) + 2;
+        } else {
+          packetLength = (int)(((header[0] << 8) | (header[1] & 0xFF)) & 0x3FFF) + 3;
+        }
+        if (packetLength <= headerLength) {
+          sslv2 = false;
+        }
+      } else {
+        sslv2 = false;
+      }
+
+      if (!sslv2) {
+        throw new IOException("not an SSL/TLS record");
+      }
+    }
+
+    assert packetLength > 0;
+
+    byte[] buf = new byte[packetLength - 5];
+    inStream.readBytes(buf, 0, packetLength - 5);
+    dst.put(header);
+    dst.put(buf);
+    return packetLength;
+  }
+
+  public SSLSession getSession() {
+    return engine.getSession();
+  }
+
+}
diff --git a/java/com/tigervnc/network/Socket.java b/java/com/tigervnc/network/Socket.java
new file mode 100644 (file)
index 0000000..212d130
--- /dev/null
@@ -0,0 +1,109 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Socket - abstract base-class for any kind of network stream/socket
+
+package com.tigervnc.network;
+
+import com.tigervnc.rdr.*;
+import java.nio.channels.*;
+import java.nio.channels.spi.SelectorProvider;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+abstract public class Socket {
+
+  public Socket(FileDescriptor fd) {
+    instream = new FdInStream(fd);
+    outstream = new FdOutStream(fd);
+    ownStreams = true; isShutdown_ = false;
+    queryConnection = false;
+  }
+
+  public FdInStream inStream() {return instream;}
+  public FdOutStream outStream() {return outstream;}
+  public FileDescriptor getFd() {return outstream.getFd();}
+
+  // if shutdown() is overridden then the override MUST call on to here
+  public void shutdown() {isShutdown_ = true;}
+  public void close() throws IOException {getFd().close();}
+  public final boolean isShutdown() {return isShutdown_;}
+
+  // information about this end of the socket
+  abstract public int getMyPort();
+
+  // information about the remote end of the socket
+  abstract public String getPeerAddress(); // a string e.g. "192.168.0.1"
+  abstract public int getPeerPort();
+  abstract public String getPeerEndpoint(); // <address>::<port>
+
+  // Is the remote end on the same machine?
+  abstract public boolean sameMachine();
+
+  // Was there a "?" in the ConnectionFilter used to accept this Socket?
+  public void setRequiresQuery() {queryConnection = true;}
+  public final boolean requiresQuery() {return queryConnection;}
+
+  protected Socket() {
+    instream = null; outstream = null; ownStreams = false;
+    isShutdown_ = false; queryConnection = false;
+  }
+
+  protected Socket(FdInStream i, FdOutStream o, boolean own) {
+    instream = i; outstream = o; ownStreams = own;
+    isShutdown_ = false; queryConnection = false;
+  }
+
+  protected FdInStream instream;
+  protected FdOutStream outstream;
+  boolean ownStreams;
+  boolean isShutdown_;
+  boolean queryConnection;
+}
+
+/*
+abstract class ConnectionFilter {
+  public abstract boolean verifyConnection(Socket s);
+};
+
+abstract class SocketListener {
+  public SocketListener() { 
+    fd = null; filter = null;
+  }
+
+  // shutdown() stops the socket from accepting further connections
+  public abstract void shutdown();
+
+  // accept() returns a new Socket object if there is a connection
+  // attempt in progress AND if the connection passes the filter
+  // if one is installed.  Otherwise, returns 0.
+  public abstract Socket accept();
+
+  // setFilter() applies the specified filter to all new connections
+  public void setFilter(ConnectionFilter f) {filter = f;}
+  //public SocketDescriptor getFd() {return fd;}
+  protected FileDescriptor fd;
+  protected ConnectionFilter filter;
+};
+*/
+
+//struct SocketException : public rdr::SystemException {
+//  SocketException(const char* text, int err_) : rdr::SystemException(text, err_) {}
+//};
diff --git a/java/com/tigervnc/network/SocketDescriptor.java b/java/com/tigervnc/network/SocketDescriptor.java
new file mode 100644 (file)
index 0000000..375b033
--- /dev/null
@@ -0,0 +1,204 @@
+/* 
+ * Copyright (C) 2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+package com.tigervnc.network;
+
+import java.io.IOException;
+import java.lang.Exception;
+
+import java.net.SocketAddress;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.channels.spi.SelectorProvider;
+
+import java.util.Set;
+import java.util.Iterator;
+
+public class SocketDescriptor extends SocketChannel 
+                              implements FileDescriptor {
+
+  public SocketDescriptor() throws Exception {
+    super(SelectorProvider.provider());
+    try {
+      channel = SocketChannel.open();
+      channel.configureBlocking(false);
+      selector = Selector.open();
+    } catch (java.io.IOException e) {
+      throw new Exception(e.toString());
+    }
+    try {
+      channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE );
+    } catch (java.nio.channels.ClosedChannelException e) {
+      throw new Exception(e.toString());
+    }
+  }
+
+  public int read(byte[] buf, int bufPtr, int length) throws Exception {
+    int n;
+    ByteBuffer b = ByteBuffer.allocate(length);
+    try {
+      n = channel.read(b);
+    } catch (java.io.IOException e) {
+      System.out.println(e.toString());
+      throw new Exception(e.toString());
+    }
+    //if (n == 0)
+    //  throw new Exception;
+    b.flip();
+    b.get(buf, bufPtr, n);
+    b.clear();
+    return n;
+
+  }
+
+  public int write(byte[] buf, int bufPtr, int length) throws Exception {
+    int n;
+    ByteBuffer b = ByteBuffer.allocate(length);
+    b.put(buf, bufPtr, length);
+    b.flip();
+    try {
+      n = channel.write(b);
+    } catch (java.io.IOException e) {
+      System.out.println(e.toString());
+      throw new Exception(e.toString());
+    }
+    b.clear();
+    return n;
+  }
+
+  public int select(int interestOps, int timeout) throws Exception {
+    int n;
+    try {
+      n = selector.select(timeout);
+    } catch (Exception e) {
+      System.out.println(e.toString());
+      throw new Exception(e.toString());
+    }
+    Set keys = selector.selectedKeys();
+    Iterator iter = keys.iterator();
+    while (iter.hasNext()) {
+      SelectionKey key = (SelectionKey)iter.next();
+      if ((key.readyOps() & interestOps) != 0) { 
+        n = 1;
+        break;
+      } else {
+        n = -1;
+      }
+    }
+    keys.clear();
+    return n;
+  }
+
+  public int write(ByteBuffer buf) throws IOException {
+    int n = 0;
+    try {
+      n = channel.write(buf);
+    } catch (java.io.IOException e) {
+      System.out.println(e.toString());
+      throw e;
+    }
+    return n;
+  }
+
+  public long write(ByteBuffer[] buf, int offset, int length) 
+        throws IOException
+  {
+    long n = 0;
+    try {
+      n = channel.write(buf, offset, length);
+    } catch (java.io.IOException e) {
+      System.out.println(e.toString());
+    }
+    return n;
+  }
+
+  public int read(ByteBuffer buf) throws IOException {
+    int n = 0;
+    try {
+      n = channel.read(buf);
+    } catch (java.io.IOException e) {
+      System.out.println(e.toString());
+      throw e;
+    }
+    return n;
+  }
+
+  public long read(ByteBuffer[] buf, int offset, int length) 
+        throws IOException
+  {
+    long n = 0;
+    try {
+      n = channel.read(buf, offset, length);
+    } catch (java.io.IOException e) {
+      System.out.println(e.toString());
+    }
+    return n;
+  }
+
+  public java.net.Socket socket() {
+    return channel.socket();
+  }
+
+  public boolean isConnectionPending() {
+    return channel.isConnectionPending();
+  }
+
+  public boolean connect(SocketAddress remote) throws IOException {
+    return channel.connect(remote);
+  }
+
+  public boolean finishConnect() throws IOException {
+    return channel.finishConnect();
+  }
+
+  public boolean isConnected() {
+    return channel.isConnected();
+  }
+
+  protected void implConfigureBlocking(boolean block) throws IOException {
+    channel.configureBlocking(block);
+  }
+
+  protected synchronized void implCloseSelectableChannel() throws IOException {
+    channel.close();
+    notifyAll();
+  }
+
+  protected void setChannel(SocketChannel channel_) {
+    try {
+      if (channel != null)
+        channel.close();
+      if (selector != null)
+        selector.close();
+      channel = channel_;
+      channel.configureBlocking(false);
+      selector = Selector.open();
+    } catch (java.io.IOException e) {
+      System.out.println(e.toString());
+    }
+    try {
+      channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE );
+    } catch (java.nio.channels.ClosedChannelException e) {
+      System.out.println(e.toString());
+    }
+  }
+  
+  protected SocketChannel channel;
+  protected Selector selector;
+
+}
diff --git a/java/com/tigervnc/network/SocketListener.java b/java/com/tigervnc/network/SocketListener.java
new file mode 100644 (file)
index 0000000..cb78388
--- /dev/null
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2012 Brian P Hinz.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- SocketListener - abstract base-class for any kind of network stream/socket
+
+package com.tigervnc.network;
+
+import java.nio.channels.*;
+import java.nio.channels.spi.SelectorProvider;
+
+abstract public class SocketListener {
+
+  public SocketListener() {}
+
+  // shutdown() stops the socket from accepting further connections
+  abstract public void shutdown();
+
+  // accept() returns a new Socket object if there is a connection
+  // attempt in progress AND if the connection passes the filter
+  // if one is installed.  Otherwise, returns 0.
+  abstract public Socket accept();
+
+  // setFilter() applies the specified filter to all new connections
+  //public void setFilter(ConnectionFilter* f) {filter = f;}
+  public FileDescriptor getFd() {return fd;}
+
+  protected FileDescriptor fd;
+  //protected ConnectionFilter* filter;
+
+}
diff --git a/java/com/tigervnc/network/TcpListener.java b/java/com/tigervnc/network/TcpListener.java
new file mode 100644 (file)
index 0000000..63a2ff1
--- /dev/null
@@ -0,0 +1,168 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.network;
+
+import java.io.IOException;
+import java.lang.Exception;
+import java.nio.*;
+import java.nio.channels.*;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.util.Set;
+import java.util.Iterator;
+
+public class TcpListener extends SocketListener  {
+
+  public static boolean socketsInitialised = false;
+
+  public TcpListener(String listenaddr, int port, boolean localhostOnly,
+                                      SocketDescriptor sock, boolean close_) throws Exception {
+    closeFd = close_;
+    if (sock != null) {
+      fd = sock;
+      return;
+    }
+
+    TcpSocket.initSockets();
+    try {
+      channel = ServerSocketChannel.open();
+      channel.configureBlocking(false);
+    } catch(IOException e) {
+      throw new Exception("unable to create listening socket: "+e.toString());
+    }
+
+    // - Bind it to the desired port
+    InetAddress addr = null;
+  
+    try {
+      if (localhostOnly) {
+        addr = InetAddress.getByName(null);
+      } else if (listenaddr != null) {
+          addr = java.net.InetAddress.getByName(listenaddr);
+      } else {
+        // FIXME: need to be sure we get the wildcard address?
+        addr = InetAddress.getByName(null);
+        //addr = InetAddress.getLocalHost();
+      }
+    } catch (UnknownHostException e) {
+      System.out.println(e.toString());
+      System.exit(-1);
+    }
+  
+    try {
+      channel.socket().bind(new InetSocketAddress(addr, port));
+    } catch (IOException e) {
+      throw new Exception("unable to bind listening socket: "+e.toString());
+    }
+  
+    // - Set it to be a listening socket
+    try {
+      selector = Selector.open();
+      channel.register(selector, SelectionKey.OP_ACCEPT);
+    } catch (IOException e) {
+      throw new Exception("unable to set socket to listening mode: "+e.toString());
+    }
+  }
+
+  public TcpListener(String listenaddr, int port) throws Exception {
+    this(listenaddr, port, false, null, true);
+  }
+
+//  TcpListener::~TcpListener() {
+//    if (closeFd) closesocket(fd);
+//  }
+
+  public void shutdown() {
+    //shutdown(getFd(), 2);
+  }
+
+  public TcpSocket accept() {
+    SocketChannel new_sock = null;
+
+    // Accept an incoming connection
+    try {
+      if (selector.select() > 0) {
+        Set keys = selector.selectedKeys();
+        Iterator iter = keys.iterator();
+        while (iter.hasNext()) {
+          SelectionKey key = (SelectionKey)iter.next();
+          iter.remove();
+          if (key.isAcceptable()) {
+            new_sock = channel.accept();
+            break;
+          }
+        }
+        keys.clear();
+        if (new_sock == null)
+          return null;
+      }
+    } catch (IOException e) {
+      //throw SocketException("unable to accept new connection", errorNumber);
+      System.out.println(e.toString());
+    }
+
+    // Disable Nagle's algorithm, to reduce latency
+    TcpSocket.enableNagles(new_sock, false);
+
+    // Create the socket object & check connection is allowed
+    SocketDescriptor fd = null;
+    try {
+      fd = new SocketDescriptor();
+    } catch (java.lang.Exception e) {
+      System.out.println(e.toString());
+      System.exit(-1);
+    }
+    fd.setChannel(new_sock);
+    TcpSocket s = new TcpSocket(fd);
+    //if (filter && !filter->verifyConnection(s)) {
+    //  delete s;
+    //  return 0;
+    //}
+    return s;
+  }
+
+/*
+void TcpListener::getMyAddresses(std::list<char*>* result) {
+  const hostent* addrs = gethostbyname(0);
+  if (addrs == 0)
+    throw rdr::SystemException("gethostbyname", errorNumber);
+  if (addrs->h_addrtype != AF_INET)
+    throw rdr::Exception("getMyAddresses: bad family");
+  for (int i=0; addrs->h_addr_list[i] != 0; i++) {
+    const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
+    char* addr = new char[strlen(addrC)+1];
+    strcpy(addr, addrC);
+    result->push_back(addr);
+  }
+}
+  */
+
+  //public int getMyPort() {
+  //  return TcpSocket.getSockPort();
+  //}
+
+  private boolean closeFd;
+  private ServerSocketChannel channel;
+  private Selector selector;
+
+}
+
diff --git a/java/com/tigervnc/network/TcpSocket.java b/java/com/tigervnc/network/TcpSocket.java
new file mode 100644 (file)
index 0000000..926aa97
--- /dev/null
@@ -0,0 +1,201 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.network;
+
+import com.tigervnc.rdr.FdInStream;
+import com.tigervnc.rdr.FdOutStream;
+import com.tigervnc.rfb.LogWriter;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.*;
+import java.nio.channels.*;
+
+public class TcpSocket extends Socket {
+
+  // -=- Socket initialisation
+  public static boolean socketsInitialised = false;
+  public static void initSockets() {
+    if (socketsInitialised)
+      return;
+    socketsInitialised = true;
+  }
+
+  // -=- TcpSocket
+
+  public TcpSocket(SocketDescriptor sock, boolean close) {
+    super(new FdInStream(sock), new FdOutStream(sock), true);
+    //this.sock = sock;
+    closeFd = close;
+  }
+
+  public TcpSocket(SocketDescriptor sock) {
+    this(sock, true);
+  }
+
+  public TcpSocket(String host, int port) throws Exception {
+    closeFd = true;
+    SocketDescriptor sock = null;
+    InetAddress addr = null;
+    boolean result = false;
+
+    // - Create a socket
+    initSockets();
+    
+    try {
+      addr = java.net.InetAddress.getByName(host);
+    } catch(UnknownHostException e) {
+      throw new Exception("unable to resolve host by name: "+e.toString());
+    }
+
+    try {
+      sock = new SocketDescriptor();
+    } catch(UnknownHostException e) {
+      throw new Exception("unable to create socket: "+e.toString());
+    }
+
+    /* Attempt to connect to the remote host */
+    try {
+      result = sock.connect(new InetSocketAddress(addr, port));
+    } catch(java.io.IOException e) {
+      //throw new java.lang.Exception(e.getMessage());
+      System.out.println("connect failed: "+e.getMessage());
+    }
+
+    if (!result && sock.isConnectionPending()) {
+      while (!result) {
+        try {
+          result = sock.finishConnect();
+        } catch(java.nio.channels.ClosedChannelException e) {
+          throw new Exception(e.getMessage());
+        }
+      }
+    }
+
+    if (!result)
+      throw new Exception("unable connect to socket");
+
+    // Disable Nagle's algorithm, to reduce latency
+    enableNagles(sock, false);
+
+    // Create the input and output streams
+    instream = new FdInStream(sock);
+    outstream = new FdOutStream(sock);
+    ownStreams = true;
+  }
+
+  protected void finalize() throws Exception {
+    if (closeFd)
+      try {
+        ((SocketDescriptor)getFd()).close();
+      } catch (IOException e) {
+        throw new Exception(e.toString());
+      }
+  }
+
+  public int getMyPort() {
+    SocketAddress address = ((SocketDescriptor)getFd()).socket().getLocalSocketAddress();
+    return ((InetSocketAddress)address).getPort();
+  }
+
+  public String getPeerAddress() {
+    SocketAddress peer = ((SocketDescriptor)getFd()).socket().getRemoteSocketAddress();
+    if (peer != null)
+      return peer.toString();
+    return "";
+  }
+
+  public int getPeerPort() {
+    SocketAddress address = ((SocketDescriptor)getFd()).socket().getRemoteSocketAddress();
+    return ((InetSocketAddress)address).getPort();
+  }
+
+  public String getPeerEndpoint() {
+    String address = getPeerAddress();
+    int port = getPeerPort();
+    return address+"::"+port;
+  }
+
+  public boolean sameMachine() {
+    SocketAddress peeraddr = ((SocketDescriptor)getFd()).socket().getRemoteSocketAddress();
+    SocketAddress myaddr = ((SocketDescriptor)getFd()).socket().getLocalSocketAddress();
+    return myaddr.equals(peeraddr);
+  }
+
+  public void shutdown() {
+    super.shutdown();
+  }
+  
+  public void close() throws IOException {
+    ((SocketDescriptor)getFd()).close();
+  }
+  
+  public static boolean enableNagles(SocketChannel sock, boolean enable) {
+    try {
+      sock.socket().setTcpNoDelay(!enable);
+    } catch(SocketException e) {
+      vlog.error(e.getMessage());
+      return false;
+    }
+    return true;
+  }
+
+  public static boolean isSocket(java.net.Socket sock) {
+    return sock.getClass().toString().equals("com.tigervnc.net.Socket");
+  }
+
+  public boolean isConnected() {
+    return ((SocketDescriptor)getFd()).isConnected();
+  }
+
+  public int getSockPort() {
+    return ((InetSocketAddress)((SocketDescriptor)getFd()).socket().getRemoteSocketAddress()).getPort();
+  }
+
+  private boolean closeFd;
+  static LogWriter vlog = new LogWriter("TcpSocket");
+
+}
+
+/* Tunnelling support. */
+/*
+public int findFreeTcpPort() {
+  int sock;
+
+  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+    throw SocketException("unable to create socket", errorNumber);
+
+  int port = 0;
+  if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
+    throw SocketException("unable to find free port", errorNumber);
+
+  socklen_t n = sizeof(addr);
+  if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
+    throw SocketException("unable to get port number", errorNumber);
+
+  closesocket(sock);
+  return ntohs(addr.sin_port);
+}
+*/
+
diff --git a/java/com/tigervnc/rdr/FdInStream.java b/java/com/tigervnc/rdr/FdInStream.java
new file mode 100644 (file)
index 0000000..c638f4b
--- /dev/null
@@ -0,0 +1,219 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+import com.tigervnc.network.*;
+import java.nio.channels.Selector;
+import java.nio.channels.SelectionKey;
+import java.util.Set;
+import java.util.Iterator;
+
+public class FdInStream extends InStream {
+
+  static final int defaultBufSize = 8192;
+  static final int minBulkSize = 1024;
+
+  public FdInStream(FileDescriptor fd_, int bufSize_) {
+    fd = fd_;
+    bufSize = bufSize_;
+    b = new byte[bufSize];
+    ptr = end = offset = 0;
+    timeoutms = 0;
+    timing = false;
+    timeWaitedIn100us = 5;
+    timedKbits = 0;
+  }
+
+  public FdInStream(FileDescriptor fd_) { this(fd_, defaultBufSize); }
+
+  public final void readBytes(byte[] data, int dataPtr, int length) {
+    if (length < minBulkSize) {
+      super.readBytes(data, dataPtr, length);
+      return;
+    }
+
+    int n = end - ptr;
+    if (n > length) n = length;
+
+    System.arraycopy(b, ptr, data, dataPtr, n);
+    dataPtr += n;
+    length -= n;
+    ptr += n;
+
+    while (length > 0) {
+      n = readWithTimeoutOrCallback(data, dataPtr, length);
+      dataPtr += n;
+      length -= n;
+      offset += n;
+    }
+  }
+
+  public void setTimeout(int timeoutms_) {
+    timeoutms = timeoutms_;
+  }
+
+  public void setBlockCallback(FdInStreamBlockCallback blockCallback_)
+  {
+    blockCallback = blockCallback_;
+    timeoutms = 0;
+  }
+
+  public final int pos() { return offset + ptr; }
+
+  public final void startTiming() {
+    timing = true;
+
+    // Carry over up to 1s worth of previous rate for smoothing.
+
+    if (timeWaitedIn100us > 10000) {
+      timedKbits = timedKbits * 10000 / timeWaitedIn100us;
+      timeWaitedIn100us = 10000;
+    }
+  }
+
+  public final void stopTiming() {
+    timing = false; 
+    if (timeWaitedIn100us < timedKbits/2)
+      timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
+  }
+
+  public final long kbitsPerSecond() {
+    return timedKbits * 10000 / timeWaitedIn100us;
+  }
+
+  public final long timeWaited() { return timeWaitedIn100us; }
+
+  protected int overrun(int itemSize, int nItems, boolean wait) {
+    if (itemSize > bufSize)
+      throw new Exception("FdInStream overrun: max itemSize exceeded");
+
+    if (end - ptr != 0)
+      System.arraycopy(b, ptr, b, 0, end - ptr);
+
+    offset += ptr;
+    end -= ptr;
+    ptr = 0;
+
+    int bytes_to_read;
+    while (end < itemSize) {
+      bytes_to_read = bufSize - end;
+      if (!timing) {
+        // When not timing, we must be careful not to read too much
+        // extra data into the buffer. Otherwise, the line speed
+        // estimation might stay at zero for a long time: All reads
+        // during timing=1 can be satisfied without calling
+        // readWithTimeoutOrCallback. However, reading only 1 or 2 bytes
+        // bytes is ineffecient.
+        bytes_to_read = Math.min(bytes_to_read, Math.max(itemSize*nItems, 8));
+      }
+      int n = readWithTimeoutOrCallback(b, end, bytes_to_read, wait);
+      if (n == 0) return 0;
+      end += n;
+    }
+
+    if (itemSize * nItems > end - ptr)
+      nItems = (end - ptr) / itemSize;
+
+    return nItems;
+  }
+
+  protected int readWithTimeoutOrCallback(byte[] buf, int bufPtr, int len, boolean wait) {
+    long before = 0;
+    long timeout;
+    if (timing)
+      before = System.nanoTime();
+
+    int n;
+    while (true) {
+    
+      if (!wait) {
+        timeout = 0;
+      } else if (timeoutms != -1) {
+        timeout = (long)timeoutms;
+      } else {
+        timeout = 0;
+      }
+
+      try {
+        n = fd.select(SelectionKey.OP_READ, timeoutms);
+      } catch (java.lang.Exception e) {
+        System.out.println(e.toString());
+        throw new Exception(e.toString());
+      }
+        
+          
+      if (n > 0) break;
+      if (!wait) return 0;
+      //if (blockCallback == null) throw TimedOut();
+    
+      blockCallback.blockCallback();
+    }
+
+    try {
+      n = fd.read(buf, bufPtr, len);
+    } catch (java.lang.Exception e) {
+      System.out.println("read:"+e.toString());
+      throw new Exception(e.toString());
+    }
+
+    if (n == 0) throw new EndOfStream();
+
+    if (timing) {
+      long after = System.nanoTime();
+      long newTimeWaited = (after - before) / 100000;
+      int newKbits = n * 8 / 1000;
+
+      // limit rate to between 10kbit/s and 40Mbit/s
+
+      if (newTimeWaited > newKbits*1000) {
+        newTimeWaited = newKbits*1000;
+      } else if (newTimeWaited < newKbits/4) {
+        newTimeWaited = newKbits/4;
+      }
+
+      timeWaitedIn100us += newTimeWaited;
+      timedKbits += newKbits;
+    }
+
+    return n;
+  }
+
+  private int readWithTimeoutOrCallback(byte[] buf, int bufPtr, int len) { 
+    return readWithTimeoutOrCallback(buf, bufPtr, len, true);
+  }
+
+  public FileDescriptor getFd() {
+    return fd;
+  }
+
+  public void setFd(FileDescriptor fd_) {
+    fd = fd_;
+  }
+
+  private int offset;
+  private int bufSize;
+  private FileDescriptor fd;
+  private FdInStreamBlockCallback blockCallback;
+  protected int timeoutms;
+
+  protected boolean timing;
+  protected long timeWaitedIn100us;
+  protected long timedKbits;
+}
diff --git a/java/com/tigervnc/rdr/FdInStreamBlockCallback.java b/java/com/tigervnc/rdr/FdInStreamBlockCallback.java
new file mode 100644 (file)
index 0000000..45d5d2a
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (C) 2010-2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+public interface FdInStreamBlockCallback {
+  abstract public void blockCallback();
+}
diff --git a/java/com/tigervnc/rdr/FdOutStream.java b/java/com/tigervnc/rdr/FdOutStream.java
new file mode 100644 (file)
index 0000000..d2e95ea
--- /dev/null
@@ -0,0 +1,177 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2012 TigerVnc Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+import com.tigervnc.network.*;
+import java.nio.channels.SelectionKey;
+
+public class FdOutStream extends OutStream {
+
+  static final int defaultBufSize = 8192;
+  static final int minBulkSize = 1024;
+
+  public FdOutStream(FileDescriptor fd_, boolean blocking_, int timeoutms_, int bufSize_)
+  {
+    fd = fd_;
+    blocking = blocking_;
+    timeoutms = timeoutms_;
+    bufSize = bufSize_;
+    b = new byte[bufSize];
+    offset = 0;
+    ptr = sentUpTo = start = 0;
+    end = start + bufSize;
+  }
+
+  public FdOutStream(FileDescriptor fd_) { this(fd_, false, 0, defaultBufSize); }
+
+  public void setTimeout(int timeoutms_) {
+    timeoutms = timeoutms_;
+  }
+
+  public void setBlocking(boolean blocking_) {
+    blocking = blocking_;
+  }
+
+  public int length() 
+  { 
+    return offset + ptr - sentUpTo; 
+  }
+
+  public void flush() 
+  {
+    int timeoutms_;
+
+    if (blocking)
+      timeoutms_ = timeoutms;
+    else
+      timeoutms_ = 0;
+
+    while (sentUpTo < ptr) {
+      int n = writeWithTimeout(b, sentUpTo, ptr - sentUpTo, timeoutms_);
+
+      // Timeout?
+      if (n == 0) {
+        // If non-blocking then we're done here
+        if (!blocking)
+          break;
+
+        // Otherwise try blocking (with possible timeout)
+        if ((timeoutms_ == 0) && (timeoutms != 0)) {
+          timeoutms_ = timeoutms;
+          break;
+        }
+
+        // Proper timeout
+        //throw TimedOut();
+      }
+
+      sentUpTo += n;
+      offset += n;
+    }
+
+    // Managed to flush everything?
+    if (sentUpTo == ptr)
+      ptr = sentUpTo = start;
+  }
+
+  private int writeWithTimeout(byte[] data, int dataPtr, int length, int timeoutms)
+  {
+    long timeout;
+    int n;
+
+    do {
+    
+      if (timeoutms != -1) {
+        timeout = (long)timeoutms;
+      } else {
+        timeout = 0;
+      }
+
+      try {
+        n = fd.select(SelectionKey.OP_WRITE, timeoutms);
+      } catch (java.lang.Exception e) {
+        System.out.println(e.toString());
+        throw new Exception(e.toString());
+      }
+          
+    } while (n < 0);
+
+    try {
+      n = fd.write(data, dataPtr, length);
+    } catch (java.lang.Exception e) {
+      System.out.println("read:"+e.toString());
+      throw new Exception(e.toString());
+    }
+    
+    return n;
+  }
+
+  protected int overrun(int itemSize, int nItems) 
+  {
+    if (itemSize > bufSize)
+      throw new Exception("FdOutStream overrun: max itemSize exceeded");
+
+    // First try to get rid of the data we have
+    flush();
+
+    // Still not enough space?
+    if (itemSize > end - ptr) {
+      // Can we shuffle things around?
+      // (don't do this if it gains us less than 25%)
+      if ((sentUpTo - start > bufSize / 4) &&
+          (itemSize < bufSize - (ptr - sentUpTo))) {
+        System.arraycopy(b, ptr, b, start, ptr - sentUpTo);
+        ptr = start + (ptr - sentUpTo);
+        sentUpTo = start;
+      } else {
+        // Have to get rid of more data, so turn off non-blocking
+        // for a bit...
+        boolean realBlocking;
+
+        realBlocking = blocking;
+        blocking = true;
+        flush();
+        blocking = realBlocking;
+      }
+    }
+
+    // Can we fit all the items asked for?
+    if (itemSize * nItems > end - ptr)
+      nItems = (end - ptr) / itemSize;
+
+    return nItems;
+  }
+
+  public FileDescriptor getFd() {
+    return fd;
+  }
+
+  public void setFd(FileDescriptor fd_) {
+    fd = fd_;
+  }
+
+  protected FileDescriptor fd;
+  protected boolean blocking;
+  protected int timeoutms;
+  protected int start;
+  protected int sentUpTo;
+  protected int offset;
+  protected int bufSize;
+}
index a18ea4edb790afcb8176e0542450662a60997ba0..8e58fbda07dd9ac243f9c088b0679a54df5e73b5 100644 (file)
@@ -23,6 +23,8 @@
 
 package com.tigervnc.rdr;
 
+import com.tigervnc.network.*;
+
 abstract public class InStream {
 
   // check() ensures there is buffer data for at least one item of size
diff --git a/java/com/tigervnc/rdr/JavaInStream.java b/java/com/tigervnc/rdr/JavaInStream.java
deleted file mode 100644 (file)
index faa968a..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * 
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
- * USA.
- */
-
-//
-// A JavaInStream reads from a java.io.InputStream
-//
-
-package com.tigervnc.rdr;
-
-public class JavaInStream extends InStream {
-
-  static final int defaultBufSize = 8192;
-  static final int minBulkSize = 1024;
-
-  public JavaInStream(java.io.InputStream jis_, int bufSize_) {
-    jis = jis_;
-    bufSize = bufSize_;
-    b = new byte[bufSize];
-    ptr = end = offset = 0;
-    timing = false;
-    timeWaitedIn100us = 5;
-    timedKbits = 0;
-  }
-
-  public JavaInStream(java.io.InputStream jis_) { this(jis_, defaultBufSize); }
-
-  public void readBytes(byte[] data, int dataPtr, int length) {
-    if (length < minBulkSize) {
-      super.readBytes(data, dataPtr, length);
-      return;
-    }
-
-    int n = end - ptr;
-    if (n > length) n = length;
-
-    System.arraycopy(b, ptr, data, dataPtr, n);
-    dataPtr += n;
-    length -= n;
-    ptr += n;
-
-    while (length > 0) {
-      n = read(data, dataPtr, length);
-      dataPtr += n;
-      length -= n;
-      offset += n;
-    }
-  }
-
-  public int pos() { return offset + ptr; }
-
-  public void startTiming() {
-    timing = true;
-
-    // Carry over up to 1s worth of previous rate for smoothing.
-
-    if (timeWaitedIn100us > 10000) {
-      timedKbits = timedKbits * 10000 / timeWaitedIn100us;
-      timeWaitedIn100us = 10000;
-    }
-  }
-
-  public void stopTiming() {
-    timing = false; 
-    if (timeWaitedIn100us < timedKbits/2)
-      timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
-  }
-
-  public long kbitsPerSecond() {
-    return timedKbits * 10000 / timeWaitedIn100us;
-  }
-
-  public long timeWaited() { return timeWaitedIn100us; }
-
-  protected int overrun(int itemSize, int nItems, boolean wait) {
-    if (itemSize > bufSize)
-      throw new Exception("JavaInStream overrun: max itemSize exceeded");
-
-    if (end - ptr != 0)
-      System.arraycopy(b, ptr, b, 0, end - ptr);
-
-    offset += ptr;
-    end -= ptr;
-    ptr = 0;
-
-    while (end < itemSize) {
-      int bytes_to_read = bufSize - end;
-
-      if (!timing) {
-        bytes_to_read = Math.min(bytes_to_read, Math.max(itemSize*nItems, 8));
-      }
-
-      int n = read(b, end, bytes_to_read, wait);
-
-      end += n;
-    }
-
-    if (itemSize * nItems > end)
-      nItems = end / itemSize;
-
-    return nItems;
-  }
-
-  private int read(byte[] buf, int bufPtr, int len, boolean wait) {
-      long before = 0;
-      if (timing)
-        before = System.nanoTime();
-
-      int n = -1;
-      try {
-        n = jis.read(buf, bufPtr, len);
-      } catch (java.io.IOException e) {
-        throw new IOException(e);
-      }
-
-      if (n < 0) throw new EndOfStream();
-      if (n == 0) return 0;
-
-      if (timing) {
-        long after = System.nanoTime();
-        long newTimeWaited = (after - before) / 100000;
-        int newKbits = n * 8 / 1000;
-
-        // limit rate to between 10kbit/s and 40Mbit/s
-
-        if (newTimeWaited > newKbits*1000) {
-          newTimeWaited = newKbits*1000;
-        } else if (newTimeWaited < newKbits/4) {
-          newTimeWaited = newKbits/4;
-        }
-
-        timeWaitedIn100us += newTimeWaited;
-        timedKbits += newKbits;
-      }
-
-      return n;
-
-  }
-  private int read(byte[] buf, int bufPtr, int len) { return read(buf, bufPtr, len, true); }
-
-  private java.io.InputStream jis;
-  private int offset;
-  private int bufSize;
-
-  boolean timing;
-  long timeWaitedIn100us;
-  long timedKbits;
-}
diff --git a/java/com/tigervnc/rdr/JavaOutStream.java b/java/com/tigervnc/rdr/JavaOutStream.java
deleted file mode 100644 (file)
index 015f81f..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * 
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
- * USA.
- */
-
-//
-// A JavaOutStream writes to a java.io.OutputStream
-//
-
-package com.tigervnc.rdr;
-
-public class JavaOutStream extends OutStream {
-
-  static final int defaultBufSize = 16384;
-  static final int minBulkSize = 1024;
-
-  public JavaOutStream(java.io.OutputStream jos_, int bufSize_) {
-    jos = jos_;
-    bufSize = bufSize_;
-    b = new byte[bufSize];
-    ptr = offset = start = 0;
-    end = start + bufSize;
-  }
-
-  public JavaOutStream(java.io.OutputStream jos) { this(jos, defaultBufSize); }
-
-  public int length() 
-  { 
-    return offset + ptr - start; 
-  }
-
-  public void flush() 
-  {
-    int sentUpTo = start;
-    while (sentUpTo < ptr) {
-      try {
-        jos.write(b, sentUpTo, ptr - sentUpTo);
-        sentUpTo += ptr - sentUpTo;
-        offset += ptr - sentUpTo;
-      } catch (java.io.IOException e) {
-        throw new IOException(e);
-      }
-    }
-    ptr = start;
-  }
-
-  protected int overrun(int itemSize, int nItems) 
-  {
-    if (itemSize > bufSize)
-      throw new Exception("JavaOutStream overrun: max itemSize exceeded");
-
-    flush();
-
-    if (itemSize * nItems > end - ptr)
-      nItems = (end - ptr) / itemSize;
-
-    return nItems;
-  }
-
-  private java.io.OutputStream jos;
-  private int start;
-  private int offset;
-  private int bufSize;
-}
index 0919453edd783f5fae6205a70aa43d9440e0f24c..b4698772be64867fd3d04a968ab0753d40798cf1 100644 (file)
@@ -23,6 +23,8 @@
 
 package com.tigervnc.rdr;
 
+import com.tigervnc.network.*;
+
 abstract public class OutStream {
 
   // check() ensures there is buffer space for at least one item of size
diff --git a/java/com/tigervnc/rdr/TLSException.java b/java/com/tigervnc/rdr/TLSException.java
new file mode 100644 (file)
index 0000000..1d7ff9d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2004 Red Hat Inc.
+ * Copyright (C) 2010-2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+class TLSException extends Exception {
+  public TLSException(String s, int n) {
+    // FIXME: append enumerated n
+    super(s);
+  }
+}
diff --git a/java/com/tigervnc/rdr/TLSInStream.java b/java/com/tigervnc/rdr/TLSInStream.java
new file mode 100644 (file)
index 0000000..f0e5fbe
--- /dev/null
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2005 Martin Koegler
+ * Copyright (C) 2010-2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import javax.net.ssl.*;
+
+import com.tigervnc.network.*;
+
+public class TLSInStream extends InStream {
+
+  static final int defaultBufSize = 16384;
+
+  public TLSInStream(InStream _in, SSLEngineManager _manager) {
+    in = (FdInStream)_in;
+    manager = _manager; 
+    offset = 0;
+    SSLSession session = manager.getSession();
+    bufSize = session.getApplicationBufferSize();
+    b = new byte[bufSize];
+    ptr = end = start = 0;
+  }
+
+  public final int pos() { 
+    return offset + ptr - start;
+  }
+
+  public final void startTiming() {
+    in.startTiming();  
+  }
+
+  public final void stopTiming() {
+    in.stopTiming();  
+  }
+
+  public final long kbitsPerSecond() {
+    return in.kbitsPerSecond();
+  }
+
+  public final long timeWaited() { 
+    return in.timeWaited();
+  }
+
+  protected final int overrun(int itemSize, int nItems, boolean wait) {
+    if (itemSize > bufSize)
+      throw new Exception("TLSInStream overrun: max itemSize exceeded");
+
+    if (end - ptr != 0)
+      System.arraycopy(b, ptr, b, 0, end - ptr);
+
+    offset += ptr - start;
+    end -= ptr - start;
+    ptr = start;
+
+    while (end < start + itemSize) {
+      int n = readTLS(b, end, start + bufSize - end, wait);
+      if (!wait && n == 0) 
+        return 0;
+      end += n;
+    }
+
+    if (itemSize * nItems > end - ptr)
+      nItems = (end - ptr) / itemSize;
+
+    return nItems;
+  }
+
+  protected int readTLS(byte[] buf, int bufPtr, int len, boolean wait) 
+  {
+    int n = -1;
+
+    //n = in.check(1, 1, wait);
+    //if (n == 0)
+    //  return 0;
+
+    try {
+      n = manager.read(buf, bufPtr, len);
+    } catch (java.io.IOException e) {
+      e.printStackTrace();
+    }
+
+    if (n < 0) throw new TLSException("readTLS", n);
+
+    return n;
+  }
+    
+  private SSLEngineManager manager;
+  private int offset;
+  private int start;
+  private int bufSize;
+  private FdInStream in;
+}
diff --git a/java/com/tigervnc/rdr/TLSOutStream.java b/java/com/tigervnc/rdr/TLSOutStream.java
new file mode 100644 (file)
index 0000000..8fa1576
--- /dev/null
@@ -0,0 +1,97 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2005 Martin Koegler
+ * Copyright (C) 2010-2012 TigerVNC Team
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.rdr;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import javax.net.ssl.*;
+
+import com.tigervnc.network.*;
+
+public class TLSOutStream extends OutStream {
+
+  static final int defaultBufSize = 16384;
+
+  public TLSOutStream(OutStream _out, SSLEngineManager _manager) {
+    manager = _manager;
+    out = (FdOutStream)_out;
+    SSLSession session = manager.getSession();
+    bufSize = session.getApplicationBufferSize();
+    b = new byte[bufSize];
+    ptr = offset = start = 0;
+    end = start + bufSize;
+  }
+
+  public int length() 
+  { 
+    return offset + ptr - start; 
+  }
+
+  public void flush() 
+  {
+    int sentUpTo = start;
+    while (sentUpTo < ptr) {
+      int n = writeTLS(b, sentUpTo, ptr - sentUpTo);
+      sentUpTo += n;
+      offset += n;
+    }
+
+    ptr = start;
+    //out.flush();
+  }
+
+  protected int overrun(int itemSize, int nItems) 
+  {
+    if (itemSize > bufSize)
+      throw new Exception("TLSOutStream overrun: max itemSize exceeded");
+
+    flush();
+
+    if (itemSize * nItems > end - ptr)
+      nItems = (end - ptr) / itemSize;
+
+    return nItems;
+  }
+
+  protected int writeTLS(byte[] data, int dataPtr, int length)
+  {
+    int n = 0;
+
+    try {
+      n = manager.write(data, dataPtr, length);
+    } catch (java.io.IOException e) {
+      throw new Exception(e.toString());
+    }
+    //if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN)
+    //  return 0;
+
+    //if (n < 0)
+    //  throw new TLSException("writeTLS", n);
+  
+    return n;
+  }
+
+  private SSLEngineManager manager;
+  private FdOutStream out;
+  private int start;
+  private int offset;
+  private int bufSize;
+}
index 8a477654b44119e840483f571fb7b0545cb3398c..0ec4404457b6887efec60f3dd7a9f573f060edab 100644 (file)
@@ -20,6 +20,7 @@ package com.tigervnc.rfb;
 
 import java.util.*;
 
+import com.tigervnc.network.*;
 import com.tigervnc.rdr.*;
 
 abstract public class CConnection extends CMsgHandler {
@@ -347,10 +348,6 @@ abstract public class CConnection extends CMsgHandler {
   int serverPort;
   boolean useProtocol3_3 = false;
   boolean clientSecTypeOrder;
-  public static java.net.Socket sock;
-
-  public static java.net.Socket getSocket() { return sock; }
-  public static void setSocket(java.net.Socket sock_) { sock = sock_; }
 
   static LogWriter vlog = new LogWriter("CConnection");
 }
index 2b8bf355aee9168134c7cae5119cd956443a6dd7..3d89fd32b503e67fc68a302715d24977c1ae7f8b 100644 (file)
@@ -1,5 +1,8 @@
 /*
- * Copyright (C) 2003 Sun Microsystems, Inc.
+ * Copyright (C) 2004 Red Hat Inc.
+ * Copyright (C) 2005 Martin Koegler
+ * Copyright (C) 2010 m-privacy GmbH
+ * Copyright (C) 2010-2012 TigerVNC Team
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +23,7 @@
 package com.tigervnc.rfb;
 
 import javax.net.ssl.*;
+import java.security.*;
 import java.security.cert.*;
 import java.security.KeyStore;
 import java.io.File;
@@ -31,6 +35,9 @@ import javax.swing.JOptionPane;
 
 import com.tigervnc.vncviewer.UserPrefs;
 import com.tigervnc.rdr.*;
+import com.tigervnc.network.*;
+import java.util.concurrent.Executors;
+import java.nio.channels.SelectionKey;
 
 public class CSecurityTLS extends CSecurity {
 
@@ -43,54 +50,24 @@ public class CSecurityTLS extends CSecurity {
 
   private void initGlobal() 
   {
-    try {
-      SSLSocketFactory sslfactory;
-      SSLContext ctx = SSLContext.getInstance("TLS");
-      if (anon) {
-        ctx.init(null, null, null);
-      } else {
-        TrustManager[] myTM = new TrustManager[] { 
-          new MyX509TrustManager() 
-        };
-        ctx.init (null, myTM, null);
-      }
-      sslfactory = ctx.getSocketFactory();
+    boolean globalInitDone = false;
+
+    if (!globalInitDone) {
       try {
-        ssl = (SSLSocket)sslfactory.createSocket(CConnection.sock,
-                                                 CConnection.sock.getInetAddress().getHostName(),
-                                                 CConnection.sock.getPort(), true);
-      } catch (java.io.IOException e) { 
+        ctx = SSLContext.getInstance("TLS");
+      } catch(NoSuchAlgorithmException e) {
         throw new Exception(e.toString());
       }
 
-      if (anon) {
-        String[] supported;
-        ArrayList<String> enabled = new ArrayList<String>();
-
-        supported = ssl.getSupportedCipherSuites();
-
-        for (int i = 0; i < supported.length; i++)
-          if (supported[i].matches("TLS_DH_anon.*"))
-                 enabled.add(supported[i]);
-
-        ssl.setEnabledCipherSuites(enabled.toArray(new String[0]));
-      } else {
-        ssl.setEnabledCipherSuites(ssl.getSupportedCipherSuites());
-      }
-
-      ssl.setEnabledProtocols(new String[]{"SSLv3","TLSv1"});
-      ssl.addHandshakeCompletedListener(new MyHandshakeListener());
-    }
-    catch (java.security.GeneralSecurityException e)
-    {
-      vlog.error ("TLS handshake failed " + e.toString ());
-      return;
+      globalInitDone = true;
     }
   }
 
   public CSecurityTLS(boolean _anon) 
   {
     anon = _anon;
+    session = null;
+    
     setDefaults();
     cafile = x509ca.getData(); 
     crlfile = x509crl.getData(); 
@@ -116,40 +93,98 @@ public class CSecurityTLS extends CSecurity {
       x509crl.setDefaultStr(crlDefault);
   }
 
+// FIXME:
+// Need to shutdown the connection cleanly
+
+// FIXME?
+// add a finalizer method that calls shutdown
+
   public boolean processMsg(CConnection cc) {
-    is = cc.getInStream();
+    is = (FdInStream)cc.getInStream();
+    os = (FdOutStream)cc.getOutStream();
+    client = cc;
 
     initGlobal();
 
-    if (!is.checkNoWait(1))
-      return false;
-
-    if (is.readU8() == 0) {
-      int result = is.readU32();
-      String reason;
-      if (result == Security.secResultFailed ||
-          result == Security.secResultTooMany)
-        reason = is.readString();
-      else
-        reason = new String("Authentication failure (protocol error)");
-      throw new AuthFailureException(reason);
+    if (session == null) {
+      if (!is.checkNoWait(1))
+        return false;
+
+      if (is.readU8() == 0) {
+        int result = is.readU32();
+        String reason;
+        if (result == Security.secResultFailed ||
+            result == Security.secResultTooMany)
+          reason = is.readString();
+        else
+          reason = new String("Authentication failure (protocol error)");
+        throw new AuthFailureException(reason);
+      }
+      
+      setParam();
+
     }
 
-    // SSLSocket.getSession blocks until the handshake is complete
-    session = ssl.getSession();
-    if (!session.isValid())
-      throw new Exception("TLS Handshake failed!");
+    try {
+      manager = new SSLEngineManager(engine, is, os);
+    } catch(java.lang.Exception e) {
+      System.out.println(e.toString());
+    }
 
     try {
-      cc.setStreams(new JavaInStream(ssl.getInputStream()),
-                               new JavaOutStream(ssl.getOutputStream()));
-    } catch (java.io.IOException e) { 
-      throw new Exception("Failed to set streams");
+      manager.doHandshake();
+    } catch (java.lang.Exception e) {
+      throw new Exception(e.toString());
     }
 
+    //checkSession();
+
+    cc.setStreams(new TLSInStream(is, manager),
+                             new TLSOutStream(os, manager));
     return true;
   }
 
+  private void setParam() {
+
+    if (anon) {
+      try {
+        ctx.init(null, null, null);
+      } catch(KeyManagementException e) {
+        throw new AuthFailureException(e.toString());
+      }
+    } else {
+      try {
+        TrustManager[] myTM = new TrustManager[] { 
+          new MyX509TrustManager() 
+        };
+        ctx.init (null, myTM, null);
+      } catch (java.security.GeneralSecurityException e) {
+        throw new AuthFailureException(e.toString());
+      }
+    }
+    SSLSocketFactory sslfactory = ctx.getSocketFactory();
+    engine = ctx.createSSLEngine(client.getServerName(),
+                                 client.getServerPort());
+    engine.setUseClientMode(true);
+
+    if (anon) {
+      String[] supported;
+      ArrayList<String> enabled = new ArrayList<String>();
+
+      supported = engine.getSupportedCipherSuites();
+
+      for (int i = 0; i < supported.length; i++)
+        if (supported[i].matches("TLS_DH_anon.*"))
+                 enabled.add(supported[i]);
+
+      engine.setEnabledCipherSuites(enabled.toArray(new String[0]));
+    } else {
+      engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
+    }
+
+    engine.setEnabledProtocols(new String[]{"SSLv3","TLSv1"});
+  }
+
   class MyHandshakeListener implements HandshakeCompletedListener {
    public void handshakeCompleted(HandshakeCompletedEvent e) {
      vlog.info("Handshake succesful!");
@@ -233,16 +268,20 @@ public class CSecurityTLS extends CSecurity {
   public final String description() 
     { return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; }
 
-
-  //protected void setParam();
   //protected void checkSession();
-  protected CConnection cc;
+  protected CConnection client;
 
-  private boolean anon;
+
+
+  private SSLContext ctx;
   private SSLSession session;
+  private SSLEngine engine;
+  private SSLEngineManager manager;
+  private boolean anon;
+
   private String cafile, crlfile;
-  private InStream is;
-  private SSLSocket ssl;
+  private FdInStream is;
+  private FdOutStream os;
 
   static LogWriter vlog = new LogWriter("CSecurityTLS");
 }
index ae03b524dbc716449810adf5a272c572d91aea40..a1aa0d43e8b366991d31475b39fc1bdd27b0c67e 100644 (file)
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2012 TigerVNC Team
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -45,7 +46,9 @@ import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 import javax.swing.*;
 import javax.swing.ImageIcon;
+import java.net.InetSocketAddress;
 import java.net.URL;
+import java.net.SocketException;
 import java.util.*;
 
 import com.tigervnc.rdr.*;
@@ -53,6 +56,8 @@ import com.tigervnc.rfb.*;
 import com.tigervnc.rfb.Exception;
 import com.tigervnc.rfb.Point;
 import com.tigervnc.rfb.Rect;
+import com.tigervnc.network.Socket;
+import com.tigervnc.network.TcpSocket;
 
 class ViewportFrame extends JFrame
 {
@@ -68,7 +73,7 @@ class ViewportFrame extends JFrame
     });
     addWindowListener(new WindowAdapter() {
       public void windowClosing(WindowEvent e) {
-        cc.close();
+        cc.deleteWindow();
       }
     });
     addComponentListener(new ComponentAdapter() {
@@ -141,12 +146,12 @@ class ViewportFrame extends JFrame
 }
 
 public class CConn extends CConnection
-  implements UserPasswdGetter, UserMsgBox, OptionsDialogCallback
+  implements UserPasswdGetter, UserMsgBox, OptionsDialogCallback, FdInStreamBlockCallback
 {
   ////////////////////////////////////////////////////////////////////
   // The following methods are all called from the RFB thread
 
-  public CConn(VncViewer viewer_, java.net.Socket sock_, 
+  public CConn(VncViewer viewer_, Socket sock_, 
                String vncServerName, boolean reverse) 
   {
     serverHost = null; serverPort = 0; sock = sock_; viewer = viewer_; 
@@ -184,7 +189,7 @@ public class CConn extends CConnection
     initMenu();
 
     if (sock != null) {
-      String name = sock.getRemoteSocketAddress()+"::"+sock.getPort();
+      String name = sock.getPeerEndpoint();
       vlog.info("Accepted connection from "+name);
     } else {
       if (vncServerName != null) {
@@ -201,26 +206,17 @@ public class CConn extends CConnection
       }
 
       try {
-        sock = new java.net.Socket(serverHost, serverPort);
-      } catch (java.io.IOException e) { 
+        sock = new TcpSocket(serverHost, serverPort);
+      } catch (java.lang.Exception e) {
         throw new Exception(e.toString());
       }
       vlog.info("connected to host "+serverHost+" port "+serverPort);
     }
 
-    sameMachine = (sock.getLocalSocketAddress() == sock.getRemoteSocketAddress());
-    try {
-      sock.setTcpNoDelay(true);
-      sock.setTrafficClass(0x10);
-      setServerName(serverHost);
-      jis = new JavaInStream(sock.getInputStream());
-      jos = new JavaOutStream(sock.getOutputStream());
-    } catch (java.net.SocketException e) {
-      throw new Exception(e.toString());
-    } catch (java.io.IOException e) { 
-      throw new Exception(e.toString());
-    }
-    setStreams(jis, jos);
+    sameMachine = sock.sameMachine();
+    sock.inStream().setBlockCallback(this);
+    setServerName(serverHost);
+    setStreams(sock.inStream(), sock.outStream());
     initialiseProtocol();
   }
 
@@ -236,8 +232,20 @@ public class CConn extends CConnection
     if (viewport != null)
       viewport.dispose();
     viewport = null;
+    System.exit(1);
   } 
 
+  // blockCallback() is called when reading from the socket would block.
+  public void blockCallback() {
+    try {
+      synchronized(this) {
+        wait(1);
+      }
+    } catch (java.lang.InterruptedException e) {
+      throw new Exception(e.toString());
+    }
+  }  
+
   // getUserPasswd() is called by the CSecurity object when it needs us to read
   // a password from the user.
 
@@ -342,18 +350,14 @@ public class CConn extends CConnection
   public void clientRedirect(int port, String host, 
                              String x509subject) {
     try {
-      getSocket().close();
+      sock.close();
       setServerPort(port);
-      sock = new java.net.Socket(host, port);
-      sock.setTcpNoDelay(true);
-      sock.setTrafficClass(0x10);
-      setSocket(sock);
+      sock = new TcpSocket(host, port);
       vlog.info("Redirected to "+host+":"+port);
-      setStreams(new JavaInStream(sock.getInputStream()),
-                 new JavaOutStream(sock.getOutputStream()));
+      setStreams(sock.inStream(), sock.outStream());
       initialiseProtocol();
-    } catch (java.io.IOException e) {
-      e.printStackTrace();
+    } catch (java.lang.Exception e) {
+      throw new Exception(e.toString());
     }
   }
 
@@ -455,14 +459,14 @@ public class CConn extends CConnection
   // avoid skewing the bandwidth estimation as a result of the server
   // being slow or the network having high latency
   public void beginRect(Rect r, int encoding) {
-    ((JavaInStream)getInStream()).startTiming();
+    sock.inStream().startTiming();
     if (encoding != Encodings.encodingCopyRect) {
       lastServerEncoding = encoding;
     }
   }
 
   public void endRect(Rect r, int encoding) {
-    ((JavaInStream)getInStream()).stopTiming();
+    sock.inStream().stopTiming();
   }
 
   public void fillRect(Rect r, int p) {
@@ -579,8 +583,8 @@ public class CConn extends CConnection
   //         with something more intelligent at the server end.
   //
   private void autoSelectFormatAndEncoding() {
-    long kbitsPerSecond = ((JavaInStream)getInStream()).kbitsPerSecond();
-    long timeWaited = ((JavaInStream)getInStream()).timeWaited();
+    long kbitsPerSecond = sock.inStream().kbitsPerSecond();
+    long timeWaited = sock.inStream().timeWaited();
     boolean newFullColour = fullColour;
     int newQualityLevel = cp.qualityLevel;
 
@@ -673,12 +677,8 @@ public class CConn extends CConnection
 
   // close() closes the socket, thus waking up the RFB thread.
   public void close() {
-    try {
-      shuttingDown = true;
-      sock.close();
-    } catch (java.io.IOException e) {
-      e.printStackTrace();
-    }
+    shuttingDown = true;
+    sock.shutdown();
   }
 
   // Menu callbacks.  These are guaranteed only to be called after serverInit()
@@ -718,13 +718,13 @@ public class CConn extends CConnection
   void showInfo() {
     JOptionPane.showMessageDialog(viewport,
       "Desktop name: "+cp.name()+"\n"
-      +"Host: "+serverHost+":"+sock.getPort()+"\n"
+      +"Host: "+serverHost+":"+serverPort+"\n"
       +"Size: "+cp.width+"x"+cp.height+"\n"
       +"Pixel format: "+desktop.getPF().print()+"\n"
       +"(server default "+serverPF.print()+")\n"
       +"Requested encoding: "+Encodings.encodingName(currentEncoding)+"\n"
       +"Last used encoding: "+Encodings.encodingName(lastServerEncoding)+"\n"
-      +"Line speed estimate: "+((JavaInStream)getInStream()).kbitsPerSecond()+" kbit/s"+"\n"
+      +"Line speed estimate: "+sock.inStream().kbitsPerSecond()+" kbit/s"+"\n"
       +"Protocol version: "+cp.majorVersion+"."+cp.minorVersion+"\n"
       +"Security method: "+Security.secTypeName(csecurity.getType())
        +" ["+csecurity.description()+"]",
@@ -1276,8 +1276,6 @@ public class CConn extends CConnection
   }
 
   // the following never change so need no synchronization:
-  JavaInStream jis;
-  JavaOutStream jos;
 
 
   // viewer object is only ever accessed by the GUI thread so needs no
@@ -1319,6 +1317,7 @@ public class CConn extends CConnection
 
   public String serverHost;
   public int serverPort;
+  public Socket sock;
   public int menuKey;
   PixelFormat serverPF;
   ViewportFrame viewport;
index 97b9427cfc0d5198707ea009aaf3d5e840b58bf0..c080f98c38cce08fcfbd69af2626cdd220ba0b1d 100644 (file)
@@ -34,14 +34,13 @@ import java.awt.Image;
 import java.io.InputStream;
 import java.io.IOException;
 import java.lang.Character;
-import java.net.ServerSocket;
-import java.net.Socket;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 import javax.swing.*;
 
 import com.tigervnc.rdr.*;
 import com.tigervnc.rfb.*;
+import com.tigervnc.network.*;
 
 public class VncViewer extends java.applet.Applet implements Runnable
 {
@@ -193,31 +192,32 @@ public class VncViewer extends java.applet.Applet implements Runnable
   public void run() {
     CConn cc = null;
     Socket sock = null;
+
     if (listenMode.getValue()) {
       int port = 5500;
-      ServerSocket listener = null;
+
       if (vncServerName.getValue() != null && 
           Character.isDigit(vncServerName.getValue().charAt(0)))
         port = Integer.parseInt(vncServerName.getValue());
 
+      TcpListener listener = null;
       try {
-        listener = new ServerSocket(port);
-      } catch (IOException e) {
-        System.out.println("Could not listen on port: "+port);
-        System.exit(-1);
+        listener = new TcpListener(null, port);
+      } catch (java.lang.Exception e) {
+        System.out.println(e.toString());
+        System.exit(1);
       }
 
       vlog.info("Listening on port "+port);
 
-      try {
+      while (true) {
         sock = listener.accept();
-        listener.close();
-      } catch (IOException e) {
-        System.out.println("Accept failed: "+port);
-        System.exit(-1);
+        if (sock != null)
+          break;
+        //listener.close();
       }
-      
     }
+
     try {
       cc = new CConn(this, sock, vncServerName.getValue(), false);
       while (true)