diff options
author | Brian Hinz <bphinz@users.sourceforge.net> | 2012-02-03 05:38:23 +0000 |
---|---|---|
committer | Brian Hinz <bphinz@users.sourceforge.net> | 2012-02-03 05:38:23 +0000 |
commit | 0eaec7d2ad8d84cecb13032a6968c99eaab9a971 (patch) | |
tree | 8b20e49976145a23dcb3c7595a58829e873c51a5 /java/com/tigervnc/network | |
parent | 311a2b47cb39567953dad8170b63f3bb4eb7639e (diff) | |
download | tigervnc-0eaec7d2ad8d84cecb13032a6968c99eaab9a971.tar.gz tigervnc-0eaec7d2ad8d84cecb13032a6968c99eaab9a971.zip |
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
Diffstat (limited to 'java/com/tigervnc/network')
-rw-r--r-- | java/com/tigervnc/network/FileDescriptor.java | 31 | ||||
-rw-r--r-- | java/com/tigervnc/network/SSLEngineManager.java | 291 | ||||
-rw-r--r-- | java/com/tigervnc/network/Socket.java | 109 | ||||
-rw-r--r-- | java/com/tigervnc/network/SocketDescriptor.java | 204 | ||||
-rw-r--r-- | java/com/tigervnc/network/SocketListener.java | 46 | ||||
-rw-r--r-- | java/com/tigervnc/network/TcpListener.java | 168 | ||||
-rw-r--r-- | java/com/tigervnc/network/TcpSocket.java | 201 |
7 files changed, 1050 insertions, 0 deletions
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(); // <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 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<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 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); +} +*/ + |