Projects >> trilead-ssh2 >>9682803049898c0d990c1f8dcaabcf172fa5027c

Chunk
Conflicting content
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
package com.trilead.ssh2;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.security.SecureRandom;
import java.util.Vector;

=======

package com.trilead.ssh2;

import com.trilead.ssh2.auth.AgentProxy;
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
import com.trilead.ssh2.auth.AuthenticationManager;
import com.trilead.ssh2.channel.ChannelManager;
import com.trilead.ssh2.crypto.CryptoWishList;
Solution content
package com.trilead.ssh2;

import com.trilead.ssh2.auth.AgentProxy;
import com.trilead.ssh2.auth.AuthenticationManager;
import com.trilead.ssh2.channel.ChannelManager;
import com.trilead.ssh2.crypto.CryptoWishList;
File
Connection.java
Developer's decision
Version 2
Kind of conflict
Import
Package declaration
Chunk
Conflicting content
import com.trilead.ssh2.util.TimeoutService;
import com.trilead.ssh2.util.TimeoutService.TimeoutToken;

<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
=======
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.security.SecureRandom;
import java.util.Vector;

>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
/**
 * A Connection is used to establish an encrypted TCP/IP
 * connection to a SSH-2 server.
Solution content
import com.trilead.ssh2.util.TimeoutService;
import com.trilead.ssh2.util.TimeoutService.TimeoutToken;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.security.SecureRandom;
import java.util.Vector;

/**
 * A Connection is used to establish an encrypted TCP/IP
 * connection to a SSH-2 server.
File
Connection.java
Developer's decision
Manual
Kind of conflict
Import
Chunk
Conflicting content
		return authenticateWithKeyboardInteractive(user, null, cb);
	}

<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
=======
	public synchronized boolean authenticateWithAgent(String user, AgentProxy proxy) throws IOException {
		if (tm == null)
			throw new IllegalStateException("Connection is not established!");

		if (authenticated)
			throw new IllegalStateException("Connection is already authenticated!");

		if (am == null)
			am = new AuthenticationManager(tm);

		if (cm == null)
			cm = new ChannelManager(tm);

		if (user == null)
			throw new IllegalArgumentException("user argument is null");

		authenticated = am.authenticatePublicKey(user, proxy);

		return authenticated;
	}

>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
	/**
	 * After a successful connect, one has to authenticate oneself. This method
	 * is based on "keyboard-interactive", specified in
Solution content
		return authenticateWithKeyboardInteractive(user, null, cb);
	}

	public synchronized boolean authenticateWithAgent(String user, AgentProxy proxy) throws IOException {
		if (tm == null)
			throw new IllegalStateException("Connection is not established!");

		if (authenticated)
			throw new IllegalStateException("Connection is already authenticated!");

		if (am == null)
			am = new AuthenticationManager(tm);

		if (cm == null)
			cm = new ChannelManager(tm);

		if (user == null)
			throw new IllegalArgumentException("user argument is null");

		authenticated = am.authenticatePublicKey(user, proxy);

		return authenticated;
	}

	/**
	 * After a successful connect, one has to authenticate oneself. This method
	 * is based on "keyboard-interactive", specified in
File
Connection.java
Developer's decision
Version 2
Kind of conflict
Method declaration
Chunk
Conflicting content
	 *             proxy is buggy and does not return a proper HTTP response,
	 *             then a normal IOException is thrown instead.
	 */
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
			throws IOException
	{
=======
	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout) throws IOException {
			return connect(verifier, connectTimeout, 0, kexTimeout);
    }

    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int readTimeout, int kexTimeout)
		            throws IOException {
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
		final class TimeoutState
		{
			boolean isCancelled = false;
Solution content
	 *             proxy is buggy and does not return a proper HTTP response,
	 *             then a normal IOException is thrown instead.
	 */
	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout) throws IOException {
			return connect(verifier, connectTimeout, 0, kexTimeout);
    }

    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int readTimeout, int kexTimeout)
		            throws IOException {
		final class TimeoutState
		{
			boolean isCancelled = false;
File
Connection.java
Developer's decision
Version 2
Kind of conflict
Method declaration
Method signature
Chunk
Conflicting content
		final TimeoutState state = new TimeoutState();

		tm = new TransportManager(hostname, port);
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java

=======
		
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
		tm.setConnectionMonitors(connectionMonitors);

		/*
Solution content
		final TimeoutState state = new TimeoutState();

		tm = new TransportManager(hostname, port);
		
		tm.setConnectionMonitors(connectionMonitors);

		/*
File
Connection.java
Developer's decision
Version 1
Kind of conflict
Blank
Chunk
Conflicting content
			try
			{
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
				tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
=======
				tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, readTimeout, getOrCreateSecureRND(), proxyData);
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
			}
			catch (SocketTimeoutException se)
			{
Solution content
			try
			{
				tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, readTimeout, getOrCreateSecureRND(), proxyData);
			}
			catch (SocketTimeoutException se)
			{
File
Connection.java
Developer's decision
Version 2
Kind of conflict
Method invocation
Chunk
Conflicting content
		cm.requestGlobalTrileadPing();
	}
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java

    /**
     * Executes a process remotely and blocks until its completion.
     *
     * @param output
     *      The stdout/stderr will be sent to this stream.
     */
    public int exec(String command, OutputStream output) throws IOException, InterruptedException {
        Session session = openSession();
        try {
            session.execCommand(command);
            PumpThread t1 = new PumpThread(session.getStdout(), output);
            t1.start();
            PumpThread t2 = new PumpThread(session.getStderr(), output);
            t2.start();
            session.getStdin().close();
            t1.join();
            t2.join();

            // wait for some time since the delivery of the exit status often gets delayed
            session.waitForCondition(ChannelCondition.EXIT_STATUS,3000);
            Integer r = session.getExitStatus();
            if(r!=null) return r.intValue();
            return -1;
        } finally {
            session.close();
        }
    }

    /**
     * Pumps {@link InputStream} to {@link OutputStream}.
     *
     * @author Kohsuke Kawaguchi
     */
    private static final class PumpThread extends Thread {
        private final InputStream in;
        private final OutputStream out;

        public PumpThread(InputStream in, OutputStream out) {
            super("pump thread");
            this.in = in;
            this.out = out;
        }

        public void run() {
            byte[] buf = new byte[1024];
            try {
                while(true) {
                    int len = in.read(buf);
                    if(len<0) {
                        in.close();
                        return;
                    }
                    out.write(buf,0,len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
}
Solution content
		cm.requestGlobalTrileadPing();
	}

    /**
     * Executes a process remotely and blocks until its completion.
     *
     * @param output
     *      The stdout/stderr will be sent to this stream.
     */
    public int exec(String command, OutputStream output) throws IOException, InterruptedException {
        Session session = openSession();
        try {
            session.execCommand(command);
            PumpThread t1 = new PumpThread(session.getStdout(), output);
            t1.start();
            PumpThread t2 = new PumpThread(session.getStderr(), output);
            t2.start();
            session.getStdin().close();
            t1.join();
            t2.join();

            // wait for some time since the delivery of the exit status often gets delayed
            session.waitForCondition(ChannelCondition.EXIT_STATUS,3000);
            Integer r = session.getExitStatus();
            if(r!=null) return r.intValue();
            return -1;
        } finally {
            session.close();
        }
    }

    /**
     * Pumps {@link InputStream} to {@link OutputStream}.
     *
     * @author Kohsuke Kawaguchi
     */
    private static final class PumpThread extends Thread {
        private final InputStream in;
        private final OutputStream out;

        public PumpThread(InputStream in, OutputStream out) {
            super("pump thread");
            this.in = in;
            this.out = out;
        }

        public void run() {
            byte[] buf = new byte[1024];
            try {
                while(true) {
                    int len = in.read(buf);
                    if(len<0) {
                        in.close();
                        return;
                    }
                    out.write(buf,0,len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
File
Connection.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Method declaration
Chunk
Conflicting content
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
=======
import java.io.Reader;
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
Solution content
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
File
KnownHosts.java
Developer's decision
Version 2
Kind of conflict
Import
Chunk
Conflicting content
import com.trilead.ssh2.crypto.digest.HMAC;
import com.trilead.ssh2.crypto.digest.MD5;
import com.trilead.ssh2.crypto.digest.SHA1;
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
=======
import com.trilead.ssh2.log.Logger;
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.RSAPublicKey;
Solution content
import com.trilead.ssh2.crypto.digest.HMAC;
import com.trilead.ssh2.crypto.digest.MD5;
import com.trilead.ssh2.crypto.digest.SHA1;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.RSAPublicKey;
File
KnownHosts.java
Developer's decision
Version 2
Kind of conflict
Import
Chunk
Conflicting content
public class KnownHosts
{
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
=======
	private static final Logger LOGGER = Logger.getLogger(KnownHosts.class);

>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
	public static final int HOSTKEY_IS_OK = 0;
	public static final int HOSTKEY_IS_NEW = 1;
	public static final int HOSTKEY_HAS_CHANGED = 2;
Solution content
public class KnownHosts
{
	private static final Logger LOGGER = Logger.getLogger(KnownHosts.class);

	public static final int HOSTKEY_IS_OK = 0;
	public static final int HOSTKEY_IS_NEW = 1;
	public static final int HOSTKEY_HAS_CHANGED = 2;
File
KnownHosts.java
Developer's decision
Version 2
Kind of conflict
Attribute
Method invocation
Chunk
Conflicting content
	 * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
	 * @throws IOException
	 */
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
	public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException
	{
		if (hostnames == null)
			throw new IllegalArgumentException("hostnames may not be null");

		if ("ssh-rsa".equals(serverHostKeyAlgorithm))
		{
			RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);

			synchronized (publicKeys)
			{
				publicKeys.add(new KnownHostsEntry(hostnames, rpk));
			}
		}
		else if ("ssh-dss".equals(serverHostKeyAlgorithm))
		{
			DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);

			synchronized (publicKeys)
			{
				publicKeys.add(new KnownHostsEntry(hostnames, dpk));
			}
		}
		else
			throw new IOException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
=======
	public void addHostkey(String[] hostnames, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
		if (hostnames == null) {
			throw new IllegalArgumentException("hostnames may not be null");
		}

		if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
			final RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);

			synchronized (publicKeys) {
				publicKeys.add(new KnownHostsEntry(hostnames, rpk));
			}
		}
		else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
			final DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);

			synchronized (publicKeys) {
				publicKeys.add(new KnownHostsEntry(hostnames, dpk));
			}
		}
		else {
			throw new IOWarningException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
		}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
	}

	/**
Solution content
	 * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
	 * @throws IOException
	 */
	public void addHostkey(String[] hostnames, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
		if (hostnames == null) {
			throw new IllegalArgumentException("hostnames may not be null");
		}

		if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
			final RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);

			synchronized (publicKeys) {
				publicKeys.add(new KnownHostsEntry(hostnames, rpk));
			}
		}
		else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
			final DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);

			synchronized (publicKeys) {
				publicKeys.add(new KnownHostsEntry(hostnames, dpk));
			}
		}
		else {
			throw new IOWarningException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
		}
	}

	/**
File
KnownHosts.java
Developer's decision
Version 2
Kind of conflict
If statement
Method signature
Chunk
Conflicting content
						return false;
					isMatch = true;
				}
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
=======
				else
				{
					final int indexColon = pattern.indexOf(':');
					final int indexLastColon = pattern.indexOf(':');
					if (indexColon > 0 && indexColon < pattern.length() - 2 && indexColon == indexLastColon)
					{
						final String bracketizedHost = '[' + hostname + ']';
						if (pattern.startsWith(bracketizedHost))
						{
							if (negate)
								return false;
							isMatch = true;
						}
					}
				}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
			}
		}
Solution content
						return false;
					isMatch = true;
				}
				else
				{
					final int indexColon = pattern.indexOf(':');
					final int indexLastColon = pattern.indexOf(':');
					if (indexColon > 0 && indexColon < pattern.length() - 2 && indexColon == indexLastColon)
					{
						final String bracketizedHost = '[' + hostname + ']';
						if (pattern.startsWith(bracketizedHost))
						{
							if (negate)
								return false;
							isMatch = true;
						}
					}
				}
			}
		}
File
KnownHosts.java
Developer's decision
Version 2
Kind of conflict
If statement
Method invocation
Variable
Chunk
Conflicting content
			if (arr.length >= 3)
			{
		return isMatch;
	}

<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
	private void initialize(char[] knownHostsData) throws IOException
	{
		BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));

		while (true)
		{
			String line = br.readLine();

			if (line == null)
				break;

			line = line.trim();

			if (line.startsWith("#"))
				continue;

			String[] arr = line.split(" ");

				if ((arr[1].compareTo("ssh-rsa") == 0) || (arr[1].compareTo("ssh-dss") == 0))
				{
					String[] hostnames = arr[0].split(",");

					byte[] msg = Base64.decode(arr[2].toCharArray());

					addHostkey(hostnames, arr[1], msg);
				}
=======
	private void initialize(char[] knownHostsData) throws IOException {
		final BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
		for (String line = br.readLine(); line != null; line = br.readLine()) {
			line = line.trim();
			if (line.startsWith("#")) {
				continue;
			}

			final String[] arr = line.split(" "); 
			if (arr.length < 3) {
				continue;
			}

			final String serverHostKeyAlgorithm = arr[1];
			if (!"ssh-rsa".equals(serverHostKeyAlgorithm) && !"ssh-dss".equals(serverHostKeyAlgorithm)) {
				continue;
			}

			final String[] hostnames = arr[0].split(",");
			final byte[] msg = Base64.decode(arr[2].toCharArray());

			try {
				addHostkey(hostnames, serverHostKeyAlgorithm, msg);
			}
			catch (IOWarningException ex) {
				LOGGER.log(20, "Ignored invalid line '" + line + "'");
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
			}
		}
	}
Solution content
		return isMatch;
	}

	private void initialize(char[] knownHostsData) throws IOException {
		final BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
		for (String line = br.readLine(); line != null; line = br.readLine()) {
			line = line.trim();
			if (line.startsWith("#")) {
				continue;
			}

			final String[] arr = line.split(" "); 
			if (arr.length < 3) {
				continue;
			}

			final String serverHostKeyAlgorithm = arr[1];
			if (!"ssh-rsa".equals(serverHostKeyAlgorithm) && !"ssh-dss".equals(serverHostKeyAlgorithm)) {
				continue;
			}

			final String[] hostnames = arr[0].split(",");
			final byte[] msg = Base64.decode(arr[2].toCharArray());

			try {
				addHostkey(hostnames, serverHostKeyAlgorithm, msg);
			}
			catch (IOWarningException ex) {
				LOGGER.log(20, "Ignored invalid line '" + line + "'");
			}
		}
	}
File
KnownHosts.java
Developer's decision
Version 2
Kind of conflict
Array access
Catch clause
For statement
If statement
Method invocation
Method signature
Try statement
Variable
While statement
Chunk
Conflicting content
		}
	}

<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
	private void initialize(File knownHosts) throws IOException
	{
		char[] buff = new char[512];

		CharArrayWriter cw = new CharArrayWriter();

		knownHosts.createNewFile();

		FileReader fr = new FileReader(knownHosts);

		while (true)
		{
			int len = fr.read(buff);
			if (len < 0)
				break;
			cw.write(buff, 0, len);
		}

		fr.close();

		initialize(cw.toCharArray());
=======
	private void initialize(File knownHosts) throws IOException {
		final char[] buffer = new char[512];

		final CharArrayWriter charWriter = new CharArrayWriter();

		knownHosts.createNewFile();

		final Reader reader = new FileReader(knownHosts);
		try {
			while (true) {
				final int readCharCount = reader.read(buffer);
				if (readCharCount < 0) {
					break;
				}

				charWriter.write(buffer, 0, readCharCount);
			}
		}
		finally {
			reader.close();
		}

		initialize(charWriter.toCharArray());
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
	}

	private final boolean matchKeys(Object key1, Object key2)
Solution content
		}
	}

	private void initialize(File knownHosts) throws IOException {
		final char[] buffer = new char[512];

		final CharArrayWriter charWriter = new CharArrayWriter();

		knownHosts.createNewFile();

		final Reader reader = new FileReader(knownHosts);
		try {
			while (true) {
				final int readCharCount = reader.read(buffer);
				if (readCharCount < 0) {
					break;
				}

				charWriter.write(buffer, 0, readCharCount);
			}
		}
		finally {
			reader.close();
		}

		initialize(charWriter.toCharArray());
	}

	private final boolean matchKeys(Object key1, Object key2)
File
KnownHosts.java
Developer's decision
Version 2
Kind of conflict
Method invocation
Method signature
Try statement
Variable
While statement
Chunk
Conflicting content
<<<<<<< HEAD:src/com/trilead/ssh2/SFTPv3Client.java
=======

>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/SFTPv3Client.java
package com.trilead.ssh2;

import java.io.BufferedOutputStream;
Solution content
		return charsetName;
	}
package com.trilead.ssh2;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Vector;

import com.trilead.ssh2.packets.TypesReader;
import com.trilead.ssh2.packets.TypesWriter;
import com.trilead.ssh2.sftp.AttribFlags;
import com.trilead.ssh2.sftp.ErrorCodes;
import com.trilead.ssh2.sftp.Packet;


/**
 * A SFTPv3Client represents a SFTP (protocol version 3)
 * client connection tunnelled over a SSH-2 connection. This is a very simple
 * (synchronous) implementation.
 * 

* Basically, most methods in this class map directly to one of * the packet types described in draft-ietf-secsh-filexfer-02.txt. *

* Note: this is experimental code. *

* Error handling: the methods of this class throw IOExceptions. However, unless * there is catastrophic failure, exceptions of the type {@link SFTPv3Client} will * be thrown (a subclass of IOException). Therefore, you can implement more verbose * behavior by checking if a thrown exception if of this type. If yes, then you * can cast the exception and access detailed information about the failure. *

* Notes about file names, directory names and paths, copy-pasted * from the specs: *

    *
  • SFTP v3 represents file names as strings. File names are * assumed to use the slash ('/') character as a directory separator.
  • *
  • File names starting with a slash are "absolute", and are relative to * the root of the file system. Names starting with any other character * are relative to the user's default directory (home directory).
  • *
  • Servers SHOULD interpret a path name component ".." as referring to * the parent directory, and "." as referring to the current directory. * If the server implementation limits access to certain parts of the * file system, it must be extra careful in parsing file names when * enforcing such restrictions. There have been numerous reported * security bugs where a ".." in a path name has allowed access outside * the intended area.
  • *
  • An empty path name is valid, and it refers to the user's default * directory (usually the user's home directory).
  • *
*

* If you are still not tired then please go on and read the comment for * {@link #setCharset(String)}. * * @author Christian Plattner, plattner@trilead.com * @version $Id: SFTPv3Client.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $ */ public class SFTPv3Client { final Connection conn; final Session sess; final PrintStream debug; boolean flag_closed = false; InputStream is; OutputStream os; int protocol_version = 0; HashMap server_extensions = new HashMap(); int next_request_id = 1000; String charsetName = null; /** * Create a SFTP v3 client. * * @param conn The underlying SSH-2 connection to be used. * @param debug * @throws IOException * * @deprecated this constructor (debug version) will disappear in the future, * use {@link #SFTPv3Client(Connection)} instead. */ public SFTPv3Client(Connection conn, PrintStream debug) throws IOException { if (conn == null) throw new IllegalArgumentException("Cannot accept null argument!"); this.conn = conn; this.debug = debug; if (debug != null) debug.println("Opening session and starting SFTP subsystem."); sess = conn.openSession(); sess.startSubSystem("sftp"); is = sess.getStdout(); os = new BufferedOutputStream(sess.getStdin(), 2048); if ((is == null) || (os == null)) throw new IOException("There is a problem with the streams of the underlying channel."); init(); } /** * Create a SFTP v3 client. * * @param conn The underlying SSH-2 connection to be used. * @throws IOException */ public SFTPv3Client(Connection conn) throws IOException { this(conn, null); } /** * Set the charset used to convert between Java Unicode Strings and byte encodings * used by the server for paths and file names. Unfortunately, the SFTP v3 draft * says NOTHING about such conversions (well, with the exception of error messages * which have to be in UTF-8). Newer drafts specify to use UTF-8 for file names * (if I remember correctly). However, a quick test using OpenSSH serving a EXT-3 * filesystem has shown that UTF-8 seems to be a bad choice for SFTP v3 (tested with * filenames containing german umlauts). "windows-1252" seems to work better for Europe. * Luckily, "windows-1252" is the platform default in my case =). *

* If you don't set anything, then the platform default will be used (this is the default * behavior). * * @see #getCharset() * * @param charset the name of the charset to be used or null to use the platform's * default encoding. * @throws IOException */ public void setCharset(String charset) throws IOException { if (charset == null) { charsetName = charset; return; } try { Charset.forName(charset); } catch (Exception e) { throw (IOException) new IOException("This charset is not supported").initCause(e); } charsetName = charset; } /** * The currently used charset for filename encoding/decoding. * * @see #setCharset(String) * * @return The name of the charset (null if the platform's default charset is being used) */ public String getCharset() { private final void checkHandleValidAndOpen(SFTPv3FileHandle handle) throws IOException { if (handle.client != this) throw new IOException("The file handle was created with another SFTPv3FileHandle instance."); if (handle.isClosed == true) throw new IOException("The file handle is closed."); } private final void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException { int msglen = len + 1; if (type != Packet.SSH_FXP_INIT) msglen += 4; os.write(msglen >> 24); os.write(msglen >> 16); os.write(msglen >> 8); os.write(msglen); os.write(type); if (type != Packet.SSH_FXP_INIT) { os.write(requestId >> 24); os.write(requestId >> 16); os.write(requestId >> 8); os.write(requestId); } os.write(msg, off, len); os.flush(); } private final void sendMessage(int type, int requestId, byte[] msg) throws IOException { sendMessage(type, requestId, msg, 0, msg.length); } private final void readBytes(byte[] buff, int pos, int len) throws IOException { while (len > 0) { int count = is.read(buff, pos, len); if (count < 0) throw new IOException("Unexpected end of sftp stream."); if ((count == 0) || (count > len)) throw new IOException("Underlying stream implementation is bogus!"); len -= count; pos += count; } } /** * Read a message and guarantee that the contents is not larger than * maxlen bytes. *

* Note: receiveMessage(34000) actually means that the message may be up to 34004 * bytes (the length attribute preceeding the contents is 4 bytes). * * @param maxlen * @return the message contents * @throws IOException */ private final byte[] receiveMessage(int maxlen) throws IOException { byte[] msglen = new byte[4]; readBytes(msglen, 0, 4); int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff)); if ((len > maxlen) || (len <= 0)) throw new IOException("Illegal sftp packet len: " + len); byte[] msg = new byte[len]; readBytes(msg, 0, len); return msg; } private final int generateNextRequestID() { synchronized (this) { return next_request_id++; } } private final void closeHandle(byte[] handle) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(handle, 0, handle.length); sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes()); expectStatusOKMessage(req_id); } private SFTPv3FileAttributes readAttrs(TypesReader tr) throws IOException { /* * uint32 flags * uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE * uint32 uid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID } * uint32 gid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID * uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS * uint32 atime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME * uint32 mtime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME * uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED * string extended_type * string extended_data * ... more extended data (extended_type - extended_data pairs), * so that number of pairs equals extended_count */ SFTPv3FileAttributes fa = new SFTPv3FileAttributes(); int flags = tr.readUINT32(); if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0) { if (debug != null) debug.println("SSH_FILEXFER_ATTR_SIZE"); fa.size = new Long(tr.readUINT64()); } if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0) { if (debug != null) debug.println("SSH_FILEXFER_ATTR_V3_UIDGID"); fa.uid = new Integer(tr.readUINT32()); fa.gid = new Integer(tr.readUINT32()); } if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0) { if (debug != null) debug.println("SSH_FILEXFER_ATTR_PERMISSIONS"); fa.permissions = new Integer(tr.readUINT32()); } if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0) { if (debug != null) debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME"); fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl); fa.mtime = new Long(((long)tr.readUINT32()) & 0xffffffffl); } if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0) { int count = tr.readUINT32(); if (debug != null) debug.println("SSH_FILEXFER_ATTR_EXTENDED (" + count + ")"); /* Read it anyway to detect corrupt packets */ while (count > 0) { tr.readByteString(); tr.readByteString(); count--; } } return fa; } /** * Retrieve the file attributes of an open file. * * @param handle a SFTPv3FileHandle handle. * @return a SFTPv3FileAttributes object. * @throws IOException */ public SFTPv3FileAttributes fstat(SFTPv3FileHandle handle) throws IOException { checkHandleValidAndOpen(handle); int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); if (debug != null) { debug.println("Sending SSH_FXP_FSTAT..."); debug.flush(); } sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes()); byte[] resp = receiveMessage(34000); if (debug != null) { debug.println("Got REPLY."); debug.flush(); } TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t == Packet.SSH_FXP_ATTRS) { return readAttrs(tr); if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); throw new SFTPException(tr.readString(), errorCode); } private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(path, charsetName); if (debug != null) { debug.println("Sending SSH_FXP_STAT/SSH_FXP_LSTAT..."); debug.flush(); } sendMessage(statMethod, req_id, tw.getBytes()); byte[] resp = receiveMessage(34000); if (debug != null) { debug.println("Got REPLY."); debug.flush(); } TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t == Packet.SSH_FXP_ATTRS) { return readAttrs(tr); } if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); throw new SFTPException(tr.readString(), errorCode); } /** * Retrieve the file attributes of a file. This method /** * follows symbolic links on the server. * * @see #lstat(String) * * @param path See the {@link SFTPv3Client comment} for the class for more details. * @return a SFTPv3FileAttributes object. * @throws IOException */ public SFTPv3FileAttributes stat(String path) throws IOException { return statBoth(path, Packet.SSH_FXP_STAT); } /** * Retrieve the file attributes of a file. This method * does NOT follow symbolic links on the server. * * @see #stat(String) * * @param path See the {@link SFTPv3Client comment} for the class for more details. * @return a SFTPv3FileAttributes object. * @throws IOException */ public SFTPv3FileAttributes lstat(String path) throws IOException { return statBoth(path, Packet.SSH_FXP_LSTAT); } /** * Read the target of a symbolic link. * * @param path See the {@link SFTPv3Client comment} for the class for more details. * @return The target of the link. * @throws IOException */ public String readLink(String path) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(path, charsetName); if (debug != null) { debug.println("Sending SSH_FXP_READLINK..."); debug.flush(); } sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes()); byte[] resp = receiveMessage(34000); if (debug != null) { debug.println("Got REPLY."); debug.flush(); } TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t == Packet.SSH_FXP_NAME) { int count = tr.readUINT32(); if (count != 1) throw new IOException("The server sent an invalid SSH_FXP_NAME packet."); return tr.readString(charsetName); } if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); throw new SFTPException(tr.readString(), errorCode); } private void expectStatusOKMessage(int id) throws IOException { byte[] resp = receiveMessage(34000); if (debug != null) { debug.println("Got REPLY."); debug.flush(); } TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != id) throw new IOException("The server sent an invalid id field."); if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); if (errorCode == ErrorCodes.SSH_FX_OK) return; throw new SFTPException(tr.readString(), errorCode); } * Modify the attributes of a file. Used for operations such as changing * the ownership, permissions or access times, as well as for truncating a file. * * @param path See the {@link SFTPv3Client comment} for the class for more details. * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be * made to the attributes of the file. Empty fields will be ignored. * @throws IOException */ public void setstat(String path, SFTPv3FileAttributes attr) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(path, charsetName); tw.writeBytes(createAttrs(attr)); if (debug != null) { debug.println("Sending SSH_FXP_SETSTAT..."); debug.flush(); } sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes()); expectStatusOKMessage(req_id); } /** * Modify the attributes of a file. Used for operations such as changing * the ownership, permissions or access times, as well as for truncating a file. * * @param handle a SFTPv3FileHandle handle * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be * made to the attributes of the file. Empty fields will be ignored. * @throws IOException */ public void fsetstat(SFTPv3FileHandle handle, SFTPv3FileAttributes attr) throws IOException { checkHandleValidAndOpen(handle); int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); tw.writeBytes(createAttrs(attr)); if (debug != null) { debug.println("Sending SSH_FXP_FSETSTAT..."); debug.flush(); } } sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes()); expectStatusOKMessage(req_id); } /** * Create a symbolic link on the server. Creates a link "src" that points * to "target". * * @param src See the {@link SFTPv3Client comment} for the class for more details. * @param target See the {@link SFTPv3Client comment} for the class for more details. * @throws IOException */ public void createSymlink(String src, String target) throws IOException { int req_id = generateNextRequestID(); /* Either I am too stupid to understand the SFTP draft * or the OpenSSH guys changed the semantics of src and target. */ TypesWriter tw = new TypesWriter(); tw.writeString(target, charsetName); tw.writeString(src, charsetName); if (debug != null) { debug.println("Sending SSH_FXP_SYMLINK..."); debug.flush(); } sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes()); expectStatusOKMessage(req_id); } /** * Have the server canonicalize any given path name to an absolute path. * This is useful for converting path names containing ".." components or * relative pathnames without a leading slash into absolute paths. * * @param path See the {@link SFTPv3Client comment} for the class for more details. * @return An absolute path. * @throws IOException */ public String canonicalPath(String path) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(path, charsetName); if (debug != null) { debug.println("Sending SSH_FXP_REALPATH..."); debug.flush(); } sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes()); byte[] resp = receiveMessage(34000); if (debug != null) { debug.println("Got REPLY."); debug.flush(); } TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t == Packet.SSH_FXP_NAME) { int count = tr.readUINT32(); if (count != 1) throw new IOException("The server sent an invalid SSH_FXP_NAME packet."); return tr.readString(charsetName); } if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); throw new SFTPException(tr.readString(), errorCode); } private final Vector scanDirectory(byte[] handle) throws IOException { Vector files = new Vector(); while (true) { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(handle, 0, handle.length); if (debug != null) { debug.println("Sending SSH_FXP_READDIR..."); debug.flush(); } sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes()); /* Some servers send here a packet with size > 34000 */ /* To whom it may concern: please learn to read the specs. */ byte[] resp = receiveMessage(65536); if (debug != null) { debug.println("Got REPLY."); debug.flush(); } TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t == Packet.SSH_FXP_NAME) { int count = tr.readUINT32(); if (debug != null) debug.println("Parsing " + count + " name entries..."); while (count > 0) { SFTPv3DirectoryEntry dirEnt = new SFTPv3DirectoryEntry(); dirEnt.filename = tr.readString(charsetName); dirEnt.longEntry = tr.readString(charsetName); dirEnt.attributes = readAttrs(tr); files.addElement(dirEnt); if (debug != null) debug.println("File: '" + dirEnt.filename + "'"); count--; } continue; } if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); if (errorCode == ErrorCodes.SSH_FX_EOF) return files; throw new SFTPException(tr.readString(), errorCode); } } private final byte[] openDirectory(String path) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(path, charsetName); if (debug != null) { debug.println("Sending SSH_FXP_OPENDIR..."); debug.flush(); } sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes()); byte[] resp = receiveMessage(34000); TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t == Packet.SSH_FXP_HANDLE) { if (debug != null) { debug.println("Got SSH_FXP_HANDLE."); debug.flush(); } byte[] handle = tr.readByteString(); return handle; } if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); String errorMessage = tr.readString(); throw new SFTPException(errorMessage, errorCode); } private final String expandString(byte[] b, int off, int len) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < len; i++) { int c = b[off + i] & 0xff; if ((c >= 32) && (c <= 126)) { sb.append((char) c); else { sb.append("{0x" + Integer.toHexString(c) + "}"); } } return sb.toString(); } private void init() throws IOException { /* Send SSH_FXP_INIT (version 3) */ final int client_version = 3; if (debug != null) debug.println("Sending SSH_FXP_INIT (" + client_version + ")..."); TypesWriter tw = new TypesWriter(); tw.writeUINT32(client_version); sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes()); /* Receive SSH_FXP_VERSION */ if (debug != null) debug.println("Waiting for SSH_FXP_VERSION..."); TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */ int type = tr.readByte(); if (type != Packet.SSH_FXP_VERSION) { throw new IOException("The server did not send a SSH_FXP_VERSION packet (got " + type + ")"); } protocol_version = tr.readUINT32(); if (debug != null) debug.println("SSH_FXP_VERSION: protocol_version = " + protocol_version); if (protocol_version != 3) throw new IOException("Server version " + protocol_version + " is currently not supported"); /* Read and save extensions (if any) for later use */ while (tr.remain() != 0) { String name = tr.readString(); byte[] value = tr.readByteString(); server_extensions.put(name, value); if (debug != null) debug.println("SSH_FXP_VERSION: extension: " + name + " = '" + expandString(value, 0, value.length) + "'"); } } /** * Returns the negotiated SFTP protocol version between the client and the server. * * @return SFTP protocol version, i.e., "3". * */ public int getProtocolVersion() { return protocol_version; } /** * Close this SFTP session. NEVER forget to call this method to free up * resources - even if you got an exception from one of the other methods. * Sometimes these other methods may throw an exception, saying that the * underlying channel is closed (this can happen, e.g., if the other server * sent a close message.) However, as long as you have not called the * close() method, you are likely wasting resources. * */ public void close() { sess.close(); } /** * List the contents of a directory. * * @param dirName See the {@link SFTPv3Client comment} for the class for more details. * @return A Vector containing {@link SFTPv3DirectoryEntry} objects. * @throws IOException */ public Vector ls(String dirName) throws IOException { byte[] handle = openDirectory(dirName); Vector result = scanDirectory(handle); closeHandle(handle); return result; } /** * Create a new directory. * * @param dirName See the {@link SFTPv3Client comment} for the class for more details. * @param posixPermissions the permissions for this directory, e.g., "0700" (remember that * this is octal noation). The server will likely apply a umask. * * @throws IOException */ public void mkdir(String dirName, int posixPermissions) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(dirName, charsetName); tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS); tw.writeUINT32(posixPermissions); sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes()); expectStatusOKMessage(req_id); } /** * Remove a file. * * @param fileName See the {@link SFTPv3Client comment} for the class for more details. * @throws IOException */ public void rm(String fileName) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(fileName, charsetName); sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes()); expectStatusOKMessage(req_id); } /** * Remove an empty directory. * * @param dirName See the {@link SFTPv3Client comment} for the class for more details. * @throws IOException */ public void rmdir(String dirName) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(dirName, charsetName); sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes()); expectStatusOKMessage(req_id); } /** * Move a file or directory. * * @param oldPath See the {@link SFTPv3Client comment} for the class for more details. * @param newPath See the {@link SFTPv3Client comment} for the class for more details. * @throws IOException */ public void mv(String oldPath, String newPath) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(oldPath, charsetName); tw.writeString(newPath, charsetName); sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes()); expectStatusOKMessage(req_id); } /** * Open a file for reading. * * @param fileName See the {@link SFTPv3Client comment} for the class for more details. * @return a SFTPv3FileHandle handle * @throws IOException */ public SFTPv3FileHandle openFileRO(String fileName) throws IOException { return openFile(fileName, 0x00000001, null); // SSH_FXF_READ } /** * Open a file for reading and writing. * * @param fileName See the {@link SFTPv3Client comment} for the class for more details. * @return a SFTPv3FileHandle handle * @throws IOException */ public SFTPv3FileHandle openFileRW(String fileName) throws IOException { return openFile(fileName, 0x00000003, null); // SSH_FXF_READ | SSH_FXF_WRITE } // Append is broken (already in the specification, because there is no way to // send a write operation (what offset to use??)) // public SFTPv3FileHandle openFileRWAppend(String fileName) throws IOException // { // return openFile(fileName, 0x00000007, null); // SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND // } /** * Create a file and open it for reading and writing. * Same as {@link #createFile(String, SFTPv3FileAttributes) createFile(fileName, null)}. * * @param fileName See the {@link SFTPv3Client comment} for the class for more details. * @return a SFTPv3FileHandle handle * @throws IOException */ public SFTPv3FileHandle createFile(String fileName) throws IOException { return createFile(fileName, null); } /** * Create a file and open it for reading and writing. * You can specify the default attributes of the file (the server may or may * not respect your wishes). * * @param fileName See the {@link SFTPv3Client comment} for the class for more details. * @param attr may be null to use server defaults. Probably only * the uid, gid and permissions * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle} * structure make sense. You need only to set those fields where you want * to override the server's defaults. * @return a SFTPv3FileHandle handle * @throws IOException */ public SFTPv3FileHandle createFile(String fileName, SFTPv3FileAttributes attr) throws IOException { return openFile(fileName, 0x00000008 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_READ | SSH_FXF_WRITE } /** * Create a file (truncate it if it already exists) and open it for reading and writing. * Same as {@link #createFileTruncate(String, SFTPv3FileAttributes) createFileTruncate(fileName, null)}. * * @param fileName See the {@link SFTPv3Client comment} for the class for more details. * @return a SFTPv3FileHandle handle * @throws IOException */ public SFTPv3FileHandle createFileTruncate(String fileName) throws IOException { return createFileTruncate(fileName, null); } /** * reate a file (truncate it if it already exists) and open it for reading and writing. * You can specify the default attributes of the file (the server may or may * not respect your wishes). * * @param fileName See the {@link SFTPv3Client comment} for the class for more details. * @param attr may be null to use server defaults. Probably only * the uid, gid and permissions * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle} * structure make sense. You need only to set those fields where you want * to override the server's defaults. * @return a SFTPv3FileHandle handle * @throws IOException */ public SFTPv3FileHandle createFileTruncate(String fileName, SFTPv3FileAttributes attr) throws IOException { return openFile(fileName, 0x00000018 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_READ | SSH_FXF_WRITE } private byte[] createAttrs(SFTPv3FileAttributes attr) { TypesWriter tw = new TypesWriter(); int attrFlags = 0; if (attr == null) { tw.writeUINT32(0); } else { if (attr.size != null) attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE; if ((attr.uid != null) && (attr.gid != null)) attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID; if (attr.permissions != null) attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS; if ((attr.atime != null) && (attr.mtime != null)) attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME; tw.writeUINT32(attrFlags); if (attr.size != null) tw.writeUINT64(attr.size.longValue()); if ((attr.uid != null) && (attr.gid != null)) { tw.writeUINT32(attr.uid.intValue()); tw.writeUINT32(attr.gid.intValue()); } if (attr.permissions != null) tw.writeUINT32(attr.permissions.intValue()); if ((attr.atime != null) && (attr.mtime != null)) { tw.writeUINT32(attr.atime.intValue()); tw.writeUINT32(attr.mtime.intValue()); } } return tw.getBytes(); } private SFTPv3FileHandle openFile(String fileName, int flags, SFTPv3FileAttributes attr) throws IOException { int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(fileName, charsetName); tw.writeUINT32(flags); tw.writeBytes(createAttrs(attr)); if (debug != null) { debug.println("Sending SSH_FXP_OPEN..."); debug.flush(); } sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes()); byte[] resp = receiveMessage(34000); TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t == Packet.SSH_FXP_HANDLE) { if (debug != null) { debug.println("Got SSH_FXP_HANDLE."); debug.flush(); } return new SFTPv3FileHandle(this, tr.readByteString()); } if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); String errorMessage = tr.readString(); throw new SFTPException(errorMessage, errorCode); } /** * Read bytes from a file. No more than 32768 bytes may be read at once. * Be aware that the semantics of read() are different than for Java streams. *

*

    *
  • The server will read as many bytes as it can from the file (up to len), * and return them.
  • *
  • If EOF is encountered before reading any data, -1 is returned. *
  • If an error occurs, an exception is thrown
  • . *
  • For normal disk files, it is guaranteed that the server will return the specified * number of bytes, or up to end of file. For, e.g., device files this may return * fewer bytes than requested.
  • *
* * @param handle a SFTPv3FileHandle handle * @param fileOffset offset (in bytes) in the file * @param dst the destination byte array * @param dstoff offset in the destination byte array * @param len how many bytes to read, 0 < len <= 32768 bytes * @return the number of bytes that could be read, may be less than requested if * the end of the file is reached, -1 is returned in case of EOF * @throws IOException */ public int read(SFTPv3FileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException { checkHandleValidAndOpen(handle); if ((len > 32768) || (len <= 0)) throw new IllegalArgumentException("invalid len argument"); int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); tw.writeUINT64(fileOffset); tw.writeUINT32(len); if (debug != null) { debug.println("Sending SSH_FXP_READ..."); debug.flush(); } sendMessage(Packet.SSH_FXP_READ, req_id, tw.getBytes()); byte[] resp = receiveMessage(34000); TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t == Packet.SSH_FXP_DATA) { if (debug != null) { debug.println("Got SSH_FXP_DATA..."); debug.flush(); } int readLen = tr.readUINT32(); if ((readLen < 0) || (readLen > len)) throw new IOException("The server sent an invalid length field."); tr.readBytes(dst, dstoff, readLen); return readLen; } if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); if (errorCode == ErrorCodes.SSH_FX_EOF) { if (debug != null) { debug.println("Got SSH_FX_EOF."); debug.flush(); } return -1; } String errorMessage = tr.readString(); throw new SFTPException(errorMessage, errorCode); } /** * Write bytes to a file. If len > 32768, then the write operation will * be split into multiple writes. * * @param handle a SFTPv3FileHandle handle. * @param fileOffset offset (in bytes) in the file. * @param src the source byte array. * @param srcoff offset in the source byte array. * @param len how many bytes to write. * @throws IOException */ public void write(SFTPv3FileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException { checkHandleValidAndOpen(handle); while (len > 0) { int writeRequestLen = len; if (writeRequestLen > 32768) writeRequestLen = 32768; int req_id = generateNextRequestID(); TypesWriter tw = new TypesWriter(); tw.writeString(handle.fileHandle, 0, handle.fileHandle.length); tw.writeUINT64(fileOffset); tw.writeString(src, srcoff, writeRequestLen); if (debug != null) { debug.println("Sending SSH_FXP_WRITE..."); debug.flush(); } sendMessage(Packet.SSH_FXP_WRITE, req_id, tw.getBytes()); fileOffset += writeRequestLen; srcoff += writeRequestLen; len -= writeRequestLen; byte[] resp = receiveMessage(34000); TypesReader tr = new TypesReader(resp); int t = tr.readByte(); int rep_id = tr.readUINT32(); if (rep_id != req_id) throw new IOException("The server sent an invalid id field."); if (t != Packet.SSH_FXP_STATUS) throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")"); int errorCode = tr.readUINT32(); if (errorCode == ErrorCodes.SSH_FX_OK) continue; String errorMessage = tr.readString(); throw new SFTPException(errorMessage, errorCode); } } /** * Close a file. * * @param handle a SFTPv3FileHandle handle * @throws IOException */ public void closeFile(SFTPv3FileHandle handle) throws IOException { if (handle == null) throw new IllegalArgumentException("the handle argument may not be null"); try { if (handle.isClosed == false) { closeHandle(handle.fileHandle); } } finally { handle.isClosed = true; } } /** * Checks if the given path exists. */ public boolean exists(String path) throws IOException { return _stat(path)!=null; } /** * Graceful {@link #stat(String)} that returns null if the path doesn't exist. */ public SFTPv3FileAttributes _stat(String path) throws IOException { try { return stat(path); } catch (SFTPException e) { int c = e.getServerErrorCode(); if (c== ErrorCodes.SSH_FX_NO_SUCH_FILE || c==ErrorCodes.SSH_FX_NO_SUCH_PATH) return null; else throw e; } } /** * Makes sure that the directory exists, by creating it if necessary. */ public void mkdirs(String path, int posixPermission) throws IOException { SFTPv3FileAttributes atts = _stat(path); if (atts!=null && atts.isDirectory()) return; int idx = path.lastIndexOf("/"); if (idx>0) mkdirs(path.substring(0,idx), posixPermission); try { mkdir(path, posixPermission); } catch (IOException e) { throw (IOException)new IOException("Failed to mkdir "+path).initCause(e); } } /** * Creates a new file and writes to it. */ public OutputStream writeToFile(String path) throws IOException { final SFTPv3FileHandle h = createFile(path); return new OutputStream() { private long offset = 0; public void write(int b) throws IOException { write(new byte[]{(byte)b}); } public void write(byte[] b, int off, int len) throws IOException { SFTPv3Client.this.write(h,offset,b,off,len); offset += len; } public void close() throws IOException { closeFile(h); } }; } public InputStream read(String file) throws IOException { final SFTPv3FileHandle h = openFileRO(file); return new InputStream() { private long offset = 0; public int read() throws IOException { byte[] b = new byte[1]; if(read(b)<0) return -1; return b[0]; } public int read(byte[] b, int off, int len) throws IOException { int r = SFTPv3Client.this.read(h,offset,b,off,len); if (r<0) return -1; offset += r; return r; } public long skip(long n) throws IOException { offset += n; return n; } public void close() throws IOException { closeFile(h); } }; } public void chmod(String path, int permissions) throws IOException { SFTPv3FileAttributes atts = new SFTPv3FileAttributes(); atts.permissions = Integer.valueOf(permissions); setstat(path, atts); } }
File
SFTPv3Client.java
Developer's decision
Manual
Kind of conflict
Blank
Chunk
Conflicting content
			handle.isClosed = true;
		}
	}
<<<<<<< HEAD:src/com/trilead/ssh2/SFTPv3Client.java

    /**
     * Checks if the given path exists.
     */
    public boolean exists(String path) throws IOException {
        return _stat(path)!=null;
    }

    /**
     * Graceful {@link #stat(String)} that returns null if the path doesn't exist.
     */
    public SFTPv3FileAttributes _stat(String path) throws IOException {
        try {
            return stat(path);
        } catch (SFTPException e) {
            int c = e.getServerErrorCode();
            if (c== ErrorCodes.SSH_FX_NO_SUCH_FILE || c==ErrorCodes.SSH_FX_NO_SUCH_PATH)
                return null;
            else
                throw e;
        }
    }

    /**
     * Makes sure that the directory exists, by creating it if necessary.
     */
    public void mkdirs(String path, int posixPermission) throws IOException {
        SFTPv3FileAttributes atts = _stat(path);
        if (atts!=null && atts.isDirectory())
            return;

        int idx = path.lastIndexOf("/");
        if (idx>0)
            mkdirs(path.substring(0,idx), posixPermission);

        try {
            mkdir(path, posixPermission);
        } catch (IOException e) {
            throw (IOException)new IOException("Failed to mkdir "+path).initCause(e);
        }
    }

    /**
     * Creates a new file and writes to it.
     */
    public OutputStream writeToFile(String path) throws IOException {
        final SFTPv3FileHandle h = createFile(path);
        return new OutputStream() {
            private long offset = 0;
            public void write(int b) throws IOException {
                write(new byte[]{(byte)b});
            }

            public void write(byte[] b, int off, int len) throws IOException {
                SFTPv3Client.this.write(h,offset,b,off,len);
                offset += len;
            }

            public void close() throws IOException {
                closeFile(h);
            }
        };
    }

    public InputStream read(String file) throws IOException {
        final SFTPv3FileHandle h = openFileRO(file);
        return new InputStream() {
            private long offset = 0;

            public int read() throws IOException {
                byte[] b = new byte[1];
                if(read(b)<0)
                    return -1;
                return b[0];
            }

            public int read(byte[] b, int off, int len) throws IOException {
                int r = SFTPv3Client.this.read(h,offset,b,off,len);
                if (r<0)    return -1;
                offset += r;
                return r;
            }

            public long skip(long n) throws IOException {
                offset += n;
                return n;
            }

            public void close() throws IOException {
                closeFile(h);
            }
        };
    }

    public void chmod(String path, int permissions) throws IOException {
        SFTPv3FileAttributes atts = new SFTPv3FileAttributes();
        atts.permissions = Integer.valueOf(permissions);
        setstat(path, atts);
    }
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/SFTPv3Client.java
}
Solution content
    /**
     * Checks if the given path exists.
     */
    public boolean exists(String path) throws IOException {
        return _stat(path)!=null;
    }

    /**
     * Graceful {@link #stat(String)} that returns null if the path doesn't exist.
     */
    public SFTPv3FileAttributes _stat(String path) throws IOException {
        try {
            return stat(path);
        } catch (SFTPException e) {
            int c = e.getServerErrorCode();
            if (c== ErrorCodes.SSH_FX_NO_SUCH_FILE || c==ErrorCodes.SSH_FX_NO_SUCH_PATH)
                return null;
            else
                throw e;
        }
    }

    /**
     * Makes sure that the directory exists, by creating it if necessary.
     */
    public void mkdirs(String path, int posixPermission) throws IOException {
        SFTPv3FileAttributes atts = _stat(path);
        if (atts!=null && atts.isDirectory())
            return;

        int idx = path.lastIndexOf("/");
        if (idx>0)
            mkdirs(path.substring(0,idx), posixPermission);

        try {
            mkdir(path, posixPermission);
        } catch (IOException e) {
            throw (IOException)new IOException("Failed to mkdir "+path).initCause(e);
        }
    }

    /**
     * Creates a new file and writes to it.
     */
    public OutputStream writeToFile(String path) throws IOException {
        final SFTPv3FileHandle h = createFile(path);
        return new OutputStream() {
            private long offset = 0;
            public void write(int b) throws IOException {
                write(new byte[]{(byte)b});
            }

            public void write(byte[] b, int off, int len) throws IOException {
                SFTPv3Client.this.write(h,offset,b,off,len);
                offset += len;
            }

            public void close() throws IOException {
                closeFile(h);
            }
        };
    }

    public InputStream read(String file) throws IOException {
        final SFTPv3FileHandle h = openFileRO(file);
        return new InputStream() {
            private long offset = 0;

            public int read() throws IOException {
                byte[] b = new byte[1];
                if(read(b)<0)
                    return -1;
                return b[0];
            }

            public int read(byte[] b, int off, int len) throws IOException {
                int r = SFTPv3Client.this.read(h,offset,b,off,len);
                if (r<0)    return -1;
                offset += r;
                return r;
            }

            public long skip(long n) throws IOException {
                offset += n;
                return n;
            }

            public void close() throws IOException {
                closeFile(h);
            }
        };
    }

    public void chmod(String path, int permissions) throws IOException {
        SFTPv3FileAttributes atts = new SFTPv3FileAttributes();
        atts.permissions = Integer.valueOf(permissions);
        setstat(path, atts);
    }
}
File
SFTPv3Client.java
Developer's decision
Version 1
Kind of conflict
Comment
Method declaration
Chunk
Conflicting content
		}
<<<<<<< HEAD:src/com/trilead/ssh2/Session.java

package com.trilead.ssh2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.SecureRandom;

import com.trilead.ssh2.channel.Channel;
import com.trilead.ssh2.channel.ChannelManager;
import com.trilead.ssh2.channel.X11ServerData;


/**
 * A Session is a remote execution of a program. "Program" means
 * in this context either a shell, an application or a system command. The
 * program may or may not have a tty. Only one single program can be started on
 * a session. However, multiple sessions can be active simultaneously.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: Session.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
 */
public class Session
{
	ChannelManager cm;
	Channel cn;

	boolean flag_pty_requested = false;
	boolean flag_x11_requested = false;
	boolean flag_execution_started = false;
	boolean flag_closed = false;

	String x11FakeCookie = null;

	final SecureRandom rnd;
	
	Session(ChannelManager cm, SecureRandom rnd) throws IOException
	{
		this.cm = cm;
		this.cn = cm.openSessionChannel();
		this.rnd = rnd;
	}

	/**
	 * Basically just a wrapper for lazy people - identical to calling
	 * requestPTY("dumb", 0, 0, 0, 0, null).
	 * 
	 * @throws IOException
	 */
	public void requestDumbPTY() throws IOException
	{
		requestPTY("dumb", 0, 0, 0, 0, null);
	}

	/**
	 * Basically just another wrapper for lazy people - identical to calling
	 * requestPTY(term, 0, 0, 0, 0, null).
	 * 
	 * @throws IOException
	 */
	public void requestPTY(String term) throws IOException
	{
		requestPTY(term, 0, 0, 0, 0, null);
	}

	/**
	 * Allocate a pseudo-terminal for this session.
	 * 

* This method may only be called before a program or shell is started in * this session. *

* Different aspects can be specified: *

*

    *
  • The TERM environment variable value (e.g., vt100)
  • *
  • The terminal's dimensions.
  • *
  • The encoded terminal modes.
  • *
* Zero dimension parameters are ignored. The character/row dimensions * override the pixel dimensions (when nonzero). Pixel dimensions refer to * the drawable area of the window. The dimension parameters are only * informational. The encoding of terminal modes (parameter * terminal_modes) is described in RFC4254. * * @param term * The TERM environment variable value (e.g., vt100) * @param term_width_characters * terminal width, characters (e.g., 80) * @param term_height_characters * terminal height, rows (e.g., 24) * @param term_width_pixels * terminal width, pixels (e.g., 640) * @param term_height_pixels * terminal height, pixels (e.g., 480) * @param terminal_modes * encoded terminal modes (may be null) * @throws IOException */ public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException { if (term == null) throw new IllegalArgumentException("TERM cannot be null."); if ((terminal_modes != null) && (terminal_modes.length > 0)) { if (terminal_modes[terminal_modes.length - 1] != 0) throw new IOException("Illegal terminal modes description, does not end in zero byte"); else terminal_modes = new byte[] { 0 }; synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_pty_requested) throw new IOException("A PTY was already requested."); if (flag_execution_started) throw new IOException( "Cannot request PTY at this stage anymore, a remote execution has already started."); flag_pty_requested = true; } cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, terminal_modes); } /** * Request X11 forwarding for the current session. *

* You have to supply the name and port of your X-server. *

* This method may only be called before a program or shell is started in * this session. * * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1) * @param port the port of the real (target) X11 server (e.g., 6010) * @param cookie if non-null, then present this cookie to the real X11 server * @param singleConnection if true, then the server is instructed to only forward one single * connection, no more connections shall be forwarded after first, or after the session * channel has been closed * @throws IOException */ public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection) throws IOException { if (hostname == null) throw new IllegalArgumentException("hostname argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_x11_requested) throw new IOException("X11 forwarding was already requested."); if (flag_execution_started) throw new IOException( "Cannot request X11 forwarding at this stage anymore, a remote execution has already started."); flag_x11_requested = true; } /* X11ServerData - used to store data about the target X11 server */ X11ServerData x11data = new X11ServerData(); x11data.hostname = hostname; x11data.port = port; x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */ /* Generate fake cookie - this one is used between remote clients and our proxy */ byte[] fakeCookie = new byte[16]; String hexEncodedFakeCookie; /* Make sure that this fake cookie is unique for this connection */ while (true) { rnd.nextBytes(fakeCookie); /* Generate also hex representation of fake cookie */ StringBuffer tmp = new StringBuffer(32); for (int i = 0; i < fakeCookie.length; i++) { String digit2 = Integer.toHexString(fakeCookie[i] & 0xff); tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); } hexEncodedFakeCookie = tmp.toString(); /* Well, yes, chances are low, but we want to be on the safe side */ if (cm.checkX11Cookie(hexEncodedFakeCookie) == null) break; } /* Ask for X11 forwarding */ cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0); /* OK, that went fine, get ready to accept X11 connections... */ /* ... but only if the user has not called close() in the meantime =) */ synchronized (this) { if (flag_closed == false) { this.x11FakeCookie = hexEncodedFakeCookie; cm.registerX11Cookie(hexEncodedFakeCookie, x11data); } } /* Now it is safe to start remote X11 programs */ } /** * Execute a command on the remote machine. * * @param cmd * The command to execute on the remote host. * @throws IOException */ public void execCommand(String cmd) throws IOException { if (cmd == null) throw new IllegalArgumentException("cmd argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestExecCommand(cn, cmd); } /** * Start a shell on the remote machine. * * @throws IOException */ public void startShell() throws IOException { synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestShell(cn); } /** * Start a subsystem on the remote machine. * Unless you know what you are doing, you will never need this. * * @param name the name of the subsystem. * @throws IOException */ public void startSubSystem(String name) throws IOException { if (name == null) throw new IllegalArgumentException("name argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestSubSystem(cn, name); } /** * This method can be used to perform end-to-end session (i.e., SSH channel) * testing. It sends a 'ping' message to the server and waits for the 'pong' * from the server. *

* Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply * packet. * * @throws IOException in case of any problem or when the session is closed */ public void ping() throws IOException { synchronized (this) { /* * The following is just a nicer error, we would catch it anyway * later in the channel code */ if (flag_closed) return 1; throw new IOException("This session is closed."); } cm.requestChannelTrileadPing(cn); } public InputStream getStdout() { return cn.getStdoutStream(); } public InputStream getStderr() { return cn.getStderrStream(); } public OutputStream getStdin() { return cn.getStdinStream(); } /** * Write stdout received from the other side to the specified {@link OutputStream}. * *

* By default, when data arrives from the other side, trilead buffers them and lets * you read it at your convenience from {@link #getStdout()}. This is normally convenient, * but if all you are doing is to send the data to another {@link OutputStream} by * copying a stream, then you'll be end up wasting a thread just for this. * In such a situation, you can call this method and let the I/O handling thread of trilead * directly pass the received data to the output stream. This also eliminates the internal * buffer used for spooling. * *

* When you do this, beware of a blocking write! If a write blocks, it'll affect * all the other channels and sessions that are sharing the same SSH connection, * as there's only one I/O thread. For example, this can happen if you are writing to * {@link Socket}. * *

* If any data has already been received and spooled before calling this method, * the data will be sent to the given stream immediately. * *

* To signal the end of the stream, when the other side notifies us of EOF or when * the channel closes, the output stream gets closed. If this is not desirable, * you must wrap the output stream and ignore the {@link OutputStream#close()} call. */ public void pipeStdout(OutputStream os) throws IOException { cn.pipeStdoutStream(os); } /** * The same as {@link #pipeStdout(OutputStream)} except for stderr, not for stdout. */ public void pipeStderr(OutputStream os) throws IOException { cn.pipeStderrStream(os); } /** * This method blocks until there is more data available on either the * stdout or stderr InputStream of this Session. Very useful * if you do not want to use two parallel threads for reading from the two * InputStreams. One can also specify a timeout. NOTE: do NOT call this * method if you use concurrent threads that operate on either of the two * InputStreams of this Session (otherwise this method may * block, even though more data is available). * * @param timeout * The (non-negative) timeout in ms. 0 means no * timeout, the call may block forever. * @return *

    *
  • 0 if no more data will arrive.
  • *
  • 1 if more data is available.
  • *
  • -1 if a timeout occurred.
  • *
* * @throws IOException * @deprecated This method has been replaced with a much more powerful wait-for-condition * interface and therefore acts only as a wrapper. * */ public int waitUntilDataAvailable(long timeout) throws IOException, InterruptedException { if (timeout < 0) throw new IllegalArgumentException("timeout must not be negative!"); int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF); if ((conditions & ChannelCondition.TIMEOUT) != 0) return -1; if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0) /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */ if ((conditions & ChannelCondition.EOF) != 0) return 0; throw new IllegalStateException("Unexpected condition result (" + conditions + ")"); } /** * This method blocks until certain conditions hold true on the underlying SSH-2 channel. *

* This method returns as soon as one of the following happens: *

    *
  • at least of the specified conditions (see {@link ChannelCondition}) holds true
  • *
  • timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions) *
  • the underlying channel was closed (CLOSED will be set in result conditions) *
*

* In any case, the result value contains ALL current conditions, which may be more * than the specified condition set (i.e., never use the "==" operator to test for conditions * in the bitmask, see also comments in {@link ChannelCondition}). *

* Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two * InputStreams of this Session (otherwise this method may { * block, even though more data is available in the StreamGobblers). * * @param condition_set a bitmask based on {@link ChannelCondition} values * @param timeout non-negative timeout in ms, 0 means no timeout * @return all bitmask specifying all current conditions that are true */ public int waitForCondition(int condition_set, long timeout) throws InterruptedException { if (timeout < 0) throw new IllegalArgumentException("timeout must be non-negative!"); return cm.waitForCondition(cn, timeout, condition_set); } /** * Get the exit code/status from the remote command - if available. Be * careful - not all server implementations return this value. It is * generally a good idea to call this method only when all data from the * remote side has been consumed (see also the method). * * @return An Integer holding the exit code, or * null if no exit code is (yet) available. */ public Integer getExitStatus() { return cn.getExitStatus(); } /** * Get the name of the signal by which the process on the remote side was * stopped - if available and applicable. Be careful - not all server * implementations return this value. * * @return An String holding the name of the signal, or * null if the process exited normally or is still * running (or if the server forgot to send this information). */ public String getExitSignal() { return cn.getExitSignal(); } /** * Close this session. NEVER forget to call this method to free up resources - * even if you got an exception from one of the other methods (or when * getting an Exception on the Input- or OutputStreams). Sometimes these other * methods may throw an exception, saying that the underlying channel is * closed (this can happen, e.g., if the other server sent a close message.) * However, as long as you have not called the close() * method, you may be wasting (local) resources. * */ public void close() { synchronized (this) { if (flag_closed) return; flag_closed = true; if (x11FakeCookie != null) cm.unRegisterX11Cookie(x11FakeCookie, true); try { cm.closeChannel(cn, "Closed due to user request", true); } catch (IOException ignored) { } } } /** * Sets the receive window size. * * The receive window is a maximum number of bytes that the remote side can send to this channel without * waiting for us to consume them (AKA "in-flight bytes"). * * When your connection is over a large-latency/high-bandiwdth network, specifying a bigger value * allows the network to be efficiently utilized. OTOH, if you don't drain this channel quickly enough * all those bytes in flight can end up getting buffered. * * This value can be adjusted at runtime. */ public synchronized void setWindowSize(int newSize) { cn.setWindowSize(newSize); } } ======= package com.trilead.ssh2; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.SecureRandom; import com.trilead.ssh2.channel.Channel; import com.trilead.ssh2.channel.ChannelManager; } import com.trilead.ssh2.channel.X11ServerData; /** * A Session is a remote execution of a program. "Program" means * in this context either a shell, an application or a system command. The * program may or may not have a tty. Only one single program can be started on * a session. However, multiple sessions can be active simultaneously. * * @author Christian Plattner, plattner@trilead.com * @version $Id: Session.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $ */ public class Session { ChannelManager cm; Channel cn; boolean flag_pty_requested = false; boolean flag_x11_requested = false; boolean flag_execution_started = false; boolean flag_closed = false; String x11FakeCookie = null; final SecureRandom rnd; Session(ChannelManager cm, SecureRandom rnd) throws IOException { this.cm = cm; this.cn = cm.openSessionChannel(); this.rnd = rnd; } /** * Basically just a wrapper for lazy people - identical to calling * requestPTY("dumb", 0, 0, 0, 0, null). * * @throws IOException */ public void requestDumbPTY() throws IOException { requestPTY("dumb", 0, 0, 0, 0, null); } /** * Basically just another wrapper for lazy people - identical to calling * requestPTY(term, 0, 0, 0, 0, null). * * @throws IOException */ public void requestPTY(String term) throws IOException { requestPTY(term, 0, 0, 0, 0, null); } /** * Allocate a pseudo-terminal for this session. *

* This method may only be called before a program or shell is started in * this session. *

* Different aspects can be specified: *

*

    *
  • The TERM environment variable value (e.g., vt100)
  • *
  • The terminal's dimensions.
  • *
  • The encoded terminal modes.
  • *
* Zero dimension parameters are ignored. The character/row dimensions * override the pixel dimensions (when nonzero). Pixel dimensions refer to * the drawable area of the window. The dimension parameters are only * informational. The encoding of terminal modes (parameter * terminal_modes) is described in RFC4254. * * @param term * The TERM environment variable value (e.g., vt100) * @param term_width_characters * terminal width, characters (e.g., 80) * @param term_height_characters * terminal height, rows (e.g., 24) * @param term_width_pixels * terminal width, pixels (e.g., 640) * @param term_height_pixels * terminal height, pixels (e.g., 480) * @param terminal_modes * encoded terminal modes (may be null) * @throws IOException */ public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException { if (term == null) throw new IllegalArgumentException("TERM cannot be null."); if ((terminal_modes != null) && (terminal_modes.length > 0)) { if (terminal_modes[terminal_modes.length - 1] != 0) throw new IOException("Illegal terminal modes description, does not end in zero byte"); } else terminal_modes = new byte[] { 0 }; synchronized (this) /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_pty_requested) throw new IOException("A PTY was already requested."); if (flag_execution_started) throw new IOException( "Cannot request PTY at this stage anymore, a remote execution has already started."); flag_pty_requested = true; } cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, terminal_modes); } /** * Request X11 forwarding for the current session. *

* You have to supply the name and port of your X-server. *

* This method may only be called before a program or shell is started in * this session. * * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1) * @param port the port of the real (target) X11 server (e.g., 6010) * @param cookie if non-null, then present this cookie to the real X11 server * @param singleConnection if true, then the server is instructed to only forward one single * connection, no more connections shall be forwarded after first, or after the session * channel has been closed * @throws IOException */ public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection) throws IOException { if (hostname == null) throw new IllegalArgumentException("hostname argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_x11_requested) throw new IOException("X11 forwarding was already requested."); if (flag_execution_started) throw new IOException( "Cannot request X11 forwarding at this stage anymore, a remote execution has already started."); flag_x11_requested = true; } /* X11ServerData - used to store data about the target X11 server */ X11ServerData x11data = new X11ServerData(); x11data.hostname = hostname; x11data.port = port; x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */ /* Generate fake cookie - this one is used between remote clients and our proxy */ byte[] fakeCookie = new byte[16]; String hexEncodedFakeCookie; /* Make sure that this fake cookie is unique for this connection */ while (true) { rnd.nextBytes(fakeCookie); /* Generate also hex representation of fake cookie */ StringBuffer tmp = new StringBuffer(32); for (int i = 0; i < fakeCookie.length; i++) { String digit2 = Integer.toHexString(fakeCookie[i] & 0xff); tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); } hexEncodedFakeCookie = tmp.toString(); /* Well, yes, chances are low, but we want to be on the safe side */ if (cm.checkX11Cookie(hexEncodedFakeCookie) == null) break; } /* Ask for X11 forwarding */ cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0); /* OK, that went fine, get ready to accept X11 connections... */ /* ... but only if the user has not called close() in the meantime =) */ synchronized (this) { if (flag_closed == false) { this.x11FakeCookie = hexEncodedFakeCookie; cm.registerX11Cookie(hexEncodedFakeCookie, x11data); } } /* Now it is safe to start remote X11 programs */ } /** * Execute a command on the remote machine. * * @param cmd * The command to execute on the remote host. * @throws IOException */ public void execCommand(String cmd) throws IOException { if (cmd == null) throw new IllegalArgumentException("cmd argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestExecCommand(cn, cmd); } /** * Start a shell on the remote machine. * * @throws IOException */ public void startShell() throws IOException { synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestShell(cn); } /** * Start a subsystem on the remote machine. * Unless you know what you are doing, you will never need this. * * @param name the name of the subsystem. * @throws IOException */ public void startSubSystem(String name) throws IOException { if (name == null) throw new IllegalArgumentException("name argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestSubSystem(cn, name); } /** * This method can be used to perform end-to-end session (i.e., SSH channel) * testing. It sends a 'ping' message to the server and waits for the 'pong' * from the server. *

* Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply * packet. * * @throws IOException in case of any problem or when the session is closed */ public void ping() throws IOException { synchronized (this) { /* * The following is just a nicer error, we would catch it anyway * later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); } cm.requestChannelTrileadPing(cn); public InputStream getStdout() { return cn.getStdoutStream(); } public InputStream getStderr() { return cn.getStderrStream(); } public OutputStream getStdin() { return cn.getStdinStream(); } /** * This method blocks until there is more data available on either the * stdout or stderr InputStream of this Session. Very useful * if you do not want to use two parallel threads for reading from the two * InputStreams. One can also specify a timeout. NOTE: do NOT call this * method if you use concurrent threads that operate on either of the two * InputStreams of this Session (otherwise this method may * block, even though more data is available). * * @param timeout * The (non-negative) timeout in ms. 0 means no * timeout, the call may block forever. * @return *

    *
  • 0 if no more data will arrive.
  • *
  • 1 if more data is available.
  • *
  • -1 if a timeout occurred.
  • *
* * @throws IOException * @deprecated This method has been replaced with a much more powerful wait-for-condition * interface and therefore acts only as a wrapper. * */ public int waitUntilDataAvailable(long timeout) throws IOException { if (timeout < 0) throw new IllegalArgumentException("timeout must not be negative!"); int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF); if ((conditions & ChannelCondition.TIMEOUT) != 0) return -1; if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0) return 1; /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */ if ((conditions & ChannelCondition.EOF) != 0) return 0; throw new IllegalStateException("Unexpected condition result (" + conditions + ")"); } /** * This method blocks until certain conditions hold true on the underlying SSH-2 channel. *

* This method returns as soon as one of the following happens: *

    *
  • at least of the specified conditions (see {@link ChannelCondition}) holds true
  • *
  • timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions) *
  • the underlying channel was closed (CLOSED will be set in result conditions) *
*

* In any case, the result value contains ALL current conditions, which may be more * than the specified condition set (i.e., never use the "==" operator to test for conditions * in the bitmask, see also comments in {@link ChannelCondition}). *

* Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two * InputStreams of this Session (otherwise this method may * block, even though more data is available in the StreamGobblers). * * @param condition_set a bitmask based on {@link ChannelCondition} values * @param timeout non-negative timeout in ms, 0 means no timeout * @return all bitmask specifying all current conditions that are true */ public int waitForCondition(int condition_set, long timeout) { if (timeout < 0) throw new IllegalArgumentException("timeout must be non-negative!"); return cm.waitForCondition(cn, timeout, condition_set); } /** * Get the exit code/status from the remote command - if available. Be * careful - not all server implementations return this value. It is * generally a good idea to call this method only when all data from the * remote side has been consumed (see also the method). * * @return An Integer holding the exit code, or * null if no exit code is (yet) available. */ public Integer getExitStatus() { return cn.getExitStatus(); } /** * Get the name of the signal by which the process on the remote side was * stopped - if available and applicable. Be careful - not all server * implementations return this value. * * @return An String holding the name of the signal, or * null if the process exited normally or is still * running (or if the server forgot to send this information). */ public String getExitSignal() { return cn.getExitSignal(); } /** * Close this session. NEVER forget to call this method to free up resources - * even if you got an exception from one of the other methods (or when * getting an Exception on the Input- or OutputStreams). Sometimes these other * methods may throw an exception, saying that the underlying channel is * closed (this can happen, e.g., if the other server sent a close message.) * However, as long as you have not called the close() * method, you may be wasting (local) resources. * */ public void close() { synchronized (this) { if (flag_closed) return; flag_closed = true; if (x11FakeCookie != null) cm.unRegisterX11Cookie(x11FakeCookie, true); try { cm.closeChannel(cn, "Closed due to user request", true); } catch (IOException ignored) { } } } } >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Session.java

Solution content
package com.trilead.ssh2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.SecureRandom;

import com.trilead.ssh2.channel.Channel;
import com.trilead.ssh2.channel.ChannelManager;
import com.trilead.ssh2.channel.X11ServerData;


/**
 * A Session is a remote execution of a program. "Program" means
 * in this context either a shell, an application or a system command. The
 * program may or may not have a tty. Only one single program can be started on
 * a session. However, multiple sessions can be active simultaneously.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: Session.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
 */
public class Session
{
	ChannelManager cm;
	Channel cn;

	boolean flag_pty_requested = false;
	boolean flag_x11_requested = false;
	boolean flag_execution_started = false;
	boolean flag_closed = false;

	String x11FakeCookie = null;

	final SecureRandom rnd;
	
	Session(ChannelManager cm, SecureRandom rnd) throws IOException
	{
		this.cm = cm;

		this.cn = cm.openSessionChannel();
		this.rnd = rnd;
	}

	/**
	 * Basically just a wrapper for lazy people - identical to calling
	 * requestPTY("dumb", 0, 0, 0, 0, null).
	 * 
	 * @throws IOException
	 */
	public void requestDumbPTY() throws IOException
	{
		requestPTY("dumb", 0, 0, 0, 0, null);
	}

	/**
	 * Basically just another wrapper for lazy people - identical to calling
	 * requestPTY(term, 0, 0, 0, 0, null).
	 * 
	 * @throws IOException
	 */
	public void requestPTY(String term) throws IOException
	{
		requestPTY(term, 0, 0, 0, 0, null);
	}

	/**
	 * Allocate a pseudo-terminal for this session.
	 * 

* This method may only be called before a program or shell is started in * this session. *

* Different aspects can be specified: *

*

    *
  • The TERM environment variable value (e.g., vt100)
  • *
  • The terminal's dimensions.
  • *
  • The encoded terminal modes.
  • *
* Zero dimension parameters are ignored. The character/row dimensions * override the pixel dimensions (when nonzero). Pixel dimensions refer to * the drawable area of the window. The dimension parameters are only * informational. The encoding of terminal modes (parameter * terminal_modes) is described in RFC4254. * * @param term * The TERM environment variable value (e.g., vt100) * @param term_width_characters * terminal width, characters (e.g., 80) * @param term_height_characters * terminal height, rows (e.g., 24) * @param term_width_pixels * terminal width, pixels (e.g., 640) * @param term_height_pixels * terminal height, pixels (e.g., 480) * @param terminal_modes * encoded terminal modes (may be null) * @throws IOException */ public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException { if (term == null) throw new IllegalArgumentException("TERM cannot be null."); if ((terminal_modes != null) && (terminal_modes.length > 0)) { if (terminal_modes[terminal_modes.length - 1] != 0) throw new IOException("Illegal terminal modes description, does not end in zero byte"); } else terminal_modes = new byte[] { 0 }; synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_pty_requested) throw new IOException("A PTY was already requested."); if (flag_execution_started) throw new IOException( "Cannot request PTY at this stage anymore, a remote execution has already started."); flag_pty_requested = true; } cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, terminal_modes); } /** * Request X11 forwarding for the current session. *

* You have to supply the name and port of your X-server. *

* This method may only be called before a program or shell is started in * this session. * * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1) * @param port the port of the real (target) X11 server (e.g., 6010) * @param cookie if non-null, then present this cookie to the real X11 server * @param singleConnection if true, then the server is instructed to only forward one single * connection, no more connections shall be forwarded after first, or after the session * channel has been closed * @throws IOException */ public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection) throws IOException { if (hostname == null) throw new IllegalArgumentException("hostname argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_x11_requested) throw new IOException("X11 forwarding was already requested."); if (flag_execution_started) throw new IOException( "Cannot request X11 forwarding at this stage anymore, a remote execution has already started."); flag_x11_requested = true; } /* X11ServerData - used to store data about the target X11 server */ X11ServerData x11data = new X11ServerData(); x11data.hostname = hostname; x11data.port = port; x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */ /* Generate fake cookie - this one is used between remote clients and our proxy */ byte[] fakeCookie = new byte[16]; String hexEncodedFakeCookie; /* Make sure that this fake cookie is unique for this connection */ while (true) { rnd.nextBytes(fakeCookie); /* Generate also hex representation of fake cookie */ StringBuffer tmp = new StringBuffer(32); for (int i = 0; i < fakeCookie.length; i++) { String digit2 = Integer.toHexString(fakeCookie[i] & 0xff); tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); } hexEncodedFakeCookie = tmp.toString(); /* Well, yes, chances are low, but we want to be on the safe side */ if (cm.checkX11Cookie(hexEncodedFakeCookie) == null) break; } /* Ask for X11 forwarding */ cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0); /* OK, that went fine, get ready to accept X11 connections... */ /* ... but only if the user has not called close() in the meantime =) */ synchronized (this) { if (flag_closed == false) { this.x11FakeCookie = hexEncodedFakeCookie; cm.registerX11Cookie(hexEncodedFakeCookie, x11data); } } /* Now it is safe to start remote X11 programs */ } /** * Execute a command on the remote machine. * * @param cmd * The command to execute on the remote host. * @throws IOException */ public void execCommand(String cmd) throws IOException { if (cmd == null) throw new IllegalArgumentException("cmd argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestExecCommand(cn, cmd); } /** * Start a shell on the remote machine. * * @throws IOException */ public void startShell() throws IOException { synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestShell(cn); } /** * Start a subsystem on the remote machine. * Unless you know what you are doing, you will never need this. * * @param name the name of the subsystem. * @throws IOException */ public void startSubSystem(String name) throws IOException { if (name == null) throw new IllegalArgumentException("name argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestSubSystem(cn, name); } /** * This method can be used to perform end-to-end session (i.e., SSH channel) * testing. It sends a 'ping' message to the server and waits for the 'pong' * from the server. *

* Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply * packet. * * @throws IOException in case of any problem or when the session is closed */ public void ping() throws IOException { synchronized (this) { /* * The following is just a nicer error, we would catch it anyway * later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); } cm.requestChannelTrileadPing(cn); } public InputStream getStdout() { return cn.getStdoutStream(); } public InputStream getStderr() { return cn.getStderrStream(); } public OutputStream getStdin() { return cn.getStdinStream(); } /** * Write stdout received from the other side to the specified {@link OutputStream}. * *

* By default, when data arrives from the other side, trilead buffers them and lets * you read it at your convenience from {@link #getStdout()}. This is normally convenient, * but if all you are doing is to send the data to another {@link OutputStream} by * copying a stream, then you'll be end up wasting a thread just for this. * In such a situation, you can call this method and let the I/O handling thread of trilead * directly pass the received data to the output stream. This also eliminates the internal * buffer used for spooling. * *

* When you do this, beware of a blocking write! If a write blocks, it'll affect * all the other channels and sessions that are sharing the same SSH connection, * as there's only one I/O thread. For example, this can happen if you are writing to * {@link Socket}. * *

* If any data has already been received and spooled before calling this method, * the data will be sent to the given stream immediately. * *

* To signal the end of the stream, when the other side notifies us of EOF or when * the channel closes, the output stream gets closed. If this is not desirable, * you must wrap the output stream and ignore the {@link OutputStream#close()} call. */ public void pipeStdout(OutputStream os) throws IOException { cn.pipeStdoutStream(os); } /** * The same as {@link #pipeStdout(OutputStream)} except for stderr, not for stdout. */ public void pipeStderr(OutputStream os) throws IOException { cn.pipeStderrStream(os); } /** * This method blocks until there is more data available on either the * stdout or stderr InputStream of this Session. Very useful * if you do not want to use two parallel threads for reading from the two * InputStreams. One can also specify a timeout. NOTE: do NOT call this * method if you use concurrent threads that operate on either of the two * InputStreams of this Session (otherwise this method may * block, even though more data is available). * * @param timeout * The (non-negative) timeout in ms. 0 means no * timeout, the call may block forever. * @return *

    *
  • 0 if no more data will arrive.
  • *
  • 1 if more data is available.
  • *
  • -1 if a timeout occurred.
  • *
* * @throws IOException * @deprecated This method has been replaced with a much more powerful wait-for-condition * interface and therefore acts only as a wrapper. * */ public int waitUntilDataAvailable(long timeout) throws IOException, InterruptedException { if (timeout < 0) throw new IllegalArgumentException("timeout must not be negative!"); int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF); if ((conditions & ChannelCondition.TIMEOUT) != 0) return -1; if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0) return 1; /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */ if ((conditions & ChannelCondition.EOF) != 0) return 0; throw new IllegalStateException("Unexpected condition result (" + conditions + ")"); } /** * This method blocks until certain conditions hold true on the underlying SSH-2 channel. *

* This method returns as soon as one of the following happens: *

    *
  • at least of the specified conditions (see {@link ChannelCondition}) holds true
  • *
  • timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions) *
  • the underlying channel was closed (CLOSED will be set in result conditions) *
*

* In any case, the result value contains ALL current conditions, which may be more * than the specified condition set (i.e., never use the "==" operator to test for conditions * in the bitmask, see also comments in {@link ChannelCondition}). *

* Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two * InputStreams of this Session (otherwise this method may * block, even though more data is available in the StreamGobblers). * * @param condition_set a bitmask based on {@link ChannelCondition} values * @param timeout non-negative timeout in ms, 0 means no timeout * @return all bitmask specifying all current conditions that are true */ public int waitForCondition(int condition_set, long timeout) throws InterruptedException { if (timeout < 0) throw new IllegalArgumentException("timeout must be non-negative!"); return cm.waitForCondition(cn, timeout, condition_set); } /** * Get the exit code/status from the remote command - if available. Be * careful - not all server implementations return this value. It is * generally a good idea to call this method only when all data from the * remote side has been consumed (see also the method). * * @return An Integer holding the exit code, or * null if no exit code is (yet) available. */ public Integer getExitStatus() { return cn.getExitStatus(); } /** * Get the name of the signal by which the process on the remote side was * stopped - if available and applicable. Be careful - not all server * implementations return this value. * * @return An String holding the name of the signal, or * null if the process exited normally or is still * running (or if the server forgot to send this information). */ public String getExitSignal() { return cn.getExitSignal(); } /** * Close this session. NEVER forget to call this method to free up resources - * even if you got an exception from one of the other methods (or when * getting an Exception on the Input- or OutputStreams). Sometimes these other * methods may throw an exception, saying that the underlying channel is * closed (this can happen, e.g., if the other server sent a close message.) * However, as long as you have not called the close() * method, you may be wasting (local) resources. * */ public void close() { synchronized (this) { if (flag_closed) return; flag_closed = true; if (x11FakeCookie != null) cm.unRegisterX11Cookie(x11FakeCookie, true); try { cm.closeChannel(cn, "Closed due to user request", true); } catch (IOException ignored) { } } } /** * Sets the receive window size. * * The receive window is a maximum number of bytes that the remote side can send to this channel without * waiting for us to consume them (AKA "in-flight bytes"). * * When your connection is over a large-latency/high-bandiwdth network, specifying a bigger value * allows the network to be efficiently utilized. OTOH, if you don't drain this channel quickly enough * all those bytes in flight can end up getting buffered. * * This value can be adjusted at runtime. */ public synchronized void setWindowSize(int newSize) { cn.setWindowSize(newSize); } }

File
Session.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
 */
<<<<<<< HEAD:src/com/trilead/ssh2/StreamGobbler.java

package com.trilead.ssh2;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;

/**
 * A StreamGobbler is an InputStream that uses an internal worker
 * thread to constantly consume input from another InputStream. It uses a buffer

 * to store the consumed data. The buffer size is automatically adjusted, if needed.
 * 

* This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR * InputStreams with instances of this class, then you don't have to bother about * the shared window of STDOUT and STDERR in the low level SSH-2 protocol, * since all arriving data will be immediatelly consumed by the worker threads. * Also, as a side effect, the streams will be buffered (e.g., single byte * read() operations are faster). *

* Other SSH for Java libraries include this functionality by default in * their STDOUT and STDERR InputStream implementations, however, please be aware * that this approach has also a downside: *

* If you do not call the StreamGobbler's read() method often enough * and the peer is constantly sending huge amounts of data, then you will sooner or later * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size). * Joe Average will like this class anyway - a paranoid programmer would never use such an approach. *

* The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't", * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html. * * @author Christian Plattner, plattner@trilead.com * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ public class StreamGobbler extends InputStream { class GobblerThread extends Thread { public void run() { byte[] buff = new byte[8192]; while (true) { try { int avail = is.read(buff); synchronized (synchronizer) { if (avail <= 0) { isEOF = true; synchronizer.notifyAll(); break; } int space_available = buffer.length - write_pos; if (space_available < avail) { /* compact/resize buffer */ int unread_size = write_pos - read_pos; int need_space = unread_size + avail; byte[] new_buffer = buffer; if (need_space > buffer.length) { int inc = need_space / 3; inc = (inc < 256) ? 256 : inc; inc = (inc > 8192) ? 8192 : inc; new_buffer = new byte[need_space + inc]; } if (unread_size > 0) System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size); buffer = new_buffer; read_pos = 0; write_pos = unread_size; } System.arraycopy(buff, 0, buffer, write_pos, avail); write_pos += avail; synchronizer.notifyAll(); } } catch (IOException e) { synchronized (synchronizer) { exception = e; synchronizer.notifyAll(); break; } } } } } private InputStream is; private final GobblerThread t; private final Object synchronizer = new Object(); private boolean isEOF = false; private boolean isClosed = false; private IOException exception = null; private byte[] buffer = new byte[2048]; private int read_pos = 0; private int write_pos = 0; public StreamGobbler(InputStream is) { this.is = is; t = new GobblerThread(); t.setDaemon(true); t.start(); } public int read() throws IOException { synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); while (read_pos == write_pos) { if (exception != null) throw exception; if (isEOF) return -1; try { synchronizer.wait(); } catch (InterruptedException e) { throw new InterruptedIOException(); } } int b = buffer[read_pos++] & 0xff; return b; } } public int available() throws IOException { synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); return write_pos - read_pos; } } public int read(byte[] b) throws IOException { return read(b, 0, b.length); } public void close() throws IOException { synchronized (synchronizer) { if (isClosed) return; isClosed = true; isEOF = true; synchronizer.notifyAll(); is.close(); } } public int read(byte[] b, int off, int len) throws IOException { if (b == null) throw new NullPointerException(); if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length)) throw new IndexOutOfBoundsException(); if (len == 0) return 0; synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); while (read_pos == write_pos) { if (exception != null) throw exception; if (isEOF) return -1; try { synchronizer.wait(); } catch (InterruptedException e) { throw new InterruptedIOException(); } } int avail = write_pos - read_pos; avail = (avail > len) ? len : avail; System.arraycopy(buffer, read_pos, b, off, avail); read_pos += avail; return avail; } } } ======= package com.trilead.ssh2; import java.io.IOException; import java.io.InputStream; /** private IOException exception = null; * A StreamGobbler is an InputStream that uses an internal worker * thread to constantly consume input from another InputStream. It uses a buffer * to store the consumed data. The buffer size is automatically adjusted, if needed. *

* This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR * InputStreams with instances of this class, then you don't have to bother about * the shared window of STDOUT and STDERR in the low level SSH-2 protocol, * since all arriving data will be immediatelly consumed by the worker threads. * Also, as a side effect, the streams will be buffered (e.g., single byte * read() operations are faster). *

* Other SSH for Java libraries include this functionality by default in * their STDOUT and STDERR InputStream implementations, however, please be aware * that this approach has also a downside: *

* If you do not call the StreamGobbler's read() method often enough * and the peer is constantly sending huge amounts of data, then you will sooner or later * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size). * Joe Average will like this class anyway - a paranoid programmer would never use such an approach. *

* The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't", * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html. * * @author Christian Plattner, plattner@trilead.com } * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ */ public class StreamGobbler extends InputStream { class GobblerThread extends Thread { public void run() { byte[] buff = new byte[8192]; while (true) { try { int avail = is.read(buff); synchronized (synchronizer) { if (avail <= 0) { isEOF = true; synchronizer.notifyAll(); break; } int space_available = buffer.length - write_pos; if (space_available < avail) { /* compact/resize buffer */ int unread_size = write_pos - read_pos; int need_space = unread_size + avail; byte[] new_buffer = buffer; if (need_space > buffer.length) { int inc = need_space / 3; inc = (inc < 256) ? 256 : inc; inc = (inc > 8192) ? 8192 : inc; new_buffer = new byte[need_space + inc]; } if (unread_size > 0) System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size); buffer = new_buffer; read_pos = 0; write_pos = unread_size; } System.arraycopy(buff, 0, buffer, write_pos, avail); write_pos += avail; synchronizer.notifyAll(); } } catch (IOException e) { synchronized (synchronizer) { exception = e; synchronizer.notifyAll(); break; } } } } } private InputStream is; private GobblerThread t; private Object synchronizer = new Object(); private boolean isEOF = false; private boolean isClosed = false; private byte[] buffer = new byte[2048]; private int read_pos = 0; private int write_pos = 0; public StreamGobbler(InputStream is) { this.is = is; t = new GobblerThread(); t.setDaemon(true); t.start(); } public int read() throws IOException { synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); while (read_pos == write_pos) { if (exception != null) throw exception; if (isEOF) return -1; try { synchronizer.wait(); } catch (InterruptedException e) { } } int b = buffer[read_pos++] & 0xff; return b; } public int available() throws IOException { synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); return write_pos - read_pos; } } public int read(byte[] b) throws IOException { return read(b, 0, b.length); } public void close() throws IOException { synchronized (synchronizer) { if (isClosed) return; isClosed = true; isEOF = true; synchronizer.notifyAll(); is.close(); } } public int read(byte[] b, int off, int len) throws IOException { if (b == null) throw new NullPointerException(); if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length)) throw new IndexOutOfBoundsException(); if (len == 0) return 0; synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); while (read_pos == write_pos) { if (exception != null) throw exception; if (isEOF) return -1; try { synchronizer.wait(); } catch (InterruptedException e) { } } int avail = write_pos - read_pos; avail = (avail > len) ? len : avail; System.arraycopy(buffer, read_pos, b, off, avail); read_pos += avail; return avail; } } } >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/StreamGobbler.java

Solution content
package com.trilead.ssh2;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;

/**
 * A StreamGobbler is an InputStream that uses an internal worker
 * thread to constantly consume input from another InputStream. It uses a buffer
 * to store the consumed data. The buffer size is automatically adjusted, if needed.
 * 

* This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR * InputStreams with instances of this class, then you don't have to bother about * the shared window of STDOUT and STDERR in the low level SSH-2 protocol, * since all arriving data will be immediatelly consumed by the worker threads. * Also, as a side effect, the streams will be buffered (e.g., single byte * read() operations are faster). *

* Other SSH for Java libraries include this functionality by default in * their STDOUT and STDERR InputStream implementations, however, please be aware * that this approach has also a downside: *

* If you do not call the StreamGobbler's read() method often enough * and the peer is constantly sending huge amounts of data, then you will sooner or later * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size). * Joe Average will like this class anyway - a paranoid programmer would never use such an approach. *

* The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't", * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html. * * @author Christian Plattner, plattner@trilead.com * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ */ public class StreamGobbler extends InputStream { class GobblerThread extends Thread { public void run() { byte[] buff = new byte[8192]; while (true) { try { int avail = is.read(buff); synchronized (synchronizer) { if (avail <= 0) { isEOF = true; synchronizer.notifyAll(); break; } int space_available = buffer.length - write_pos; if (space_available < avail) { /* compact/resize buffer */ int unread_size = write_pos - read_pos; int need_space = unread_size + avail; byte[] new_buffer = buffer; if (need_space > buffer.length) { int inc = need_space / 3; inc = (inc < 256) ? 256 : inc; inc = (inc > 8192) ? 8192 : inc; new_buffer = new byte[need_space + inc]; } if (unread_size > 0) System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size); buffer = new_buffer; read_pos = 0; write_pos = unread_size; } System.arraycopy(buff, 0, buffer, write_pos, avail); write_pos += avail; synchronizer.notifyAll(); } } catch (IOException e) { synchronized (synchronizer) { exception = e; synchronizer.notifyAll(); break; } } } } } private InputStream is; private final GobblerThread t; private final Object synchronizer = new Object(); private boolean isEOF = false; private boolean isClosed = false; private IOException exception = null; private byte[] buffer = new byte[2048]; private int read_pos = 0; private int write_pos = 0; public StreamGobbler(InputStream is) { this.is = is; t = new GobblerThread(); t.setDaemon(true); t.start(); } public int read() throws IOException { synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); while (read_pos == write_pos) { if (exception != null) throw exception; if (isEOF) return -1; try { synchronizer.wait(); } catch (InterruptedException e) { throw new InterruptedIOException(); } } int b = buffer[read_pos++] & 0xff; return b; } } public int available() throws IOException { synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); return write_pos - read_pos; } } public int read(byte[] b) throws IOException { return read(b, 0, b.length); } public void close() throws IOException { synchronized (synchronizer) { if (isClosed) return; isClosed = true; isEOF = true; synchronizer.notifyAll(); is.close(); } } public int read(byte[] b, int off, int len) throws IOException { if (b == null) throw new NullPointerException(); if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length)) throw new IndexOutOfBoundsException(); if (len == 0) return 0; synchronized (synchronizer) { if (isClosed) throw new IOException("This StreamGobbler is closed."); while (read_pos == write_pos) { if (exception != null) throw exception; if (isEOF) return -1; try { synchronizer.wait(); } catch (InterruptedException e) { throw new InterruptedIOException(); } } int avail = write_pos - read_pos; avail = (avail > len) ? len : avail; System.arraycopy(buffer, read_pos, b, off, avail); read_pos += avail; return avail; } } }

File
StreamGobbler.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
 * 
				}
<<<<<<< HEAD:src/com/trilead/ssh2/auth/AuthenticationManager.java

package com.trilead.ssh2.auth;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.security.SecureRandom;
import java.util.Vector;

import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.crypto.PEMDecoder;
import com.trilead.ssh2.packets.PacketServiceAccept;
import com.trilead.ssh2.packets.PacketServiceRequest;
import com.trilead.ssh2.packets.PacketUserauthBanner;
import com.trilead.ssh2.packets.PacketUserauthFailure;
import com.trilead.ssh2.packets.PacketUserauthInfoRequest;
import com.trilead.ssh2.packets.PacketUserauthInfoResponse;
import com.trilead.ssh2.packets.PacketUserauthRequestInteractive;
import com.trilead.ssh2.packets.PacketUserauthRequestNone;
import com.trilead.ssh2.packets.PacketUserauthRequestPassword;
import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.packets.TypesWriter;
import com.trilead.ssh2.signature.DSAPrivateKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPrivateKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;


/**
 * AuthenticationManager.
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 */
public class AuthenticationManager implements MessageHandler
{
	TransportManager tm;

	Vector packets = new Vector();
	boolean connectionClosed = false;

	String banner;

	String[] remainingMethods = new String[0];
	boolean isPartialSuccess = false;

	boolean authenticated = false;
	boolean initDone = false;

	public AuthenticationManager(TransportManager tm)
	{
		this.tm = tm;
	}

	boolean methodPossible(String methName)
	{
		if (remainingMethods == null)
			return false;

		for (int i = 0; i < remainingMethods.length; i++)
		{
			if (remainingMethods[i].compareTo(methName) == 0)
				return true;
		}
		return false;
	}

	byte[] deQueue() throws IOException
	{
		synchronized (packets)
		{
			while (packets.size() == 0)
			{
				if (connectionClosed)
					throw (IOException) new IOException("The connection is closed.").initCause(tm
							.getReasonClosedCause());

				try
				{
					packets.wait();
				}
				catch (InterruptedException ign)
				{
                    throw new InterruptedIOException();
				}
			}
			/* This sequence works with J2ME */
			byte[] res = (byte[]) packets.firstElement();
			packets.removeElementAt(0);
			return res;
		}
	}

	byte[] getNextMessage() throws IOException
	{
		while (true)
		{
			byte[] msg = deQueue();

			if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
				return msg;

			PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);

			banner = sb.getBanner();
		}
	}

	public String[] getRemainingMethods(String user) throws IOException
	{
		initialize(user);
		return remainingMethods;
	}

	public boolean getPartialSuccess()
	{
		return isPartialSuccess;
	}

	private boolean initialize(String user) throws IOException
	{
		if (initDone == false)
		{
			tm.registerMessageHandler(this, 0, 255);

			PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
			tm.sendMessage(sr.getPayload());

			PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
			tm.sendMessage(urn.getPayload());

			byte[] msg = getNextMessage();
			new PacketServiceAccept(msg, 0, msg.length);
			msg = getNextMessage();

			initDone = true;

			if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{

				PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();
				return false;
			}

			throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
		}
		return authenticated;
	}

	public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
			throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("publickey") == false)
				throw new IOException("Authentication method publickey not supported by the server at this stage.");

			Object key = PEMDecoder.decode(PEMPrivateKey, password);

			if (key instanceof DSAPrivateKey)
			{
				DSAPrivateKey pk = (DSAPrivateKey) key;

				byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());

				TypesWriter tw = new TypesWriter();

				byte[] H = tm.getSessionIdentifier();

				tw.writeString(H, 0, H.length);
				tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
				tw.writeString(user);
				tw.writeString("ssh-connection");
				tw.writeString("publickey");
				tw.writeBoolean(true);
				tw.writeString("ssh-dss");
				tw.writeString(pk_enc, 0, pk_enc.length);

				byte[] msg = tw.getBytes();

				DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);

				byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);

				PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
						"ssh-dss", pk_enc, ds_enc);
				tm.sendMessage(ua.getPayload());
			}
			else if (key instanceof RSAPrivateKey)
			{
				RSAPrivateKey pk = (RSAPrivateKey) key;

				byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());

				TypesWriter tw = new TypesWriter();
				{
					byte[] H = tm.getSessionIdentifier();

					tw.writeString(H, 0, H.length);
					tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
					tw.writeString(user);
					tw.writeString("ssh-connection");
					tw.writeString("publickey");
					tw.writeBoolean(true);
					tw.writeString("ssh-rsa");
					tw.writeString(pk_enc, 0, pk_enc.length);

				byte[] msg = tw.getBytes();

				RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);

				byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);

				PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
						"ssh-rsa", pk_enc, rsa_sig_enc);
				tm.sendMessage(ua.getPayload());
			}
			else
			{
				throw new IOException("Unknown private key type returned by the PEM decoder.");
			}

			byte[] ar = getNextMessage();

			if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{
				PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();

				return false;
			}
			throw new IOException("Unexpected SSH message (type " + ar[0] + ")");

		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
		}
	}

	public boolean authenticateNone(String user) throws IOException
	{
		try
		{
			initialize(user);
			return authenticated;
		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("None authentication failed.").initCause(e);
		}
	}

	public boolean authenticatePassword(String user, String pass) throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("password") == false)
				throw new IOException("Authentication method password not supported by the server at this stage.");

			PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
			tm.sendMessage(ua.getPayload());

			byte[] ar = getNextMessage();

			if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{
				PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();

				return false;
			}

			throw new IOException("Unexpected SSH message (type " + ar[0] + ")");

		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Password authentication failed.").initCause(e);
		}
	}

	public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("keyboard-interactive") == false)
				throw new IOException(
						"Authentication method keyboard-interactive not supported by the server at this stage.");

			if (submethods == null)
				submethods = new String[0];

			PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
					submethods);

			tm.sendMessage(ua.getPayload());

			while (true)
			{
				byte[] ar = getNextMessage();

				if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
				{
					authenticated = true;
					tm.removeMessageHandler(this, 0, 255);
					return true;
				}

				if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
				{
					PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

					remainingMethods = puf.getAuthThatCanContinue();
					isPartialSuccess = puf.isPartialSuccess();

					return false;
				}

				if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
				{
					PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);

					String[] responses;

					try
					{
						responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
								.getPrompt(), pui.getEcho());
					}
					catch (Exception e)
					{
						throw (IOException) new IOException("Exception in callback.").initCause(e);
					}

					if (responses == null)
						throw new IOException("Your callback may not return NULL!");

					PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
					tm.sendMessage(puir.getPayload());

					continue;
				}

				throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
			}
		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
		}
	}

	public void handleMessage(byte[] msg, int msglen) throws IOException
	{
		synchronized (packets)
		{
			if (msg == null)
			{
				connectionClosed = true;
			}
			else
			{
				byte[] tmp = new byte[msglen];
				System.arraycopy(msg, 0, tmp, 0, msglen);
				packets.addElement(tmp);
			}

			packets.notifyAll();

			if (packets.size() > 5)
			{
				connectionClosed = true;
				throw new IOException("Error, peer is flooding us with authentication packets.");
			}
		}
	}
}
=======

package com.trilead.ssh2.auth;

import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.crypto.PEMDecoder;
import com.trilead.ssh2.packets.*;
import com.trilead.ssh2.signature.*;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;

import java.io.IOException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Vector;


/**
 * AuthenticationManager.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 */
public class AuthenticationManager implements MessageHandler
{
	TransportManager tm;

	Vector packets = new Vector();
	boolean connectionClosed = false;

	String banner;

	String[] remainingMethods = new String[0];
	boolean isPartialSuccess = false;

	boolean authenticated = false;
	boolean initDone = false;

	public AuthenticationManager(TransportManager tm)
	{
		this.tm = tm;
	}

	boolean methodPossible(String methName)
	{
		if (remainingMethods == null)
			return false;

		for (int i = 0; i < remainingMethods.length; i++)
		{
			if (remainingMethods[i].compareTo(methName) == 0)
				return true;
		}
		return false;
	}

	byte[] deQueue() throws IOException
	{
		synchronized (packets)
		{
			while (packets.size() == 0)
			{

				if (connectionClosed)
					throw (IOException) new IOException("The connection is closed.").initCause(tm
							.getReasonClosedCause());

				try
				{
					packets.wait();
				}
				catch (InterruptedException ign)
				{
				}
			}
			/* This sequence works with J2ME */
			byte[] res = (byte[]) packets.firstElement();
			packets.removeElementAt(0);
			return res;
		}
	}

	byte[] getNextMessage() throws IOException
	{
		while (true)
		{
			byte[] msg = deQueue();

			if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
				return msg;

			PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);

			banner = sb.getBanner();
		}
	}

	public String[] getRemainingMethods(String user) throws IOException
	{
		initialize(user);
		return remainingMethods;
	}

	public boolean getPartialSuccess()
	{
		return isPartialSuccess;
	}

	private boolean initialize(String user) throws IOException
	{
		if (initDone == false)
		{
			tm.registerMessageHandler(this, 0, 255);

			PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
			tm.sendMessage(sr.getPayload());

			PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
			tm.sendMessage(urn.getPayload());

			byte[] msg = getNextMessage();
			new PacketServiceAccept(msg, 0, msg.length);
			msg = getNextMessage();

			initDone = true;

			if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{
				PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();
				return false;
			}

			throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
		}
		return authenticated;
	}

	public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException {
		initialize(user);

		boolean success = false;
		Iterator agentIdentities = proxy.getIdentities().iterator();
		while(agentIdentities.hasNext()) {
			AgentIdentity identity = (AgentIdentity)agentIdentities.next();
			success = authenticatePublicKey(user, proxy, identity);
			if(success) {
				return true;
			}
		}
		return false;
	}

	boolean authenticatePublicKey(String user, AgentProxy proxy, AgentIdentity identity) throws IOException {

		if (methodPossible("publickey") == false)
			throw new IOException("Authentication method publickey not supported by the server at this stage.");

		byte[] pubKeyBlob = identity.getPublicKeyBlob();
		if(pubKeyBlob == null) {
			return false;
		}

		TypesWriter tw = new TypesWriter();
		byte[] H = tm.getSessionIdentifier();

		tw.writeString(H, 0, H.length);
		tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
		tw.writeString(user);
		tw.writeString("ssh-connection");
		tw.writeString("publickey");
		tw.writeBoolean(true);
		tw.writeString(identity.getAlgName());
		tw.writeString(pubKeyBlob, 0, pubKeyBlob.length);

		byte[] msg = tw.getBytes();
		byte[] response = identity.sign(msg);

		PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey(
				"ssh-connection", user, identity.getAlgName(), pubKeyBlob, response);
		tm.sendMessage(ua.getPayload());

		byte[] ar = getNextMessage();

		if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
		{
			authenticated = true;
			tm.removeMessageHandler(this, 0, 255);
			return true;
		}

		if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
		{
			PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

			remainingMethods = puf.getAuthThatCanContinue();
			isPartialSuccess = puf.isPartialSuccess();

			return false;
		}

		throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
	}


	public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
			throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("publickey") == false)
				throw new IOException("Authentication method publickey not supported by the server at this stage.");

			Object key = PEMDecoder.decode(PEMPrivateKey, password);

			if (key instanceof DSAPrivateKey)
			{
				DSAPrivateKey pk = (DSAPrivateKey) key;

				byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());

				TypesWriter tw = new TypesWriter();

				byte[] H = tm.getSessionIdentifier();

				tw.writeString(H, 0, H.length);
				tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
				tw.writeString(user);
				tw.writeString("ssh-connection");
				tw.writeString("publickey");
				tw.writeBoolean(true);
				tw.writeString("ssh-dss");
				tw.writeString(pk_enc, 0, pk_enc.length);

				byte[] msg = tw.getBytes();

				DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);

				byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);

				PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
						"ssh-dss", pk_enc, ds_enc);
				tm.sendMessage(ua.getPayload());
			}
			else if (key instanceof RSAPrivateKey)
			{
				RSAPrivateKey pk = (RSAPrivateKey) key;

				byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());

				TypesWriter tw = new TypesWriter();
				{
					byte[] H = tm.getSessionIdentifier();

					tw.writeString(H, 0, H.length);
					tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
					tw.writeString(user);
					tw.writeString("ssh-connection");
					tw.writeString("publickey");
					tw.writeBoolean(true);
					tw.writeString("ssh-rsa");
					tw.writeString(pk_enc, 0, pk_enc.length);
				}

				byte[] msg = tw.getBytes();

				RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);

				byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
				PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
						"ssh-rsa", pk_enc, rsa_sig_enc);
				tm.sendMessage(ua.getPayload());
			}
			else
			{
				throw new IOException("Unknown private key type returned by the PEM decoder.");
			}

			byte[] ar = getNextMessage();

			if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{
				PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();

				return false;
			}

			throw new IOException("Unexpected SSH message (type " + ar[0] + ")");

		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
		}
	}

	public boolean authenticateNone(String user) throws IOException
	{
		try
		{
			initialize(user);
			return authenticated;
		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("None authentication failed.").initCause(e);
		}
	}

	public boolean authenticatePassword(String user, String pass) throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("password") == false)
				throw new IOException("Authentication method password not supported by the server at this stage.");

			PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
			tm.sendMessage(ua.getPayload());

			byte[] ar = getNextMessage();

			if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{
				PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();

				return false;
			}

			throw new IOException("Unexpected SSH message (type " + ar[0] + ")");

		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Password authentication failed.").initCause(e);
		}
	}

	public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("keyboard-interactive") == false)
				throw new IOException(
						"Authentication method keyboard-interactive not supported by the server at this stage.");

			if (submethods == null)
				submethods = new String[0];

			PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
					submethods);

			tm.sendMessage(ua.getPayload());

			while (true)
			{
				byte[] ar = getNextMessage();

				if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
				{
					authenticated = true;
					tm.removeMessageHandler(this, 0, 255);
					return true;
				}

				if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
				{
					PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

					remainingMethods = puf.getAuthThatCanContinue();
					isPartialSuccess = puf.isPartialSuccess();

					return false;
				}

				if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
				{
					PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);

					String[] responses;

					try
					{
						responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
								.getPrompt(), pui.getEcho());
					}
					catch (Exception e)
					{
						throw (IOException) new IOException("Exception in callback.").initCause(e);
					}

					if (responses == null)
						throw new IOException("Your callback may not return NULL!");

					PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
					tm.sendMessage(puir.getPayload());

					continue;
				}

				throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
			}
		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
		}
	}

	public void handleMessage(byte[] msg, int msglen) throws IOException
	{
		synchronized (packets)
		{
			if (msg == null)
			{
				connectionClosed = true;
			}
			else
			{
				byte[] tmp = new byte[msglen];
				System.arraycopy(msg, 0, tmp, 0, msglen);
				packets.addElement(tmp);
			}

			packets.notifyAll();

			if (packets.size() > 5)
			{
				connectionClosed = true;
				throw new IOException("Error, peer is flooding us with authentication packets.");
			}
		}
	}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java
Solution content
package com.trilead.ssh2.auth;

import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.crypto.PEMDecoder;
import com.trilead.ssh2.packets.*;
import com.trilead.ssh2.signature.*;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Vector;


/**
 * AuthenticationManager.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 */
public class AuthenticationManager implements MessageHandler
{
	TransportManager tm;

	Vector packets = new Vector();
	boolean connectionClosed = false;

	String banner;

	String[] remainingMethods = new String[0];
	boolean isPartialSuccess = false;

	boolean authenticated = false;
	boolean initDone = false;

	public AuthenticationManager(TransportManager tm)
	{
		this.tm = tm;
	}

	boolean methodPossible(String methName)
	{
		if (remainingMethods == null)
			return false;

		for (int i = 0; i < remainingMethods.length; i++)
		{
			if (remainingMethods[i].compareTo(methName) == 0)
				return true;
		}
		return false;
	}

	byte[] deQueue() throws IOException
	{
		synchronized (packets)
		{
			while (packets.size() == 0)
			{
				if (connectionClosed)
					throw (IOException) new IOException("The connection is closed.").initCause(tm
							.getReasonClosedCause());

				try
				{
					packets.wait();
				}
				catch (InterruptedException ign)
				{
                    throw new InterruptedIOException();
				}
			}
			/* This sequence works with J2ME */
			byte[] res = (byte[]) packets.firstElement();
			packets.removeElementAt(0);
			return res;
		}
	}

	byte[] getNextMessage() throws IOException
	{
		while (true)
		{
			byte[] msg = deQueue();

			if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
				return msg;

			PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);

			banner = sb.getBanner();
		}
	}

	public String[] getRemainingMethods(String user) throws IOException
	{
		initialize(user);
		return remainingMethods;
	}

	public boolean getPartialSuccess()
	{
		return isPartialSuccess;
	}

	private boolean initialize(String user) throws IOException
	{
		if (initDone == false)
		{
			tm.registerMessageHandler(this, 0, 255);

			PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
			tm.sendMessage(sr.getPayload());

			PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
			tm.sendMessage(urn.getPayload());

			byte[] msg = getNextMessage();
			new PacketServiceAccept(msg, 0, msg.length);
			msg = getNextMessage();

			initDone = true;

			if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{
				PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();
				return false;
			}

			throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
		}
		return authenticated;
	}

	public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException {
		initialize(user);

		boolean success = false;
		Iterator agentIdentities = proxy.getIdentities().iterator();
		while(agentIdentities.hasNext()) {
			AgentIdentity identity = (AgentIdentity)agentIdentities.next();
			success = authenticatePublicKey(user, proxy, identity);
			if(success) {
				return true;
			}
		}
		return false;
	}

	boolean authenticatePublicKey(String user, AgentProxy proxy, AgentIdentity identity) throws IOException {

		if (methodPossible("publickey") == false)
			throw new IOException("Authentication method publickey not supported by the server at this stage.");

		byte[] pubKeyBlob = identity.getPublicKeyBlob();
		if(pubKeyBlob == null) {
			return false;
		}

		TypesWriter tw = new TypesWriter();
		byte[] H = tm.getSessionIdentifier();

		tw.writeString(H, 0, H.length);
		tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
		tw.writeString(user);
		tw.writeString("ssh-connection");
		tw.writeString("publickey");
		tw.writeBoolean(true);
		tw.writeString(identity.getAlgName());
		tw.writeString(pubKeyBlob, 0, pubKeyBlob.length);

		byte[] msg = tw.getBytes();
		byte[] response = identity.sign(msg);

		PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey(
				"ssh-connection", user, identity.getAlgName(), pubKeyBlob, response);
		tm.sendMessage(ua.getPayload());

		byte[] ar = getNextMessage();

		if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
		{
			authenticated = true;
			tm.removeMessageHandler(this, 0, 255);
			return true;
		}

		if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
		{
			PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

			remainingMethods = puf.getAuthThatCanContinue();
			isPartialSuccess = puf.isPartialSuccess();

			return false;
		}

		throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
	}


	public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
			throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("publickey") == false)
				throw new IOException("Authentication method publickey not supported by the server at this stage.");

			Object key = PEMDecoder.decode(PEMPrivateKey, password);

			if (key instanceof DSAPrivateKey)
			{
				DSAPrivateKey pk = (DSAPrivateKey) key;

				byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());

				TypesWriter tw = new TypesWriter();

				byte[] H = tm.getSessionIdentifier();

				tw.writeString(H, 0, H.length);
				tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
				tw.writeString(user);
				tw.writeString("ssh-connection");
				tw.writeString("publickey");
				tw.writeBoolean(true);
				tw.writeString("ssh-dss");
				tw.writeString(pk_enc, 0, pk_enc.length);

				byte[] msg = tw.getBytes();

				DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);

				byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);

				PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
						"ssh-dss", pk_enc, ds_enc);
				tm.sendMessage(ua.getPayload());
			}
			else if (key instanceof RSAPrivateKey)
			{
				RSAPrivateKey pk = (RSAPrivateKey) key;

				byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());

				TypesWriter tw = new TypesWriter();
				{
					byte[] H = tm.getSessionIdentifier();

					tw.writeString(H, 0, H.length);
					tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
					tw.writeString(user);
					tw.writeString("ssh-connection");
					tw.writeString("publickey");
					tw.writeBoolean(true);
					tw.writeString("ssh-rsa");
					tw.writeString(pk_enc, 0, pk_enc.length);
				}

				byte[] msg = tw.getBytes();

				RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);

				byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);

				PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
						"ssh-rsa", pk_enc, rsa_sig_enc);
				tm.sendMessage(ua.getPayload());
			}
			else
			{
				throw new IOException("Unknown private key type returned by the PEM decoder.");
			}

			byte[] ar = getNextMessage();

			if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{
				PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();

				return false;
			}

			throw new IOException("Unexpected SSH message (type " + ar[0] + ")");

		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
		}
	}

	public boolean authenticateNone(String user) throws IOException
	{
		try
		{
			initialize(user);
			return authenticated;
		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("None authentication failed.").initCause(e);
		}
	}

	public boolean authenticatePassword(String user, String pass) throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("password") == false)
				throw new IOException("Authentication method password not supported by the server at this stage.");

			PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
			tm.sendMessage(ua.getPayload());

			byte[] ar = getNextMessage();

			if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
			{
				authenticated = true;
				tm.removeMessageHandler(this, 0, 255);
				return true;
			}

			if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
			{
				PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

				remainingMethods = puf.getAuthThatCanContinue();
				isPartialSuccess = puf.isPartialSuccess();

				return false;
			}

			throw new IOException("Unexpected SSH message (type " + ar[0] + ")");

		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Password authentication failed.").initCause(e);
		}
	}

	public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
	{
		try
		{
			initialize(user);

			if (methodPossible("keyboard-interactive") == false)
				throw new IOException(
						"Authentication method keyboard-interactive not supported by the server at this stage.");

			if (submethods == null)
				submethods = new String[0];

			PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
					submethods);

			tm.sendMessage(ua.getPayload());

			while (true)
			{
				byte[] ar = getNextMessage();

				if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
				{
					authenticated = true;
					tm.removeMessageHandler(this, 0, 255);
					return true;
				}

				if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
				{
					PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);

					remainingMethods = puf.getAuthThatCanContinue();
					isPartialSuccess = puf.isPartialSuccess();

					return false;
				}

				if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
				{
					PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);

					String[] responses;

					try
					{
						responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
								.getPrompt(), pui.getEcho());
					}
					catch (Exception e)
					{
						throw (IOException) new IOException("Exception in callback.").initCause(e);
					}

					if (responses == null)
						throw new IOException("Your callback may not return NULL!");

					PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
					tm.sendMessage(puir.getPayload());

					continue;
				}

				throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
			}
		}
		catch (IOException e)
		{
			tm.close(e, false);
			throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
		}
	}

	public void handleMessage(byte[] msg, int msglen) throws IOException
	{
		synchronized (packets)
		{
			if (msg == null)
			{
				connectionClosed = true;
			}
			else
			{
				byte[] tmp = new byte[msglen];
				System.arraycopy(msg, 0, tmp, 0, msglen);
				packets.addElement(tmp);
			}

			packets.notifyAll();

			if (packets.size() > 5)
			{
				connectionClosed = true;
				throw new IOException("Error, peer is flooding us with authentication packets.");
			}
		}
	}
}
File
AuthenticationManager.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
		{
<<<<<<< HEAD:src/com/trilead/ssh2/channel/Channel.java

package com.trilead.ssh2.channel;

import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.transport.TransportManager;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;

import static com.trilead.ssh2.util.IOUtils.closeQuietly;

/**
 * Channel.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class Channel
{
	/*
	 * OK. Here is an important part of the JVM Specification:
	 * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
	 * 
	 * Any association between locks and variables is purely conventional.
	 * Locking any lock conceptually flushes all variables from a thread's
	 * working memory, and unlocking any lock forces the writing out to main
	 * memory of all variables that the thread has assigned. That a lock may be
	 * associated with a particular object or a class is purely a convention.
	 * (...)
	 * 
	 * If a thread uses a particular shared variable only after locking a
	 * particular lock and before the corresponding unlocking of that same lock,
	 * then the thread will read the shared value of that variable from main
	 * memory after the lock operation, if necessary, and will copy back to main
	 * memory the value most recently assigned to that variable before the
	 * unlock operation.
	 * 
	 * This, in conjunction with the mutual exclusion rules for locks, suffices
	 * to guarantee that values are correctly transmitted from one thread to
	 * another through shared variables.
	 * 
	 * ====> Always keep that in mind when modifying the Channel/ChannelManger
	 * code.
	 * 
	 */

	static final int STATE_OPENING = 1;
	static final int STATE_OPEN = 2;
	static final int STATE_CLOSED = 4;

	private static final int CHANNEL_BUFFER_SIZE = Integer.getInteger(
			Channel.class.getName()+".bufferSize",
			1024*1024 + 16*1024).intValue();

    /**
     * This channel's session size.
     */
    // @GuarydedBy("this")
    int channelBufferSize = CHANNEL_BUFFER_SIZE;

	/*
	 * To achieve correctness, the following rules have to be respected when
	 * accessing this object:
	 */

	// These fields can always be read
	final ChannelManager cm;
	final ChannelOutputStream stdinStream;

    /**
     * One stream.
     *
     * Either {@link #stream} and {@link #buffer} is set, or the {@link #sink} is set, but those
     * are mutually exclusive. The former is used when we are buffering data and let the application
     * read it via {@link InputStream}, and the latter is used when we are passing through the data
     * to another {@link OutputStream}.
     *
     * The synchronization is done by {@link Channel}
     */
    class Output {
        ChannelInputStream stream;
        FifoBuffer buffer = new FifoBuffer(Channel.this, 2048, channelBufferSize);
        OutputStream sink;

        public void write(byte[] buf, int start, int len) throws IOException {
            if (buffer!=null) {
                try {
                    buffer.write(buf,start,len);
                } catch (InterruptedException e) {
                    throw new InterruptedIOException();
                }
            } else {
                sink.write(buf,start,len);
                freeupWindow(len);
            }
        }

        /**
         * How many bytes can be read from the buffer?
         */
        public int readable() {
            if (buffer!=null)   return buffer.readable();
            else                return 0;
        }

        /**
         * See {@link InputStream#available()}
         */
        public int available() {
            if (buffer==null)
                throw new IllegalStateException("Output is being piped to "+sink);

            int sz = buffer.readable();
            if (sz>0)    return sz;
            return isEOF() ? -1 : 0;
        }

        /**
         * Read from the buffer.
         */
        public int read(byte[] buf, int start, int len) throws InterruptedException {
            return buffer.read(buf,start,len);
        }

        /**
         * Called when there will be no more data arriving to this output any more.
         * Not that buffer might still have some more data that needs to be drained.
         */
        public void eof() {
            if (buffer!=null)
                buffer.close();
            else
                closeQuietly(sink);
        }

        /**
         * Instead of spooling data, let our I/O thread write to the given {@link OutputStream}.
         */
        public void pipeTo(OutputStream os) throws IOException {
            sink = os;
            if (buffer.readable()!=0) {
                freeupWindow(buffer.writeTo(os));
            }

            buffer = null;
            stream = null;
        }
    }

    final Output stdout = new Output();
    final Output stderr = new Output();

    // These two fields will only be written while the Channel is in state
	// STATE_OPENING.
	// The code makes sure that the two fields are written out when the state is
	// changing to STATE_OPEN.
	// Therefore, if you know that the Channel is in state STATE_OPEN, then you
	// can read these two fields without synchronizing on the Channel. However, make
	// sure that you get the latest values (e.g., flush caches by synchronizing on any
	// object). However, to be on the safe side, you can lock the channel.

	int localID = -1;
	int remoteID = -1;

	/*
	 * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
	 * msg.
	 * 
	 * This is a little bit complicated, but we have to do it in that way, since
	 * we cannot keep a lock on the Channel during the send operation (this
	 * would block sometimes the receiver thread, and, in extreme cases, can
	 * lead to a deadlock on both sides of the connection (senders are blocked
	 * since the receive buffers on the other side are full, and receiver
	 * threads wait for the senders to finish). It all depends on the
	 * implementation on the other side. But we cannot make any assumptions, we
	 * have to assume the worst case. Confused? Just believe me.
	 */

	/*
	 * If you send a message on a channel, then you have to aquire the
	 * "channelSendLock" and check the "closeMessageSent" flag (this variable
	 * may only be accessed while holding the "channelSendLock" !!!
	 * 
	 * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
	 * above.
	 */

	final Object channelSendLock = new Object();
	boolean closeMessageSent = false;

	/*
	 * Stop memory fragmentation by allocating this often used buffer.
	 * May only be used while holding the channelSendLock
	 */

	final byte[] msgWindowAdjust = new byte[9];

	// If you access (read or write) any of the following fields, then you have
	// to synchronize on the channel.

	int state = STATE_OPENING;

	boolean closeMessageRecv = false;

	/* This is a stupid implementation. At the moment we can only wait
	 * for one pending request per channel.
	 */
	int successCounter = 0;
	int failedCounter = 0;

	int localWindow = 0; /* locally, we use a small window, < 2^31 */
	long remoteWindow = 0; /* long for readable  2^32 - 1 window support */

	int localMaxPacketSize = -1;
	int remoteMaxPacketSize = -1;


    private boolean eof = false;

    synchronized void eof() {
        stdout.eof();
        stderr.eof();
        eof = true;
    }
    boolean isEOF() {
        return eof;
    }

	Integer exit_status;

	String exit_signal;

	// we keep the x11 cookie so that this channel can be closed when this
	// specific x11 forwarding gets stopped

	String hexX11FakeCookie;

	// reasonClosed is special, since we sometimes need to access it
	// while holding the channelSendLock.
	// We protect it with a private short term lock.

	private final Object reasonClosedLock = new Object();
	private String reasonClosed = null;

	public Channel(ChannelManager cm)
	{
		this.cm = cm;

		this.localWindow = channelBufferSize;
		this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE - 1024; // leave enough slack

		this.stdinStream = new ChannelOutputStream(this);
		this.stdout.stream = new ChannelInputStream(this, false);
		this.stderr.stream = new ChannelInputStream(this, true);
	}

	/* Methods to allow access from classes outside of this package */

    public synchronized void setWindowSize(int newSize) {
        if (newSize<=0)  throw new IllegalArgumentException("Invalid value: "+newSize);
        this.channelBufferSize = newSize;
        // next time when the other side sends us something, we'll issue SSH_MSG_CHANNEL_WINDOW_ADJUST
    }

	public ChannelInputStream getStderrStream()
	{
		return stderr.stream;
	}

	public ChannelOutputStream getStdinStream()
	{
		return stdinStream;
	}

	public ChannelInputStream getStdoutStream()
	{
		return stdout.stream;
	}

    public synchronized void pipeStdoutStream(OutputStream os) throws IOException {
        stdout.pipeTo(os);
    }

    public synchronized void pipeStderrStream(OutputStream os) throws IOException {
        stderr.pipeTo(os);
    }

	public String getExitSignal()
	{
		synchronized (this)
		{
			return exit_signal;
		}
	}

	public Integer getExitStatus()
	{
		synchronized (this)
			return exit_status;
		}
	}

	public String getReasonClosed()
	{
		synchronized (reasonClosedLock)
		{
			return reasonClosed;
		}
	}

	public void setReasonClosed(String reasonClosed)
	{
		synchronized (reasonClosedLock)
		{
			if (this.reasonClosed == null)
				this.reasonClosed = reasonClosed;
		}
	}

    /**
     * Update the flow control couner and if necessary, sends ACK to the other end to
     * let it send more data.
     */
    void freeupWindow(int copylen) throws IOException {
        if (copylen <= 0) return;

        int increment = 0;
        int remoteID;
        int localID;

        synchronized (this) {
            if (localWindow <= ((channelBufferSize * 3) / 4)) {
                // have enough local window been consumed? if so, we'll send Ack

                // the window control is on the combined bytes of stdout & stderr
                int space = channelBufferSize - stdout.readable() - stderr.readable();

                increment = space - localWindow;
                if (increment > 0)    // increment<0 can't happen, but be defensive
                    localWindow += increment;
            }

            remoteID = this.remoteID; /* read while holding the lock */
            localID = this.localID; /* read while holding the lock */

        }

        /*
         * If a consumer reads stdout and stdin in parallel, we may end up with
         * sending two msgWindowAdjust messages. Luckily, it
         * does not matter in which order they arrive at the server.
         */

        if (increment > 0)
        {
            if (log.isEnabled())
                log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");

            synchronized (channelSendLock)
            {
                byte[] msg = msgWindowAdjust;

                msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
                msg[1] = (byte) (remoteID >> 24);
                msg[2] = (byte) (remoteID >> 16);
                msg[3] = (byte) (remoteID >> 8);
                msg[4] = (byte) (remoteID);
                msg[5] = (byte) (increment >> 24);
                msg[6] = (byte) (increment >> 16);
                msg[7] = (byte) (increment >> 8);
                msg[8] = (byte) (increment);

                if (closeMessageSent == false)
                    cm.tm.sendMessage(msg);
            }
        }
    }

    private static final Logger log = Logger.getLogger(Channel.class);
}
=======

package com.trilead.ssh2.channel;

/**
 * Channel.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class Channel
{
	/*
	 * OK. Here is an important part of the JVM Specification:
	 * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
	 * 
	 * Any association between locks and variables is purely conventional.
	 * Locking any lock conceptually flushes all variables from a thread's
	 * working memory, and unlocking any lock forces the writing out to main
	 * memory of all variables that the thread has assigned. That a lock may be

	 * associated with a particular object or a class is purely a convention.
	 * (...)
	 * 
	 * If a thread uses a particular shared variable only after locking a
	 * particular lock and before the corresponding unlocking of that same lock,
	 * then the thread will read the shared value of that variable from main
	 * memory after the lock operation, if necessary, and will copy back to main
	 * memory the value most recently assigned to that variable before the
	 * unlock operation.
	 * 
	 * This, in conjunction with the mutual exclusion rules for locks, suffices
	 * to guarantee that values are correctly transmitted from one thread to
	 * another through shared variables.
	 * 
	 * ====> Always keep that in mind when modifying the Channel/ChannelManger
	 * code.
	 * 
	 */

	static final int STATE_OPENING = 1;
	static final int STATE_OPEN = 2;
	static final int STATE_CLOSED = 4;

	static final int CHANNEL_BUFFER_SIZE = 30000;

	/*
	 * To achieve correctness, the following rules have to be respected when
	 * accessing this object:
	 */

	// These fields can always be read
	final ChannelManager cm;
	final ChannelOutputStream stdinStream;
	final ChannelInputStream stdoutStream;
	final ChannelInputStream stderrStream;

	// These two fields will only be written while the Channel is in state
	// STATE_OPENING.
	// The code makes sure that the two fields are written out when the state is
	// changing to STATE_OPEN.
	// Therefore, if you know that the Channel is in state STATE_OPEN, then you
	// can read these two fields without synchronizing on the Channel. However, make
	// sure that you get the latest values (e.g., flush caches by synchronizing on any
	// object). However, to be on the safe side, you can lock the channel.

	int localID = -1;
	int remoteID = -1;

	/*
	 * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
	 * msg.
	 * 
	 * This is a little bit complicated, but we have to do it in that way, since
	 * we cannot keep a lock on the Channel during the send operation (this
	 * would block sometimes the receiver thread, and, in extreme cases, can
	 * lead to a deadlock on both sides of the connection (senders are blocked
	 * since the receive buffers on the other side are full, and receiver
	 * threads wait for the senders to finish). It all depends on the
	 * implementation on the other side. But we cannot make any assumptions, we
	 * have to assume the worst case. Confused? Just believe me.
	 */

	/*
	 * If you send a message on a channel, then you have to aquire the
	 * "channelSendLock" and check the "closeMessageSent" flag (this variable
	 * may only be accessed while holding the "channelSendLock" !!!
	 * 
	 * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
	 * above.
	 */

	final Object channelSendLock = new Object();
	boolean closeMessageSent = false;

	/*
	 * Stop memory fragmentation by allocating this often used buffer.
	 * May only be used while holding the channelSendLock
	 */

	final byte[] msgWindowAdjust = new byte[9];

	// If you access (read or write) any of the following fields, then you have
	// to synchronize on the channel.

	int state = STATE_OPENING;

	boolean closeMessageRecv = false;

	/* This is a stupid implementation. At the moment we can only wait
	 * for one pending request per channel.
	 */
	int successCounter = 0;
	int failedCounter = 0;

	int localWindow = 0; /* locally, we use a small window, < 2^31 */
	long remoteWindow = 0; /* long for readable  2^32 - 1 window support */
	int localMaxPacketSize = -1;
	int remoteMaxPacketSize = -1;

	final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
	final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];

	int stdoutReadpos = 0;
	int stdoutWritepos = 0;
	int stderrReadpos = 0;
	int stderrWritepos = 0;

	boolean EOF = false;

	Integer exit_status;

	String exit_signal;

	// we keep the x11 cookie so that this channel can be closed when this
	// specific x11 forwarding gets stopped

	String hexX11FakeCookie;

	// reasonClosed is special, since we sometimes need to access it
	// while holding the channelSendLock.
	// We protect it with a private short term lock.

	private final Object reasonClosedLock = new Object();
	private String reasonClosed = null;

	public Channel(ChannelManager cm)
	{
		this.cm = cm;

		this.localWindow = CHANNEL_BUFFER_SIZE;
		this.localMaxPacketSize = 35000 - 1024; // leave enough slack

		this.stdinStream = new ChannelOutputStream(this);
		this.stdoutStream = new ChannelInputStream(this, false);
		this.stderrStream = new ChannelInputStream(this, true);
	}

	/* Methods to allow access from classes outside of this package */

	public ChannelInputStream getStderrStream()
	{
		return stderrStream;
	}

	public ChannelOutputStream getStdinStream()
	{
		return stdinStream;
	}

	public ChannelInputStream getStdoutStream()
	{
		return stdoutStream;
	}

	public String getExitSignal()
	{
		synchronized (this)
		{
			return exit_signal;
		}
	}

	public Integer getExitStatus()
	{
		synchronized (this)
		{
			return exit_status;
		}
	}

	public String getReasonClosed()
	{
		synchronized (reasonClosedLock)
		{
			return reasonClosed;
		}
	}

	public void setReasonClosed(String reasonClosed)
	{
		synchronized (reasonClosedLock)
		{
			if (this.reasonClosed == null)
				this.reasonClosed = reasonClosed;
		}
	}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/Channel.java
Solution content
     * to another {@link OutputStream}.

package com.trilead.ssh2.channel;

import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.transport.TransportManager;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;

import static com.trilead.ssh2.util.IOUtils.closeQuietly;

/**
 * Channel.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class Channel
{
	/*
	 * OK. Here is an important part of the JVM Specification:
	 * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
	 * 
	 * Any association between locks and variables is purely conventional.
	 * Locking any lock conceptually flushes all variables from a thread's
	 * working memory, and unlocking any lock forces the writing out to main
	 * memory of all variables that the thread has assigned. That a lock may be
	 * associated with a particular object or a class is purely a convention.
	 * (...)
	 * 
	 * If a thread uses a particular shared variable only after locking a
	 * particular lock and before the corresponding unlocking of that same lock,
	 * then the thread will read the shared value of that variable from main
	 * memory after the lock operation, if necessary, and will copy back to main
	 * memory the value most recently assigned to that variable before the
	 * unlock operation.
	 * 
	 * This, in conjunction with the mutual exclusion rules for locks, suffices
	 * to guarantee that values are correctly transmitted from one thread to
	 * another through shared variables.
	 * 
	 * ====> Always keep that in mind when modifying the Channel/ChannelManger
	 * code.
	 * 
	 */

	static final int STATE_OPENING = 1;
	static final int STATE_OPEN = 2;
	static final int STATE_CLOSED = 4;

	private static final int CHANNEL_BUFFER_SIZE = Integer.getInteger(
			Channel.class.getName()+".bufferSize",
			1024*1024 + 16*1024).intValue();

    /**
     * This channel's session size.
     */
    // @GuarydedBy("this")
    int channelBufferSize = CHANNEL_BUFFER_SIZE;

	/*
	 * To achieve correctness, the following rules have to be respected when
	 * accessing this object:
	 */

	// These fields can always be read
	final ChannelManager cm;
	final ChannelOutputStream stdinStream;

    /**
     * One stream.
     *
     * Either {@link #stream} and {@link #buffer} is set, or the {@link #sink} is set, but those
     * are mutually exclusive. The former is used when we are buffering data and let the application
     * read it via {@link InputStream}, and the latter is used when we are passing through the data
     *
     * The synchronization is done by {@link Channel}
     */
    class Output {
        ChannelInputStream stream;
        FifoBuffer buffer = new FifoBuffer(Channel.this, 2048, channelBufferSize);
        OutputStream sink;

        public void write(byte[] buf, int start, int len) throws IOException {
            if (buffer!=null) {
                try {
                    buffer.write(buf,start,len);
                } catch (InterruptedException e) {
                    throw new InterruptedIOException();
                }
            } else {
                sink.write(buf,start,len);
                freeupWindow(len);
            }
        }

        /**
         * How many bytes can be read from the buffer?
         */
        public int readable() {
            if (buffer!=null)   return buffer.readable();
            else                return 0;
        }

        /**
         * See {@link InputStream#available()}
         */
        public int available() {
            if (buffer==null)
                throw new IllegalStateException("Output is being piped to "+sink);

            int sz = buffer.readable();
            if (sz>0)    return sz;
            return isEOF() ? -1 : 0;
        }

        /**
         * Read from the buffer.
         */
        public int read(byte[] buf, int start, int len) throws InterruptedException {
            return buffer.read(buf,start,len);
        }

        /**
         * Called when there will be no more data arriving to this output any more.
         * Not that buffer might still have some more data that needs to be drained.
         */
        public void eof() {
            if (buffer!=null)
                buffer.close();
            else
                closeQuietly(sink);
        }

        /**
         * Instead of spooling data, let our I/O thread write to the given {@link OutputStream}.
         */
        public void pipeTo(OutputStream os) throws IOException {
            sink = os;
            if (buffer.readable()!=0) {
                freeupWindow(buffer.writeTo(os));
            }

            buffer = null;
            stream = null;
        }
    }

    final Output stdout = new Output();
    final Output stderr = new Output();

    // These two fields will only be written while the Channel is in state
	// STATE_OPENING.
	// The code makes sure that the two fields are written out when the state is
	// changing to STATE_OPEN.
	// Therefore, if you know that the Channel is in state STATE_OPEN, then you
	// can read these two fields without synchronizing on the Channel. However, make
	// sure that you get the latest values (e.g., flush caches by synchronizing on any
	// object). However, to be on the safe side, you can lock the channel.

	int localID = -1;
	int remoteID = -1;

	/*
	 * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
	 * msg.
	 * 
	 * This is a little bit complicated, but we have to do it in that way, since
	 * we cannot keep a lock on the Channel during the send operation (this
	 * would block sometimes the receiver thread, and, in extreme cases, can
	 * lead to a deadlock on both sides of the connection (senders are blocked
	 * since the receive buffers on the other side are full, and receiver
	 * threads wait for the senders to finish). It all depends on the
    }

	 * implementation on the other side. But we cannot make any assumptions, we
	 * have to assume the worst case. Confused? Just believe me.
	 */

	/*
	 * If you send a message on a channel, then you have to aquire the
	 * "channelSendLock" and check the "closeMessageSent" flag (this variable
	 * may only be accessed while holding the "channelSendLock" !!!
	 * 
	 * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
	 * above.
	 */

	final Object channelSendLock = new Object();
	boolean closeMessageSent = false;

	/*
	 * Stop memory fragmentation by allocating this often used buffer.
	 * May only be used while holding the channelSendLock
	 */

	final byte[] msgWindowAdjust = new byte[9];

	// If you access (read or write) any of the following fields, then you have
	// to synchronize on the channel.

	int state = STATE_OPENING;

	boolean closeMessageRecv = false;

	/* This is a stupid implementation. At the moment we can only wait
	 * for one pending request per channel.
	 */
	int successCounter = 0;
	int failedCounter = 0;

	int localWindow = 0; /* locally, we use a small window, < 2^31 */
	long remoteWindow = 0; /* long for readable  2^32 - 1 window support */

	int localMaxPacketSize = -1;
	int remoteMaxPacketSize = -1;


    private boolean eof = false;

    synchronized void eof() {
        stdout.eof();
        stderr.eof();
        eof = true;
    }
    boolean isEOF() {
        return eof;
    }

	Integer exit_status;

	String exit_signal;

	// we keep the x11 cookie so that this channel can be closed when this
	// specific x11 forwarding gets stopped

	String hexX11FakeCookie;

	// reasonClosed is special, since we sometimes need to access it
	// while holding the channelSendLock.
	// We protect it with a private short term lock.

	private final Object reasonClosedLock = new Object();
	private String reasonClosed = null;

	public Channel(ChannelManager cm)
	{
		this.cm = cm;

		this.localWindow = channelBufferSize;
		this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE - 1024; // leave enough slack

		this.stdinStream = new ChannelOutputStream(this);
		this.stdout.stream = new ChannelInputStream(this, false);
		this.stderr.stream = new ChannelInputStream(this, true);
	}

	/* Methods to allow access from classes outside of this package */

    public synchronized void setWindowSize(int newSize) {
        if (newSize<=0)  throw new IllegalArgumentException("Invalid value: "+newSize);
        this.channelBufferSize = newSize;
        // next time when the other side sends us something, we'll issue SSH_MSG_CHANNEL_WINDOW_ADJUST
    }

	public ChannelInputStream getStderrStream()
	{
		return stderr.stream;
	}

	public ChannelOutputStream getStdinStream()
	{
		return stdinStream;
	}

	public ChannelInputStream getStdoutStream()
	{
		return stdout.stream;
	}

    public synchronized void pipeStdoutStream(OutputStream os) throws IOException {
        stdout.pipeTo(os);
    public synchronized void pipeStderrStream(OutputStream os) throws IOException {
        stderr.pipeTo(os);
    }

	public String getExitSignal()
	{
		synchronized (this)
		{
			return exit_signal;
		}
	}

	public Integer getExitStatus()
	{
		synchronized (this)
		{
			return exit_status;
		}
	}

	public String getReasonClosed()
	{
		synchronized (reasonClosedLock)
		{
			return reasonClosed;
		}
	}

	public void setReasonClosed(String reasonClosed)
	{
		synchronized (reasonClosedLock)
		{
			if (this.reasonClosed == null)
				this.reasonClosed = reasonClosed;
		}
	}

    /**
     * Update the flow control couner and if necessary, sends ACK to the other end to
     * let it send more data.
     */
    void freeupWindow(int copylen) throws IOException {
        if (copylen <= 0) return;

        int increment = 0;
        int remoteID;
        int localID;

        synchronized (this) {
            if (localWindow <= ((channelBufferSize * 3) / 4)) {
                // have enough local window been consumed? if so, we'll send Ack

                // the window control is on the combined bytes of stdout & stderr
                int space = channelBufferSize - stdout.readable() - stderr.readable();

                increment = space - localWindow;
                if (increment > 0)    // increment<0 can't happen, but be defensive
                    localWindow += increment;
            }

            remoteID = this.remoteID; /* read while holding the lock */
            localID = this.localID; /* read while holding the lock */

        }

        /*
         * If a consumer reads stdout and stdin in parallel, we may end up with
         * sending two msgWindowAdjust messages. Luckily, it
         * does not matter in which order they arrive at the server.
         */

        if (increment > 0)
        {
            if (log.isEnabled())
                log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");

            synchronized (channelSendLock)
            {
                byte[] msg = msgWindowAdjust;

                msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
                msg[1] = (byte) (remoteID >> 24);
                msg[2] = (byte) (remoteID >> 16);
                msg[3] = (byte) (remoteID >> 8);
                msg[4] = (byte) (remoteID);
                msg[5] = (byte) (increment >> 24);
                msg[6] = (byte) (increment >> 16);
                msg[7] = (byte) (increment >> 8);
                msg[8] = (byte) (increment);

                if (closeMessageSent == false)
                    cm.tm.sendMessage(msg);
            }
        }
    }

    private static final Logger log = Logger.getLogger(Channel.class);
}
File
Channel.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
		{
<<<<<<< HEAD:src/com/trilead/ssh2/channel/ChannelManager.java

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.HashMap;
import java.util.Vector;

import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
import com.trilead.ssh2.packets.PacketChannelOpenFailure;
import com.trilead.ssh2.packets.PacketChannelTrileadPing;
import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
import com.trilead.ssh2.packets.PacketOpenSessionChannel;
	}
import com.trilead.ssh2.packets.PacketSessionExecCommand;
import com.trilead.ssh2.packets.PacketSessionPtyRequest;
import com.trilead.ssh2.packets.PacketSessionStartShell;
import com.trilead.ssh2.packets.PacketSessionSubsystemRequest;
import com.trilead.ssh2.packets.PacketSessionX11Request;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.packets.TypesReader;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;

/**
 * ChannelManager. Please read the comments in Channel.java.
 * 

* Besides the crypto part, this is the core of the library. * * @author Christian Plattner, plattner@trilead.com * @version $Id: ChannelManager.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $ */ public class ChannelManager implements MessageHandler { private static final Logger log = Logger.getLogger(ChannelManager.class); private HashMap x11_magic_cookies = new HashMap(); /*package*/ TransportManager tm; private Vector channels = new Vector(); private int nextLocalChannel = 100; private boolean shutdown = false; private int globalSuccessCounter = 0; private int globalFailedCounter = 0; } try synchronized (remoteForwardings) } { public ChannelManager(TransportManager tm) remoteForwardings.remove(rfd); } throw e; { this.tm = tm; private HashMap remoteForwardings = new HashMap(); } } } try private Vector listenerThreads = new Vector(); return bindPort; { private boolean listenerThreadsAllowed = true; tm.registerMessageHandler(this, 80, 100); } private Channel getChannel(int id) { synchronized (channels) { for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); if (c.localID == id) return c; } } return null; } private void removeChannel(int id) { synchronized (channels) { for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); if (c.localID == id) { channels.removeElementAt(i); break; } } } } private int addChannel(Channel c) { synchronized (channels) { channels.addElement(c); return nextLocalChannel++; } } private void waitUntilChannelOpen(Channel c) throws IOException { synchronized (c) { while (c.state == Channel.STATE_OPENING) { try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } if (c.state != Channel.STATE_OPEN) { removeChannel(c.localID); String detail = c.getReasonClosed(); if (detail == null) detail = "state: " + c.state; throw new IOException("Could not open channel (" + detail + ")"); } } } private final boolean waitForGlobalRequestResult() throws IOException { synchronized (channels) { while ((globalSuccessCounter == 0) && (globalFailedCounter == 0)) { if (shutdown) { throw new IOException("The connection is being shutdown"); } try { channels.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } if ((globalFailedCounter == 0) && (globalSuccessCounter == 1)) return true; if ((globalFailedCounter == 1) && (globalSuccessCounter == 0)) return false; throw new IOException("Illegal state. The server sent " + globalSuccessCounter + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages."); } } private final boolean waitForChannelRequestResult(Channel c) throws IOException { synchronized (c) { while ((c.successCounter == 0) && (c.failedCounter == 0)) { if (c.state != Channel.STATE_OPEN) { String detail = c.getReasonClosed(); if (detail == null) detail = "state: " + c.state; throw new IOException("This SSH2 channel is not open (" + detail + ")"); } c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } if ((c.failedCounter == 0) && (c.successCounter == 1)) return true; if ((c.failedCounter == 1) && (c.successCounter == 0)) return false; throw new IOException("Illegal state. The server sent " + c.successCounter + " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages."); } } public void registerX11Cookie(String hexFakeCookie, X11ServerData data) { synchronized (x11_magic_cookies) { x11_magic_cookies.put(hexFakeCookie, data); } } public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels) { if (hexFakeCookie == null) throw new IllegalStateException("hexFakeCookie may not be null"); synchronized (x11_magic_cookies) { x11_magic_cookies.remove(hexFakeCookie); } if (killChannels == false) return; if (log.isEnabled()) log.log(50, "Closing all X11 channels for the given fake cookie"); Vector channel_copy; synchronized (channels) { channel_copy = (Vector) channels.clone(); } for (int i = 0; i < channel_copy.size(); i++) { Channel c = (Channel) channel_copy.elementAt(i); synchronized (c) { if (hexFakeCookie.equals(c.hexX11FakeCookie) == false) continue; } try { closeChannel(c, "Closing X11 channel since the corresponding session is closing", true); } catch (IOException e) { } public X11ServerData checkX11Cookie(String hexFakeCookie) { synchronized (x11_magic_cookies) { if (hexFakeCookie != null) return (X11ServerData) x11_magic_cookies.get(hexFakeCookie); } return null; } public void closeAllChannels() { if (log.isEnabled()) log.log(50, "Closing all channels"); Vector channel_copy; synchronized (channels) { channel_copy = (Vector) channels.clone(); } for (int i = 0; i < channel_copy.size(); i++) { Channel c = (Channel) channel_copy.elementAt(i); try { closeChannel(c, "Closing all channels", true); } catch (IOException e) { } } } public void closeChannel(Channel c, String reason, boolean force) throws IOException { byte msg[] = new byte[5]; synchronized (c) { if (force) { c.state = Channel.STATE_CLOSED; c.eof(); } c.setReasonClosed(reason); msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE; msg = new byte[1 + 8 + thislen]; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); c.notifyAll(); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(msg); c.closeMessageSent = true; } if (log.isEnabled()) log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")"); } public void sendEOF(Channel c) throws IOException { byte[] msg = new byte[5]; synchronized (c) { if (c.state != Channel.STATE_OPEN) return; msg[0] = Packets.SSH_MSG_CHANNEL_EOF; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(msg); } if (log.isEnabled()) log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")"); } public void sendOpenConfirmation(Channel c) throws IOException { PacketChannelOpenConfirmation pcoc = null; synchronized (c) { if (c.state != Channel.STATE_OPENING) return; c.state = Channel.STATE_OPEN; pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(pcoc.getPayload()); } } } public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException { while (len > 0) { int thislen = 0; byte[] msg; synchronized (c) { while (true) { if (c.state == Channel.STATE_CLOSED) throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")"); if (c.state != Channel.STATE_OPEN) throw new IOException("SSH channel in strange state. (" + c.state + ")"); if (c.remoteWindow != 0) break; try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } /* len > 0, no sign extension can happen when comparing */ thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow; int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9); /* The worst case scenario =) a true bottleneck */ if (estimatedMaxDataLen <= 0) { estimatedMaxDataLen = 1; } if (thislen > estimatedMaxDataLen) thislen = estimatedMaxDataLen; c.remoteWindow -= thislen; catch (IOException e) remoteForwardings.remove(rfd); msg[0] = Packets.SSH_MSG_CHANNEL_DATA; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); msg[5] = (byte) (thislen >> 24); msg[6] = (byte) (thislen >> 16); msg[7] = (byte) (thislen >> 8); msg[8] = (byte) (thislen); System.arraycopy(buffer, pos, msg, 9, thislen); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")"); tm.sendMessage(msg); } pos += thislen; len -= thislen; } } public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort) throws IOException { RemoteForwardingData rfd = new RemoteForwardingData(); rfd.bindAddress = bindAddress; rfd.bindPort = bindPort; rfd.targetAddress = targetAddress; rfd.targetPort = targetPort; synchronized (remoteForwardings) { Integer key = new Integer(bindPort); if (remoteForwardings.get(key) != null) { throw new IOException("There is already a forwarding for remote port " + bindPort); } remoteForwardings.put(key, rfd); } synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort); tm.sendMessage(pgf.getPayload()); if (log.isEnabled()) log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")"); try { if (waitForGlobalRequestResult() == false) throw new IOException("The server denied the request (did you enable port forwarding?)"); public void requestCancelGlobalForward(int bindPort) throws IOException { RemoteForwardingData rfd = null; synchronized (remoteForwardings) { rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(bindPort)); if (rfd == null) throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort); } synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress, rfd.bindPort); tm.sendMessage(pgcf.getPayload()); if (log.isEnabled()) log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")"); try { if (waitForGlobalRequestResult() == false) throw new IOException("The server denied the request."); } finally { synchronized (remoteForwardings) { /* Only now we are sure that no more forwarded connections will arrive */ { if (waitForChannelRequestResult(c) == false) } public void registerThread(IChannelWorkerThread thr) throws IOException { synchronized (listenerThreads) { if (listenerThreadsAllowed == false) throw new IOException("Too late, this connection is closed."); listenerThreads.addElement(thr); } } public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address, int originator_port) throws IOException { Channel c = new Channel(this); synchronized (c) { c.localID = addChannel(c); // end of synchronized block forces writing out to main memory } PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow, c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port); tm.sendMessage(dtc.getPayload()); waitUntilChannelOpen(c); return c; } public Channel openSessionChannel() throws IOException { Channel c = new Channel(this); synchronized (c) { c.localID = addChannel(c); // end of synchronized block forces the writing out to main memory } if (log.isEnabled()) log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")"); PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize); tm.sendMessage(smo.getPayload()); waitUntilChannelOpen(c); return c; } public void requestGlobalTrileadPing() throws IOException { synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing(); tm.sendMessage(pgtp.getPayload()); if (log.isEnabled()) log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'."); try { if (waitForGlobalRequestResult() == true) if (msglen <= 13) throw new IOException("Your server is alive - but buggy. " + "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not."); } catch (IOException e) { throw (IOException) new IOException("The ping request failed.").initCause(e); } } public void requestChannelTrileadPing(Channel c) throws IOException { PacketChannelTrileadPing pctp; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")"); pctp = new PacketChannelTrileadPing(c.remoteID); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(pctp.getPayload()); } try { if (waitForChannelRequestResult(c) == true) throw new IOException("Your server is alive - but buggy. " + "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not."); } } catch (IOException e) { throw (IOException) new IOException("The ping request failed.").initCause(e); } } public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException { PacketSessionPtyRequest spr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")"); spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, terminal_modes); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(spr.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("PTY request failed").initCause(e); } } public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber) throws IOException { PacketSessionX11Request psr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")"); psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol, x11AuthenticationCookie, x11ScreenNumber); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(psr.getPayload()); } if (log.isEnabled()) log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")"); throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The X11 request failed.").initCause(e); } } public void requestSubSystem(Channel c, String subSystemName) throws IOException { PacketSessionSubsystemRequest ssr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")"); ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(ssr.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The subsystem request failed.").initCause(e); } } public void requestExecCommand(Channel c, String cmd) throws IOException { PacketSessionExecCommand sm; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")"); sm = new PacketSessionExecCommand(c.remoteID, true, cmd); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(sm.getPayload()); } if (log.isEnabled()) log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')"); try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The execute request failed.").initCause(e); } } public void requestShell(Channel c) throws IOException { PacketSessionStartShell sm; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")"); sm = new PacketSessionStartShell(c.remoteID, true); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(sm.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The shell request failed.").initCause(e); } } public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException { throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id); if (dataType != Packets.SSH_EXTENDED_DATA_STDERR) throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")"); if (len != (msglen - 13)) throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13) + ", got " + len + ")"); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")"); synchronized (c) { if (c.state == Channel.STATE_CLOSED) return; // ignore if (c.state != Channel.STATE_OPEN) throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state (" + c.state + ")"); if (c.localWindow < len) throw new IOException("Remote sent too much data, does not fit into window."); c.localWindow -= len; c.stderr.write(msg,13,len); } } /** * Wait until for a condition. * * @param c * Channel * @param timeout * in ms, 0 means no timeout. * @param condition_mask * minimum event mask * @return all current events * */ public int waitForCondition(Channel c, long timeout, int condition_mask) throws InterruptedException { long end_time = 0; boolean end_time_set = false; synchronized (c) { while (true) { int current_cond = 0; int stdoutAvail = c.stdout.readable(); int stderrAvail = c.stderr.readable(); if (stdoutAvail > 0) current_cond = current_cond | ChannelCondition.STDOUT_DATA; if (stderrAvail > 0) current_cond = current_cond | ChannelCondition.STDERR_DATA; if (c.isEOF()) current_cond = current_cond | ChannelCondition.EOF; if (c.getExitStatus() != null) current_cond = current_cond | ChannelCondition.EXIT_STATUS; if (c.getExitSignal() != null) current_cond = current_cond | ChannelCondition.EXIT_SIGNAL; if (c.state == Channel.STATE_CLOSED) return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF; if ((current_cond & condition_mask) != 0) return current_cond; if (timeout > 0) { if (!end_time_set) { end_time = System.currentTimeMillis() + timeout; end_time_set = true; } else { timeout = end_time - System.currentTimeMillis(); if (timeout <= 0) return current_cond | ChannelCondition.TIMEOUT; } } if (timeout > 0) c.wait(timeout); else c.wait(); } } public int getAvailable(Channel c, boolean extended) throws IOException { synchronized (c) { return (extended ? c.stderr : c.stdout ).available(); } } public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException { int copylen; synchronized (c) { try { copylen = (extended ? c.stderr : c.stdout).read(target, off, len); } catch (InterruptedException e) { throw new InterruptedIOException(); } if (copylen<=0) return copylen; } c.freeupWindow(copylen); return copylen; } public void msgChannelData(byte[] msg, int msglen) throws IOException { if (msglen <= 9) throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")"); c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id); if (len != (msglen - 9)) throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got " + len + ")"); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")"); synchronized (c) { if (c.state == Channel.STATE_CLOSED) return; // ignore if (c.state != Channel.STATE_OPEN) throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")"); if (c.localWindow < len) throw new IOException("Remote sent too much data, does not fit into window."); c.localWindow -= len; c.stdout.write(msg,9,len); } } public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException { if (msglen != 9) throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id); synchronized (c) { final long huge = 0xFFFFffffL; /* 2^32 - 1 */ c.remoteWindow += (windowChange & huge); /* avoid sign extension */ /* TODO - is this a good heuristic? */ if ((c.remoteWindow > huge)) c.remoteWindow = huge; c.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")"); } public void msgChannelOpen(byte[] msg, int msglen) throws IOException { TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type String channelType = tr.readString(); int remoteID = tr.readUINT32(); /* sender channel */ int remoteWindow = tr.readUINT32(); /* initial window size */ int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */ if ("x11".equals(channelType)) { synchronized (x11_magic_cookies) { /* If we did not request X11 forwarding, then simply ignore this bogus request. */ if (x11_magic_cookies.size() == 0) { PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", ""); tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "Unexpected X11 request, denying it!"); return; } } String remoteOriginatorAddress = tr.readString(); int remoteOriginatorPort = tr.readUINT32(); Channel c = new Channel(this); synchronized (c) { c.remoteID = remoteID; c.remoteMaxPacketSize = remoteMaxPacketSize; c.localID = addChannel(c); } /* * The open confirmation message will be sent from another thread */ RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort); rxat.setDaemon(true); rxat.start(); return; } if ("forwarded-tcpip".equals(channelType)) { String remoteConnectedAddress = tr.readString(); /* address that was connected */ int remoteConnectedPort = tr.readUINT32(); /* port that was connected */ String remoteOriginatorAddress = tr.readString(); /* originator IP address */ int remoteOriginatorPort = tr.readUINT32(); /* originator port */ RemoteForwardingData rfd = null; synchronized (remoteForwardings) { rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(remoteConnectedPort)); } if (rfd == null) { PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "No thanks, unknown port in forwarded-tcpip request", ""); /* Always try to be polite. */ tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "Unexpected forwarded-tcpip request, denying it!"); return; } Channel c = new Channel(this); synchronized (c) { c.remoteID = remoteID; c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */ c.remoteMaxPacketSize = remoteMaxPacketSize; c.localID = addChannel(c); } /* * The open confirmation message will be sent from another thread. */ RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort, remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort); rat.setDaemon(true); rat.start(); return; } /* Tell the server that we have no idea what it is talking about */ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE, "Unknown channel type", ""); tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")"); } public void msgChannelRequest(byte[] msg, int msglen) throws IOException { TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type int id = tr.readUINT32(); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id); String type = tr.readString("US-ASCII"); boolean wantReply = tr.readBoolean(); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')"); if (type.equals("exit-status")) { if (wantReply != false) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true"); int exit_status = tr.readUINT32(); if (tr.remain() != 0) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message"); if (msglen < 5) synchronized (c) { c.exit_status = new Integer(exit_status); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")"); return; } if (type.equals("exit-signal")) { if (wantReply != false) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true"); String signame = tr.readString("US-ASCII"); tr.readBoolean(); tr.readString(); tr.readString(); if (tr.remain() != 0) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message"); synchronized (c) { c.exit_signal = signame; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")"); return; } /* We simply ignore unknown channel requests, however, if the server wants a reply, * then we signal that we have no idea what it is about. */ if (wantReply) { byte[] reply = new byte[5]; reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE; reply[1] = (byte) (c.remoteID >> 24); reply[2] = (byte) (c.remoteID >> 16); reply[3] = (byte) (c.remoteID >> 8); reply[4] = (byte) (c.remoteID); tm.sendAsynchronousMessage(reply); } if (log.isEnabled()) log.log(50, "Channel request '" + type + "' is not known, ignoring it"); } public void msgChannelEOF(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id); c.eof(); if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")"); } public void msgChannelClose(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id); synchronized (c) { c.eof(); c.state = Channel.STATE_CLOSED; c.setReasonClosed("Close requested by remote"); c.closeMessageRecv = true; removeChannel(c.localID); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")"); } public void msgChannelSuccess(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")"); public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id); synchronized (c) { c.successCounter++; c.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")"); } public void msgChannelFailure(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id); synchronized (c) { c.failedCounter++; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")"); } public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException { PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen); Channel c = getChannel(sm.recipientChannelID); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel " + sm.recipientChannelID); synchronized (c) { if (c.state != Channel.STATE_OPENING) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel " + sm.recipientChannelID); c.remoteID = sm.senderChannelID; c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */ c.remoteMaxPacketSize = sm.maxPacketSize; c.state = Channel.STATE_OPEN; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: " + sm.senderChannelID + ")"); } { throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")"); TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type int id = tr.readUINT32(); /* sender channel */ Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id); int reasonCode = tr.readUINT32(); String description = tr.readString("UTF-8"); String reasonCodeSymbolicName = null; switch (reasonCode) { case 1: reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED"; break; case 2: reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED"; break; case 3: reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE"; break; case 4: reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE"; break; default: reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")"; } { } StringBuffer descriptionBuffer = new StringBuffer(); descriptionBuffer.append(description); for (int i = 0; i < descriptionBuffer.length(); i++) { char cc = descriptionBuffer.charAt(i); if ((cc >= 32) && (cc <= 126)) continue; descriptionBuffer.setCharAt(i, '\uFFFD'); } synchronized (c) { c.eof(); c.state = Channel.STATE_CLOSED; c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '" + descriptionBuffer.toString() + "')"); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")"); } public void msgGlobalRequest(byte[] msg, int msglen) throws IOException { /* Currently we do not support any kind of global request */ TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type String requestName = tr.readString(); boolean wantReply = tr.readBoolean(); if (wantReply) { byte[] reply_failure = new byte[1]; reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE; tm.sendAsynchronousMessage(reply_failure); } /* We do not clean up the requestName String - that is OK for debug */ if (log.isEnabled()) log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")"); } public void msgGlobalSuccess() throws IOException { synchronized (channels) { globalSuccessCounter++; channels.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_REQUEST_SUCCESS"); } public void msgGlobalFailure() throws IOException { synchronized (channels) { globalFailedCounter++; channels.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_REQUEST_FAILURE"); } public void handleMessage(byte[] msg, int msglen) throws IOException if (msg == null) { if (log.isEnabled()) log.log(50, "HandleMessage: got shutdown"); synchronized (listenerThreads) { for (int i = 0; i < listenerThreads.size(); i++) { IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads.elementAt(i); lat.stopWorking(); } listenerThreadsAllowed = false; } synchronized (channels) { shutdown = true; for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); synchronized (c) { c.eof(); c.state = Channel.STATE_CLOSED; c.setReasonClosed("The connection is being shutdown"); c.closeMessageRecv = true; /* * You never know, perhaps * we are waiting for a * pending close message * from the server... */ c.notifyAll(); } /* Works with J2ME */ channels.setSize(0); channels.trimToSize(); channels.notifyAll(); /* Notify global response waiters */ return; } } switch (msg[0]) { case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION: msgChannelOpenConfirmation(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST: msgChannelWindowAdjust(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_DATA: msgChannelData(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA: msgChannelExtendedData(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_REQUEST: msgChannelRequest(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_EOF: msgChannelEOF(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_OPEN: msgChannelOpen(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_CLOSE: msgChannelClose(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_SUCCESS: msgChannelSuccess(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_FAILURE: msgChannelFailure(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE: msgChannelOpenFailure(msg, msglen); break; case Packets.SSH_MSG_GLOBAL_REQUEST: msgGlobalRequest(msg, msglen); break; case Packets.SSH_MSG_REQUEST_SUCCESS: msgGlobalSuccess(); break; case Packets.SSH_MSG_REQUEST_FAILURE: msgGlobalFailure(); break; default: throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff)); } } } ======= package com.trilead.ssh2.channel; import java.io.IOException; import java.io.InterruptedIOException; import java.util.HashMap; import java.util.Vector; import com.trilead.ssh2.ChannelCondition; import com.trilead.ssh2.log.Logger; import com.trilead.ssh2.packets.PacketChannelOpenConfirmation; import com.trilead.ssh2.packets.PacketChannelOpenFailure; import com.trilead.ssh2.packets.PacketChannelTrileadPing; import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest; import com.trilead.ssh2.packets.PacketGlobalForwardRequest; import com.trilead.ssh2.packets.PacketGlobalTrileadPing; import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel; import com.trilead.ssh2.packets.PacketOpenSessionChannel; import com.trilead.ssh2.packets.PacketSessionExecCommand; import com.trilead.ssh2.packets.PacketSessionPtyRequest; import com.trilead.ssh2.packets.PacketSessionStartShell; import com.trilead.ssh2.packets.PacketSessionSubsystemRequest; import com.trilead.ssh2.packets.PacketSessionX11Request; import com.trilead.ssh2.packets.Packets; import com.trilead.ssh2.packets.TypesReader; import com.trilead.ssh2.transport.MessageHandler; import com.trilead.ssh2.transport.TransportManager; /** * ChannelManager. Please read the comments in Channel.java. *

* Besides the crypto part, this is the core of the library. * * @author Christian Plattner, plattner@trilead.com * @version $Id: ChannelManager.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $ */ public class ChannelManager implements MessageHandler { private static final Logger log = Logger.getLogger(ChannelManager.class); private HashMap x11_magic_cookies = new HashMap(); private TransportManager tm; } private Vector channels = new Vector(); private int nextLocalChannel = 100; private boolean shutdown = false; private int globalSuccessCounter = 0; private int globalFailedCounter = 0; private HashMap remoteForwardings = new HashMap(); private Vector listenerThreads = new Vector(); private boolean listenerThreadsAllowed = true; public ChannelManager(TransportManager tm) { this.tm = tm; tm.registerMessageHandler(this, 80, 100); } private Channel getChannel(int id) { synchronized (channels) { for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); if (c.localID == id) return c; } } return null; } private void removeChannel(int id) { synchronized (channels) { for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); if (c.localID == id) { channels.removeElementAt(i); break; } } } } private int addChannel(Channel c) { synchronized (channels) { channels.addElement(c); return nextLocalChannel++; } } private void waitUntilChannelOpen(Channel c) throws IOException { synchronized (c) { while (c.state == Channel.STATE_OPENING) { try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } if (c.state != Channel.STATE_OPEN) { removeChannel(c.localID); String detail = c.getReasonClosed(); if (detail == null) detail = "state: " + c.state; throw new IOException("Could not open channel (" + detail + ")"); } } } private final boolean waitForGlobalRequestResult() throws IOException { synchronized (channels) { while ((globalSuccessCounter == 0) && (globalFailedCounter == 0)) { if (shutdown) { throw new IOException("The connection is being shutdown"); } try { channels.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } if ((globalFailedCounter == 0) && (globalSuccessCounter == 1)) return true; if ((globalFailedCounter == 1) && (globalSuccessCounter == 0)) return false; throw new IOException("Illegal state. The server sent " + globalSuccessCounter + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages."); } } private final boolean waitForChannelRequestResult(Channel c) throws IOException { synchronized (c) { while ((c.successCounter == 0) && (c.failedCounter == 0)) { if (c.state != Channel.STATE_OPEN) { String detail = c.getReasonClosed(); if (detail == null) detail = "state: " + c.state; throw new IOException("This SSH2 channel is not open (" + detail + ")"); } try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } if ((c.failedCounter == 0) && (c.successCounter == 1)) return true; if ((c.failedCounter == 1) && (c.successCounter == 0)) return false; throw new IOException("Illegal state. The server sent " + c.successCounter + " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages."); } } public void registerX11Cookie(String hexFakeCookie, X11ServerData data) { synchronized (x11_magic_cookies) { x11_magic_cookies.put(hexFakeCookie, data); } } public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels) { if (hexFakeCookie == null) throw new IllegalStateException("hexFakeCookie may not be null"); synchronized (x11_magic_cookies) { x11_magic_cookies.remove(hexFakeCookie); } if (killChannels == false) return; if (log.isEnabled()) log.log(50, "Closing all X11 channels for the given fake cookie"); Vector channel_copy; synchronized (channels) { channel_copy = (Vector) channels.clone(); } for (int i = 0; i < channel_copy.size(); i++) { Channel c = (Channel) channel_copy.elementAt(i); synchronized (c) { if (hexFakeCookie.equals(c.hexX11FakeCookie) == false) continue; } try { closeChannel(c, "Closing X11 channel since the corresponding session is closing", true); } catch (IOException e) { } } } public X11ServerData checkX11Cookie(String hexFakeCookie) { synchronized (x11_magic_cookies) { if (hexFakeCookie != null) return (X11ServerData) x11_magic_cookies.get(hexFakeCookie); } return null; } public void closeAllChannels() { if (log.isEnabled()) log.log(50, "Closing all channels"); Vector channel_copy; synchronized (channels) { channel_copy = (Vector) channels.clone(); } for (int i = 0; i < channel_copy.size(); i++) { Channel c = (Channel) channel_copy.elementAt(i); try { closeChannel(c, "Closing all channels", true); } catch (IOException e) { } } } public void closeChannel(Channel c, String reason, boolean force) throws IOException { byte msg[] = new byte[5]; synchronized (c) { if (force) { c.state = Channel.STATE_CLOSED; c.EOF = true; } c.setReasonClosed(reason); msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); c.notifyAll(); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(msg); c.closeMessageSent = true; } if (log.isEnabled()) log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")"); } public void sendEOF(Channel c) throws IOException { byte[] msg = new byte[5]; synchronized (c) { if (c.state != Channel.STATE_OPEN) return; msg[0] = Packets.SSH_MSG_CHANNEL_EOF; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(msg); } if (log.isEnabled()) log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")"); } public void sendOpenConfirmation(Channel c) throws IOException { PacketChannelOpenConfirmation pcoc = null; synchronized (c) { if (c.state != Channel.STATE_OPENING) return; c.state = Channel.STATE_OPEN; pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(pcoc.getPayload()); } } public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException { while (len > 0) { int thislen = 0; byte[] msg; synchronized (c) { while (true) { if (c.state == Channel.STATE_CLOSED) throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")"); if (c.state != Channel.STATE_OPEN) throw new IOException("SSH channel in strange state. (" + c.state + ")"); if (c.remoteWindow != 0) break; try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } /* len > 0, no sign extension can happen when comparing */ thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow; int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9); /* The worst case scenario =) a true bottleneck */ if (estimatedMaxDataLen <= 0) { estimatedMaxDataLen = 1; } if (thislen > estimatedMaxDataLen) thislen = estimatedMaxDataLen; c.remoteWindow -= thislen; msg = new byte[1 + 8 + thislen]; msg[0] = Packets.SSH_MSG_CHANNEL_DATA; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); msg[5] = (byte) (thislen >> 24); msg[6] = (byte) (thislen >> 16); msg[7] = (byte) (thislen >> 8); msg[8] = (byte) (thislen); System.arraycopy(buffer, pos, msg, 9, thislen); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")"); tm.sendMessage(msg); } pos += thislen; len -= thislen; } } public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort) throws IOException { RemoteForwardingData rfd = new RemoteForwardingData(); rfd.bindAddress = bindAddress; rfd.bindPort = bindPort; rfd.targetAddress = targetAddress; rfd.targetPort = targetPort; synchronized (remoteForwardings) { Integer key = new Integer(bindPort); if (remoteForwardings.get(key) != null) { throw new IOException("There is already a forwarding for remote port " + bindPort); } remoteForwardings.put(key, rfd); } synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort); tm.sendMessage(pgf.getPayload()); if (log.isEnabled()) log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")"); try { if (waitForGlobalRequestResult() == false) throw new IOException("The server denied the request (did you enable port forwarding?)"); } catch (IOException e) { synchronized (remoteForwardings) { remoteForwardings.remove(rfd); } throw e; } return bindPort; } public void requestCancelGlobalForward(int bindPort) throws IOException { RemoteForwardingData rfd = null; synchronized (remoteForwardings) { rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(bindPort)); if (rfd == null) throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort); } synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress, rfd.bindPort); tm.sendMessage(pgcf.getPayload()); if (log.isEnabled()) log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")"); try { if (waitForGlobalRequestResult() == false) throw new IOException("The server denied the request."); } finally { synchronized (remoteForwardings) { /* Only now we are sure that no more forwarded connections will arrive */ remoteForwardings.remove(rfd); } } } public void registerThread(IChannelWorkerThread thr) throws IOException { synchronized (listenerThreads) { if (listenerThreadsAllowed == false) throw new IOException("Too late, this connection is closed."); listenerThreads.addElement(thr); } } public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address, int originator_port) throws IOException { Channel c = new Channel(this); synchronized (c) { c.localID = addChannel(c); // end of synchronized block forces writing out to main memory } PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow, c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port); tm.sendMessage(dtc.getPayload()); waitUntilChannelOpen(c); return c; } public Channel openSessionChannel() throws IOException { Channel c = new Channel(this); synchronized (c) { c.localID = addChannel(c); // end of synchronized block forces the writing out to main memory } if (log.isEnabled()) log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")"); PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize); tm.sendMessage(smo.getPayload()); waitUntilChannelOpen(c); return c; } public void requestGlobalTrileadPing() throws IOException { synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing(); tm.sendMessage(pgtp.getPayload()); if (log.isEnabled()) log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'."); try { if (waitForGlobalRequestResult() == true) throw new IOException("Your server is alive - but buggy. " + "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not."); } catch (IOException e) { throw (IOException) new IOException("The ping request failed.").initCause(e); } } public void requestChannelTrileadPing(Channel c) throws IOException { PacketChannelTrileadPing pctp; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")"); pctp = new PacketChannelTrileadPing(c.remoteID); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(pctp.getPayload()); } try { if (waitForChannelRequestResult(c) == true) throw new IOException("Your server is alive - but buggy. " + "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not."); } catch (IOException e) { throw (IOException) new IOException("The ping request failed.").initCause(e); } } public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException { PacketSessionPtyRequest spr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")"); spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, terminal_modes); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(spr.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("PTY request failed").initCause(e); } } public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber) throws IOException { PacketSessionX11Request psr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")"); psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol, x11AuthenticationCookie, x11ScreenNumber); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(psr.getPayload()); } if (log.isEnabled()) log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")"); try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The X11 request failed.").initCause(e); } } public void requestSubSystem(Channel c, String subSystemName) throws IOException { PacketSessionSubsystemRequest ssr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")"); ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(ssr.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The subsystem request failed.").initCause(e); } } public void requestExecCommand(Channel c, String cmd) throws IOException { PacketSessionExecCommand sm; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")"); sm = new PacketSessionExecCommand(c.remoteID, true, cmd); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(sm.getPayload()); } if (log.isEnabled()) log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')"); try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The execute request failed.").initCause(e); } } public void requestShell(Channel c) throws IOException { PacketSessionStartShell sm; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")"); sm = new PacketSessionStartShell(c.remoteID, true); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(sm.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The shell request failed.").initCause(e); } } public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException { if (msglen <= 13) throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff); Channel c = getChannel(id); if (c == null) { throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id); if (dataType != Packets.SSH_EXTENDED_DATA_STDERR) throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")"); if (len != (msglen - 13)) throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13) + ", got " + len + ")"); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")"); synchronized (c) { if (c.state == Channel.STATE_CLOSED) return; // ignore if (c.state != Channel.STATE_OPEN) throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state (" + c.state + ")"); if (c.localWindow < len) throw new IOException("Remote sent too much data, does not fit into window."); c.localWindow -= len; System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len); c.stderrWritepos += len; c.notifyAll(); } } /** * Wait until for a condition. * * @param c * Channel * @param timeout * in ms, 0 means no timeout. * @param condition_mask * minimum event mask * @return all current events * */ public int waitForCondition(Channel c, long timeout, int condition_mask) { long end_time = 0; boolean end_time_set = false; synchronized (c) { while (true) { int current_cond = 0; int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos; int stderrAvail = c.stderrWritepos - c.stderrReadpos; if (stdoutAvail > 0) current_cond = current_cond | ChannelCondition.STDOUT_DATA; if (stderrAvail > 0) current_cond = current_cond | ChannelCondition.STDERR_DATA; if (c.EOF) current_cond = current_cond | ChannelCondition.EOF; if (c.getExitStatus() != null) current_cond = current_cond | ChannelCondition.EXIT_STATUS; if (c.getExitSignal() != null) current_cond = current_cond | ChannelCondition.EXIT_SIGNAL; if (c.state == Channel.STATE_CLOSED) return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF; if ((current_cond & condition_mask) != 0) return current_cond; if (timeout > 0) { if (!end_time_set) { end_time = System.currentTimeMillis() + timeout; end_time_set = true; } else { timeout = end_time - System.currentTimeMillis(); if (timeout <= 0) return current_cond | ChannelCondition.TIMEOUT; } } try { if (timeout > 0) c.wait(timeout); else c.wait(); } catch (InterruptedException e) { } } } } public int getAvailable(Channel c, boolean extended) throws IOException synchronized (c) { int avail; if (extended) avail = c.stderrWritepos - c.stderrReadpos; else avail = c.stdoutWritepos - c.stdoutReadpos; return ((avail > 0) ? avail : (c.EOF ? -1 : 0)); } } public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException { int copylen = 0; int increment = 0; int remoteID = 0; int localID = 0; synchronized (c) { int stdoutAvail = 0; int stderrAvail = 0; while (true) { /* * Data available? We have to return remaining data even if the * channel is already closed. */ stdoutAvail = c.stdoutWritepos - c.stdoutReadpos; stderrAvail = c.stderrWritepos - c.stderrReadpos; if ((!extended) && (stdoutAvail != 0)) break; if ((extended) && (stderrAvail != 0)) break; /* Do not wait if more data will never arrive (EOF or CLOSED) */ if ((c.EOF) || (c.state != Channel.STATE_OPEN)) return -1; try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } /* OK, there is some data. Return it. */ if (!extended) { copylen = (stdoutAvail > len) ? len : stdoutAvail; System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen); c.stdoutReadpos += copylen; if (c.stdoutReadpos != c.stdoutWritepos) System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos - c.stdoutReadpos); c.stdoutWritepos -= c.stdoutReadpos; c.stdoutReadpos = 0; } else { copylen = (stderrAvail > len) ? len : stderrAvail; System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen); c.stderrReadpos += copylen; if (c.stderrReadpos != c.stderrWritepos) System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos - c.stderrReadpos); c.stderrWritepos -= c.stderrReadpos; c.stderrReadpos = 0; } if (c.state != Channel.STATE_OPEN) return copylen; if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2)) { int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos, Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos); increment = minFreeSpace - c.localWindow; c.localWindow = minFreeSpace; } remoteID = c.remoteID; /* read while holding the lock */ localID = c.localID; /* read while holding the lock */ } /* * If a consumer reads stdout and stdin in parallel, we may end up with * sending two msgWindowAdjust messages. Luckily, it * does not matter in which order they arrive at the server. */ if (increment > 0) { if (log.isEnabled()) log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")"); synchronized (c.channelSendLock) { byte[] msg = c.msgWindowAdjust; msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST; msg[1] = (byte) (remoteID >> 24); msg[2] = (byte) (remoteID >> 16); msg[3] = (byte) (remoteID >> 8); msg[4] = (byte) (remoteID); msg[5] = (byte) (increment >> 24); msg[6] = (byte) (increment >> 16); msg[7] = (byte) (increment >> 8); msg[8] = (byte) (increment); if (c.closeMessageSent == false) tm.sendMessage(msg); } } return copylen; } public void msgChannelData(byte[] msg, int msglen) throws IOException { if (msglen <= 9) throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id); if (len != (msglen - 9)) throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got " + len + ")"); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")"); synchronized (c) { if (c.state == Channel.STATE_CLOSED) return; // ignore if (c.state != Channel.STATE_OPEN) throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")"); if (c.localWindow < len) throw new IOException("Remote sent too much data, does not fit into window."); c.localWindow -= len; System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len); c.stdoutWritepos += len; c.notifyAll(); } } public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException { if (msglen != 9) throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id); synchronized (c) { final long huge = 0xFFFFffffL; /* 2^32 - 1 */ c.remoteWindow += (windowChange & huge); /* avoid sign extension */ /* TODO - is this a good heuristic? */ if ((c.remoteWindow > huge)) c.remoteWindow = huge; c.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")"); } public void msgChannelOpen(byte[] msg, int msglen) throws IOException { TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type String channelType = tr.readString(); int remoteID = tr.readUINT32(); /* sender channel */ int remoteWindow = tr.readUINT32(); /* initial window size */ int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */ if ("x11".equals(channelType)) { synchronized (x11_magic_cookies) { /* If we did not request X11 forwarding, then simply ignore this bogus request. */ if (x11_magic_cookies.size() == 0) { PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", ""); tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "Unexpected X11 request, denying it!"); return; } } String remoteOriginatorAddress = tr.readString(); int remoteOriginatorPort = tr.readUINT32(); Channel c = new Channel(this); synchronized (c) { c.remoteID = remoteID; c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */ c.remoteMaxPacketSize = remoteMaxPacketSize; c.localID = addChannel(c); } /* * The open confirmation message will be sent from another thread */ RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort); rxat.setDaemon(true); rxat.start(); return; } if ("forwarded-tcpip".equals(channelType)) { String remoteConnectedAddress = tr.readString(); /* address that was connected */ int remoteConnectedPort = tr.readUINT32(); /* port that was connected */ String remoteOriginatorAddress = tr.readString(); /* originator IP address */ int remoteOriginatorPort = tr.readUINT32(); /* originator port */ RemoteForwardingData rfd = null; synchronized (remoteForwardings) { rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(remoteConnectedPort)); } if (rfd == null) { PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "No thanks, unknown port in forwarded-tcpip request", ""); /* Always try to be polite. */ tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "Unexpected forwarded-tcpip request, denying it!"); return; } Channel c = new Channel(this); synchronized (c) { c.remoteID = remoteID; c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */ c.remoteMaxPacketSize = remoteMaxPacketSize; c.localID = addChannel(c); } /* * The open confirmation message will be sent from another thread. */ RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort, remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort); rat.setDaemon(true); rat.start(); return; } /* Tell the server that we have no idea what it is talking about */ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE, "Unknown channel type", ""); tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")"); } public void msgChannelRequest(byte[] msg, int msglen) throws IOException { TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type int id = tr.readUINT32(); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id); String type = tr.readString("US-ASCII"); boolean wantReply = tr.readBoolean(); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')"); if (type.equals("exit-status")) { if (wantReply != false) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true"); int exit_status = tr.readUINT32(); if (tr.remain() != 0) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message"); synchronized (c) { c.exit_status = new Integer(exit_status); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")"); return; } if (type.equals("exit-signal")) { if (wantReply != false) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true"); String signame = tr.readString("US-ASCII"); tr.readBoolean(); tr.readString(); tr.readString(); if (tr.remain() != 0) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message"); synchronized (c) { c.exit_signal = signame; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")"); return; } /* We simply ignore unknown channel requests, however, if the server wants a reply, * then we signal that we have no idea what it is about. */ if (wantReply) { byte[] reply = new byte[5]; reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE; reply[1] = (byte) (c.remoteID >> 24); reply[2] = (byte) (c.remoteID >> 16); reply[3] = (byte) (c.remoteID >> 8); reply[4] = (byte) (c.remoteID); tm.sendAsynchronousMessage(reply); } if (log.isEnabled()) log.log(50, "Channel request '" + type + "' is not known, ignoring it"); } public void msgChannelEOF(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id); synchronized (c) { c.EOF = true; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")"); } public void msgChannelClose(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id); synchronized (c) { c.EOF = true; c.state = Channel.STATE_CLOSED; c.setReasonClosed("Close requested by remote"); c.closeMessageRecv = true; removeChannel(c.localID); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")"); } public void msgChannelSuccess(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id); synchronized (c) { c.successCounter++; c.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")"); } public void msgChannelFailure(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id); synchronized (c) { c.failedCounter++; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")"); } public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException { PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen); Channel c = getChannel(sm.recipientChannelID); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel " + sm.recipientChannelID); synchronized (c) { if (c.state != Channel.STATE_OPENING) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel " + sm.recipientChannelID); c.remoteID = sm.senderChannelID; c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */ c.remoteMaxPacketSize = sm.maxPacketSize; c.state = Channel.STATE_OPEN; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: " + sm.senderChannelID + ")"); } public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException { if (msglen < 5) throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")"); TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type int id = tr.readUINT32(); /* sender channel */ Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id); int reasonCode = tr.readUINT32(); String description = tr.readString("UTF-8"); String reasonCodeSymbolicName = null; switch (reasonCode) { case 1: reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED"; break; case 2: reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED"; break; case 3: reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE"; break; case 4: reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE"; break; default: reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")"; } StringBuffer descriptionBuffer = new StringBuffer(); descriptionBuffer.append(description); for (int i = 0; i < descriptionBuffer.length(); i++) { char cc = descriptionBuffer.charAt(i); if ((cc >= 32) && (cc <= 126)) continue; descriptionBuffer.setCharAt(i, '\uFFFD'); } synchronized (c) { c.EOF = true; c.state = Channel.STATE_CLOSED; c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '" + descriptionBuffer.toString() + "')"); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")"); } public void msgGlobalRequest(byte[] msg, int msglen) throws IOException { /* Currently we do not support any kind of global request */ TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type String requestName = tr.readString(); boolean wantReply = tr.readBoolean(); if (wantReply) { byte[] reply_failure = new byte[1]; reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE; tm.sendAsynchronousMessage(reply_failure); } /* We do not clean up the requestName String - that is OK for debug */ if (log.isEnabled()) log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")"); } public void msgGlobalSuccess() throws IOException { synchronized (channels) { globalSuccessCounter++; channels.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_REQUEST_SUCCESS"); } public void msgGlobalFailure() throws IOException { synchronized (channels) { globalFailedCounter++; channels.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_REQUEST_FAILURE"); } public void handleMessage(byte[] msg, int msglen) throws IOException { if (msg == null) { if (log.isEnabled()) log.log(50, "HandleMessage: got shutdown"); synchronized (listenerThreads) { for (int i = 0; i < listenerThreads.size(); i++) { IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads.elementAt(i); lat.stopWorking(); } listenerThreadsAllowed = false; } synchronized (channels) { shutdown = true; for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); synchronized (c) { c.EOF = true; c.state = Channel.STATE_CLOSED; c.setReasonClosed("The connection is being shutdown"); c.closeMessageRecv = true; /* * You never know, perhaps * we are waiting for a * pending close message * from the server... */ c.notifyAll(); } } /* Works with J2ME */ channels.setSize(0); channels.trimToSize(); channels.notifyAll(); /* Notify global response waiters */ return; } } switch (msg[0]) { case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION: msgChannelOpenConfirmation(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST: msgChannelWindowAdjust(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_DATA: msgChannelData(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA: msgChannelExtendedData(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_REQUEST: msgChannelRequest(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_EOF: msgChannelEOF(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_OPEN: msgChannelOpen(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_CLOSE: msgChannelClose(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_SUCCESS: msgChannelSuccess(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_FAILURE: msgChannelFailure(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE: msgChannelOpenFailure(msg, msglen); break; case Packets.SSH_MSG_GLOBAL_REQUEST: msgGlobalRequest(msg, msglen); break; case Packets.SSH_MSG_REQUEST_SUCCESS: msgGlobalSuccess(); break; case Packets.SSH_MSG_REQUEST_FAILURE: msgGlobalFailure(); break; default: throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff)); } } } >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/ChannelManager.java

Solution content

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.HashMap;
import java.util.Vector;

import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
import com.trilead.ssh2.packets.PacketChannelOpenFailure;
import com.trilead.ssh2.packets.PacketChannelTrileadPing;
import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
import com.trilead.ssh2.packets.PacketOpenSessionChannel;
import com.trilead.ssh2.packets.PacketSessionExecCommand;
import com.trilead.ssh2.packets.PacketSessionPtyRequest;
import com.trilead.ssh2.packets.PacketSessionStartShell;
import com.trilead.ssh2.packets.PacketSessionSubsystemRequest;
import com.trilead.ssh2.packets.PacketSessionX11Request;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.packets.TypesReader;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;

/**
 * ChannelManager. Please read the comments in Channel.java.
 * 

* Besides the crypto part, this is the core of the library. * * @author Christian Plattner, plattner@trilead.com * @version $Id: ChannelManager.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $ */ public class ChannelManager implements MessageHandler { private static final Logger log = Logger.getLogger(ChannelManager.class); private HashMap x11_magic_cookies = new HashMap(); /*package*/ TransportManager tm; private Vector channels = new Vector(); private int nextLocalChannel = 100; private boolean shutdown = false; private int globalSuccessCounter = 0; private int globalFailedCounter = 0; private HashMap remoteForwardings = new HashMap(); private Vector listenerThreads = new Vector(); private boolean listenerThreadsAllowed = true; public ChannelManager(TransportManager tm) if (c.state != Channel.STATE_OPEN) { { this.tm = tm; tm.registerMessageHandler(this, 80, 100); } private Channel getChannel(int id) { synchronized (channels) { for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); if (c.localID == id) return c; } } return null; } private void removeChannel(int id) { synchronized (channels) { for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); if (c.localID == id) { channels.removeElementAt(i); break; } } } } private int addChannel(Channel c) { synchronized (channels) { channels.addElement(c); return nextLocalChannel++; } } private void waitUntilChannelOpen(Channel c) throws IOException { synchronized (c) { while (c.state == Channel.STATE_OPENING) { try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } removeChannel(c.localID); String detail = c.getReasonClosed(); if (detail == null) detail = "state: " + c.state; throw new IOException("Could not open channel (" + detail + ")"); } } } private final boolean waitForGlobalRequestResult() throws IOException { synchronized (channels) { while ((globalSuccessCounter == 0) && (globalFailedCounter == 0)) { if (shutdown) { throw new IOException("The connection is being shutdown"); } try { channels.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } if ((globalFailedCounter == 0) && (globalSuccessCounter == 1)) return true; if ((globalFailedCounter == 1) && (globalSuccessCounter == 0)) return false; throw new IOException("Illegal state. The server sent " + globalSuccessCounter + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages."); } } private final boolean waitForChannelRequestResult(Channel c) throws IOException { synchronized (c) { while ((c.successCounter == 0) && (c.failedCounter == 0)) { if (c.state != Channel.STATE_OPEN) { String detail = c.getReasonClosed(); if (detail == null) detail = "state: " + c.state; throw new IOException("This SSH2 channel is not open (" + detail + ")"); } try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } if ((c.failedCounter == 0) && (c.successCounter == 1)) return true; if ((c.failedCounter == 1) && (c.successCounter == 0)) return false; throw new IOException("Illegal state. The server sent " + c.successCounter + " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages."); } } public void registerX11Cookie(String hexFakeCookie, X11ServerData data) { synchronized (x11_magic_cookies) { x11_magic_cookies.put(hexFakeCookie, data); } } public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels) { if (hexFakeCookie == null) throw new IllegalStateException("hexFakeCookie may not be null"); synchronized (x11_magic_cookies) { x11_magic_cookies.remove(hexFakeCookie); } if (killChannels == false) return; if (log.isEnabled()) log.log(50, "Closing all X11 channels for the given fake cookie"); Vector channel_copy; synchronized (channels) { channel_copy = (Vector) channels.clone(); } for (int i = 0; i < channel_copy.size(); i++) { Channel c = (Channel) channel_copy.elementAt(i); synchronized (c) { if (hexFakeCookie.equals(c.hexX11FakeCookie) == false) continue; } try { closeChannel(c, "Closing X11 channel since the corresponding session is closing", true); } catch (IOException e) { } } } public X11ServerData checkX11Cookie(String hexFakeCookie) { synchronized (x11_magic_cookies) { if (hexFakeCookie != null) return (X11ServerData) x11_magic_cookies.get(hexFakeCookie); } return null; } public void closeAllChannels() { if (log.isEnabled()) log.log(50, "Closing all channels"); Vector channel_copy; synchronized (channels) { channel_copy = (Vector) channels.clone(); } for (int i = 0; i < channel_copy.size(); i++) { Channel c = (Channel) channel_copy.elementAt(i); try { closeChannel(c, "Closing all channels", true); } catch (IOException e) { } } } public void closeChannel(Channel c, String reason, boolean force) throws IOException { byte msg[] = new byte[5]; synchronized (c) { if (force) { c.state = Channel.STATE_CLOSED; c.eof(); } c.setReasonClosed(reason); msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); c.notifyAll(); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(msg); c.closeMessageSent = true; } if (log.isEnabled()) log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")"); } public void sendEOF(Channel c) throws IOException { byte[] msg = new byte[5]; synchronized (c) { if (c.state != Channel.STATE_OPEN) return; msg[0] = Packets.SSH_MSG_CHANNEL_EOF; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(msg); } if (log.isEnabled()) log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")"); } public void sendOpenConfirmation(Channel c) throws IOException { PacketChannelOpenConfirmation pcoc = null; synchronized (c) { if (c.state != Channel.STATE_OPENING) return; c.state = Channel.STATE_OPEN; pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) return; tm.sendMessage(pcoc.getPayload()); } } public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException { while (len > 0) { int thislen = 0; byte[] msg; synchronized (c) { while (true) { if (c.state == Channel.STATE_CLOSED) throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")"); if (c.state != Channel.STATE_OPEN) throw new IOException("SSH channel in strange state. (" + c.state + ")"); if (c.remoteWindow != 0) break; try { c.wait(); } catch (InterruptedException ignore) { throw new InterruptedIOException(); } } /* len > 0, no sign extension can happen when comparing */ thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow; int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9); /* The worst case scenario =) a true bottleneck */ if (estimatedMaxDataLen <= 0) { estimatedMaxDataLen = 1; } if (thislen > estimatedMaxDataLen) thislen = estimatedMaxDataLen; c.remoteWindow -= thislen; msg = new byte[1 + 8 + thislen]; msg[0] = Packets.SSH_MSG_CHANNEL_DATA; msg[1] = (byte) (c.remoteID >> 24); msg[2] = (byte) (c.remoteID >> 16); msg[3] = (byte) (c.remoteID >> 8); msg[4] = (byte) (c.remoteID); msg[5] = (byte) (thislen >> 24); msg[6] = (byte) (thislen >> 16); msg[7] = (byte) (thislen >> 8); msg[8] = (byte) (thislen); System.arraycopy(buffer, pos, msg, 9, thislen); } synchronized (c.channelSendLock) { if (c.closeMessageSent == true) throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")"); tm.sendMessage(msg); } pos += thislen; len -= thislen; } } public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort) throws IOException { RemoteForwardingData rfd = new RemoteForwardingData(); rfd.bindAddress = bindAddress; rfd.bindPort = bindPort; rfd.targetAddress = targetAddress; rfd.targetPort = targetPort; synchronized (remoteForwardings) { Integer key = new Integer(bindPort); if (remoteForwardings.get(key) != null) { throw new IOException("There is already a forwarding for remote port " + bindPort); } remoteForwardings.put(key, rfd); } { synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort); tm.sendMessage(pgf.getPayload()); if (log.isEnabled()) log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")"); try { if (waitForGlobalRequestResult() == false) throw new IOException("The server denied the request (did you enable port forwarding?)"); } catch (IOException e) { synchronized (remoteForwardings) { remoteForwardings.remove(rfd); } throw e; } return bindPort; } public void requestCancelGlobalForward(int bindPort) throws IOException { RemoteForwardingData rfd = null; synchronized (remoteForwardings) { rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(bindPort)); if (rfd == null) throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort); } synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress, rfd.bindPort); tm.sendMessage(pgcf.getPayload()); if (log.isEnabled()) log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")"); try { if (waitForGlobalRequestResult() == false) throw new IOException("The server denied the request."); } finally { synchronized (remoteForwardings) { /* Only now we are sure that no more forwarded connections will arrive */ remoteForwardings.remove(rfd); } } } public void registerThread(IChannelWorkerThread thr) throws IOException { synchronized (listenerThreads) { if (listenerThreadsAllowed == false) throw new IOException("Too late, this connection is closed."); listenerThreads.addElement(thr); } } public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address, int originator_port) throws IOException { Channel c = new Channel(this); synchronized (c) { c.localID = addChannel(c); // end of synchronized block forces writing out to main memory } PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow, c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port); tm.sendMessage(dtc.getPayload()); waitUntilChannelOpen(c); return c; } public Channel openSessionChannel() throws IOException { Channel c = new Channel(this); synchronized (c) { c.localID = addChannel(c); // end of synchronized block forces the writing out to main memory } if (log.isEnabled()) log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")"); PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize); tm.sendMessage(smo.getPayload()); waitUntilChannelOpen(c); return c; } public void requestGlobalTrileadPing() throws IOException { synchronized (channels) { globalSuccessCounter = globalFailedCounter = 0; } PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing(); tm.sendMessage(pgtp.getPayload()); if (log.isEnabled()) log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'."); try { if (waitForGlobalRequestResult() == true) throw new IOException("Your server is alive - but buggy. " + "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not."); } catch (IOException e) { throw (IOException) new IOException("The ping request failed.").initCause(e); } } public void requestChannelTrileadPing(Channel c) throws IOException { PacketChannelTrileadPing pctp; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")"); pctp = new PacketChannelTrileadPing(c.remoteID); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(pctp.getPayload()); } try { if (waitForChannelRequestResult(c) == true) throw new IOException("Your server is alive - but buggy. " + "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not."); } catch (IOException e) { throw (IOException) new IOException("The ping request failed.").initCause(e); } } public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException { PacketSessionPtyRequest spr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")"); spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, terminal_modes); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(spr.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("PTY request failed").initCause(e); } } public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol, if (c.state != Channel.STATE_OPEN) String x11AuthenticationCookie, int x11ScreenNumber) throws IOException { PacketSessionX11Request psr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")"); psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol, x11AuthenticationCookie, x11ScreenNumber); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(psr.getPayload()); } if (log.isEnabled()) log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")"); try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The X11 request failed.").initCause(e); } } public void requestSubSystem(Channel c, String subSystemName) throws IOException { PacketSessionSubsystemRequest ssr; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")"); ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(ssr.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The subsystem request failed.").initCause(e); } } public void requestExecCommand(Channel c, String cmd) throws IOException { PacketSessionExecCommand sm; synchronized (c) { if (c.state != Channel.STATE_OPEN) throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")"); sm = new PacketSessionExecCommand(c.remoteID, true, cmd); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(sm.getPayload()); } if (log.isEnabled()) log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')"); try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The execute request failed.").initCause(e); } } public void requestShell(Channel c) throws IOException { PacketSessionStartShell sm; synchronized (c) throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")"); sm = new PacketSessionStartShell(c.remoteID, true); c.successCounter = c.failedCounter = 0; } synchronized (c.channelSendLock) { if (c.closeMessageSent) throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")"); tm.sendMessage(sm.getPayload()); } try { if (waitForChannelRequestResult(c) == false) throw new IOException("The server denied the request."); } catch (IOException e) { throw (IOException) new IOException("The shell request failed.").initCause(e); } } public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException { if (msglen <= 13) throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id); if (dataType != Packets.SSH_EXTENDED_DATA_STDERR) throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")"); if (len != (msglen - 13)) throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13) + ", got " + len + ")"); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")"); synchronized (c) { if (c.state == Channel.STATE_CLOSED) return; // ignore if (c.state != Channel.STATE_OPEN) throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state (" + c.state + ")"); if (c.localWindow < len) throw new IOException("Remote sent too much data, does not fit into window."); c.localWindow -= len; c.stderr.write(msg,13,len); } } /** * Wait until for a condition. * * @param c * Channel * @param timeout * in ms, 0 means no timeout. * @param condition_mask * minimum event mask * @return all current events * */ public int waitForCondition(Channel c, long timeout, int condition_mask) throws InterruptedException { long end_time = 0; boolean end_time_set = false; synchronized (c) { while (true) { int current_cond = 0; int stdoutAvail = c.stdout.readable(); int stderrAvail = c.stderr.readable(); if (stdoutAvail > 0) current_cond = current_cond | ChannelCondition.STDOUT_DATA; if (stderrAvail > 0) current_cond = current_cond | ChannelCondition.STDERR_DATA; if (c.isEOF()) current_cond = current_cond | ChannelCondition.EOF; if (c.getExitStatus() != null) current_cond = current_cond | ChannelCondition.EXIT_STATUS; if (c.getExitSignal() != null) current_cond = current_cond | ChannelCondition.EXIT_SIGNAL; if (c.state == Channel.STATE_CLOSED) return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF; if ((current_cond & condition_mask) != 0) return current_cond; if (timeout > 0) { if (!end_time_set) { end_time = System.currentTimeMillis() + timeout; end_time_set = true; } else { timeout = end_time - System.currentTimeMillis(); if (timeout <= 0) return current_cond | ChannelCondition.TIMEOUT; } } if (timeout > 0) c.wait(timeout); else c.wait(); } } } public int getAvailable(Channel c, boolean extended) throws IOException { synchronized (c) { return (extended ? c.stderr : c.stdout ).available(); } } public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException { int copylen; synchronized (c) { try { copylen = (extended ? c.stderr : c.stdout).read(target, off, len); } catch (InterruptedException e) { throw new InterruptedIOException(); } if (copylen<=0) return copylen; } c.freeupWindow(copylen); return copylen; } Channel c = new Channel(this); public void msgChannelData(byte[] msg, int msglen) throws IOException { if (msglen <= 9) throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id); if (len != (msglen - 9)) throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got " + len + ")"); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")"); synchronized (c) { if (c.state == Channel.STATE_CLOSED) return; // ignore if (c.state != Channel.STATE_OPEN) throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")"); if (c.localWindow < len) throw new IOException("Remote sent too much data, does not fit into window."); c.localWindow -= len; c.stdout.write(msg,9,len); } } public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException { if (msglen != 9) throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id); synchronized (c) { final long huge = 0xFFFFffffL; /* 2^32 - 1 */ c.remoteWindow += (windowChange & huge); /* avoid sign extension */ /* TODO - is this a good heuristic? */ if ((c.remoteWindow > huge)) c.remoteWindow = huge; c.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")"); } public void msgChannelOpen(byte[] msg, int msglen) throws IOException { TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type String channelType = tr.readString(); int remoteID = tr.readUINT32(); /* sender channel */ int remoteWindow = tr.readUINT32(); /* initial window size */ int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */ if ("x11".equals(channelType)) { synchronized (x11_magic_cookies) { /* If we did not request X11 forwarding, then simply ignore this bogus request. */ if (x11_magic_cookies.size() == 0) { PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", ""); tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "Unexpected X11 request, denying it!"); return; } } String remoteOriginatorAddress = tr.readString(); int remoteOriginatorPort = tr.readUINT32(); synchronized (c) { c.remoteID = remoteID; c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */ c.remoteMaxPacketSize = remoteMaxPacketSize; c.localID = addChannel(c); } /* * The open confirmation message will be sent from another thread */ RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort); rxat.setDaemon(true); rxat.start(); return; } if ("forwarded-tcpip".equals(channelType)) { String remoteConnectedAddress = tr.readString(); /* address that was connected */ int remoteConnectedPort = tr.readUINT32(); /* port that was connected */ String remoteOriginatorAddress = tr.readString(); /* originator IP address */ int remoteOriginatorPort = tr.readUINT32(); /* originator port */ RemoteForwardingData rfd = null; synchronized (remoteForwardings) { rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(remoteConnectedPort)); } if (rfd == null) { PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "No thanks, unknown port in forwarded-tcpip request", ""); /* Always try to be polite. */ tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "Unexpected forwarded-tcpip request, denying it!"); return; } Channel c = new Channel(this); synchronized (c) { c.remoteID = remoteID; c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */ c.remoteMaxPacketSize = remoteMaxPacketSize; c.localID = addChannel(c); } /* * The open confirmation message will be sent from another thread. */ RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort, remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort); rat.setDaemon(true); rat.start(); return; } /* Tell the server that we have no idea what it is talking about */ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE, "Unknown channel type", ""); tm.sendAsynchronousMessage(pcof.getPayload()); if (log.isEnabled()) log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")"); } public void msgChannelRequest(byte[] msg, int msglen) throws IOException { TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type int id = tr.readUINT32(); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id); String type = tr.readString("US-ASCII"); boolean wantReply = tr.readBoolean(); if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')"); if (type.equals("exit-status")) { if (wantReply != false) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true"); int exit_status = tr.readUINT32(); if (tr.remain() != 0) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message"); synchronized (c) { c.exit_status = new Integer(exit_status); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")"); return; } if (type.equals("exit-signal")) { if (wantReply != false) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true"); String signame = tr.readString("US-ASCII"); tr.readBoolean(); tr.readString(); tr.readString(); if (tr.remain() != 0) throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message"); synchronized (c) { c.exit_signal = signame; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")"); return; } /* We simply ignore unknown channel requests, however, if the server wants a reply, * then we signal that we have no idea what it is about. */ if (wantReply) { byte[] reply = new byte[5]; reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE; reply[1] = (byte) (c.remoteID >> 24); reply[2] = (byte) (c.remoteID >> 16); reply[3] = (byte) (c.remoteID >> 8); reply[4] = (byte) (c.remoteID); tm.sendAsynchronousMessage(reply); } if (log.isEnabled()) log.log(50, "Channel request '" + type + "' is not known, ignoring it"); } public void msgChannelEOF(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id); c.eof(); if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")"); } public void msgChannelClose(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id); synchronized (c) { c.eof(); c.state = Channel.STATE_CLOSED; c.setReasonClosed("Close requested by remote"); c.closeMessageRecv = true; removeChannel(c.localID); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")"); } public void msgChannelSuccess(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id); synchronized (c) { c.successCounter++; c.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")"); } public void msgChannelFailure(byte[] msg, int msglen) throws IOException { if (msglen != 5) throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")"); int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff); Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id); synchronized (c) { c.failedCounter++; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")"); } public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException { tm.sendAsynchronousMessage(reply_failure); PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen); Channel c = getChannel(sm.recipientChannelID); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel " + sm.recipientChannelID); synchronized (c) { if (c.state != Channel.STATE_OPENING) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel " + sm.recipientChannelID); c.remoteID = sm.senderChannelID; c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */ c.remoteMaxPacketSize = sm.maxPacketSize; c.state = Channel.STATE_OPEN; c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: " + sm.senderChannelID + ")"); } public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException { if (msglen < 5) throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")"); TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type int id = tr.readUINT32(); /* sender channel */ Channel c = getChannel(id); if (c == null) throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id); int reasonCode = tr.readUINT32(); String description = tr.readString("UTF-8"); String reasonCodeSymbolicName = null; switch (reasonCode) { case 1: reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED"; break; case 2: reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED"; break; case 3: } reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE"; break; case 4: reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE"; break; default: reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")"; } StringBuffer descriptionBuffer = new StringBuffer(); descriptionBuffer.append(description); for (int i = 0; i < descriptionBuffer.length(); i++) { char cc = descriptionBuffer.charAt(i); if ((cc >= 32) && (cc <= 126)) continue; descriptionBuffer.setCharAt(i, '\uFFFD'); } synchronized (c) { c.eof(); c.state = Channel.STATE_CLOSED; c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '" + descriptionBuffer.toString() + "')"); c.notifyAll(); } if (log.isEnabled()) log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")"); } public void msgGlobalRequest(byte[] msg, int msglen) throws IOException { /* Currently we do not support any kind of global request */ TypesReader tr = new TypesReader(msg, 0, msglen); tr.readByte(); // skip packet type String requestName = tr.readString(); boolean wantReply = tr.readBoolean(); if (wantReply) { byte[] reply_failure = new byte[1]; reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE; /* We do not clean up the requestName String - that is OK for debug */ if (log.isEnabled()) log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")"); } public void msgGlobalSuccess() throws IOException { synchronized (channels) { globalSuccessCounter++; channels.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_REQUEST_SUCCESS"); } public void msgGlobalFailure() throws IOException { synchronized (channels) { globalFailedCounter++; channels.notifyAll(); } if (log.isEnabled()) log.log(80, "Got SSH_MSG_REQUEST_FAILURE"); } public void handleMessage(byte[] msg, int msglen) throws IOException { if (msg == null) { if (log.isEnabled()) log.log(50, "HandleMessage: got shutdown"); synchronized (listenerThreads) { for (int i = 0; i < listenerThreads.size(); i++) { IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads.elementAt(i); lat.stopWorking(); } listenerThreadsAllowed = false; } synchronized (channels) { shutdown = true; for (int i = 0; i < channels.size(); i++) { Channel c = (Channel) channels.elementAt(i); synchronized (c) { c.eof(); c.state = Channel.STATE_CLOSED; c.setReasonClosed("The connection is being shutdown"); c.closeMessageRecv = true; /* * You never know, perhaps * we are waiting for a * pending close message * from the server... */ c.notifyAll(); } } /* Works with J2ME */ channels.setSize(0); channels.trimToSize(); channels.notifyAll(); /* Notify global response waiters */ return; } } switch (msg[0]) { case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION: msgChannelOpenConfirmation(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST: msgChannelWindowAdjust(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_DATA: msgChannelData(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA: msgChannelExtendedData(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_REQUEST: msgChannelRequest(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_EOF: msgChannelEOF(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_OPEN: msgChannelOpen(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_CLOSE: msgChannelClose(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_SUCCESS: msgChannelSuccess(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_FAILURE: msgChannelFailure(msg, msglen); break; case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE: msgChannelOpenFailure(msg, msglen); break; case Packets.SSH_MSG_GLOBAL_REQUEST: msgGlobalRequest(msg, msglen); break; case Packets.SSH_MSG_REQUEST_SUCCESS: msgGlobalSuccess(); break; case Packets.SSH_MSG_REQUEST_FAILURE: msgGlobalFailure(); break; default: throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff)); } } }

File
ChannelManager.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD:src/com/trilead/ssh2/channel/LocalAcceptThread.java

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * LocalAcceptThread.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class LocalAcceptThread extends Thread implements IChannelWorkerThread
{
	ChannelManager cm;
	String host_to_connect;
	int port_to_connect;

	final ServerSocket ss;

	public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
			throws IOException
	{
		this.cm = cm;
		this.host_to_connect = host_to_connect;
		this.port_to_connect = port_to_connect;

		ss = new ServerSocket(local_port);
	}

	public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
			int port_to_connect) throws IOException
	{
		this.cm = cm;
		this.host_to_connect = host_to_connect;
		this.port_to_connect = port_to_connect;

		ss = new ServerSocket();
		ss.bind(localAddress);
	}

	public void run()
	{
		try
		{
			cm.registerThread(this);
		}
		catch (IOException e)
		{
			stopWorking();
			return;
		}

		while (true)
		{
			Socket s = null;

			try
			{
				s = ss.accept();
			}
			catch (IOException e)
			{
				stopWorking();
				return;
			}

			Channel cn = null;
			StreamForwarder r2l = null;
			StreamForwarder l2r = null;

			try
			{
				/* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */

				cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
						.getPort());

			}
			catch (IOException e)
			{
				/* Simply close the local socket and wait for the next incoming connection */

				try
				{
					s.close();
				}
				catch (IOException ignore)
				{
				}

				continue;
			}

			try
			{
				r2l = new StreamForwarder(cn, null, null, cn.getStdoutStream(), s.getOutputStream(), "RemoteToLocal");
				l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
			}
			catch (IOException e)
			{
				try
				{
					/* This message is only visible during debugging, since we discard the channel immediatelly */
					cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
							true);
				}
				catch (IOException ignore)
				{
				}

				continue;
			}

			r2l.setDaemon(true);
			l2r.setDaemon(true);
			r2l.start();
			l2r.start();
		}
	}

	public void stopWorking()
	{
		try
		{
			/* This will lead to an IOException in the ss.accept() call */
			ss.close();
		}
		catch (IOException e)
		{
		}
	}
}
=======

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * LocalAcceptThread.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class LocalAcceptThread extends Thread implements IChannelWorkerThread
{
	ChannelManager cm;
	String host_to_connect;
	int port_to_connect;

	final ServerSocket ss;

	public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
			throws IOException
	{
		this.cm = cm;
		this.host_to_connect = host_to_connect;
		this.port_to_connect = port_to_connect;

		ss = new ServerSocket(local_port);
	}

	public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
			int port_to_connect) throws IOException
	{
		this.cm = cm;
		this.host_to_connect = host_to_connect;
		this.port_to_connect = port_to_connect;

		ss = new ServerSocket();
		ss.bind(localAddress);
	}

	public void run()
	{
		try
		{
			cm.registerThread(this);
		}
		catch (IOException e)
		{
			stopWorking();
			return;
		}

		while (true)
		{
			Socket s = null;

			try
			{
				s = ss.accept();
			}
			catch (IOException e)
			{
				stopWorking();
				return;
			}

			Channel cn = null;
			StreamForwarder r2l = null;
			StreamForwarder l2r = null;

			try
			{
				/* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */

				cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
						.getPort());

			}
			catch (IOException e)
			{
				/* Simply close the local socket and wait for the next incoming connection */

				try
				{
					s.close();
				}
				catch (IOException ignore)
				{
				}

				continue;
			}

			try
			{
				r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
				l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
			}
			catch (IOException e)
			{
				try
				{
					/* This message is only visible during debugging, since we discard the channel immediatelly */
					cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
							true);
				}
				catch (IOException ignore)
				{
				}

				continue;
			}

			r2l.setDaemon(true);
			l2r.setDaemon(true);
			r2l.start();
			l2r.start();
		}
	}

	public void stopWorking()
	{
		try
		{
			/* This will lead to an IOException in the ss.accept() call */
			ss.close();
		}
		catch (IOException e)
		{
		}
	}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java
Solution content
		try
		{

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * LocalAcceptThread.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class LocalAcceptThread extends Thread implements IChannelWorkerThread
{
	ChannelManager cm;
	String host_to_connect;
	int port_to_connect;

	final ServerSocket ss;

	public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
			throws IOException
	{
		this.cm = cm;
		this.host_to_connect = host_to_connect;
		this.port_to_connect = port_to_connect;

		ss = new ServerSocket(local_port);
	}

	public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
			int port_to_connect) throws IOException
	{
		this.cm = cm;
		this.host_to_connect = host_to_connect;
		this.port_to_connect = port_to_connect;

		ss = new ServerSocket();
		ss.bind(localAddress);
	}

	public void run()
	{
			cm.registerThread(this);
		}
		catch (IOException e)
		{
			stopWorking();
			return;
		}

		while (true)
		{
			Socket s = null;

			try
			{
				s = ss.accept();
			}
			catch (IOException e)
			{
				stopWorking();
				return;
			}

			Channel cn = null;
			StreamForwarder r2l = null;
			StreamForwarder l2r = null;

			try
			{
				/* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */

				cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
						.getPort());

			}
			catch (IOException e)
			{
				/* Simply close the local socket and wait for the next incoming connection */

				try
				{
					s.close();
				}
				catch (IOException ignore)
				{
				}

				continue;
			}

			try
			{
				r2l = new StreamForwarder(cn, null, null, cn.getStdoutStream(), s.getOutputStream(), "RemoteToLocal");
				l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
			}
			catch (IOException e)
			{
				try
				{
					/* This message is only visible during debugging, since we discard the channel immediatelly */
					cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
							true);
				}
				catch (IOException ignore)
				{
				}

				continue;
			}

			r2l.setDaemon(true);
			l2r.setDaemon(true);
			r2l.start();
			l2r.start();
		}
	}

	public void stopWorking()
	{
		try
		{
			/* This will lead to an IOException in the ss.accept() call */
			ss.close();
		}
		catch (IOException e)
		{
		}
	}
}
File
LocalAcceptThread.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD:src/com/trilead/ssh2/channel/RemoteAcceptThread.java

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.Socket;

import com.trilead.ssh2.log.Logger;


/**
 * RemoteAcceptThread.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class RemoteAcceptThread extends Thread
{
	private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);

	Channel c;

	String remoteConnectedAddress;
	int remoteConnectedPort;
	String remoteOriginatorAddress;
	int remoteOriginatorPort;
	String targetAddress;
	int targetPort;

	Socket s;

	public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
			String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
	{
		this.c = c;
		this.remoteConnectedAddress = remoteConnectedAddress;
		this.remoteConnectedPort = remoteConnectedPort;
		this.remoteOriginatorAddress = remoteOriginatorAddress;
		this.remoteOriginatorPort = remoteOriginatorPort;
		this.targetAddress = targetAddress;
		this.targetPort = targetPort;

		if (log.isEnabled())
			log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
					+ remoteOriginatorAddress + "/" + remoteOriginatorPort);
	}

	public void run()
	{
		try
		{
			c.cm.sendOpenConfirmation(c);

			s = new Socket(targetAddress, targetPort);

			StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
					"RemoteToLocal");
			StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
					"LocalToRemote");

			/* No need to start two threads, one can be executed in the current thread */
			
			r2l.setDaemon(true);
			r2l.start();
			l2r.run();

			while (r2l.isAlive())
			{
				try
				{
					r2l.join();
				}
				catch (InterruptedException e)
				{
                    throw new InterruptedIOException();
				}
			}

			/* If the channel is already closed, then this is a no-op */

			c.cm.closeChannel(c, "EOF on both streams reached.", true);
			s.close();
		}
		catch (IOException e)
		{
			log.log(50, "IOException in proxy code: " + e.getMessage());

			try
			{
				c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
			}
			catch (IOException e1)
			{
			}
			try
			{
				if (s != null)
					s.close();
			}
			catch (IOException e1)
			{
			}
		}
	}
}
=======

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.net.Socket;

import com.trilead.ssh2.log.Logger;


/**
 * RemoteAcceptThread.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class RemoteAcceptThread extends Thread
{
	private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);

	Channel c;

	String remoteConnectedAddress;
	int remoteConnectedPort;
	String remoteOriginatorAddress;
	int remoteOriginatorPort;
	String targetAddress;
	int targetPort;

	Socket s;

	public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
			String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
	{
		this.c = c;
		this.remoteConnectedAddress = remoteConnectedAddress;
		this.remoteConnectedPort = remoteConnectedPort;
		this.remoteOriginatorAddress = remoteOriginatorAddress;
		this.remoteOriginatorPort = remoteOriginatorPort;
		this.targetAddress = targetAddress;
		this.targetPort = targetPort;

		if (log.isEnabled())
			log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
					+ remoteOriginatorAddress + "/" + remoteOriginatorPort);
	}

	public void run()
	{
		try
		{
			c.cm.sendOpenConfirmation(c);

			s = new Socket(targetAddress, targetPort);

			StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
					"RemoteToLocal");
			StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
					"LocalToRemote");

			/* No need to start two threads, one can be executed in the current thread */
			
			r2l.setDaemon(true);
			r2l.start();
			l2r.run();

			while (r2l.isAlive())
			{
				try
				{
					r2l.join();
				}
				catch (InterruptedException e)
				{
				}
			}

			/* If the channel is already closed, then this is a no-op */

			c.cm.closeChannel(c, "EOF on both streams reached.", true);
			s.close();
		}
		catch (IOException e)
		{
			log.log(50, "IOException in proxy code: " + e.getMessage());

			try
			{
				c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
			}
			catch (IOException e1)
			{
			}
			try
			{
				if (s != null)
					s.close();
			}
			catch (IOException e1)
			{
			}
		}
	}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java
Solution content
		catch (IOException e)
		{

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.Socket;

import com.trilead.ssh2.log.Logger;


/**
 * RemoteAcceptThread.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class RemoteAcceptThread extends Thread
{
	private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);

	Channel c;

	String remoteConnectedAddress;
	int remoteConnectedPort;
	String remoteOriginatorAddress;
	int remoteOriginatorPort;
	String targetAddress;
	int targetPort;

	Socket s;

	public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
			String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
	{
		this.c = c;
		this.remoteConnectedAddress = remoteConnectedAddress;
		this.remoteConnectedPort = remoteConnectedPort;
		this.remoteOriginatorAddress = remoteOriginatorAddress;
		this.remoteOriginatorPort = remoteOriginatorPort;
		this.targetAddress = targetAddress;
		this.targetPort = targetPort;

		if (log.isEnabled())
			log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
					+ remoteOriginatorAddress + "/" + remoteOriginatorPort);
	}

	public void run()
	{
		try
		{
			c.cm.sendOpenConfirmation(c);

			s = new Socket(targetAddress, targetPort);

			StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
					"RemoteToLocal");
			StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
					"LocalToRemote");

			/* No need to start two threads, one can be executed in the current thread */
			
			r2l.setDaemon(true);
			r2l.start();
			l2r.run();

			while (r2l.isAlive())
			{
				try
				{
					r2l.join();
				}
				catch (InterruptedException e)
				{
                    throw new InterruptedIOException();
				}
			}

			/* If the channel is already closed, then this is a no-op */

			c.cm.closeChannel(c, "EOF on both streams reached.", true);
			s.close();
		}
			log.log(50, "IOException in proxy code: " + e.getMessage());

			try
			{
				c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
			}
			catch (IOException e1)
			{
			}
			try
			{
				if (s != null)
					s.close();
			}
			catch (IOException e1)
			{
			}
		}
	}
}
File
RemoteAcceptThread.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD:src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
=======

>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
package com.trilead.ssh2.channel;

import java.io.IOException;
Solution content
package com.trilead.ssh2.channel;

import java.io.IOException;
File
RemoteX11AcceptThread.java
Developer's decision
Version 1
Kind of conflict
Blank
Chunk
Conflicting content
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
<<<<<<< HEAD:src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
import java.io.InterruptedIOException;
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
import java.net.Socket;

import com.trilead.ssh2.log.Logger;
Solution content
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InterruptedIOException;
import java.net.Socket;

import com.trilead.ssh2.log.Logger;
File
RemoteX11AcceptThread.java
Developer's decision
Version 1
Kind of conflict
Import
Chunk
Conflicting content
				}
				catch (InterruptedException e)
				{
<<<<<<< HEAD:src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
					throw new InterruptedIOException();
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
				}
			}
Solution content
				}
				catch (InterruptedException e)
				{
					throw new InterruptedIOException();
				}
			}
File
RemoteX11AcceptThread.java
Developer's decision
Version 1
Kind of conflict
Throw statement
Chunk
Conflicting content
			}

			if (sibling != null)
<<<<<<< HEAD:src/com/trilead/ssh2/channel/StreamForwarder.java

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * A StreamForwarder forwards data between two given streams. 
 * If two StreamForwarder threads are used (one for each direction)
 * then one can be configured to shutdown the underlying channel/socket
 * if both threads have finished forwarding (EOF).
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class StreamForwarder extends Thread
{
	OutputStream os;
	InputStream is;
	byte[] buffer;
	Channel c;
	StreamForwarder sibling;
	Socket s;
	String mode;

	StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
			throws IOException
	{
		this.is = is;
		this.os = os;
		this.mode = mode;
		this.c = c;
		this.sibling = sibling;
		this.s = s;
        // window size is for the other side of the network with some latency.
        // we don't need such a big buffer for a copy stream tight loop
        this.buffer = new byte[8192/*c.channelBufferSize*/];
    }

	public void run()
	{
		try
		{
			while (true)
			{
				int len = is.read(buffer);
				if (len <= 0)
					break;
				os.write(buffer, 0, len);
				os.flush();
			}
		}
		catch (IOException ignore)
		{
			try
			{
				c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
						+ ignore.getMessage(), true);
			}
			catch (IOException e)
			{
			}
		}
		finally
		{
			try
			{
				os.close();
			}
			catch (IOException e1)
			{
			}
			try
			{
				is.close();
			}
			catch (IOException e2)
			{
			{
				while (sibling.isAlive())
				{
					try
					{
						sibling.join();
					}
					catch (InterruptedException e)
					{
					}
				}

				try
				{
					c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
				}
				catch (IOException e3)
				{
				}

				try
				{
					if (s != null)
						s.close();
				}
				catch (IOException e1)
				{
				}
			}
		}
	}
=======

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * A StreamForwarder forwards data between two given streams. 
 * If two StreamForwarder threads are used (one for each direction)
 * then one can be configured to shutdown the underlying channel/socket
 * if both threads have finished forwarding (EOF).
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class StreamForwarder extends Thread
{
	OutputStream os;
	InputStream is;
	byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
	Channel c;
	StreamForwarder sibling;
	Socket s;
	String mode;

	StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
			throws IOException
	{
		this.is = is;
		this.os = os;
		this.mode = mode;
		this.c = c;
		this.sibling = sibling;
		this.s = s;
	}

	public void run()
	{
		try
		{
			while (true)
			{
				int len = is.read(buffer);
				if (len <= 0)
					break;
				os.write(buffer, 0, len);
				os.flush();
			}
		}
		catch (IOException ignore)
		{
			try
			{
				c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
						+ ignore.getMessage(), true);
			}
			catch (IOException e)
			{
			}
		}
		finally
		{
			try
			{
				os.close();
			}
			catch (IOException e1)
			{
			}
			try
			{
				is.close();
			}
			catch (IOException e2)
			{
			}

			if (sibling != null)
			{
				while (sibling.isAlive())
				{
					try
					{
						sibling.join();
					}
					catch (InterruptedException e)
					{
					}
				}

				try
				{
					c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
				}
				catch (IOException e3)
				{
				}

				try
				{
					if (s != null)
						s.close();
				}
				catch (IOException e1)
				{
				}
			}
		}
	}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/StreamForwarder.java
}
Solution content
	StreamForwarder sibling;

package com.trilead.ssh2.channel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * A StreamForwarder forwards data between two given streams. 
 * If two StreamForwarder threads are used (one for each direction)
 * then one can be configured to shutdown the underlying channel/socket
 * if both threads have finished forwarding (EOF).
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class StreamForwarder extends Thread
{
	OutputStream os;
	InputStream is;
	byte[] buffer;
	Channel c;
	Socket s;
	String mode;

	StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
			throws IOException
	{
		this.is = is;
		this.os = os;
		this.mode = mode;
		this.c = c;
		this.sibling = sibling;
		this.s = s;
        // window size is for the other side of the network with some latency.
        // we don't need such a big buffer for a copy stream tight loop
        this.buffer = new byte[8192/*c.channelBufferSize*/];
    }

	public void run()
	{
		try
		{
			while (true)
			{
				int len = is.read(buffer);
				if (len <= 0)
					break;
				os.write(buffer, 0, len);
				os.flush();
			}
		}
		catch (IOException ignore)
		{
			try
			{
				c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
						+ ignore.getMessage(), true);
			}
			catch (IOException e)
			{
			}
		}
		finally
		{
			try
			{
				os.close();
			}
			catch (IOException e1)
			{
			}
			try
			{
				is.close();
			}
			catch (IOException e2)
			{
			}

			if (sibling != null)
			{
				while (sibling.isAlive())
				{
					try
					{
						sibling.join();
					}
					catch (InterruptedException e)
					{
					}
				}

				try
				{
					c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
				}
				catch (IOException e3)
				{
				}

				try
				{
					if (s != null)
						s.close();
				}
				catch (IOException e1)
				{
				}
			}
		}
	}
}
File
StreamForwarder.java
Developer's decision
Version 1
Kind of conflict
Attribute
Comment
Import
Method declaration
Method invocation
Package declaration
Chunk
Conflicting content
import java.math.BigInteger;
import java.security.SecureRandom;

<<<<<<< HEAD:src/com/trilead/ssh2/signature/DSASHA1Verify.java
=======
import com.trilead.ssh2.IOWarningException;
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java
import com.trilead.ssh2.crypto.digest.SHA1;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.TypesReader;
Solution content
import java.math.BigInteger;
import java.security.SecureRandom;

import com.trilead.ssh2.IOWarningException;
import com.trilead.ssh2.crypto.digest.SHA1;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.TypesReader;
File
DSASHA1Verify.java
Developer's decision
Version 2
Kind of conflict
Import
Chunk
Conflicting content
{
	private static final Logger log = Logger.getLogger(DSASHA1Verify.class);

<<<<<<< HEAD:src/com/trilead/ssh2/signature/DSASHA1Verify.java
	public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException
	{
		TypesReader tr = new TypesReader(key);

		String key_format = tr.readString();

		if (key_format.equals("ssh-dss") == false)
			throw new IllegalArgumentException("This is not a ssh-dss public key!");

		BigInteger p = tr.readMPINT();
		BigInteger q = tr.readMPINT();
		BigInteger g = tr.readMPINT();
		BigInteger y = tr.readMPINT();

		if (tr.remain() != 0)
			throw new IOException("Padding in DSA public key!");
=======
	public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException {
		final TypesReader tr = new TypesReader(key);

		final String key_format = tr.readString();
		if (!key_format.equals("ssh-dss")) {
			throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting ssh-dss");
		}

		final BigInteger p = tr.readMPINT();
		final BigInteger q = tr.readMPINT();
		final BigInteger g = tr.readMPINT();
		final BigInteger y = tr.readMPINT();

		if (tr.remain() != 0) {
			throw new IOException("Padding in DSA public key!");
		}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java

		return new DSAPublicKey(p, q, g, y);
	}
Solution content
{
	private static final Logger log = Logger.getLogger(DSASHA1Verify.class);

	public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException {
		final TypesReader tr = new TypesReader(key);

		final String key_format = tr.readString();
		if (!key_format.equals("ssh-dss")) {
			throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting ssh-dss");
		}

		final BigInteger p = tr.readMPINT();
		final BigInteger q = tr.readMPINT();
		final BigInteger g = tr.readMPINT();
		final BigInteger y = tr.readMPINT();

		if (tr.remain() != 0) {
			throw new IOException("Padding in DSA public key!");
		}

		return new DSAPublicKey(p, q, g, y);
	}
File
DSASHA1Verify.java
Developer's decision
Version 2
Kind of conflict
If statement
Method invocation
Method signature
Variable
Chunk
Conflicting content
	{
<<<<<<< HEAD:src/com/trilead/ssh2/signature/RSAPrivateKey.java
package com.trilead.ssh2.signature;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.GeneralSecurityException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;

/**
 * RSAPrivateKey.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 */
public class RSAPrivateKey
{
	private BigInteger d;
	private BigInteger e;
	private BigInteger n;

	public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
	{
		this.d = d;
		this.e = e;
		this.n = n;
	}

	public BigInteger getD()
	{
		return d;
	}
	
	public BigInteger getE()
		return e;
	}

	public BigInteger getN()
	{
		return n;
	}
	
	public RSAPublicKey getPublicKey()
	{
		return new RSAPublicKey(e, n);
	}

    /**
     * Converts this to a JCE API representation of the RSA key pair.
     */
    public KeyPair toJCEKeyPair() throws GeneralSecurityException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return new KeyPair(
                kf.generatePublic(new RSAPublicKeySpec(getN(), getE())),
                kf.generatePrivate(new RSAPrivateKeySpec(getN(), getD())));
    }
=======
package com.trilead.ssh2.signature;

import java.math.BigInteger;

/**
 * RSAPrivateKey.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 */
public class RSAPrivateKey
{
	private BigInteger d;
	private BigInteger e;
	private BigInteger n;

	public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
	{
		this.d = d;
		this.e = e;
		this.n = n;
	}

	public BigInteger getD()
	{
		return d;
	}
	
	public BigInteger getE()
	{
		return e;
	}

	public BigInteger getN()
	{
		return n;
	}
	
	public RSAPublicKey getPublicKey()
	{
		return new RSAPublicKey(e, n);
	}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/signature/RSAPrivateKey.java
}
Solution content
package com.trilead.ssh2.signature;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.GeneralSecurityException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;

/**
 * RSAPrivateKey.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 */
public class RSAPrivateKey
{
	private BigInteger d;
	private BigInteger e;
	private BigInteger n;

	public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
	{
		this.d = d;
		this.e = e;
		this.n = n;
	}

	public BigInteger getD()
	{
		return d;
	}
	
	public BigInteger getE()
	{
		return e;
	}

	public BigInteger getN()
	{
		return n;
	}
	
	public RSAPublicKey getPublicKey()
	{
		return new RSAPublicKey(e, n);
	}

    /**
     * Converts this to a JCE API representation of the RSA key pair.
     */
    public KeyPair toJCEKeyPair() throws GeneralSecurityException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return new KeyPair(
                kf.generatePublic(new RSAPublicKeySpec(getN(), getE())),
                kf.generatePrivate(new RSAPrivateKeySpec(getN(), getD())));
    }
}
File
RSAPrivateKey.java
Developer's decision
Version 1
Kind of conflict
Attribute
Comment
Import
Method declaration
Package declaration
Chunk
Conflicting content
			len++;
			if (pos >= buffer.length)
<<<<<<< HEAD:src/com/trilead/ssh2/transport/ClientServerHello.java
				throw new IOException("The server sent a too long line: "+new String(buffer, "ISO-8859-1"));
=======
				throw new IOException("The server sent a too long line.");
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/ClientServerHello.java
		}

		return len;
Solution content
			len++;
			if (pos >= buffer.length)
				throw new IOException("The server sent a too long line: "+new String(buffer, "ISO-8859-1"));
		}

		return len;
File
ClientServerHello.java
Developer's decision
Version 1
Kind of conflict
Throw statement
Chunk
Conflicting content
<<<<<<< HEAD:src/com/trilead/ssh2/transport/KexManager.java

package com.trilead.ssh2.transport;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.security.SecureRandom;

import com.trilead.ssh2.ConnectionInfo;
import com.trilead.ssh2.DHGexParameters;
import com.trilead.ssh2.ServerHostKeyVerifier;
import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.crypto.KeyMaterial;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
import com.trilead.ssh2.crypto.dh.DhExchange;
import com.trilead.ssh2.crypto.dh.DhGroupExchange;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketKexDHInit;
import com.trilead.ssh2.packets.PacketKexDHReply;
import com.trilead.ssh2.packets.PacketKexDhGexGroup;
import com.trilead.ssh2.packets.PacketKexDhGexInit;
import com.trilead.ssh2.packets.PacketKexDhGexReply;
import com.trilead.ssh2.packets.PacketKexDhGexRequest;
import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
import com.trilead.ssh2.packets.PacketKexInit;
import com.trilead.ssh2.packets.PacketNewKeys;
import com.trilead.ssh2.packets.Packets;

		}
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;


/**
 * KexManager.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class KexManager
{
	private static final Logger log = Logger.getLogger(KexManager.class);

	KexState kxs;
	int kexCount = 0;
	KeyMaterial km;
	byte[] sessionId;
	ClientServerHello csh;

	final Object accessLock = new Object();
	ConnectionInfo lastConnInfo = null;

	boolean connectionClosed = false;

	boolean ignore_next_kex_packet = false;

	final TransportManager tm;

	CryptoWishList nextKEXcryptoWishList;
	DHGexParameters nextKEXdhgexParameters;

	ServerHostKeyVerifier verifier;
	final String hostname;
	final int port;
	final SecureRandom rnd;

	public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
			ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
	{
		this.tm = tm;
		this.csh = csh;
		this.nextKEXcryptoWishList = initialCwl;
		this.nextKEXdhgexParameters = new DHGexParameters();
		this.hostname = hostname;
		this.port = port;
		this.verifier = keyVerifier;
		this.rnd = rnd;
	}

	public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
	{
		synchronized (accessLock)
		{
			while (true)
			{
				if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
					return lastConnInfo;

				if (connectionClosed)
					throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
							.initCause(tm.getReasonClosedCause());

				try
				{
					accessLock.wait();
				}
				catch (InterruptedException e)
				{
                    throw new InterruptedIOException();
				}
			}
		}
	}

	private String getFirstMatch(String[] client, String[] server) throws NegotiateException
	{
		if (client == null || server == null)
			throw new IllegalArgumentException();

		if (client.length == 0)
			return null;

		for (int i = 0; i < client.length; i++)
		{
			for (int j = 0; j < server.length; j++)
			{
				if (client[i].equals(server[j]))
					return client[i];
			}
		}
		throw new NegotiateException();
	}

	private boolean compareFirstOfNameList(String[] a, String[] b)
	{
		if (a == null || b == null)
			throw new IllegalArgumentException();

		if ((a.length == 0) && (b.length == 0))
			return true;

		if ((a.length == 0) || (b.length == 0))
			return false;

		return (a[0].equals(b[0]));
	}

	private boolean isGuessOK(KexParameters cpar, KexParameters spar)
	{
		if (cpar == null || spar == null)
			throw new IllegalArgumentException();

		if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
		{
			return false;
		}

		if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
		{
			return false;
		}

		/*
		 * We do NOT check here if the other algorithms can be agreed on, this
		 * is just a check if kex_algorithms and server_host_key_algorithms were
		 * guessed right!
		 */

		return true;
	}

	private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
	{
		NegotiatedParameters np = new NegotiatedParameters();

		try
		{
			np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);

			log.log(20, "kex_algo=" + np.kex_algo);

			np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
					server.server_host_key_algorithms);

			log.log(20, "server_host_key_algo=" + np.server_host_key_algo);

			np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
					server.encryption_algorithms_client_to_server);
			np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
					server.encryption_algorithms_server_to_client);

			log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
			log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);

			np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
					server.mac_algorithms_client_to_server);
			np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
					server.mac_algorithms_server_to_client);

			log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
			log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);

			np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
					server.compression_algorithms_client_to_server);
			np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
					server.compression_algorithms_server_to_client);

			log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
			log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);

		catch (NegotiateException e)
		{
			return null;
		}

		try
		{
			np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
					server.languages_client_to_server);
		}
		catch (NegotiateException e1)
		{
			np.lang_client_to_server = null;
		}

		try
		{
			np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
					server.languages_server_to_client);
		}
		catch (NegotiateException e2)
		{
			np.lang_server_to_client = null;
		}

		if (isGuessOK(client, server))
			np.guessOK = true;

		return np;
	}

	public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
	{
		nextKEXcryptoWishList = cwl;
		nextKEXdhgexParameters = dhgex;

		if (kxs == null)
		{
			kxs = new KexState();
			kxs.dhgexParameters = nextKEXdhgexParameters;
			PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
			kxs.localKEX = kp;
			tm.sendKexMessage(kp.getPayload());
		}
	}

	private boolean establishKeyMaterial()
	{
		try
		{
			int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
			int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
			int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);

			int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
			int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
			int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);

			km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
					enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
		}
		catch (IllegalArgumentException e)
		{
			return false;
		}
		return true;
	}

	private void finishKex() throws IOException
	{
		if (sessionId == null)
			sessionId = kxs.H;

		establishKeyMaterial();

		/* Tell the other side that we start using the new material */

		PacketNewKeys ign = new PacketNewKeys();
		tm.sendKexMessage(ign.getPayload());

		BlockCipher cbc;
		MAC mac;

		try
		{
			cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
					km.initial_iv_client_to_server);

			mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);

		}
		catch (IllegalArgumentException e1)
		{
			throw new IOException("Fatal error during MAC startup!");
		}

		tm.changeSendCipher(cbc, mac);
		tm.kexFinished();
	}

	public static final String[] getDefaultServerHostkeyAlgorithmList()
	{
		return new String[] { "ssh-rsa", "ssh-dss" };
	}

	public static final void checkServerHostkeyAlgorithmsList(String[] algos)
	{
		for (int i = 0; i < algos.length; i++)
		{
			if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
				throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
		}
	}

	public static final String[] getDefaultKexAlgorithmList()
	{
		return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
				"diffie-hellman-group1-sha1" };
	}

	public static final void checkKexAlgorithmList(String[] algos)
	{
		for (int i = 0; i < algos.length; i++)
		{
			if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
				continue;

			if ("diffie-hellman-group14-sha1".equals(algos[i]))
				continue;

			if ("diffie-hellman-group1-sha1".equals(algos[i]))
				continue;

			throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
		}
	}

	private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
	{
		if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
		{
			RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
			RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);

			log.log(50, "Verifying ssh-rsa signature");

			return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
		}

		if (kxs.np.server_host_key_algo.equals("ssh-dss"))
		{
			DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
			DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);

			log.log(50, "Verifying ssh-dss signature");

			return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
		}

		throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
	}

	public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
	{
		PacketKexInit kip;

		if (msg == null)
		{
			synchronized (accessLock)
			{
				connectionClosed = true;
				accessLock.notifyAll();
				return;
			}
		}

		if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
			throw new IOException("Unexpected KEX message (type " + msg[0] + ")");

		if (ignore_next_kex_packet)
		{
			ignore_next_kex_packet = false;
			return;
		}

		if (msg[0] == Packets.SSH_MSG_KEXINIT)
		{
			if ((kxs != null) && (kxs.state != 0))
				throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");

			if (kxs == null)
			{
				/*
				 * Ah, OK, peer wants to do KEX. Let's be nice and play
				 * together.
				 */
				kxs = new KexState();
				kxs.dhgexParameters = nextKEXdhgexParameters;
				kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
				kxs.localKEX = kip;
				tm.sendKexMessage(kip.getPayload());
			}

			kip = new PacketKexInit(msg, 0, msglen);
			kxs.remoteKEX = kip;

			kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());

			if (kxs.np == null)
				throw new IOException("Cannot negotiate, proposals do not match.");

			if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
			{
				/*
				 * Guess was wrong, we need to ignore the next kex packet.
				 */

				ignore_next_kex_packet = true;
			}

			if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
			{
				if (kxs.dhgexParameters.getMin_group_len() == 0)
				{
					PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
					tm.sendKexMessage(dhgexreq.getPayload());

				}
				else
				{
					PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
					tm.sendKexMessage(dhgexreq.getPayload());
				}
				kxs.state = 1;
				return;
			}

			if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
					|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
			{
				kxs.dhx = new DhExchange();

				if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
					kxs.dhx.init(1, rnd);
				else
					kxs.dhx.init(14, rnd);

				PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
				tm.sendKexMessage(kp.getPayload());
				kxs.state = 1;
				return;
			}


			throw new IllegalStateException("Unkown KEX method!");
		}

		if (msg[0] == Packets.SSH_MSG_NEWKEYS)
		{
			if (km == null)
				throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");

			BlockCipher cbc;
			MAC mac;

			try
			{
				cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
						km.enc_key_server_to_client, km.initial_iv_server_to_client);

				mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);

			}
			catch (IllegalArgumentException e1)
			{
				throw new IOException("Fatal error during MAC startup!");
			}

			tm.changeRecvCipher(cbc, mac);

			ConnectionInfo sci = new ConnectionInfo();

			kexCount++;

			sci.keyExchangeAlgorithm = kxs.np.kex_algo;
			sci.keyExchangeCounter = kexCount;
			sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
			sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
			sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
			sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
			sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
			sci.serverHostKey = kxs.hostkey;

			synchronized (accessLock)
			{
				lastConnInfo = sci;
				accessLock.notifyAll();
			}

			kxs = null;
			return;
		}

		if ((kxs == null) || (kxs.state == 0))
			throw new IOException("Unexpected Kex submessage!");

		if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
		{
			if (kxs.state == 1)
			{
				PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
				kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
				kxs.dhgx.init(rnd);
				PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
				tm.sendKexMessage(dhgexinit.getPayload());
				kxs.state = 2;
				return;
			}

			if (kxs.state == 2)
			{
				PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);

				kxs.hostkey = dhgexrpl.getHostKey();

				if (verifier != null)
				{
					boolean vres = false;

					try
					{
						vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
					}
					catch (Exception e)
					{
						throw (IOException) new IOException(
								"The server hostkey was not accepted by the verifier callback.").initCause(e);
					}

					if (vres == false)
						throw new IOException("The server hostkey was not accepted by the verifier callback");
				}

				kxs.dhgx.setF(dhgexrpl.getF());

				try
				{
					kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
							kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
							kxs.dhgexParameters);
				}
				catch (IllegalArgumentException e)
				{
					throw (IOException) new IOException("KEX error.").initCause(e);
				}

				boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);

				if (res == false)
					throw new IOException("Hostkey signature sent by remote is wrong!");

				kxs.K = kxs.dhgx.getK();

				finishKex();
				kxs.state = -1;
				return;
			}

			throw new IllegalStateException("Illegal State in KEX Exchange!");
		}

		if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
				|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
		{
			if (kxs.state == 1)
			{

				PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);

				kxs.hostkey = dhr.getHostKey();

				if (verifier != null)
				{
					boolean vres = false;

					try
					{
						vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
					}
					catch (Exception e)
					{
						throw (IOException) new IOException(
								"The server hostkey was not accepted by the verifier callback.").initCause(e);
					}

					if (vres == false)
						throw new IOException("The server hostkey was not accepted by the verifier callback");
				}

				kxs.dhx.setF(dhr.getF());

				try
				{
					kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
							kxs.remoteKEX.getPayload(), dhr.getHostKey());
				}
				catch (IllegalArgumentException e)
				{
					throw (IOException) new IOException("KEX error.").initCause(e);
				}

				boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);

				if (res == false)
					throw new IOException("Hostkey signature sent by remote is wrong!");

				kxs.K = kxs.dhx.getK();

				finishKex();
				kxs.state = -1;
				return;
			}
		}

		throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
	}
}
=======

package com.trilead.ssh2.transport;

import java.io.IOException;
import java.security.SecureRandom;

import com.trilead.ssh2.ConnectionInfo;
import com.trilead.ssh2.DHGexParameters;
import com.trilead.ssh2.ServerHostKeyVerifier;
		{
import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.crypto.KeyMaterial;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
import com.trilead.ssh2.crypto.dh.DhExchange;
import com.trilead.ssh2.crypto.dh.DhGroupExchange;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketKexDHInit;
import com.trilead.ssh2.packets.PacketKexDHReply;
import com.trilead.ssh2.packets.PacketKexDhGexGroup;
import com.trilead.ssh2.packets.PacketKexDhGexInit;
import com.trilead.ssh2.packets.PacketKexDhGexReply;
import com.trilead.ssh2.packets.PacketKexDhGexRequest;
import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
import com.trilead.ssh2.packets.PacketKexInit;
import com.trilead.ssh2.packets.PacketNewKeys;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;


/**
 * KexManager.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class KexManager
{
	private static final Logger log = Logger.getLogger(KexManager.class);

	KexState kxs;
	int kexCount = 0;
	KeyMaterial km;
	byte[] sessionId;
	ClientServerHello csh;

	final Object accessLock = new Object();
	ConnectionInfo lastConnInfo = null;

	boolean connectionClosed = false;

	boolean ignore_next_kex_packet = false;

	final TransportManager tm;

	CryptoWishList nextKEXcryptoWishList;
	DHGexParameters nextKEXdhgexParameters;

	ServerHostKeyVerifier verifier;
	final String hostname;
	final int port;
	final SecureRandom rnd;

	public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
			ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
	{
		this.tm = tm;
		this.csh = csh;
		this.nextKEXcryptoWishList = initialCwl;
		this.nextKEXdhgexParameters = new DHGexParameters();
		this.hostname = hostname;
		this.port = port;
		this.verifier = keyVerifier;
		this.rnd = rnd;
	}

	public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
	{
		synchronized (accessLock)
		{
			while (true)
			{
				if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
					return lastConnInfo;

				if (connectionClosed)
					throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
							.initCause(tm.getReasonClosedCause());

				try
				{
					accessLock.wait();
				}
				catch (InterruptedException e)
				{
				}
			}
		}
	}

	private String getFirstMatch(String[] client, String[] server) throws NegotiateException
	{
		if (client == null || server == null)
			throw new IllegalArgumentException();

		if (client.length == 0)
			return null;

		for (int i = 0; i < client.length; i++)
			for (int j = 0; j < server.length; j++)
			{
				if (client[i].equals(server[j]))
					return client[i];
			}
		}
		throw new NegotiateException();
	}

	private boolean compareFirstOfNameList(String[] a, String[] b)
	{
		if (a == null || b == null)
			throw new IllegalArgumentException();

		if ((a.length == 0) && (b.length == 0))
			return true;

		if ((a.length == 0) || (b.length == 0))
			return false;

		return (a[0].equals(b[0]));
	}

	private boolean isGuessOK(KexParameters cpar, KexParameters spar)
	{
		if (cpar == null || spar == null)
			throw new IllegalArgumentException();

		if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
		{
			return false;
		}

		if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
		{
			return false;
		}
		/*
		 * We do NOT check here if the other algorithms can be agreed on, this
		 * is just a check if kex_algorithms and server_host_key_algorithms were
		 * guessed right!
		 */

		return true;
	}

	private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
	{
		NegotiatedParameters np = new NegotiatedParameters();

		try
		{
			np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);

			log.log(20, "kex_algo=" + np.kex_algo);

			np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
					server.server_host_key_algorithms);

			log.log(20, "server_host_key_algo=" + np.server_host_key_algo);

			np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
					server.encryption_algorithms_client_to_server);
			np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
					server.encryption_algorithms_server_to_client);

			log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
			log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);

			np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
					server.mac_algorithms_client_to_server);
			np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
					server.mac_algorithms_server_to_client);

			log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
			log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);

			np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
					server.compression_algorithms_client_to_server);
			np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
					server.compression_algorithms_server_to_client);

			log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
			log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);

		}
		catch (NegotiateException e)
		{
			return null;
		}

		try
		{
			np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
					server.languages_client_to_server);
		}
		catch (NegotiateException e1)
		{
			np.lang_client_to_server = null;
		}

		try
		{
			np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
					server.languages_server_to_client);
		}
		catch (NegotiateException e2)
		{
			np.lang_server_to_client = null;
		}

		if (isGuessOK(client, server))
			np.guessOK = true;

		return np;
	}

	public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
	{
		nextKEXcryptoWishList = cwl;
		nextKEXdhgexParameters = dhgex;

		if (kxs == null)
		{
			kxs = new KexState();

			kxs.dhgexParameters = nextKEXdhgexParameters;
			PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
			kxs.localKEX = kp;
			tm.sendKexMessage(kp.getPayload());
		}
	}

	private boolean establishKeyMaterial()
	{
		try
		{
			int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
			int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
		}
			int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);

			int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
			int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
			int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);

			km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
					enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
		}
		catch (IllegalArgumentException e)
		{
			return false;
		}
		return true;
	}

	private void finishKex() throws IOException
	{
		if (sessionId == null)
			sessionId = kxs.H;

		establishKeyMaterial();

		/* Tell the other side that we start using the new material */

		PacketNewKeys ign = new PacketNewKeys();
		tm.sendKexMessage(ign.getPayload());

		BlockCipher cbc;
		MAC mac;

		try
		{
			cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
					km.initial_iv_client_to_server);

			mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);

		}
		catch (IllegalArgumentException e1)
		{
			throw new IOException("Fatal error during MAC startup!");
		}

		tm.changeSendCipher(cbc, mac);
		tm.kexFinished();
	}

	public static final String[] getDefaultServerHostkeyAlgorithmList()
	{
		return new String[] { "ssh-rsa", "ssh-dss" };
	}

	public static final void checkServerHostkeyAlgorithmsList(String[] algos)
	{
		for (int i = 0; i < algos.length; i++)
		{
			if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
				throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
		}
	}

	public static final String[] getDefaultKexAlgorithmList()
	{
		return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
				"diffie-hellman-group1-sha1" };
	}

	public static final void checkKexAlgorithmList(String[] algos)
	{
		for (int i = 0; i < algos.length; i++)
		{
			if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
				continue;

			if ("diffie-hellman-group14-sha1".equals(algos[i]))
				continue;

			if ("diffie-hellman-group1-sha1".equals(algos[i]))
				continue;

			throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
		}
	}

	private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
	{
		if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
		{
			RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
			RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);

			log.log(50, "Verifying ssh-rsa signature");

			return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
		}

		if (kxs.np.server_host_key_algo.equals("ssh-dss"))
		{
			DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
			DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);

			log.log(50, "Verifying ssh-dss signature");

			return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);

		throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
	}

	public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
	{
		PacketKexInit kip;

		if (msg == null)
		{
			synchronized (accessLock)
			{
				connectionClosed = true;
				accessLock.notifyAll();
				return;
			}
		}

		if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
			throw new IOException("Unexpected KEX message (type " + msg[0] + ")");

		if (ignore_next_kex_packet)
		{
			ignore_next_kex_packet = false;
			return;
		}

		if (msg[0] == Packets.SSH_MSG_KEXINIT)
		{
			if ((kxs != null) && (kxs.state != 0))
				throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");

			if (kxs == null)
			{
				/*
				 * Ah, OK, peer wants to do KEX. Let's be nice and play
				 * together.
				 */
				kxs = new KexState();
				kxs.dhgexParameters = nextKEXdhgexParameters;
				kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
				kxs.localKEX = kip;
				tm.sendKexMessage(kip.getPayload());
			}

			kip = new PacketKexInit(msg, 0, msglen);
			kxs.remoteKEX = kip;

			kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());

			if (kxs.np == null)
				throw new IOException("Cannot negotiate, proposals do not match.");

			if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
			{
				/*
				 * Guess was wrong, we need to ignore the next kex packet.
				 */

				ignore_next_kex_packet = true;
			}

			if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
			{
				if (kxs.dhgexParameters.getMin_group_len() == 0)
				{
					PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
					tm.sendKexMessage(dhgexreq.getPayload());

				}
				else
				{
					PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
					tm.sendKexMessage(dhgexreq.getPayload());
				}
				kxs.state = 1;
				return;
			}

			if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
					|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
			{
				kxs.dhx = new DhExchange();

				if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
					kxs.dhx.init(1, rnd);
				else
					kxs.dhx.init(14, rnd);

				PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
				tm.sendKexMessage(kp.getPayload());
				kxs.state = 1;
				return;
			}

			throw new IllegalStateException("Unkown KEX method!");
		}

		if (msg[0] == Packets.SSH_MSG_NEWKEYS)
		{
			if (km == null)
				throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");

			BlockCipher cbc;
			MAC mac;

			try
			{
				cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
						km.enc_key_server_to_client, km.initial_iv_server_to_client);

				mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);

			}
			catch (IllegalArgumentException e1)
			{
				throw new IOException("Fatal error during MAC startup!");
			}

			tm.changeRecvCipher(cbc, mac);

			ConnectionInfo sci = new ConnectionInfo();

			kexCount++;

			sci.keyExchangeAlgorithm = kxs.np.kex_algo;
			sci.keyExchangeCounter = kexCount;
			sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
			sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
			sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
			sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
			sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
			sci.serverHostKey = kxs.hostkey;

			synchronized (accessLock)
			{
				lastConnInfo = sci;
				accessLock.notifyAll();
			}

			kxs = null;
			return;
		}

		if ((kxs == null) || (kxs.state == 0))
			throw new IOException("Unexpected Kex submessage!");

		if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
		{
			if (kxs.state == 1)
			{
				PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
				kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
				kxs.dhgx.init(rnd);
				PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
				tm.sendKexMessage(dhgexinit.getPayload());
				kxs.state = 2;
				return;
			}

			if (kxs.state == 2)
			{
				PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);

				kxs.hostkey = dhgexrpl.getHostKey();

				if (verifier != null)
				{
					boolean vres = false;

					try
					{
						vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
					}
					catch (Exception e)
					{
						throw (IOException) new IOException(
								"The server hostkey was not accepted by the verifier callback.").initCause(e);
					}

					if (vres == false)
						throw new IOException("The server hostkey was not accepted by the verifier callback");
				}

				kxs.dhgx.setF(dhgexrpl.getF());

				try
				{
					kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
							kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
							kxs.dhgexParameters);
				}
				catch (IllegalArgumentException e)
				{
					throw (IOException) new IOException("KEX error.").initCause(e);
				}

				boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);

				if (res == false)
					throw new IOException("Hostkey signature sent by remote is wrong!");

				kxs.K = kxs.dhgx.getK();

				finishKex();
				kxs.state = -1;
				return;
			}

			throw new IllegalStateException("Illegal State in KEX Exchange!");
		}

		if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
				|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
		{
			if (kxs.state == 1)
			{

				PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);

				kxs.hostkey = dhr.getHostKey();

				if (verifier != null)
				{
					boolean vres = false;

					try
					{
						vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
					}
					catch (Exception e)
					{
						throw (IOException) new IOException(
								"The server hostkey was not accepted by the verifier callback.").initCause(e);
					}

					if (vres == false)
						throw new IOException("The server hostkey was not accepted by the verifier callback");
				}

				kxs.dhx.setF(dhr.getF());

				try
				{
					kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
							kxs.remoteKEX.getPayload(), dhr.getHostKey());
				}
				catch (IllegalArgumentException e)
				{
					throw (IOException) new IOException("KEX error.").initCause(e);
				}

				boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);

				if (res == false)
					throw new IOException("Hostkey signature sent by remote is wrong!");

				kxs.K = kxs.dhx.getK();

				finishKex();
				kxs.state = -1;
				return;
			}
		}

		throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
	}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/KexManager.java
Solution content
		}


package com.trilead.ssh2.transport;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.security.SecureRandom;

import com.trilead.ssh2.ConnectionInfo;
import com.trilead.ssh2.DHGexParameters;
import com.trilead.ssh2.ServerHostKeyVerifier;
import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.crypto.KeyMaterial;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
import com.trilead.ssh2.crypto.dh.DhExchange;
import com.trilead.ssh2.crypto.dh.DhGroupExchange;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketKexDHInit;
import com.trilead.ssh2.packets.PacketKexDHReply;
import com.trilead.ssh2.packets.PacketKexDhGexGroup;
import com.trilead.ssh2.packets.PacketKexDhGexInit;
import com.trilead.ssh2.packets.PacketKexDhGexReply;
import com.trilead.ssh2.packets.PacketKexDhGexRequest;
import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
import com.trilead.ssh2.packets.PacketKexInit;
import com.trilead.ssh2.packets.PacketNewKeys;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;


/**
 * KexManager.
 * 
 * @author Christian Plattner, plattner@trilead.com
			return false;
 * @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class KexManager
{
	private static final Logger log = Logger.getLogger(KexManager.class);

	KexState kxs;
	int kexCount = 0;
	KeyMaterial km;
	byte[] sessionId;
	ClientServerHello csh;

	final Object accessLock = new Object();
	ConnectionInfo lastConnInfo = null;

	boolean connectionClosed = false;

	boolean ignore_next_kex_packet = false;

	final TransportManager tm;

	CryptoWishList nextKEXcryptoWishList;
	DHGexParameters nextKEXdhgexParameters;

	ServerHostKeyVerifier verifier;
	final String hostname;
	final int port;
	final SecureRandom rnd;

	public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
			ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
	{
		this.tm = tm;
		this.csh = csh;
		this.nextKEXcryptoWishList = initialCwl;
		this.nextKEXdhgexParameters = new DHGexParameters();
		this.hostname = hostname;
		this.port = port;
		this.verifier = keyVerifier;
		this.rnd = rnd;
	}

	public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
	{
		synchronized (accessLock)
		{
			while (true)
			{
				if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
					return lastConnInfo;

				if (connectionClosed)
					throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
							.initCause(tm.getReasonClosedCause());

				try
				{
					accessLock.wait();
				}
				catch (InterruptedException e)
				{
                    throw new InterruptedIOException();
				}
			}
		}
	}

	private String getFirstMatch(String[] client, String[] server) throws NegotiateException
	{
		if (client == null || server == null)
			throw new IllegalArgumentException();

		if (client.length == 0)
			return null;

		for (int i = 0; i < client.length; i++)
		{
			for (int j = 0; j < server.length; j++)
			{
				if (client[i].equals(server[j]))
					return client[i];
			}
		}
		throw new NegotiateException();
	}

	private boolean compareFirstOfNameList(String[] a, String[] b)
	{
		if (a == null || b == null)
			throw new IllegalArgumentException();

		if ((a.length == 0) && (b.length == 0))
			return true;

		if ((a.length == 0) || (b.length == 0))
			return false;

		return (a[0].equals(b[0]));
	}

	private boolean isGuessOK(KexParameters cpar, KexParameters spar)
	{
		if (cpar == null || spar == null)
			throw new IllegalArgumentException();

		if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
		{
			return false;
		}

		if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
		{
		/*
		 * We do NOT check here if the other algorithms can be agreed on, this
		 * is just a check if kex_algorithms and server_host_key_algorithms were
		 * guessed right!
		 */

		return true;
	}

	private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
	{
		NegotiatedParameters np = new NegotiatedParameters();

		try
		{
			np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);

			log.log(20, "kex_algo=" + np.kex_algo);

			np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
					server.server_host_key_algorithms);

			log.log(20, "server_host_key_algo=" + np.server_host_key_algo);

			np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
					server.encryption_algorithms_client_to_server);
			np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
					server.encryption_algorithms_server_to_client);

			log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
			log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);

			np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
					server.mac_algorithms_client_to_server);
			np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
					server.mac_algorithms_server_to_client);

			log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
			log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);

			np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
					server.compression_algorithms_client_to_server);
			np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
					server.compression_algorithms_server_to_client);

			log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
			log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);

		}
		catch (NegotiateException e)
		{
			return null;
		}

		try
		{
			np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
					server.languages_client_to_server);
		}
		catch (NegotiateException e1)
		{
			np.lang_client_to_server = null;
		}

		try
		{
			np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
					server.languages_server_to_client);
		}
		catch (NegotiateException e2)
		{
			np.lang_server_to_client = null;
		}

		if (isGuessOK(client, server))
			np.guessOK = true;

		return np;
	}

	public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
	{
		nextKEXcryptoWishList = cwl;
		nextKEXdhgexParameters = dhgex;

		if (kxs == null)
		{
			kxs = new KexState();

			kxs.dhgexParameters = nextKEXdhgexParameters;
			PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
			kxs.localKEX = kp;
			tm.sendKexMessage(kp.getPayload());
		}
	}

	private boolean establishKeyMaterial()
	{
		try
		{
			int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
			int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
		}
			int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);

			int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
			int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
			int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);

			km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
					enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
		}
		catch (IllegalArgumentException e)
		{
			return false;
		}
		return true;
	}

	private void finishKex() throws IOException
	{
		if (sessionId == null)
			sessionId = kxs.H;

		establishKeyMaterial();

		/* Tell the other side that we start using the new material */

		PacketNewKeys ign = new PacketNewKeys();
		tm.sendKexMessage(ign.getPayload());

		BlockCipher cbc;
		MAC mac;

		try
		{
			cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
					km.initial_iv_client_to_server);

			mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);

		}
		catch (IllegalArgumentException e1)
		{
			throw new IOException("Fatal error during MAC startup!");
		}

		tm.changeSendCipher(cbc, mac);
		tm.kexFinished();
	}

	public static final String[] getDefaultServerHostkeyAlgorithmList()
	{
		return new String[] { "ssh-rsa", "ssh-dss" };
	}

	public static final void checkServerHostkeyAlgorithmsList(String[] algos)
	{
		for (int i = 0; i < algos.length; i++)
		{
			if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
				throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
		}
	}

	public static final String[] getDefaultKexAlgorithmList()
	{
		return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
				"diffie-hellman-group1-sha1" };
	}

	public static final void checkKexAlgorithmList(String[] algos)
	{
		for (int i = 0; i < algos.length; i++)
		{
			if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
				continue;

			if ("diffie-hellman-group14-sha1".equals(algos[i]))
				continue;

			if ("diffie-hellman-group1-sha1".equals(algos[i]))
				continue;

			throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
		}
	}

	private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
	{
		if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
		{
			RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
			RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);

			log.log(50, "Verifying ssh-rsa signature");

			return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
		}

		if (kxs.np.server_host_key_algo.equals("ssh-dss"))
		{
			DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
			DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);

			log.log(50, "Verifying ssh-dss signature");

			return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);

		throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
	}

	public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
	{
		PacketKexInit kip;

		if (msg == null)
		{
			synchronized (accessLock)
			{
				connectionClosed = true;
				accessLock.notifyAll();
				return;
			}
		}

		if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
			throw new IOException("Unexpected KEX message (type " + msg[0] + ")");

		if (ignore_next_kex_packet)
		{
			ignore_next_kex_packet = false;
			return;
		}

		if (msg[0] == Packets.SSH_MSG_KEXINIT)
		{
			if ((kxs != null) && (kxs.state != 0))
				throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");

			if (kxs == null)
			{
				/*
				 * Ah, OK, peer wants to do KEX. Let's be nice and play
				 * together.
				 */
				kxs = new KexState();
				kxs.dhgexParameters = nextKEXdhgexParameters;
				kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
				kxs.localKEX = kip;
				tm.sendKexMessage(kip.getPayload());
			}

			kip = new PacketKexInit(msg, 0, msglen);
			kxs.remoteKEX = kip;

			kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());

			if (kxs.np == null)
				throw new IOException("Cannot negotiate, proposals do not match.");

			if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
			{
				/*
				 * Guess was wrong, we need to ignore the next kex packet.
				 */

				ignore_next_kex_packet = true;
			}

			if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
			{
				if (kxs.dhgexParameters.getMin_group_len() == 0)
				{
					PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
					tm.sendKexMessage(dhgexreq.getPayload());

				}
				else
				{
					PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
					tm.sendKexMessage(dhgexreq.getPayload());
				}
				kxs.state = 1;
				return;
			}

			if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
					|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
			{
				kxs.dhx = new DhExchange();

				if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
					kxs.dhx.init(1, rnd);
				else
					kxs.dhx.init(14, rnd);

				PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
				tm.sendKexMessage(kp.getPayload());
				kxs.state = 1;
				return;
			}

			throw new IllegalStateException("Unkown KEX method!");
		}

		if (msg[0] == Packets.SSH_MSG_NEWKEYS)
		{
			if (km == null)
				throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");

			BlockCipher cbc;
			MAC mac;

			try
			{
				cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
						km.enc_key_server_to_client, km.initial_iv_server_to_client);

				mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);

			}
			catch (IllegalArgumentException e1)
			{
				throw new IOException("Fatal error during MAC startup!");
			}

			tm.changeRecvCipher(cbc, mac);

			ConnectionInfo sci = new ConnectionInfo();

			kexCount++;

			sci.keyExchangeAlgorithm = kxs.np.kex_algo;
			sci.keyExchangeCounter = kexCount;
			sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
			sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
			sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
			sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
			sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
			sci.serverHostKey = kxs.hostkey;

			synchronized (accessLock)
			{
				lastConnInfo = sci;
				accessLock.notifyAll();
			}

			kxs = null;
			return;
		}

		if ((kxs == null) || (kxs.state == 0))
			throw new IOException("Unexpected Kex submessage!");

		if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
		{
			if (kxs.state == 1)
			{
				PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
				kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
				kxs.dhgx.init(rnd);
				PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
				tm.sendKexMessage(dhgexinit.getPayload());
				kxs.state = 2;
				return;
			}

			if (kxs.state == 2)
			{
				PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);

				kxs.hostkey = dhgexrpl.getHostKey();

				if (verifier != null)
				{
					boolean vres = false;

					try
					{
						vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
					}
					catch (Exception e)
					{
						throw (IOException) new IOException(
								"The server hostkey was not accepted by the verifier callback.").initCause(e);
					}

					if (vres == false)
						throw new IOException("The server hostkey was not accepted by the verifier callback");
				}

				kxs.dhgx.setF(dhgexrpl.getF());

				try
				{
					kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
							kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
							kxs.dhgexParameters);
				}
				catch (IllegalArgumentException e)
				{
					throw (IOException) new IOException("KEX error.").initCause(e);
				}

				boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);

				if (res == false)
					throw new IOException("Hostkey signature sent by remote is wrong!");

				kxs.K = kxs.dhgx.getK();

				finishKex();
				kxs.state = -1;
				return;
			}

			throw new IllegalStateException("Illegal State in KEX Exchange!");
		}

		if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
				|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
		{
			if (kxs.state == 1)
			{

				PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);

				kxs.hostkey = dhr.getHostKey();

				if (verifier != null)
				{
					boolean vres = false;

					try
					{
						vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
					}
					catch (Exception e)
					{
						throw (IOException) new IOException(
								"The server hostkey was not accepted by the verifier callback.").initCause(e);
					}

					if (vres == false)
						throw new IOException("The server hostkey was not accepted by the verifier callback");
				}

				kxs.dhx.setF(dhr.getF());

				try
				{
					kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
							kxs.remoteKEX.getPayload(), dhr.getHostKey());
				}
				catch (IllegalArgumentException e)
				{
					throw (IOException) new IOException("KEX error.").initCause(e);
				}

				boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);

				if (res == false)
					throw new IOException("Hostkey signature sent by remote is wrong!");

				kxs.K = kxs.dhx.getK();

				finishKex();
				kxs.state = -1;
				return;
			}
		}

		throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
	}
}
File
KexManager.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
 * TransportConnection.
 * 
	}
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportConnection.java

package com.trilead.ssh2.transport;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;

import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CipherInputStream;
import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
import com.trilead.ssh2.crypto.cipher.NullCipher;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;


/**
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class TransportConnection
{
	private static final Logger log = Logger.getLogger(TransportConnection.class);

	int send_seq_number = 0;

	int recv_seq_number = 0;

	CipherInputStream cis;

	CipherOutputStream cos;

	boolean useRandomPadding = false;

	/* Depends on current MAC and CIPHER */

	MAC send_mac;

	byte[] send_mac_buffer;

	int send_padd_blocksize = 8;

	MAC recv_mac;

	byte[] recv_mac_buffer;

	byte[] recv_mac_buffer_cmp;

	int recv_padd_blocksize = 8;

	/* won't change */

	final byte[] send_padding_buffer = new byte[256];

	final byte[] send_packet_header_buffer = new byte[5];

	final byte[] recv_padding_buffer = new byte[256];

	final byte[] recv_packet_header_buffer = new byte[5];

	boolean recv_packet_header_present = false;

	ClientServerHello csh;

	final SecureRandom rnd;

	public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd)
	{
		this.cis = new CipherInputStream(new NullCipher(), is);
		this.cos = new CipherOutputStream(new NullCipher(), os);
		this.rnd = rnd;
	}

	public void changeRecvCipher(BlockCipher bc, MAC mac)
	{
		cis.changeCipher(bc);
		recv_mac = mac;
		recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
		recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
		recv_padd_blocksize = bc.getBlockSize();
		if (recv_padd_blocksize < 8)
			recv_padd_blocksize = 8;
	}

	public void changeSendCipher(BlockCipher bc, MAC mac)
	{
		if ((bc instanceof NullCipher) == false)
		{
			/* Only use zero byte padding for the first few packets */
			useRandomPadding = true;
			/* Once we start encrypting, there is no way back */
		}

		cos.changeCipher(bc);
		send_mac = mac;
		send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
		send_padd_blocksize = bc.getBlockSize();
		if (send_padd_blocksize < 8)
			send_padd_blocksize = 8;
	}

	public void sendMessage(byte[] message) throws IOException
	{
		sendMessage(message, 0, message.length, 0);
	}

	public void sendMessage(byte[] message, int off, int len) throws IOException
	{
		sendMessage(message, off, len, 0);
	}

	public int getPacketOverheadEstimate()
	{
		// return an estimate for the paket overhead (for send operations)
		return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
	}

	public void sendMessage(byte[] message, int off, int len, int padd) throws IOException
	{
		if (padd < 4)
			padd = 4;
		else if (padd > 64)
			padd = 64;

		int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */

		int slack = packet_len % send_padd_blocksize;

		if (slack != 0)
		{
			packet_len += (send_padd_blocksize - slack);
		}

		if (packet_len < 16)
			packet_len = 16;

		int padd_len = packet_len - (5 + len);

		if (useRandomPadding)
		{
			for (int i = 0; i < padd_len; i = i + 4)
			{
				/*
				 * don't waste calls to rnd.nextInt() (by using only 8bit of the
				 * output). just believe me: even though we may write here up to 3
				 * bytes which won't be used, there is no "buffer overflow" (i.e.,
				 * arrayindexoutofbounds). the padding buffer is big enough =) (256
				 * bytes, and that is bigger than any current cipher block size + 64).
				 */

				int r = rnd.nextInt();
				send_padding_buffer[i] = (byte) r;
				send_padding_buffer[i + 1] = (byte) (r >> 8);
				send_padding_buffer[i + 2] = (byte) (r >> 16);
				send_padding_buffer[i + 3] = (byte) (r >> 24);
			}
		}
		else
		{
			/* use zero padding for unencrypted traffic */
			for (int i = 0; i < padd_len; i++)
				send_padding_buffer[i] = 0;
			/* Actually this code is paranoid: we never filled any

			 * bytes into the padding buffer so far, therefore it should
			 * consist of zeros only.
			 */
		}

		send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
		send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
		send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
		send_packet_header_buffer[3] = (byte) ((packet_len - 4));
		send_packet_header_buffer[4] = (byte) padd_len;

		cos.write(send_packet_header_buffer, 0, 5);
		cos.write(message, off, len);
		cos.write(send_padding_buffer, 0, padd_len);

		if (send_mac != null)
		{
			send_mac.initMac(send_seq_number);
			send_mac.update(send_packet_header_buffer, 0, 5);
			send_mac.update(message, off, len);
			send_mac.update(send_padding_buffer, 0, padd_len);

			send_mac.getMac(send_mac_buffer, 0);
			cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
		}

		cos.flush();

		if (log.isEnabled())
		{
			log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
		}

		send_seq_number++;
	}

	public int peekNextMessageLength() throws IOException
	{
		if (recv_packet_header_present == false)
		{
			cis.read(recv_packet_header_buffer, 0, 5);
			recv_packet_header_present = true;
		}

		int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
				| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
				| ((recv_packet_header_buffer[3] & 0xff));

		int padding_length = recv_packet_header_buffer[4] & 0xff;

		if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12)
			throw new IOException("Illegal packet size! (" + packet_length + ")");

		int payload_length = packet_length - padding_length - 1;

		if (payload_length < 0)
			throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");

		return payload_length;
	}

	public int receiveMessage(byte buffer[], int off, int len) throws IOException
	{
		if (recv_packet_header_present == false)
		{
			cis.read(recv_packet_header_buffer, 0, 5);
		}
		else
			recv_packet_header_present = false;

		int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
				| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
				| ((recv_packet_header_buffer[3] & 0xff));

		int padding_length = recv_packet_header_buffer[4] & 0xff;

		if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12)
			throw new IOException("Illegal packet size! (" + packet_length + ")");

		int payload_length = packet_length - padding_length - 1;

		if (payload_length < 0)
			throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");

		if (payload_length >= len)
			throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");

		cis.read(buffer, off, payload_length);
		cis.read(recv_padding_buffer, 0, padding_length);

		if (recv_mac != null)
		{
			cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);

			recv_mac.initMac(recv_seq_number);
			recv_mac.update(recv_packet_header_buffer, 0, 5);
			recv_mac.update(buffer, off, payload_length);
			recv_mac.update(recv_padding_buffer, 0, padding_length);
			recv_mac.getMac(recv_mac_buffer_cmp, 0);

			for (int i = 0; i < recv_mac_buffer.length; i++)
			{
				if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
					throw new IOException("Remote sent corrupt MAC.");
			}
		}

		recv_seq_number++;

		if (log.isEnabled())
		{
			log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
					+ " bytes payload");
		}

		return payload_length;
	}
}
=======

package com.trilead.ssh2.transport;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;

import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CipherInputStream;
import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
import com.trilead.ssh2.crypto.cipher.NullCipher;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;


/**
 * TransportConnection.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class TransportConnection
{
	private static final Logger log = Logger.getLogger(TransportConnection.class);

	int send_seq_number = 0;

	int recv_seq_number = 0;

	CipherInputStream cis;

	CipherOutputStream cos;

	boolean useRandomPadding = false;

	/* Depends on current MAC and CIPHER */

	MAC send_mac;

	byte[] send_mac_buffer;

	int send_padd_blocksize = 8;

	MAC recv_mac;

	byte[] recv_mac_buffer;

	byte[] recv_mac_buffer_cmp;

	int recv_padd_blocksize = 8;

	/* won't change */

	final byte[] send_padding_buffer = new byte[256];

	final byte[] send_packet_header_buffer = new byte[5];

	final byte[] recv_padding_buffer = new byte[256];

	final byte[] recv_packet_header_buffer = new byte[5];

	boolean recv_packet_header_present = false;

	ClientServerHello csh;

	final SecureRandom rnd;

	public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd)
	{
		this.cis = new CipherInputStream(new NullCipher(), is);
		this.cos = new CipherOutputStream(new NullCipher(), os);
		this.rnd = rnd;
	}

	public void changeRecvCipher(BlockCipher bc, MAC mac)
	{
		cis.changeCipher(bc);
		recv_mac = mac;
		recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
		recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
		recv_padd_blocksize = bc.getBlockSize();
		if (recv_padd_blocksize < 8)
			recv_padd_blocksize = 8;
	}

	public void changeSendCipher(BlockCipher bc, MAC mac)
	{
		if ((bc instanceof NullCipher) == false)
		{
			/* Only use zero byte padding for the first few packets */
			useRandomPadding = true;
			/* Once we start encrypting, there is no way back */
		}

		cos.changeCipher(bc);
		send_mac = mac;
		send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
		send_padd_blocksize = bc.getBlockSize();
		if (send_padd_blocksize < 8)
			send_padd_blocksize = 8;
	}

	public void sendMessage(byte[] message) throws IOException
	{
		sendMessage(message, 0, message.length, 0);
	}

	public void sendMessage(byte[] message, int off, int len) throws IOException
	{
		sendMessage(message, off, len, 0);
	}

	public int getPacketOverheadEstimate()
	{
		// return an estimate for the paket overhead (for send operations)
		return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
	}

	public void sendMessage(byte[] message, int off, int len, int padd) throws IOException
	{
		if (padd < 4)
			padd = 4;
		else if (padd > 64)
			padd = 64;

		int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */

		int slack = packet_len % send_padd_blocksize;

		if (slack != 0)
		{
			packet_len += (send_padd_blocksize - slack);
		}

		if (packet_len < 16)
			packet_len = 16;

		int padd_len = packet_len - (5 + len);

		if (useRandomPadding)
		{
			for (int i = 0; i < padd_len; i = i + 4)
			{
				/*
				 * don't waste calls to rnd.nextInt() (by using only 8bit of the
				 * output). just believe me: even though we may write here up to 3
				 * bytes which won't be used, there is no "buffer overflow" (i.e.,
				 * arrayindexoutofbounds). the padding buffer is big enough =) (256
				 * bytes, and that is bigger than any current cipher block size + 64).
				 */

				int r = rnd.nextInt();
				send_padding_buffer[i] = (byte) r;
				send_padding_buffer[i + 1] = (byte) (r >> 8);
				send_padding_buffer[i + 2] = (byte) (r >> 16);
				send_padding_buffer[i + 3] = (byte) (r >> 24);
			}
		}
		else
		{
			/* use zero padding for unencrypted traffic */
			for (int i = 0; i < padd_len; i++)
				send_padding_buffer[i] = 0;
			/* Actually this code is paranoid: we never filled any
			 * bytes into the padding buffer so far, therefore it should
			 * consist of zeros only.
			 */
		}

		send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
		send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
		send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
		send_packet_header_buffer[3] = (byte) ((packet_len - 4));
		send_packet_header_buffer[4] = (byte) padd_len;

		cos.write(send_packet_header_buffer, 0, 5);
		cos.write(message, off, len);
		cos.write(send_padding_buffer, 0, padd_len);

		if (send_mac != null)
		{
			send_mac.initMac(send_seq_number);
			send_mac.update(send_packet_header_buffer, 0, 5);
			send_mac.update(message, off, len);
			send_mac.update(send_padding_buffer, 0, padd_len);

			send_mac.getMac(send_mac_buffer, 0);
			cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
		}

		cos.flush();

		if (log.isEnabled())
		{
			log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
		}

		send_seq_number++;
	public int peekNextMessageLength() throws IOException
	{
		if (recv_packet_header_present == false)
		{
			cis.read(recv_packet_header_buffer, 0, 5);
			recv_packet_header_present = true;
		}

		int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
				| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
				| ((recv_packet_header_buffer[3] & 0xff));

		int padding_length = recv_packet_header_buffer[4] & 0xff;

		if (packet_length > 35000 || packet_length < 12)
			throw new IOException("Illegal packet size! (" + packet_length + ")");

		int payload_length = packet_length - padding_length - 1;

		if (payload_length < 0)
			throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");

		return payload_length;
	}

	public int receiveMessage(byte buffer[], int off, int len) throws IOException
	{
		if (recv_packet_header_present == false)
		{
			cis.read(recv_packet_header_buffer, 0, 5);
		}
		else
			recv_packet_header_present = false;

		int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
				| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
				| ((recv_packet_header_buffer[3] & 0xff));

		int padding_length = recv_packet_header_buffer[4] & 0xff;

		if (packet_length > 35000 || packet_length < 12)
			throw new IOException("Illegal packet size! (" + packet_length + ")");

		int payload_length = packet_length - padding_length - 1;

		if (payload_length < 0)
			throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");

		if (payload_length >= len)
			throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");

		cis.read(buffer, off, payload_length);
		cis.read(recv_padding_buffer, 0, padding_length);

		if (recv_mac != null)
		{
			cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);

			recv_mac.initMac(recv_seq_number);
			recv_mac.update(recv_packet_header_buffer, 0, 5);
			recv_mac.update(buffer, off, payload_length);
			recv_mac.update(recv_padding_buffer, 0, padding_length);
			recv_mac.getMac(recv_mac_buffer_cmp, 0);

			for (int i = 0; i < recv_mac_buffer.length; i++)
			{
				if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
					throw new IOException("Remote sent corrupt MAC.");
			}
		}

		recv_seq_number++;

		if (log.isEnabled())
		{
			log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
					+ " bytes payload");
		}

		return payload_length;
	}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportConnection.java
Solution content
import java.io.InputStream;
		}

package com.trilead.ssh2.transport;

import java.io.IOException;
import java.io.OutputStream;
import java.security.SecureRandom;

import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CipherInputStream;
import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
import com.trilead.ssh2.crypto.cipher.NullCipher;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;


/**
 * TransportConnection.
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */
public class TransportConnection
{
	private static final Logger log = Logger.getLogger(TransportConnection.class);

	int send_seq_number = 0;

	int recv_seq_number = 0;

	CipherInputStream cis;

	CipherOutputStream cos;

	boolean useRandomPadding = false;

	/* Depends on current MAC and CIPHER */

	MAC send_mac;

	byte[] send_mac_buffer;

	int send_padd_blocksize = 8;

	MAC recv_mac;

	byte[] recv_mac_buffer;

	byte[] recv_mac_buffer_cmp;

	int recv_padd_blocksize = 8;

	/* won't change */

	final byte[] send_padding_buffer = new byte[256];

	final byte[] send_packet_header_buffer = new byte[5];

	final byte[] recv_padding_buffer = new byte[256];

	final byte[] recv_packet_header_buffer = new byte[5];

	boolean recv_packet_header_present = false;

	ClientServerHello csh;

	final SecureRandom rnd;

	public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd)
	{
		this.cis = new CipherInputStream(new NullCipher(), is);
		this.cos = new CipherOutputStream(new NullCipher(), os);
		this.rnd = rnd;
	}

	public void changeRecvCipher(BlockCipher bc, MAC mac)
	{
		cis.changeCipher(bc);
		recv_mac = mac;
		recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
		recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
		recv_padd_blocksize = bc.getBlockSize();
		if (recv_padd_blocksize < 8)
			recv_padd_blocksize = 8;
	}

	public void changeSendCipher(BlockCipher bc, MAC mac)
	{
		if ((bc instanceof NullCipher) == false)
		{
			/* Only use zero byte padding for the first few packets */
			useRandomPadding = true;
			/* Once we start encrypting, there is no way back */
		}

		cos.changeCipher(bc);
		send_mac = mac;
		send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
		send_padd_blocksize = bc.getBlockSize();
		if (send_padd_blocksize < 8)
			send_padd_blocksize = 8;
	}

	public void sendMessage(byte[] message) throws IOException
	{
		sendMessage(message, 0, message.length, 0);
	}

	public void sendMessage(byte[] message, int off, int len) throws IOException
	{
		sendMessage(message, off, len, 0);
	}

	public int getPacketOverheadEstimate()
	{
		// return an estimate for the paket overhead (for send operations)
		return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
	}

	public void sendMessage(byte[] message, int off, int len, int padd) throws IOException
	{
		if (padd < 4)
			padd = 4;
		else if (padd > 64)
			padd = 64;

		int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */

		int slack = packet_len % send_padd_blocksize;

		if (slack != 0)
		{
			packet_len += (send_padd_blocksize - slack);
		}

		if (packet_len < 16)
			packet_len = 16;

		int padd_len = packet_len - (5 + len);

		if (useRandomPadding)
		{
			for (int i = 0; i < padd_len; i = i + 4)
			{
				/*
				 * don't waste calls to rnd.nextInt() (by using only 8bit of the
				 * output). just believe me: even though we may write here up to 3
				 * bytes which won't be used, there is no "buffer overflow" (i.e.,
				 * arrayindexoutofbounds). the padding buffer is big enough =) (256
				 * bytes, and that is bigger than any current cipher block size + 64).
				 */

				int r = rnd.nextInt();
				send_padding_buffer[i] = (byte) r;
				send_padding_buffer[i + 1] = (byte) (r >> 8);
				send_padding_buffer[i + 2] = (byte) (r >> 16);
				send_padding_buffer[i + 3] = (byte) (r >> 24);
			}
		}
		else
		{
			/* use zero padding for unencrypted traffic */
			for (int i = 0; i < padd_len; i++)
				send_padding_buffer[i] = 0;
			/* Actually this code is paranoid: we never filled any
			 * bytes into the padding buffer so far, therefore it should
			 * consist of zeros only.
			 */
		}

		send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
		send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
		send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
		send_packet_header_buffer[3] = (byte) ((packet_len - 4));
		send_packet_header_buffer[4] = (byte) padd_len;

		cos.write(send_packet_header_buffer, 0, 5);
		cos.write(message, off, len);
		cos.write(send_padding_buffer, 0, padd_len);

		if (send_mac != null)
		{
			send_mac.initMac(send_seq_number);
			send_mac.update(send_packet_header_buffer, 0, 5);
			send_mac.update(message, off, len);
			send_mac.update(send_padding_buffer, 0, padd_len);

			send_mac.getMac(send_mac_buffer, 0);
			cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
		}

		cos.flush();

		if (log.isEnabled())
		{
			log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
		}

		send_seq_number++;
	}

	public int peekNextMessageLength() throws IOException
	{
		if (recv_packet_header_present == false)
		{
			cis.read(recv_packet_header_buffer, 0, 5);
			recv_packet_header_present = true;
		}

		int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
				| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
				| ((recv_packet_header_buffer[3] & 0xff));

		int padding_length = recv_packet_header_buffer[4] & 0xff;

		if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12)
			throw new IOException("Illegal packet size! (" + packet_length + ")");

		int payload_length = packet_length - padding_length - 1;

		if (payload_length < 0)
			throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");

		return payload_length;
	}

	public int receiveMessage(byte buffer[], int off, int len) throws IOException
	{
		if (recv_packet_header_present == false)
		{
			cis.read(recv_packet_header_buffer, 0, 5);
		}
		else
			recv_packet_header_present = false;

		int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
				| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
				| ((recv_packet_header_buffer[3] & 0xff));

		int padding_length = recv_packet_header_buffer[4] & 0xff;

		if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12)
			throw new IOException("Illegal packet size! (" + packet_length + ")");

		int payload_length = packet_length - padding_length - 1;

		if (payload_length < 0)
			throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");

		if (payload_length >= len)
			throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");

		cis.read(buffer, off, payload_length);
		cis.read(recv_padding_buffer, 0, padding_length);

		if (recv_mac != null)
		{
			cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);

			recv_mac.initMac(recv_seq_number);
			recv_mac.update(recv_packet_header_buffer, 0, 5);
			recv_mac.update(buffer, off, payload_length);
			recv_mac.update(recv_padding_buffer, 0, padding_length);
			recv_mac.getMac(recv_mac_buffer_cmp, 0);

			for (int i = 0; i < recv_mac_buffer.length; i++)
			{
				if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
					throw new IOException("Remote sent corrupt MAC.");
			}

		recv_seq_number++;

		if (log.isEnabled())
		{
			log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
					+ " bytes payload");
		}

		return payload_length;
	}
}
File
TransportConnection.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
=======

>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
package com.trilead.ssh2.transport;

import java.io.IOException;
Solution content
package com.trilead.ssh2.transport;

import java.io.IOException;
File
TransportManager.java
Developer's decision
Version 1
Kind of conflict
Blank
Chunk
Conflicting content
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
import java.io.InterruptedIOException;
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
Solution content
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
File
TransportManager.java
Developer's decision
Version 1
Kind of conflict
Import
Chunk
Conflicting content
 */
public class TransportManager
{
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
    private static final Logger log = Logger.getLogger(TransportManager.class);

    class HandlerEntry
=======
	private static final Logger log = Logger.getLogger(TransportManager.class);

	class HandlerEntry
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
	{
		MessageHandler mh;
		int low;
Solution content
 */
public class TransportManager
{
    private static final Logger log = Logger.getLogger(TransportManager.class);

    class HandlerEntry
	{
		MessageHandler mh;
		int low;
File
TransportManager.java
Developer's decision
Version 1
Kind of conflict
Attribute
Class signature
Method invocation
Chunk
Conflicting content
		}
	}

<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
	private void establishConnection(ProxyData proxyData, int connectTimeout) throws IOException
=======
	private void establishConnection(ProxyData proxyData, int connectTimeout, int readTimeout) throws IOException
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
	{
		/* See the comment for createInetAddress() */
Solution content
		}
	}

	private void establishConnection(ProxyData proxyData, int connectTimeout, int readTimeout) throws IOException
	{
		/* See the comment for createInetAddress() */
File
TransportManager.java
Developer's decision
Version 2
Kind of conflict
Method signature
Chunk
Conflicting content
		{
			InetAddress addr = createInetAddress(hostname);
			sock.connect(new InetSocketAddress(addr, port), connectTimeout);
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
			sock.setSoTimeout(0);
=======
			sock.setSoTimeout(readTimeout);
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
			return;
		}
Solution content
		{
			InetAddress addr = createInetAddress(hostname);
			sock.connect(new InetSocketAddress(addr, port), connectTimeout);
			sock.setSoTimeout(readTimeout);
			return;
		}
File
TransportManager.java
Developer's decision
Version 2
Kind of conflict
Method invocation
Chunk
Conflicting content
			InetAddress addr = createInetAddress(pd.proxyHost);
			sock.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout);
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
			sock.setSoTimeout(0);
=======
			sock.setSoTimeout(readTimeout);
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java

			/* OK, now tell the proxy where we actually want to connect to */
Solution content
			InetAddress addr = createInetAddress(pd.proxyHost);
			sock.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout);
			sock.setSoTimeout(readTimeout);

			/* OK, now tell the proxy where we actually want to connect to */
File
TransportManager.java
Developer's decision
Version 2
Kind of conflict
Method invocation
Chunk
Conflicting content
		throw new IOException("Unsupported ProxyData");
	}

<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
	public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
			int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
	{
		/* First, establish the TCP connection to the SSH-2 server */

		establishConnection(proxyData, connectTimeout);
=======
    public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
            int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException {
        initialize(cwl, verifier, dhgex, connectTimeout, 0, rnd, proxyData);
    }
    
    public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
			int connectTimeout, int readTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
	{
		/* First, establish the TCP connection to the SSH-2 server */

		establishConnection(proxyData, connectTimeout, readTimeout);
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java

		/* Parse the server line and say hello - important: this information is later needed for the
		 * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
Solution content
		throw new IOException("Unsupported ProxyData");
	}

    public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
            int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException {
        initialize(cwl, verifier, dhgex, connectTimeout, 0, rnd, proxyData);
    }
    
    public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
			int connectTimeout, int readTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
	{
		/* First, establish the TCP connection to the SSH-2 server */

		establishConnection(proxyData, connectTimeout, readTimeout);

		/* Parse the server line and say hello - important: this information is later needed for the
		 * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
File
TransportManager.java
Developer's decision
Version 2
Kind of conflict
Comment
Method declaration
Method invocation
Method signature
Chunk
Conflicting content
				}
				catch (InterruptedException e)
				{
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
					throw new InterruptedIOException();
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
				}
			}
Solution content
				}
				catch (InterruptedException e)
				{
					throw new InterruptedIOException();
				}
			}
File
TransportManager.java
Developer's decision
Version 1
Kind of conflict
Throw statement
Chunk
Conflicting content
	public void receiveLoop() throws IOException
	{
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
		byte[] msg = new byte[MAX_PACKET_SIZE];
=======
		byte[] msg = new byte[35000];
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java

		while (true)
		{
Solution content
	public void receiveLoop() throws IOException
	{
		byte[] msg = new byte[MAX_PACKET_SIZE];

		while (true)
		{
File
TransportManager.java
Developer's decision
Version 1
Kind of conflict
Method invocation
Variable
Chunk
Conflicting content
			mh.handleMessage(msg, msglen);
		}
	}
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java

    /**
     * Advertised maximum SSH packet size that the other side can send to us.
     */
    public static final int MAX_PACKET_SIZE = Integer.getInteger(
    			TransportManager.class.getName()+".maxPacketSize",
    			64*1024);
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
}
Solution content
			mh.handleMessage(msg, msglen);
		}
	}

    /**
     * Advertised maximum SSH packet size that the other side can send to us.
     */
    public static final int MAX_PACKET_SIZE = Integer.getInteger(
    			TransportManager.class.getName()+".maxPacketSize",
    			64*1024);
}
File
TransportManager.java
Developer's decision
Version 1
Kind of conflict
Attribute
Comment
Method invocation