148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood/*
248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Please refer to the LICENSE.txt for licensing details.
448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */
548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodpackage ch.ethz.ssh2.transport;
748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.IOException;
948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.InputStream;
1048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.io.OutputStream;
1148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.net.InetAddress;
1248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.net.InetSocketAddress;
1348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.net.Socket;
1448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.net.SocketTimeoutException;
1548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.net.UnknownHostException;
1648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.security.SecureRandom;
1748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.util.List;
1848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport java.util.Vector;
1948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
2048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.ConnectionInfo;
2148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.ConnectionMonitor;
2248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.DHGexParameters;
2348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.HTTPProxyData;
2448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.HTTPProxyException;
2548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.ProxyData;
2648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.ServerHostKeyVerifier;
2748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.Base64;
2848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.CryptoWishList;
2948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.cipher.BlockCipher;
3048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.crypto.digest.MAC;
3148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.log.Logger;
3248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.packets.PacketDisconnect;
3348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.packets.Packets;
3448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.packets.TypesReader;
3548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.util.StringEncoder;
3648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodimport ch.ethz.ssh2.util.Tokenizer;
3748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
3848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood/*
3948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * Yes, the "standard" is a big mess. On one side, the say that arbitary channel
4048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * packets are allowed during kex exchange, on the other side we need to blindly
4148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that
4248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the next packet is not a channel data packet? Yes, we could check if it is in
4348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the KEX range. But the standard says nothing about this. The OpenSSH guys
4448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * block local "normal" traffic during KEX. That's fine - however, they assume
4548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * that the other side is doing the same. During re-key, if they receive traffic
4648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * other than KEX, they become horribly irritated and kill the connection. Since
4748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * we are very likely going to communicate with OpenSSH servers, we have to play
4848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * the same game - even though we could do better.
4948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood *
5048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * btw: having stdout and stderr on the same channel, with a shared window, is
5148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * also a VERY good idea... =(
5248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */
5348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
5448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood/**
5548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * TransportManager.
5648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood *
5748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @author Christian Plattner
5848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood * @version $Id: TransportManager.java 41 2011-06-02 10:36:41Z dkocher@sudo.ch $
5948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood */
6048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwoodpublic class TransportManager
6148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood{
6248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private static final Logger log = Logger.getLogger(TransportManager.class);
6348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
6448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private static class HandlerEntry
6548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
6648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		MessageHandler mh;
6748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		int low;
6848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		int high;
6948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
7048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
7148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private final List<byte[]> asynchronousQueue = new Vector<byte[]>();
7248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private Thread asynchronousThread = null;
7348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
7448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	class AsynchronousWorker extends Thread
7548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
7648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		@Override
7748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		public void run()
7848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
7948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			while (true)
8048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
8148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				byte[] msg = null;
8248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
8348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				synchronized (asynchronousQueue)
8448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
8548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					if (asynchronousQueue.size() == 0)
8648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
8748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						/* After the queue is empty for about 2 seconds, stop this thread */
8848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
8948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						try
9048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						{
9148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood							asynchronousQueue.wait(2000);
9248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						}
9348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						catch (InterruptedException ignore)
9448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						{
9548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						}
9648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
9748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						if (asynchronousQueue.size() == 0)
9848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						{
9948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood							asynchronousThread = null;
10048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood							return;
10148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						}
10248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
10348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
10448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					msg = asynchronousQueue.remove(0);
10548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
10648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
10748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				/* The following invocation may throw an IOException.
10848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * There is no point in handling it - it simply means
10948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * that the connection has a problem and we should stop
11048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * sending asynchronously messages. We do not need to signal that
11148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * we have exited (asynchronousThread = null): further
11248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * messages in the queue cannot be sent by this or any
11348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * other thread.
11448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * Other threads will sooner or later (when receiving or
11548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * sending the next message) get the same IOException and
11648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * get to the same conclusion.
11748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 */
11848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
11948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				try
12048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
12148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					sendMessage(msg);
12248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
12348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				catch (IOException e)
12448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
12548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					return;
12648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
12748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
12848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
12948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
13048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
13148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private String hostname;
13248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private int port;
13348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private final Socket sock = new Socket();
13448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
13548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private final Object connectionSemaphore = new Object();
13648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
13748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private boolean flagKexOngoing = false;
13848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private boolean connectionClosed = false;
13948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
14048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private Throwable reasonClosedCause = null;
14148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
14248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private TransportConnection tc;
14348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private KexManager km;
14448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
14548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private final List<HandlerEntry> messageHandlers = new Vector<HandlerEntry>();
14648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
14748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private Thread receiveThread;
14848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
14948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private List<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
15048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private boolean monitorsWereInformed = false;
15148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
15248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	/**
15348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 * There were reports that there are JDKs which use
15448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 * the resolver even though one supplies a dotted IP
15548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 * address in the Socket constructor. That is why we
15648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 * try to generate the InetAdress "by hand".
15748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 *
15848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 * @param host
15948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 * @return the InetAddress
16048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 * @throws UnknownHostException
16148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 */
16248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private InetAddress createInetAddress(String host) throws UnknownHostException
16348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
16448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		/* Check if it is a dotted IP4 address */
16548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
16648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		InetAddress addr = parseIPv4Address(host);
16748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
16848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		if (addr != null)
16948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
17048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			return addr;
17148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
17248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
17348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		return InetAddress.getByName(host);
17448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
17548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
17648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private InetAddress parseIPv4Address(String host) throws UnknownHostException
17748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
17848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		if (host == null)
17948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
18048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			return null;
18148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
18248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
18348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		String[] quad = Tokenizer.parseTokens(host, '.');
18448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
18548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		if ((quad == null) || (quad.length != 4))
18648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
18748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			return null;
18848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
18948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
19048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		byte[] addr = new byte[4];
19148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
19248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		for (int i = 0; i < 4; i++)
19348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
19448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			int part = 0;
19548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
19648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if ((quad[i].length() == 0) || (quad[i].length() > 3))
19748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
19848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				return null;
19948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
20048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
20148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			for (int k = 0; k < quad[i].length(); k++)
20248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
20348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				char c = quad[i].charAt(k);
20448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
20548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				/* No, Character.isDigit is not the same */
20648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if ((c < '0') || (c > '9'))
20748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
20848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					return null;
20948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
21048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
21148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				part = part * 10 + (c - '0');
21248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
21348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
21448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (part > 255) /* 300.1.2.3 is invalid =) */
21548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
21648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				return null;
21748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
21848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
21948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			addr[i] = (byte) part;
22048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
22148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
22248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		return InetAddress.getByAddress(host, addr);
22348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
22448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
22548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public TransportManager(String host, int port) throws IOException
22648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
22748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		this.hostname = host;
22848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		this.port = port;
22948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
23048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
23148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public int getPacketOverheadEstimate()
23248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
23348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		return tc.getPacketOverheadEstimate();
23448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
23548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
23648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void setTcpNoDelay(boolean state) throws IOException
23748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
23848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		sock.setTcpNoDelay(state);
23948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
24048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
24148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void setSoTimeout(int timeout) throws IOException
24248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
24348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		sock.setSoTimeout(timeout);
24448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
24548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
24648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException
24748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
24848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		return km.getOrWaitForConnectionInfo(kexNumber);
24948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
25048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
25148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public Throwable getReasonClosedCause()
25248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
25348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (connectionSemaphore)
25448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
25548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			return reasonClosedCause;
25648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
25748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
25848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
25948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public byte[] getSessionIdentifier()
26048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
26148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		return km.sessionId;
26248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
26348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
26448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void close(Throwable cause, boolean useDisconnectPacket)
26548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
26648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		if (useDisconnectPacket == false)
26748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
26848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* OK, hard shutdown - do not aquire the semaphore,
26948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * perhaps somebody is inside (and waits until the remote
27048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * side is ready to accept new data). */
27148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
27248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			try
27348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
27448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				sock.close();
27548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
27648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			catch (IOException ignore)
27748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
27848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
27948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
28048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* OK, whoever tried to send data, should now agree that
28148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * there is no point in further waiting =)
28248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * It is safe now to aquire the semaphore.
28348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 */
28448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
28548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
28648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (connectionSemaphore)
28748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
28848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (connectionClosed == false)
28948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
29048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if (useDisconnectPacket == true)
29148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
29248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					try
29348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
29448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						byte[] msg = new PacketDisconnect(Packets.SSH_DISCONNECT_BY_APPLICATION, cause.getMessage(), "")
29548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood								.getPayload();
29648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						if (tc != null)
29748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						{
29848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood							tc.sendMessage(msg);
29948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						}
30048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
30148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					catch (IOException ignore)
30248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
30348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
30448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
30548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					try
30648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
30748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						sock.close();
30848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
30948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					catch (IOException ignore)
31048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
31148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
31248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
31348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
31448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				connectionClosed = true;
31548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				reasonClosedCause = cause; /* may be null */
31648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
31748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			connectionSemaphore.notifyAll();
31848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
31948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
32048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		/* No check if we need to inform the monitors */
32148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
32248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		List<ConnectionMonitor> monitors = new Vector<ConnectionMonitor>();
32348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
32448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (this)
32548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
32648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* Short term lock to protect "connectionMonitors"
32748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * and "monitorsWereInformed"
32848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * (they may be modified concurrently)
32948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 */
33048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
33148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (monitorsWereInformed == false)
33248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
33348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				monitorsWereInformed = true;
33448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				monitors.addAll(connectionMonitors);
33548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
33648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
33748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
33848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		for (ConnectionMonitor cmon : monitors)
33948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
34048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			try
34148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
34248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				cmon.connectionLost(reasonClosedCause);
34348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
34448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			catch (Exception ignore)
34548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
34648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
34748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
34848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
34948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
35048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private void establishConnection(ProxyData proxyData, int connectTimeout) throws IOException
35148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
35248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		/* See the comment for createInetAddress() */
35348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
35448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		if (proxyData == null)
35548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
35648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			InetAddress addr = createInetAddress(hostname);
35748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			sock.connect(new InetSocketAddress(addr, port), connectTimeout);
35848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			return;
35948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
36048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
36148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		if (proxyData instanceof HTTPProxyData)
36248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
36348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			HTTPProxyData pd = (HTTPProxyData) proxyData;
36448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
36548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* At the moment, we only support HTTP proxies */
36648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
36748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			InetAddress addr = createInetAddress(pd.proxyHost);
36848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			sock.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout);
36948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
37048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* OK, now tell the proxy where we actually want to connect to */
37148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
37248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			StringBuilder sb = new StringBuilder();
37348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
37448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			sb.append("CONNECT ");
37548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			sb.append(hostname);
37648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			sb.append(':');
37748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			sb.append(port);
37848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			sb.append(" HTTP/1.0\r\n");
37948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
38048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if ((pd.proxyUser != null) && (pd.proxyPass != null))
38148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
38248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				String credentials = pd.proxyUser + ":" + pd.proxyPass;
38348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				char[] encoded = Base64.encode(StringEncoder.GetBytes(credentials));
38448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				sb.append("Proxy-Authorization: Basic ");
38548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				sb.append(encoded);
38648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				sb.append("\r\n");
38748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
38848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
38948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (pd.requestHeaderLines != null)
39048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
39148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				for (int i = 0; i < pd.requestHeaderLines.length; i++)
39248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
39348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					if (pd.requestHeaderLines[i] != null)
39448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
39548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						sb.append(pd.requestHeaderLines[i]);
39648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						sb.append("\r\n");
39748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
39848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
39948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
40048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
40148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			sb.append("\r\n");
40248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
40348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			OutputStream out = sock.getOutputStream();
40448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
40548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			out.write(StringEncoder.GetBytes(sb.toString()));
40648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			out.flush();
40748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
40848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* Now parse the HTTP response */
40948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
41048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			byte[] buffer = new byte[1024];
41148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			InputStream in = sock.getInputStream();
41248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
41348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			int len = ClientServerHello.readLineRN(in, buffer);
41448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
41548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			String httpReponse = StringEncoder.GetString(buffer, 0, len);
41648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
41748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (httpReponse.startsWith("HTTP/") == false)
41848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
41948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new IOException("The proxy did not send back a valid HTTP response.");
42048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
42148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
42248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* "HTTP/1.X XYZ X" => 14 characters minimum */
42348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
42448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' '))
42548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
42648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new IOException("The proxy did not send back a valid HTTP response.");
42748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
42848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
42948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			int errorCode = 0;
43048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
43148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			try
43248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
43348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				errorCode = Integer.parseInt(httpReponse.substring(9, 12));
43448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
43548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			catch (NumberFormatException ignore)
43648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
43748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new IOException("The proxy did not send back a valid HTTP response.");
43848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
43948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
44048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if ((errorCode < 0) || (errorCode > 999))
44148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
44248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new IOException("The proxy did not send back a valid HTTP response.");
44348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
44448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
44548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (errorCode != 200)
44648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
44748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new HTTPProxyException(httpReponse.substring(13), errorCode);
44848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
44948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
45048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* OK, read until empty line */
45148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
45248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			while (true)
45348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
45448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				len = ClientServerHello.readLineRN(in, buffer);
45548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if (len == 0)
45648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
45748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					break;
45848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
45948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
46048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			return;
46148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
46248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
46348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		throw new IOException("Unsupported ProxyData");
46448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
46548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
46648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void initialize(String identification, CryptoWishList cwl, ServerHostKeyVerifier verifier,
46748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						   DHGexParameters dhgex, int connectTimeout, SecureRandom rnd, ProxyData proxyData)
46848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			throws IOException
46948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
47048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		/* First, establish the TCP connection to the SSH-2 server */
47148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
47248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		establishConnection(proxyData, connectTimeout);
47348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
47448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		/* Parse the server line and say hello - important: this information is later needed for the
47548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		 * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
47648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		 * for later use.
47748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		 */
47848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
47948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		ClientServerHello csh = new ClientServerHello(identification, sock.getInputStream(), sock.getOutputStream());
48048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
48148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
48248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
48348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		km = new KexManager(this, csh, cwl, hostname, port, verifier, rnd);
48448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		km.initiateKEX(cwl, dhgex);
48548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
48648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		receiveThread = new Thread(new Runnable()
48748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
48848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			public void run()
48948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
49048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				try
49148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
49248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					receiveLoop();
49348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
49448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				catch (IOException e)
49548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
49648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					close(e, false);
49748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
49848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					log.warning("Receive thread: error in receiveLoop: " + e.getMessage());
49948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
50048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
50148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if (log.isDebugEnabled())
50248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
50348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					log.debug("Receive thread: back from receiveLoop");
50448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
50548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
50648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				/* Tell all handlers that it is time to say goodbye */
50748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
50848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if (km != null)
50948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
51048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					try
51148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
51248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						km.handleMessage(null, 0);
51348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
51448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					catch (IOException ignored)
51548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
51648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
51748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
51848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
51948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				for (HandlerEntry he : messageHandlers)
52048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
52148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					try
52248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
52348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						he.mh.handleMessage(null, 0);
52448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
52548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					catch (Exception ignore)
52648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
52748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
52848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
52948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
53048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		});
53148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
53248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		receiveThread.setDaemon(true);
53348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		receiveThread.start();
53448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
53548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
53648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void registerMessageHandler(MessageHandler mh, int low, int high)
53748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
53848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		HandlerEntry he = new HandlerEntry();
53948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		he.mh = mh;
54048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		he.low = low;
54148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		he.high = high;
54248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
54348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (messageHandlers)
54448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
54548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			messageHandlers.add(he);
54648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
54748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
54848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
54948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void removeMessageHandler(MessageHandler mh, int low, int high)
55048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
55148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (messageHandlers)
55248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
55348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			for (int i = 0; i < messageHandlers.size(); i++)
55448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
55548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				HandlerEntry he = messageHandlers.get(i);
55648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if ((he.mh == mh) && (he.low == low) && (he.high == high))
55748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
55848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					messageHandlers.remove(i);
55948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					break;
56048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
56148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
56248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
56348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
56448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
56548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void sendKexMessage(byte[] msg) throws IOException
56648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
56748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (connectionSemaphore)
56848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
56948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (connectionClosed)
57048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
57148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
57248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
57348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
57448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			flagKexOngoing = true;
57548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
57648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			try
57748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
57848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				tc.sendMessage(msg);
57948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
58048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			catch (IOException e)
58148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
58248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				close(e, false);
58348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw e;
58448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
58548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
58648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
58748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
58848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void kexFinished() throws IOException
58948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
59048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (connectionSemaphore)
59148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
59248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			flagKexOngoing = false;
59348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			connectionSemaphore.notifyAll();
59448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
59548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
59648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
59748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
59848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
59948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		km.initiateKEX(cwl, dhgex);
60048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
60148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
60248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void changeRecvCipher(BlockCipher bc, MAC mac)
60348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
60448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		tc.changeRecvCipher(bc, mac);
60548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
60648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
60748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void changeSendCipher(BlockCipher bc, MAC mac)
60848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
60948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		tc.changeSendCipher(bc, mac);
61048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
61148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
61248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void sendAsynchronousMessage(byte[] msg) throws IOException
61348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
61448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (asynchronousQueue)
61548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
61648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			asynchronousQueue.add(msg);
61748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
61848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* This limit should be flexible enough. We need this, otherwise the peer
61948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * can flood us with global requests (and other stuff where we have to reply
62048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * with an asynchronous message) and (if the server just sends data and does not
62148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * read what we send) this will probably put us in a low memory situation
62248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * (our send queue would grow and grow and...) */
62348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
62448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (asynchronousQueue.size() > 100)
62548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
62648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new IOException("Error: the peer is not consuming our asynchronous replies.");
62748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
62848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
62948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/* Check if we have an asynchronous sending thread */
63048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
63148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (asynchronousThread == null)
63248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
63348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				asynchronousThread = new AsynchronousWorker();
63448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				asynchronousThread.setDaemon(true);
63548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				asynchronousThread.start();
63648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
63748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				/* The thread will stop after 2 seconds of inactivity (i.e., empty queue) */
63848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
63948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
64048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
64148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
64248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void setConnectionMonitors(List<ConnectionMonitor> monitors)
64348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
64448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		synchronized (this)
64548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
64648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			connectionMonitors = new Vector<ConnectionMonitor>();
64748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			connectionMonitors.addAll(monitors);
64848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
64948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
65048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
65148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	/**
65248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 * True if no response message expected.
65348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	 */
65448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	private boolean idle;
65548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
65648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void sendMessage(byte[] msg) throws IOException
65748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
65848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		if (Thread.currentThread() == receiveThread)
65948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
66048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			throw new IOException("Assertion error: sendMessage may never be invoked by the receiver thread!");
66148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
66248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
66348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		boolean wasInterrupted = false;
66448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
66548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		try
66648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
66748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			synchronized (connectionSemaphore)
66848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
66948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				while (true)
67048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
67148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					if (connectionClosed)
67248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
67348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						throw (IOException) new IOException("Sorry, this connection is closed.")
67448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood								.initCause(reasonClosedCause);
67548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
67648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
67748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					if (flagKexOngoing == false)
67848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
67948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						break;
68048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
68148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
68248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					try
68348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
68448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						connectionSemaphore.wait();
68548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
68648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					catch (InterruptedException e)
68748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
68848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						wasInterrupted = true;
68948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
69048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
69148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
69248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				try
69348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
69448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					tc.sendMessage(msg);
69548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					idle = false;
69648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
69748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				catch (IOException e)
69848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
69948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					close(e, false);
70048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					throw e;
70148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
70248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
70348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
70448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		finally
70548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
70648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (wasInterrupted)
70748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				Thread.currentThread().interrupt();
70848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
70948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
71048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
71148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	public void receiveLoop() throws IOException
71248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	{
71348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		byte[] msg = new byte[35000];
71448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
71548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		while (true)
71648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		{
71748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			int msglen;
71848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			try
71948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
72048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				msglen = tc.receiveMessage(msg, 0, msg.length);
72148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
72248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			catch (SocketTimeoutException e)
72348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
72448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				// Timeout in read
72548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if (idle)
72648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
72748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					log.debug("Ignoring socket timeout");
72848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					continue;
72948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
73048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw e;
73148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
73248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			idle = true;
73348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
73448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			int type = msg[0] & 0xff;
73548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
73648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (type == Packets.SSH_MSG_IGNORE)
73748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
73848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				continue;
73948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
74048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
74148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (type == Packets.SSH_MSG_DEBUG)
74248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
74348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if (log.isDebugEnabled())
74448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
74548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					TypesReader tr = new TypesReader(msg, 0, msglen);
74648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					tr.readByte();
74748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					tr.readBoolean();
74848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					StringBuilder debugMessageBuffer = new StringBuilder();
74948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					debugMessageBuffer.append(tr.readString("UTF-8"));
75048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
75148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					for (int i = 0; i < debugMessageBuffer.length(); i++)
75248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
75348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						char c = debugMessageBuffer.charAt(i);
75448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
75548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						if ((c >= 32) && (c <= 126))
75648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						{
75748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood							continue;
75848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						}
75948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						debugMessageBuffer.setCharAt(i, '\uFFFD');
76048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
76148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
76248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					log.debug("DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'");
76348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
76448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				continue;
76548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
76648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
76748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (type == Packets.SSH_MSG_UNIMPLEMENTED)
76848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
76948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen.");
77048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
77148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
77248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (type == Packets.SSH_MSG_DISCONNECT)
77348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
77448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				TypesReader tr = new TypesReader(msg, 0, msglen);
77548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				tr.readByte();
77648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				int reason_code = tr.readUINT32();
77748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				StringBuilder reasonBuffer = new StringBuilder();
77848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				reasonBuffer.append(tr.readString("UTF-8"));
77948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
78048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				/*
78148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * Do not get fooled by servers that send abnormal long error
78248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * messages
78348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 */
78448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
78548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if (reasonBuffer.length() > 255)
78648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
78748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					reasonBuffer.setLength(255);
78848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					reasonBuffer.setCharAt(254, '.');
78948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					reasonBuffer.setCharAt(253, '.');
79048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					reasonBuffer.setCharAt(252, '.');
79148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
79248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
79348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				/*
79448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * Also, check that the server did not send characters that may
79548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * screw up the receiver -> restrict to reasonable US-ASCII
79648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * subset -> "printable characters" (ASCII 32 - 126). Replace
79748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 * all others with 0xFFFD (UNICODE replacement character).
79848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				 */
79948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
80048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				for (int i = 0; i < reasonBuffer.length(); i++)
80148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
80248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					char c = reasonBuffer.charAt(i);
80348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
80448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					if ((c >= 32) && (c <= 126))
80548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					{
80648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						continue;
80748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					}
80848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					reasonBuffer.setCharAt(i, '\uFFFD');
80948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
81048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
81148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new IOException("Peer sent DISCONNECT message (reason code " + reason_code + "): "
81248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood						+ reasonBuffer.toString());
81348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
81448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
81548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			/*
81648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 * Is it a KEX Packet?
81748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			 */
81848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
81948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if ((type == Packets.SSH_MSG_KEXINIT) || (type == Packets.SSH_MSG_NEWKEYS)
82048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					|| ((type >= 30) && (type <= 49)))
82148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
82248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				km.handleMessage(msg, msglen);
82348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				continue;
82448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
82548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
82648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			MessageHandler mh = null;
82748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
82848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			for (int i = 0; i < messageHandlers.size(); i++)
82948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
83048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				HandlerEntry he = messageHandlers.get(i);
83148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				if ((he.low <= type) && (type <= he.high))
83248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				{
83348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					mh = he.mh;
83448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood					break;
83548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				}
83648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
83748ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
83848ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			if (mh == null)
83948ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			{
84048ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood				throw new IOException("Unexpected SSH message (type " + type + ")");
84148ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			}
84248ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood
84348ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood			mh.handleMessage(msg, msglen);
84448ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood		}
84548ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood	}
84648ded2421114c4c87ef3f8005c9f793a5d077cbdMike Lockwood}
847