Index: invirt-vnc-client/InvirtTrustManager.java =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ invirt-vnc-client/InvirtTrustManager.java 2008-10-31 06:09:10.000000000 -0400 @@ -0,0 +1,123 @@ +// This code is based on http://svntrac.hanhuy.com/repo/browser/hanhuy/trunk/cms/src/com/hanhuy/ria/client/RIATrustManager.java +/* + * Copyright 2006 Perry Nguyen + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +public class InvirtTrustManager implements X509TrustManager { + private X509TrustManager trustManager; + private final static char[] KEY_STORE_PASSWORD = + { 'f', 'o', 'o', 'b', 'a', 'r' }; + private final static String KEY_STORE_RESOURCE = + "trust.store"; + + private KeyStore loadKeyStore() throws Exception { + InputStream in = getClass().getClassLoader().getResourceAsStream( + KEY_STORE_RESOURCE); + KeyStore ks = null; + try { + if (in == null) { + //log.severe("Unable to open KeyStore"); + throw new NullPointerException(); + } + ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(in, KEY_STORE_PASSWORD); + /*if (log.isLoggable(Level.FINEST)) { + for (Enumeration aliases = ks.aliases(); + aliases.hasMoreElements();) { + String alias = aliases.nextElement(); + log.finest("ALIAS: " + alias); + } + }*/ + } catch (NoSuchAlgorithmException e) { + throwError(e); + } catch (CertificateException e) { + throwError(e); + } catch (IOException e) { + throwError(e); + } catch (KeyStoreException e) { + throwError(e); + } finally { + try { + if (in != null) + in.close(); + } + catch (IOException e) { } // ignore + } + return ks; + } + private void createTrustManager() { + try { + try { + KeyStore keystore = loadKeyStore(); + TrustManagerFactory factory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + factory.init(keystore); + TrustManager[] trustManagers = factory.getTrustManagers(); + if (trustManagers.length == 0) + throw new IllegalStateException("No trust manager found"); + setTrustManager((X509TrustManager) trustManagers[0]); + } catch (NoSuchAlgorithmException e) { + throwError(e); + } catch (KeyStoreException e) { + throwError(e); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + private void throwError(Exception e) throws Exception { + //HttpClientError error = new HttpClientError(e.getMessage()); + //error.initCause(e); + throw e; + } + public X509TrustManager getTrustManager() { + if (trustManager == null) + createTrustManager(); + return trustManager; + } + + public void setTrustManager(X509TrustManager trustManager) { + this.trustManager = trustManager; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + getTrustManager().checkClientTrusted(chain, authType); + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + getTrustManager().checkServerTrusted(chain, authType); + + } + + public X509Certificate[] getAcceptedIssuers() { + return getTrustManager().getAcceptedIssuers(); + } + +} \ No newline at end of file Index: invirt-vnc-client/Makefile =================================================================== --- invirt-vnc-client.orig/Makefile 2008-10-31 06:09:10.000000000 -0400 +++ invirt-vnc-client/Makefile 2008-10-31 06:09:10.000000000 -0400 @@ -17,8 +17,10 @@ DesCipher.class CapabilityInfo.class CapsContainer.class \ RecordingFrame.class SessionRecorder.class \ SocketFactory.class HTTPConnectSocketFactory.class \ + VNCProxyConnectSocketFactory.class VNCProxyConnectSocket.class \ HTTPConnectSocket.class ReloginPanel.class \ - InStream.class MemInStream.class ZlibInStream.class + InStream.class MemInStream.class ZlibInStream.class \ + VNCProxyConnectSocketWrapper.class SocketWrapper.class SocketWrapper\$$WrappingSocketImpl.class InvirtTrustManager.class SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \ VncCanvas2.java \ @@ -26,8 +28,10 @@ DesCipher.java CapabilityInfo.java CapsContainer.java \ RecordingFrame.java SessionRecorder.java \ SocketFactory.java HTTPConnectSocketFactory.java \ + VNCProxyConnectSocketFactory.java VNCProxyConnectSocket.java \ HTTPConnectSocket.java ReloginPanel.java \ - InStream.java MemInStream.java ZlibInStream.java + InStream.java MemInStream.java ZlibInStream.java \ + VNCProxyConnectSocketWrapper.java SocketWrapper.java InvirtTrustManager.java all: $(CLASSES) $(ARCHIVE) Index: invirt-vnc-client/RfbProto.java =================================================================== --- invirt-vnc-client.orig/RfbProto.java 2007-04-26 22:36:00.000000000 -0400 +++ invirt-vnc-client/RfbProto.java 2008-10-31 06:09:10.000000000 -0400 @@ -208,11 +208,13 @@ port = p; if (viewer.socketFactory == null) { + System.out.println("Null socketFactory"); sock = new Socket(host, port); } else { try { Class factoryClass = Class.forName(viewer.socketFactory); SocketFactory factory = (SocketFactory)factoryClass.newInstance(); + System.out.println("Using socketFactory " + factory); if (viewer.inAnApplet) sock = factory.createSocket(host, port, viewer); else @@ -236,7 +238,7 @@ try { sock.close(); closed = true; - System.out.println("RFB socket closed"); + System.out.println("RFB socket closed " + sock); if (rec != null) { rec.close(); rec = null; Index: invirt-vnc-client/SocketWrapper.java =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ invirt-vnc-client/SocketWrapper.java 2008-10-31 06:09:10.000000000 -0400 @@ -0,0 +1,263 @@ +/* + * Written by Dawid Kurzyniec and released to the public domain, as explained + * at http://creativecommons.org/licenses/publicdomain + */ +// Upstream is at http://www.dcl.mathcs.emory.edu/downloads/h2o/doc/api/edu/emory/mathcs/util/net/SocketWrapper.html + +//package edu.emory.mathcs.util.net; + +import java.io.*; +import java.net.*; +import java.nio.channels.*; + +/** + * Wrapper for sockets which enables to add functionality in subclasses + * on top of existing, connected sockets. It is useful when direct subclassing + * of delegate socket class is not possible, e.g. if the delegate socket is + * created by a library. Possible usage example is socket factory chaining. + * This class delegates all socket-related requests to the wrapped delegate, + * as of JDK 1.4. + * + * @author Dawid Kurzyniec + * @version 1.4 + */ +public abstract class SocketWrapper extends Socket { + + /** + * the wrapped delegate socket. + */ + protected final Socket delegate; + + /** + * Creates new socket wrapper for a given socket. The delegate + * must be connected and bound and it must not be closed. + * @param delegate the delegate socket to wrap + * @throws SocketException if the delegate socket is closed, not bound, + * or not connected + */ + protected SocketWrapper(Socket delegate) throws SocketException { + super(new WrappingSocketImpl(delegate)); + this.delegate = delegate; + System.out.println("Creating SocketWrapper $Rev$"); + } + + public SocketChannel getChannel() { + return delegate.getChannel(); + } + + /** + * Returns true, indicating that the socket is bound. + * + * @return true + */ + public boolean isBound() { + return true; + } + + public boolean isClosed() { + return super.isClosed() || delegate.isClosed(); + } + + /** + * Returns true, indicating that the socket is connected. + * + * @return true + */ + public boolean isConnected() { + return true; + } + + public boolean isInputShutdown() { + return super.isInputShutdown() || delegate.isInputShutdown(); + } + + public boolean isOutputShutdown() { + return super.isInputShutdown() || delegate.isOutputShutdown(); + } + + private static class WrappingSocketImpl extends SocketImpl { + private final Socket delegate; + WrappingSocketImpl(Socket delegate) throws SocketException { + if (delegate == null) { + throw new NullPointerException(); + } + if (delegate.isClosed()) { + throw new SocketException("Delegate server socket is closed"); + } + if (!(delegate.isBound())) { + throw new SocketException("Delegate server socket is not bound"); + } + if (!(delegate.isConnected())) { + throw new SocketException("Delegate server socket is not connected"); + } + this.delegate = delegate; + } + + protected void create(boolean stream) {} + + protected void connect(String host, int port) { + // delegate is always connected, thus this method is never called + throw new UnsupportedOperationException(); + } + + protected void connect(InetAddress address, int port) { + // delegate is always connected, thus this method is never called + throw new UnsupportedOperationException(); + } + + protected void connect(SocketAddress address, int timeout) { + // delegate is always connected, thus this method is never called + throw new UnsupportedOperationException(); + } + + protected void bind(InetAddress host, int port) { + // delegate is always bound, thus this method is never called + throw new UnsupportedOperationException(); + } + + protected void listen(int backlog) { + // this wrapper is never used by a ServerSocket + throw new UnsupportedOperationException(); + } + + protected void accept(SocketImpl s) { + // this wrapper is never used by a ServerSocket + throw new UnsupportedOperationException(); + } + + protected InputStream getInputStream() throws IOException { + return delegate.getInputStream(); + } + + protected OutputStream getOutputStream() throws IOException { + return delegate.getOutputStream(); + } + + protected int available() throws IOException { + return getInputStream().available(); + } + + protected void close() throws IOException { + System.out.println("Calling delegate.close"); + delegate.close(); + } + + protected void shutdownInput() throws IOException { + delegate.shutdownInput(); + } + + protected void shutdownOutput() throws IOException { + delegate.shutdownOutput(); + } + + protected FileDescriptor getFileDescriptor() { + // this wrapper is never used by a ServerSocket + throw new UnsupportedOperationException(); + } + + protected InetAddress getInetAddress() { + return delegate.getInetAddress(); + } + + protected int getPort() { + return delegate.getPort(); + } + + protected boolean supportsUrgentData() { + return false; // must be overridden in sub-class + } + + protected void sendUrgentData (int data) throws IOException { + delegate.sendUrgentData(data); + } + + protected int getLocalPort() { + return delegate.getLocalPort(); + } + + public Object getOption(int optID) throws SocketException { + switch (optID) { + case SocketOptions.IP_TOS: + return new Integer(delegate.getTrafficClass()); + case SocketOptions.SO_BINDADDR: + return delegate.getLocalAddress(); + case SocketOptions.SO_KEEPALIVE: + return Boolean.valueOf(delegate.getKeepAlive()); + case SocketOptions.SO_LINGER: + return new Integer(delegate.getSoLinger()); + case SocketOptions.SO_OOBINLINE: + return Boolean.valueOf(delegate.getOOBInline()); + case SocketOptions.SO_RCVBUF: + return new Integer(delegate.getReceiveBufferSize()); + case SocketOptions.SO_REUSEADDR: + return Boolean.valueOf(delegate.getReuseAddress()); + case SocketOptions.SO_SNDBUF: + return new Integer(delegate.getSendBufferSize()); + case SocketOptions.SO_TIMEOUT: + return new Integer(delegate.getSoTimeout()); + case SocketOptions.TCP_NODELAY: + return Boolean.valueOf(delegate.getTcpNoDelay()); + case SocketOptions.SO_BROADCAST: + default: + throw new IllegalArgumentException("Unsupported option type"); + } + } + + public void setOption(int optID, Object value) throws SocketException { + switch (optID) { + case SocketOptions.SO_BINDADDR: + throw new IllegalArgumentException("Socket is bound"); + case SocketOptions.SO_KEEPALIVE: + delegate.setKeepAlive(((Boolean)value).booleanValue()); + break; + case SocketOptions.SO_LINGER: + if (value instanceof Boolean) { + delegate.setSoLinger(((Boolean)value).booleanValue(), 0); + } + else { + delegate.setSoLinger(true, ((Integer)value).intValue()); + } + break; + case SocketOptions.SO_OOBINLINE: + delegate.setOOBInline(((Boolean)value).booleanValue()); + break; + case SocketOptions.SO_RCVBUF: + delegate.setReceiveBufferSize(((Integer)value).intValue()); + break; + case SocketOptions.SO_REUSEADDR: + delegate.setReuseAddress(((Boolean)value).booleanValue()); + break; + case SocketOptions.SO_SNDBUF: + delegate.setSendBufferSize(((Integer)value).intValue()); + break; + case SocketOptions.SO_TIMEOUT: + delegate.setSoTimeout(((Integer)value).intValue()); + break; + case SocketOptions.TCP_NODELAY: + delegate.setTcpNoDelay(((Boolean)value).booleanValue()); + break; + case SocketOptions.SO_BROADCAST: + default: + throw new IllegalArgumentException("Unsupported option type"); + } + } + } + + public void close() throws IOException { + System.out.println("Calling SocketWrapper.delegate.close"); + delegate.close(); + } + + public boolean equals(Object obj) { + if (!(obj instanceof SocketWrapper)) return false; + SocketWrapper that = (SocketWrapper)obj; + return this.delegate.equals(that.delegate); + } + + public int hashCode() { + return delegate.hashCode() ^ 0x01010101; + } + public String toString() { + return ""; + } +} \ No newline at end of file Index: invirt-vnc-client/VNCProxyConnectSocket.java =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ invirt-vnc-client/VNCProxyConnectSocket.java 2008-10-31 06:09:10.000000000 -0400 @@ -0,0 +1,61 @@ +// +// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved. +// Copyright 2007 MIT Student Information Processing Board +// +// 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. +// + +// +// VNCProxySocket.java together with VNCProxySocketFactory.java +// implement an alternate way to connect to VNC servers via one or two +// VNCProxy proxies supporting the VNCProxy VNCCONNECT method. +// + +import java.net.*; +import java.io.*; + +class VNCProxyConnectSocket extends Socket { + + public VNCProxyConnectSocket(String host, int port, + String vmname, String authtoken) + throws IOException { + + // Connect to the specified HTTP proxy + super(host, port); + + // Send the CONNECT request + getOutputStream().write(("CONNECTVNC " + vmname + + " VNCProxy/1.0\r\nAuth-token: " + authtoken + + "\r\n\r\n").getBytes()); + + // Read the first line of the response + DataInputStream is = new DataInputStream(getInputStream()); + String str = is.readLine(); + + // Check the HTTP error code -- it should be "200" on success + if (!str.startsWith("VNCProxy/1.0 200 ")) { + if (str.startsWith("VNCProxy/1.0 ")) + str = str.substring(13); + throw new IOException("Proxy reports \"" + str + "\""); + } + + // Success -- skip remaining HTTP headers + do { + str = is.readLine(); + } while (str.length() != 0); + } +} + Index: invirt-vnc-client/VNCProxyConnectSocketFactory.java =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ invirt-vnc-client/VNCProxyConnectSocketFactory.java 2008-10-31 06:09:10.000000000 -0400 @@ -0,0 +1,98 @@ +// +// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved. +// Copyright 2007 MIT Student Information Processing Board +// +// 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. +// + +// +// VNCProxyConnectSocketFactory.java together with VNCProxyConnectSocket.java +// implement an alternate way to connect to VNC servers via one or two +// VNCProxy proxies supporting the VNCProxy CONNECT method. +// + +import java.applet.*; +import java.net.*; +import javax.net.ssl.*; +import java.io.*; + +class VNCProxyConnectSocketFactory implements SocketFactory { + + SSLSocketFactory factory; + + public VNCProxyConnectSocketFactory() { + try { + SSLContext c = SSLContext.getInstance("SSL"); + c.init(null, + new TrustManager[] { new InvirtTrustManager() }, + null); + factory = + (SSLSocketFactory)c.getSocketFactory(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public Socket createSocket(String host, int port, Applet applet) + throws IOException { + + return createSocket(host, port, + applet.getParameter("VMNAME"), + applet.getParameter("AUTHTOKEN")); + } + + public Socket createSocket(String host, int port, String[] args) + throws IOException { + + return createSocket(host, port, + readArg(args, "VMNAME"), + readArg(args, "AUTHTOKEN")); + } + + public Socket createSocket(String host, int port, + String vmname, String authtoken) + throws IOException { + + if (vmname == null || authtoken == null) { + System.out.println("Incomplete parameter list for VNCProxyConnectSocket"); + return new Socket(host, port); + } + + System.out.println("VNCProxy CONNECT via proxy " + host + + " port " + port + " to vm " + vmname); + SSLSocket ssls = (SSLSocket)factory.createSocket(host, port); + ssls.startHandshake(); + VNCProxyConnectSocketWrapper s = + new VNCProxyConnectSocketWrapper(ssls, vmname, authtoken); + + return (Socket)s; + } + + private String readArg(String[] args, String name) { + + for (int i = 0; i < args.length; i += 2) { + if (args[i].equalsIgnoreCase(name)) { + try { + return args[i+1]; + } catch (Exception e) { + return null; + } + } + } + return null; + } +} + Index: invirt-vnc-client/VNCProxyConnectSocketWrapper.java =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ invirt-vnc-client/VNCProxyConnectSocketWrapper.java 2008-10-31 06:09:10.000000000 -0400 @@ -0,0 +1,60 @@ +// +// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved. +// Copyright 2007 MIT Student Information Processing Board +// +// 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. +// + +// +// VNCProxySocket.java together with VNCProxySocketFactory.java +// implement an alternate way to connect to VNC servers via one or two +// VNCProxy proxies supporting the VNCProxy VNCCONNECT method. +// + +import java.net.*; +import java.io.*; + +class VNCProxyConnectSocketWrapper extends SocketWrapper { + + public VNCProxyConnectSocketWrapper(Socket sock, + String vmname, String authtoken) + throws IOException { + + super(sock); + + // Send the CONNECT request + getOutputStream().write(("CONNECTVNC " + vmname + + " VNCProxy/1.0\r\nAuth-token: " + authtoken + + "\r\n\r\n").getBytes()); + + // Read the first line of the response + DataInputStream is = new DataInputStream(getInputStream()); + String str = is.readLine(); + + // Check the HTTP error code -- it should be "200" on success + if (!str.startsWith("VNCProxy/1.0 200 ")) { + if (str.startsWith("VNCProxy/1.0 ")) + str = str.substring(13); + throw new IOException("Proxy reports \"" + str + "\""); + } + + // Success -- skip remaining HTTP headers + do { + str = is.readLine(); + } while (str.length() != 0); + } +} +