From: Brian Hinz Date: Fri, 3 Feb 2012 05:38:23 +0000 (+0000) Subject: Replace all stream-based IO with non-blocking NIO-based implementation. Still a... X-Git-Tag: v1.2.90~233 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0eaec7d2ad8d84cecb13032a6968c99eaab9a971;p=tigervnc.git Replace all stream-based IO with non-blocking NIO-based implementation. Still a fair amount of cleanup to do, particularly in the SSL handler, which is not very robust, and exception handling in general. All core functionality appears to be working fine though. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4842 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt index f0a884b8..f8364fba 100644 --- a/java/CMakeLists.txt +++ b/java/CMakeLists.txt @@ -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 index 00000000..e2d04fab --- /dev/null +++ b/java/com/tigervnc/network/FileDescriptor.java @@ -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 index 00000000..0f5ad66a --- /dev/null +++ b/java/com/tigervnc/network/SSLEngineManager.java @@ -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 index 00000000..212d130d --- /dev/null +++ b/java/com/tigervnc/network/Socket.java @@ -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(); //
:: + + // 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 index 00000000..375b0335 --- /dev/null +++ b/java/com/tigervnc/network/SocketDescriptor.java @@ -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 index 00000000..cb78388e --- /dev/null +++ b/java/com/tigervnc/network/SocketListener.java @@ -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 index 00000000..63a2ff19 --- /dev/null +++ b/java/com/tigervnc/network/TcpListener.java @@ -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* 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 index 00000000..926aa975 --- /dev/null +++ b/java/com/tigervnc/network/TcpSocket.java @@ -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 index 00000000..c638f4b2 --- /dev/null +++ b/java/com/tigervnc/rdr/FdInStream.java @@ -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 index 00000000..45d5d2ae --- /dev/null +++ b/java/com/tigervnc/rdr/FdInStreamBlockCallback.java @@ -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 index 00000000..d2e95ea0 --- /dev/null +++ b/java/com/tigervnc/rdr/FdOutStream.java @@ -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; +} diff --git a/java/com/tigervnc/rdr/InStream.java b/java/com/tigervnc/rdr/InStream.java index a18ea4ed..8e58fbda 100644 --- a/java/com/tigervnc/rdr/InStream.java +++ b/java/com/tigervnc/rdr/InStream.java @@ -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 index faa968ac..00000000 --- a/java/com/tigervnc/rdr/JavaInStream.java +++ /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 index 015f81f5..00000000 --- a/java/com/tigervnc/rdr/JavaOutStream.java +++ /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; -} diff --git a/java/com/tigervnc/rdr/OutStream.java b/java/com/tigervnc/rdr/OutStream.java index 0919453e..b4698772 100644 --- a/java/com/tigervnc/rdr/OutStream.java +++ b/java/com/tigervnc/rdr/OutStream.java @@ -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 index 00000000..1d7ff9d7 --- /dev/null +++ b/java/com/tigervnc/rdr/TLSException.java @@ -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 index 00000000..f0e5fbe0 --- /dev/null +++ b/java/com/tigervnc/rdr/TLSInStream.java @@ -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 index 00000000..8fa15762 --- /dev/null +++ b/java/com/tigervnc/rdr/TLSOutStream.java @@ -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; +} diff --git a/java/com/tigervnc/rfb/CConnection.java b/java/com/tigervnc/rfb/CConnection.java index 8a477654..0ec44044 100644 --- a/java/com/tigervnc/rfb/CConnection.java +++ b/java/com/tigervnc/rfb/CConnection.java @@ -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"); } diff --git a/java/com/tigervnc/rfb/CSecurityTLS.java b/java/com/tigervnc/rfb/CSecurityTLS.java index 2b8bf355..3d89fd32 100644 --- a/java/com/tigervnc/rfb/CSecurityTLS.java +++ b/java/com/tigervnc/rfb/CSecurityTLS.java @@ -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 enabled = new ArrayList(); - - 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 enabled = new ArrayList(); + + 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"); } diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java index ae03b524..a1aa0d43 100644 --- a/java/com/tigervnc/vncviewer/CConn.java +++ b/java/com/tigervnc/vncviewer/CConn.java @@ -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; diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java index 97b9427c..c080f98c 100644 --- a/java/com/tigervnc/vncviewer/VncViewer.java +++ b/java/com/tigervnc/vncviewer/VncViewer.java @@ -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)