/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * Copyright (C) 2012 Brian P. Hinz * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package com.tigervnc.network; import com.tigervnc.rdr.FdInStream; import com.tigervnc.rdr.FdOutStream; import com.tigervnc.rdr.Exception; import com.tigervnc.rfb.LogWriter; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; import java.nio.*; import java.nio.channels.*; import java.util.Set; import java.util.Iterator; 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); 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(Exception e) { throw new SocketException("unable to create socket: "+e.toString()); } /* Attempt to connect to the remote host */ try { result = sock.connect(new InetSocketAddress(addr, port)); Selector selector = Selector.open(); SelectionKey connect_key = sock.socket().getChannel().register(selector, SelectionKey.OP_CONNECT); // Try for the connection for 250ms while (selector.select(250) > 0) { while (!result) { Set keys = selector.selectedKeys(); Iterator i = keys.iterator(); while (i.hasNext()) { SelectionKey key = (SelectionKey)i.next(); // Remove the current key i.remove(); // Attempt a connection if (key.isConnectable()) { if (sock.isConnectionPending()) sock.finishConnect(); result = true; } } } } if (!result) throw new SocketException("unable to connect to socket: Host is down"); } catch(java.io.IOException e) { throw new SocketException("unable to connect:"+e.getMessage()); } // 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.getMessage()); } } public int getMyPort() { return getSockPort(); } public String getPeerAddress() { InetAddress peer = ((SocketDescriptor)getFd()).socket().getInetAddress(); if (peer != null) return peer.getHostAddress(); return ""; } public String getPeerName() { InetAddress peer = ((SocketDescriptor)getFd()).socket().getInetAddress(); if (peer != null) return peer.getHostName(); return ""; } public int getPeerPort() { int port = ((SocketDescriptor)getFd()).socket().getPort(); return port; } public String getPeerEndpoint() { String address = getPeerAddress(); int port = getPeerPort(); return address+"::"+port; } public boolean sameMachine() throws Exception { try { SocketAddress peeraddr = ((SocketDescriptor)getFd()).getRemoteAddress(); SocketAddress myaddr = ((SocketDescriptor)getFd()).getLocalAddress(); return myaddr.equals(peeraddr); } catch (IOException e) { throw new Exception(e.getMessage()); } } public void shutdown() throws Exception { super.shutdown(); try { ((SocketDescriptor)getFd()).shutdown(); } catch (IOException e) { throw new Exception(e.getMessage()); } } public void close() throws IOException { ((SocketDescriptor)getFd()).close(); } public static boolean enableNagles(SocketDescriptor sock, boolean enable) { try { sock.channel.socket().setTcpNoDelay(!enable); } catch(java.net.SocketException e) { vlog.error("unable to setsockopt TCP_NODELAY: "+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 ((SocketDescriptor)getFd()).socket().getLocalPort(); } /* Tunnelling support. */ public static int findFreeTcpPort() { java.net.ServerSocket sock; int port; try { sock = new java.net.ServerSocket(0); port = sock.getLocalPort(); sock.close(); } catch (java.io.IOException e) { throw new SocketException("unable to create socket: "+e.toString()); } return port; } private boolean closeFd; static LogWriter vlog = new LogWriter("TcpSocket"); }