diff options
| author | Kenny Root <kenny@the-b.org> | 2008-11-11 07:18:26 +0000 | 
|---|---|---|
| committer | Kenny Root <kenny@the-b.org> | 2008-11-11 07:18:26 +0000 | 
| commit | 0e11054cdbf94c903cb060d42edb8cfe2badc15e (patch) | |
| tree | 8717b158cd76a5a1cebe9eb15df6fa00b9214ed6 /lib/src/main/java/com | |
| parent | 9b3322b1b870b3e677723d33ea6502c2faf22173 (diff) | |
| download | sshlib-0e11054cdbf94c903cb060d42edb8cfe2badc15e.tar.gz sshlib-0e11054cdbf94c903cb060d42edb8cfe2badc15e.tar.bz2 sshlib-0e11054cdbf94c903cb060d42edb8cfe2badc15e.zip  | |
* Add compression option to hosts
Diffstat (limited to 'lib/src/main/java/com')
9 files changed, 362 insertions, 4 deletions
diff --git a/lib/src/main/java/com/trilead/ssh2/Connection.java b/lib/src/main/java/com/trilead/ssh2/Connection.java index 2b244c2..1741a4c 100644 --- a/lib/src/main/java/com/trilead/ssh2/Connection.java +++ b/lib/src/main/java/com/trilead/ssh2/Connection.java @@ -89,6 +89,7 @@ public class Connection  	private AuthenticationManager am;
  	private boolean authenticated = false;
 +	private boolean compression = false;
  	private ChannelManager cm;
  	private CryptoWishList cryptoWishList = new CryptoWishList();
 @@ -575,6 +576,20 @@ public class Connection  	}
  	/**
 +	 * Controls whether compression is used on the link or not.
 +	 * <p>
 +	 * Note: This can only be called before connect()
 +	 * @param enabled whether to enable compression
 +	 * @throws IOException
 +	 */
 +	public synchronized void setCompression(boolean enabled) throws IOException {
 +		if (tm != null)
 +			throw new IOException("Connection to " + hostname + " is already in connected state!");
 +		
 +		compression = enabled;
 +	}
 +	
 +	/**
  	 * Close the connection to the SSH-2 server. All assigned sessions will be
  	 * closed, too. Can be called at any time. Don't forget to call this once
  	 * you don't need a connection anymore - otherwise the receiver thread may
 @@ -735,6 +750,12 @@ public class Connection  		tm.setConnectionMonitors(connectionMonitors);
 +		// Don't offer compression if not requested
 +		if (!compression) {
 +			cryptoWishList.c2s_comp_algos = new String[] { "none" };
 +			cryptoWishList.s2c_comp_algos = new String[] { "none" };
 +		}
 +		
  		/*
  		 * Make sure that the runnable below will observe the new value of "tm"
  		 * and "state" (the runnable will be executed in a different thread,
 diff --git a/lib/src/main/java/com/trilead/ssh2/compression/CompressionFactory.java b/lib/src/main/java/com/trilead/ssh2/compression/CompressionFactory.java new file mode 100644 index 0000000..30b3d21 --- /dev/null +++ b/lib/src/main/java/com/trilead/ssh2/compression/CompressionFactory.java @@ -0,0 +1,96 @@ +/* +	ConnectBot: simple, powerful, open-source SSH client for Android +	Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey +	 +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. +	 +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU General Public License for more details. +	 +	You should have received a copy of the GNU General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ +package com.trilead.ssh2.compression; + +import java.util.Vector; + +/** + * @author Kenny Root + * + */ +public class CompressionFactory { +	static class CompressorEntry +	{ +		String type; +		String compressorClass; + +		public CompressorEntry(String type, String compressorClass) +		{ +			this.type = type; +			this.compressorClass = compressorClass; +		} +	} +	 +	static Vector<CompressorEntry> compressors = new Vector<CompressorEntry>(); + +	static +	{ +		/* Higher Priority First */ + +		compressors.addElement(new CompressorEntry("zlib", "com.trilead.ssh2.compression.Zlib")); +		compressors.addElement(new CompressorEntry("zlib@openssh.com", "com.trilead.ssh2.compression.Zlib")); +		compressors.addElement(new CompressorEntry("none", "")); +	} +	 +	public static String[] getDefaultCompressorList() +	{ +		String list[] = new String[compressors.size()]; +		for (int i = 0; i < compressors.size(); i++) +		{ +			CompressorEntry ce = compressors.elementAt(i); +			list[i] = new String(ce.type); +		} +		return list; +	} +	 +	public static void checkCompressorList(String[] compressorCandidates) +	{ +		for (int i = 0; i < compressorCandidates.length; i++) +			getEntry(compressorCandidates[i]); +	} + +	public static ICompressor createCompressor(String type) +	{ +		try +		{ +			CompressorEntry ce = getEntry(type); +			if ("".equals(ce.compressorClass)) +				return null; +			 +			Class<?> cc = Class.forName(ce.compressorClass); +			ICompressor cmp = (ICompressor) cc.newInstance(); + +			return cmp; +		} +		catch (Exception e) +		{ +			throw new IllegalArgumentException("Cannot instantiate " + type); +		} +	} + +	private static CompressorEntry getEntry(String type) +	{ +		for (int i = 0; i < compressors.size(); i++) +		{ +			CompressorEntry ce = compressors.elementAt(i); +			if (ce.type.equals(type)) +				return ce; +		} +		throw new IllegalArgumentException("Unkown algorithm " + type); +	} +} diff --git a/lib/src/main/java/com/trilead/ssh2/compression/ICompressor.java b/lib/src/main/java/com/trilead/ssh2/compression/ICompressor.java new file mode 100644 index 0000000..a71d31b --- /dev/null +++ b/lib/src/main/java/com/trilead/ssh2/compression/ICompressor.java @@ -0,0 +1,30 @@ +/* +	ConnectBot: simple, powerful, open-source SSH client for Android +	Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey +	 +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. +	 +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU General Public License for more details. +	 +	You should have received a copy of the GNU General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +package com.trilead.ssh2.compression; + +/** + * @author Kenny Root + *  + */ +public interface ICompressor { +	int getBufferSize(); +	 +	int compress(byte[] buf, int start, int len, byte[] output); + +	byte[] uncompress(byte[] buf, int start, int[] len); +} diff --git a/lib/src/main/java/com/trilead/ssh2/compression/Zlib.java b/lib/src/main/java/com/trilead/ssh2/compression/Zlib.java new file mode 100644 index 0000000..c81a719 --- /dev/null +++ b/lib/src/main/java/com/trilead/ssh2/compression/Zlib.java @@ -0,0 +1,121 @@ +/* +	ConnectBot: simple, powerful, open-source SSH client for Android +	Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey +	 +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. +	 +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU General Public License for more details. +	 +	You should have received a copy of the GNU General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +package com.trilead.ssh2.compression; + +import com.jcraft.jzlib.JZlib; +import com.jcraft.jzlib.ZStream; + +/** + * @author Kenny Root + *  + */ +public class Zlib implements ICompressor { +	static private final int BUF_SIZE = 4096; +	static private final int LEVEL = 5; + +	private ZStream deflate; +	private byte[] deflate_tmpbuf = new byte[BUF_SIZE]; + +	private ZStream inflate; +	private byte[] inflate_tmpbuf = new byte[BUF_SIZE]; +	private byte[] inflated_buf; + +	public Zlib() { +		deflate = new ZStream(); +		inflate = new ZStream(); + +		deflate.deflateInit(LEVEL); +		inflate.inflateInit(); +		inflated_buf = new byte[BUF_SIZE]; +	} +	 +	public int getBufferSize() { +		return BUF_SIZE; +	} + +	public int compress(byte[] buf, int start, int len, byte[] output) { +		deflate.next_in = buf; +		deflate.next_in_index = start; +		deflate.avail_in = len - start; + +		int status; +		int outputlen = start; + +		do { +			deflate.next_out = deflate_tmpbuf; +			deflate.next_out_index = 0; +			deflate.avail_out = BUF_SIZE; +			status = deflate.deflate(JZlib.Z_PARTIAL_FLUSH); +			switch (status) { +			case JZlib.Z_OK: +				System.arraycopy(deflate_tmpbuf, 0, output, outputlen, BUF_SIZE +						- deflate.avail_out); +				outputlen += (BUF_SIZE - deflate.avail_out); +				break; +			default: +				System.err.println("compress: deflate returnd " + status); +			} +		} while (deflate.avail_out == 0); +		return outputlen; +	} + +	public byte[] uncompress(byte[] buffer, int start, int[] length) { +		int inflated_end = 0; + +		inflate.next_in = buffer; +		inflate.next_in_index = start; +		inflate.avail_in = length[0]; + +		while (true) { +			inflate.next_out = inflate_tmpbuf; +			inflate.next_out_index = 0; +			inflate.avail_out = BUF_SIZE; +			int status = inflate.inflate(JZlib.Z_PARTIAL_FLUSH); +			switch (status) { +			case JZlib.Z_OK: +				if (inflated_buf.length < inflated_end + BUF_SIZE +						- inflate.avail_out) { +					byte[] foo = new byte[inflated_end + BUF_SIZE +							- inflate.avail_out]; +					System.arraycopy(inflated_buf, 0, foo, 0, inflated_end); +					inflated_buf = foo; +				} +				System.arraycopy(inflate_tmpbuf, 0, inflated_buf, inflated_end, +						BUF_SIZE - inflate.avail_out); +				inflated_end += (BUF_SIZE - inflate.avail_out); +				length[0] = inflated_end; +				break; +			case JZlib.Z_BUF_ERROR: +				if (inflated_end > buffer.length - start) { +					byte[] foo = new byte[inflated_end + start]; +					System.arraycopy(buffer, 0, foo, 0, start); +					System.arraycopy(inflated_buf, 0, foo, start, inflated_end); +					buffer = foo; +				} else { +					System.arraycopy(inflated_buf, 0, buffer, start, +							inflated_end); +				} +				length[0] = inflated_end; +				return buffer; +			default: +				System.err.println("uncompress: inflate returnd " + status); +				return null; +			} +		} +	} +} diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java b/lib/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java index c49befa..8fc64e5 100644 --- a/lib/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java +++ b/lib/src/main/java/com/trilead/ssh2/crypto/CryptoWishList.java @@ -1,6 +1,7 @@  package com.trilead.ssh2.crypto;
 +import com.trilead.ssh2.compression.CompressionFactory;
  import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
  import com.trilead.ssh2.crypto.digest.MAC;
  import com.trilead.ssh2.transport.KexManager;
 @@ -20,4 +21,6 @@ public class CryptoWishList  	public String[] s2c_enc_algos = BlockCipherFactory.getDefaultCipherList();
  	public String[] c2s_mac_algos = MAC.getMacList();
  	public String[] s2c_mac_algos = MAC.getMacList();
 +	public String[] c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
 +	public String[] s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
  }
 diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java index 965ef06..7da5067 100644 --- a/lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java +++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java @@ -4,6 +4,7 @@ package com.trilead.ssh2.packets;  import java.io.IOException;
  import java.security.SecureRandom;
 +import com.trilead.ssh2.compression.CompressionFactory;
  import com.trilead.ssh2.crypto.CryptoWishList;
  import com.trilead.ssh2.transport.KexParameters;
 @@ -31,8 +32,8 @@ public class PacketKexInit  		kp.encryption_algorithms_server_to_client = cwl.s2c_enc_algos;
  		kp.mac_algorithms_client_to_server = cwl.c2s_mac_algos;
  		kp.mac_algorithms_server_to_client = cwl.s2c_mac_algos;
 -		kp.compression_algorithms_client_to_server = new String[] { "none" };
 -		kp.compression_algorithms_server_to_client = new String[] { "none" };
 +		kp.compression_algorithms_client_to_server = cwl.c2s_comp_algos;
 +		kp.compression_algorithms_server_to_client = cwl.s2c_comp_algos;
  		kp.languages_client_to_server = new String[] {};
  		kp.languages_server_to_client = new String[] {};
  		kp.first_kex_packet_follows = false;
 diff --git a/lib/src/main/java/com/trilead/ssh2/transport/KexManager.java b/lib/src/main/java/com/trilead/ssh2/transport/KexManager.java index 686e6cd..a2da737 100644 --- a/lib/src/main/java/com/trilead/ssh2/transport/KexManager.java +++ b/lib/src/main/java/com/trilead/ssh2/transport/KexManager.java @@ -7,6 +7,8 @@ import java.security.SecureRandom;  import com.trilead.ssh2.ConnectionInfo;
  import com.trilead.ssh2.DHGexParameters;
  import com.trilead.ssh2.ServerHostKeyVerifier;
 +import com.trilead.ssh2.compression.CompressionFactory;
 +import com.trilead.ssh2.compression.ICompressor;
  import com.trilead.ssh2.crypto.CryptoWishList;
  import com.trilead.ssh2.crypto.KeyMaterial;
  import com.trilead.ssh2.crypto.cipher.BlockCipher;
 @@ -283,6 +285,7 @@ public class KexManager  		BlockCipher cbc;
  		MAC mac;
 +		ICompressor comp;
  		try
  		{
 @@ -290,6 +293,8 @@ public class KexManager  					km.initial_iv_client_to_server);
  			mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
 +			
 +			comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server);
  		}
  		catch (IllegalArgumentException e1)
 @@ -298,6 +303,7 @@ public class KexManager  		}
  		tm.changeSendCipher(cbc, mac);
 +		tm.changeSendCompression(comp);
  		tm.kexFinished();
  	}
 @@ -464,6 +470,7 @@ public class KexManager  			BlockCipher cbc;
  			MAC mac;
 +			ICompressor comp;
  			try
  			{
 @@ -471,7 +478,8 @@ public class KexManager  						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);
 -
 +				
 +				comp = CompressionFactory.createCompressor(kxs.np.comp_algo_server_to_client);
  			}
  			catch (IllegalArgumentException e1)
  			{
 @@ -479,6 +487,7 @@ public class KexManager  			}
  			tm.changeRecvCipher(cbc, mac);
 +			tm.changeRecvCompression(comp);
  			ConnectionInfo sci = new ConnectionInfo();
 diff --git a/lib/src/main/java/com/trilead/ssh2/transport/TransportConnection.java b/lib/src/main/java/com/trilead/ssh2/transport/TransportConnection.java index a193503..2384773 100644 --- a/lib/src/main/java/com/trilead/ssh2/transport/TransportConnection.java +++ b/lib/src/main/java/com/trilead/ssh2/transport/TransportConnection.java @@ -6,6 +6,7 @@ import java.io.InputStream;  import java.io.OutputStream;
  import java.security.SecureRandom;
 +import com.trilead.ssh2.compression.ICompressor;
  import com.trilead.ssh2.crypto.cipher.BlockCipher;
  import com.trilead.ssh2.crypto.cipher.CipherInputStream;
  import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
 @@ -50,6 +51,16 @@ public class TransportConnection  	byte[] recv_mac_buffer_cmp;
  	int recv_padd_blocksize = 8;
 +	
 +	ICompressor recv_comp = null;
 +	
 +	ICompressor send_comp = null;
 +	
 +	boolean can_compress = false;
 +
 +	byte[] recv_comp_buffer;
 +	
 +	byte[] send_comp_buffer;
  	/* won't change */
 @@ -101,7 +112,23 @@ public class TransportConnection  		if (send_padd_blocksize < 8)
  			send_padd_blocksize = 8;
  	}
 +	
 +	public void changeRecvCompression(ICompressor comp)
 +	{
 +		recv_comp = comp;
 +		
 +		if (comp != null)
 +			recv_comp_buffer = new byte[comp.getBufferSize()];
 +	}
 +	public void changeSendCompression(ICompressor comp)
 +	{
 +		send_comp = comp;
 +		
 +		if (comp != null)
 +			send_comp_buffer = new byte[comp.getBufferSize()];
 +	}
 +	
  	public void sendMessage(byte[] message) throws IOException
  	{
  		sendMessage(message, 0, message.length, 0);
 @@ -124,6 +151,12 @@ public class TransportConnection  			padd = 4;
  		else if (padd > 64)
  			padd = 64;
 +		
 +		// TODO add compression somewhere here
 +		if (send_comp != null && can_compress) {
 +			len = send_comp.compress(message, off, len, send_comp_buffer);
 +			message = send_comp_buffer;
 +		}
  		int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
 @@ -279,6 +312,24 @@ public class TransportConnection  					+ " bytes payload");
  		}
 -		return payload_length;
 +		if (recv_comp != null && can_compress) {
 +			int[] uncomp_len = new int[] { payload_length };
 +			buffer = recv_comp.uncompress(buffer, off, uncomp_len);
 +			
 +			if (buffer == null) {
 +				throw new IOException("Error while inflating remote data");
 +			} else {
 +				return uncomp_len[0];
 +			}
 +		} else {
 +			return payload_length;
 +		}
 +	}
 +
 +	/**
 +	 * 
 +	 */
 +	public void startCompression() {
 +		can_compress = true;
  	}
  }
 diff --git a/lib/src/main/java/com/trilead/ssh2/transport/TransportManager.java b/lib/src/main/java/com/trilead/ssh2/transport/TransportManager.java index aeb4fce..c81dbdf 100644 --- a/lib/src/main/java/com/trilead/ssh2/transport/TransportManager.java +++ b/lib/src/main/java/com/trilead/ssh2/transport/TransportManager.java @@ -18,6 +18,7 @@ import com.trilead.ssh2.HTTPProxyData;  import com.trilead.ssh2.HTTPProxyException;
  import com.trilead.ssh2.ProxyData;
  import com.trilead.ssh2.ServerHostKeyVerifier;
 +import com.trilead.ssh2.compression.ICompressor;
  import com.trilead.ssh2.crypto.Base64;
  import com.trilead.ssh2.crypto.CryptoWishList;
  import com.trilead.ssh2.crypto.cipher.BlockCipher;
 @@ -586,6 +587,27 @@ public class TransportManager  		tc.changeSendCipher(bc, mac);
  	}
 +	/**
 +	 * @param comp
 +	 */
 +	public void changeRecvCompression(ICompressor comp) {
 +		tc.changeRecvCompression(comp);
 +	}
 +
 +	/**
 +	 * @param comp
 +	 */
 +	public void changeSendCompression(ICompressor comp) {
 +		tc.changeSendCompression(comp);
 +	}
 +
 +	/**
 +	 * 
 +	 */
 +	public void startCompression() {
 +		tc.startCompression();
 +	}
 +
  	public void sendAsynchronousMessage(byte[] msg) throws IOException
  	{
  		synchronized (asynchronousQueue)
 @@ -755,6 +777,10 @@ public class TransportManager  				continue;
  			}
 +			if (type == Packets.SSH_MSG_USERAUTH_SUCCESS) {
 +				tc.startCompression();
 +			}
 +			
  			MessageHandler mh = null;
  			for (int i = 0; i < messageHandlers.size(); i++)
  | 
