package ssh;

import ssh.RSA.*;
import Tools.*;
import jp.*;
import proof.*;
import sdsi.*;

import java.io.*;
import java.net.*;
import java.util.*;
import java.math.*;

/**
 * This class is a little fancier than the basic SSHSocket class
 * in that it knows how to recognize connections back to the local
 * VM, and optimize away the SSH handshake and encryption gunk.
 * That saves a 1500ms public key operation (latency) and the bandwidth
 * cost of the secret-key encryption layer. This feature is pretty
 * important for use with RMI, which can't identify local connections on
 * its own.
 * 
 * @author Jon Howell <jonh@cs.dartmouth.edu>
 * 
 * @license This code is Copyright 1999 Jon Howell. It is available for
 * use under the GNU Public License, available at:
 * http://www.gnu.org/copyleft/gpl.html
 * 
 * @rcs $Id: SSHOptSocket.java,v 1.5 2000/05/22 01:35:57 jonh Exp $
 */

public class SSHOptSocket
	extends java.net.Socket
	implements SRPConstants {

	Socket realImplementation;

	/**
	 * Initiates a connection to a remote server.
	 */
	public SSHOptSocket(SSHContext context,
		InetAddress remoteAddress, int remotePort,
		InetAddress localAddress, int localPort)
		throws IOException {
		realImplementation =
			SSHServerSocket.connectIfLocal(remoteAddress, remotePort,
				context.publicKey);
		if (realImplementation==null) {
			realImplementation =
				new SSHSocket(context, remoteAddress,
					remotePort, localAddress, localPort);
		} else {
//			System.err.println(
//				"SSHOptSocket: saved a connection by making a local socket.");
		}
	}

	public SSHOptSocket(SSHContext context,
		InetAddress remoteAddress, int remotePort)
		throws IOException {
		this(context, remoteAddress, remotePort, null, 0);
	}

	public SSHOptSocket(SSHContext context,
		String remoteHost, int remotePort)
		throws IOException {
		this(context, InetAddress.getByName(remoteHost), remotePort, null, 0);
	}

	/**
	 * How to find out what public key identifies the other end of this
	 * connection.
	 */
	public RSAKey getOppositeKey() {
		return ((KeyedSocket) realImplementation).getOppositeKey();
	}

	// these become meaningful once connect has returned successfully
	public InputStream getInputStream()
		throws IOException {
		return realImplementation.getInputStream();
	}

	public OutputStream getOutputStream()
		throws IOException {
		return realImplementation.getOutputStream();
	}

	/**
	 * Make sure encrypted stream gets flushed cleanly.
	 */
	public void close()
		throws IOException {
		realImplementation.close();
	}

	/**
	 * pass through all other Socket stuff. Aaargh how I wish
	 * java.net.Socket were an interface.
	 * These stubs were automatically generated, hence the terrible
	 * parameter names.
	 */
	public java.net.InetAddress getInetAddress() {
		return realImplementation.getInetAddress();
	}

	public java.net.InetAddress getLocalAddress() {
		return realImplementation.getLocalAddress();
	}

	public int getLocalPort() {
		return realImplementation.getLocalPort();
	}

	public int getPort() {
		return realImplementation.getPort();
	}

	public synchronized int getReceiveBufferSize()
		throws java.net.SocketException {
		return realImplementation.getReceiveBufferSize();
	}

	public synchronized int getSendBufferSize()
		throws java.net.SocketException {
		return realImplementation.getSendBufferSize();
	}

	public int getSoLinger()
		throws java.net.SocketException {
		return realImplementation.getSoLinger();
	}

	public synchronized int getSoTimeout()
		throws java.net.SocketException {
		return realImplementation.getSoTimeout();
	}

	public boolean getTcpNoDelay()
		throws java.net.SocketException {
		return realImplementation.getTcpNoDelay();
	}

	public synchronized void setReceiveBufferSize(int p0)
		throws java.net.SocketException {
		realImplementation.setReceiveBufferSize(p0);
	}

	public synchronized void setSendBufferSize(int p0)
		throws java.net.SocketException {
		realImplementation.setSendBufferSize(p0);
	}

	public void setSoLinger(boolean p0, int p1)
		throws java.net.SocketException {
		realImplementation.setSoLinger(p0, p1);
	}

	public synchronized void setSoTimeout(int p0)
		throws java.net.SocketException {
		realImplementation.setSoTimeout(p0);
	}

	public void setTcpNoDelay(boolean p0)
		throws java.net.SocketException {
		realImplementation.setTcpNoDelay(p0);
	}
}
