public Buffer read(Buffer buf) 
		{
			int j=0;
			while(true)
			{
				buf.reset();
				io.getByte(buf.buffer, buf.index, cipher_size); buf.index+=cipher_size;
				if(s2ccipher!=null)
				{
					s2ccipher.update(buf.buffer, 0, cipher_size, buf.buffer, 0);
				}
//				j=((buf.buffer[0]<<24)&0xff000000)|
//					((buf.buffer[1]<<16)&0x00ff0000)|
//					((buf.buffer[2]<< 8)&0x0000ff00)|
//					((buf.buffer[3]    )&0x000000ff);
				j=Util.ToInt32( buf.buffer, 0 );
				j=j-4-cipher_size+8;
				if(j<0 || (buf.index+j)>buf.buffer.Length)
				{
					throw new IOException("invalid data");
				}
				if(j>0)
				{
					io.getByte(buf.buffer, buf.index, j); buf.index+=(j);
					if(s2ccipher!=null)
					{
						s2ccipher.update(buf.buffer, cipher_size, j, buf.buffer, cipher_size);
					}
				}

				if(s2cmac!=null)
				{
					s2cmac.update(seqi);
					s2cmac.update(buf.buffer, 0, buf.index);
					byte[] result=s2cmac.doFinal();
					io.getByte(mac_buf, 0, mac_buf.Length);
					if(!Arrays.equals(result, mac_buf))
					{
						throw new IOException("MAC Error");
					}
				}
				seqi++;

				if(inflater!=null)
				{
					//inflater.uncompress(buf);
					int pad=buf.buffer[4];
					uncompress_len[0]=buf.index-5-pad;
					byte[] foo=inflater.uncompress(buf.buffer, 5, uncompress_len);
					if(foo!=null)
					{
						buf.buffer=foo;
						buf.index=5+uncompress_len[0];
					}
					else
					{
						System.Console.Error.WriteLine("fail in inflater");
						break;
					}
				}

				int type=buf.buffer[5]&0xff;
				//System.Console.WriteLine("read: "+type);
				if(type==SSH_MSG_DISCONNECT)
				{
					buf.rewind();
					buf.getInt();buf.getShort();
					int reason_code=buf.getInt();
					byte[] description=buf.getString();
					byte[] language_tag=buf.getString();
					/*
						System.Console.Error.WriteLine("SSH_MSG_DISCONNECT:"+
											   " "+reason_code+
								   " "+new String(description)+
								   " "+new String(language_tag));
					*/
					throw new JSchException("SSH_MSG_DISCONNECT:"+
						" "+reason_code+
						" "+new String(description)+
						" "+new String(language_tag));
					//break;
				}
				else if(type==SSH_MSG_IGNORE)
				{
				}
				else if(type==SSH_MSG_DEBUG)
				{
					buf.rewind();
					buf.getInt();buf.getShort();
					/*
						byte always_display=(byte)buf.getByte();
						byte[] message=buf.getString();
						byte[] language_tag=buf.getString();
						System.Console.Error.WriteLine("SSH_MSG_DEBUG:"+
								   " "+new String(message)+
								   " "+new String(language_tag));
					*/
				}
				else if(type==SSH_MSG_CHANNEL_WINDOW_ADJUST)
				{
					buf.rewind();
					buf.getInt();buf.getShort();
					Channel c=Channel.getChannel(buf.getInt(), this);
					if(c==null)
					{
					}
					else
					{
						c.addRemoteWindowSize(buf.getInt());
					}
				}
				else
				{
					break;
				}
			}
			buf.rewind();
			return buf;
		}
		public void run()
		{
			thread=this;

			byte[] foo;
			Buffer buf=new Buffer();
			Packet packet=new Packet(buf);
			int i=0;
			Channel channel;
			int[] start=new int[1];
			int[] length=new int[1];
			KeyExchange kex=null;

			try
			{
				while(_isConnected &&
					thread!=null)
				{
					buf=read(buf);
					int msgType=buf.buffer[5]&0xff;
					//      if(msgType!=94)
					//System.Console.WriteLine("read: 94 ? "+msgType);

					if(kex!=null && kex.getState()==msgType)
					{
						bool result=kex.next(buf);
						if(!result)
						{
							throw new JSchException("verify: "+result);
						}
						continue;
					}

					switch(msgType)
					{
						case SSH_MSG_KEXINIT:
							//System.Console.WriteLine("KEXINIT");
							kex=receive_kexinit(buf);
							break;

						case SSH_MSG_NEWKEYS:
							//System.Console.WriteLine("NEWKEYS");
							send_newkeys();
							receive_newkeys(buf, kex);
							kex=null;
							break;

						case SSH_MSG_CHANNEL_DATA:
							buf.getInt();
							buf.getByte();
							buf.getByte();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							foo=buf.getString(start, length);
							if(channel==null)
							{
								break;
							}
							try
							{
								channel.write(foo, start[0], length[0]);
							}
							catch(Exception e)
							{
								//System.Console.WriteLine(e);
								try{channel.disconnect();}
								catch(Exception ee){}
								break;
							}
							int len=length[0];
							channel.setLocalWindowSize(channel.lwsize-len);
							if(channel.lwsize<channel.lwsize_max/2)
							{
								packet.reset();
								buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST);
								buf.putInt(channel.getRecipient());
								buf.putInt(channel.lwsize_max-channel.lwsize);
								write(packet);
								channel.setLocalWindowSize(channel.lwsize_max);
							}
							break;

						case SSH_MSG_CHANNEL_EXTENDED_DATA:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							buf.getInt();                   // data_type_code == 1
							foo=buf.getString(start, length);
							//System.Console.WriteLine("stderr: "+new String(foo,start[0],length[0]));
							if(channel==null)
							{
								break;
							}
							//channel.write(foo, start[0], length[0]);
							channel.write_ext(foo, start[0], length[0]);

							len=length[0];
							channel.setLocalWindowSize(channel.lwsize-len);
							if(channel.lwsize<channel.lwsize_max/2)
							{
								packet.reset();
								buf.putByte((byte)SSH_MSG_CHANNEL_WINDOW_ADJUST);
								buf.putInt(channel.getRecipient());
								buf.putInt(channel.lwsize_max-channel.lwsize);
								write(packet);
								channel.setLocalWindowSize(channel.lwsize_max);
							}
							break;

						case SSH_MSG_CHANNEL_WINDOW_ADJUST:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							if(channel==null)
							{
								break;
							}
							channel.addRemoteWindowSize(buf.getInt());
							break;

						case SSH_MSG_CHANNEL_EOF:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							if(channel!=null)
							{
								//channel._eof_remote=true;
								//channel.eof();
								channel.eof_remote();
							}
							/*
							packet.reset();
							buf.putByte((byte)SSH_MSG_CHANNEL_EOF);
							buf.putInt(channel.getRecipient());
							write(packet);
							*/
							break;
						case SSH_MSG_CHANNEL_CLOSE:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							if(channel!=null)
							{
								//	      channel.close();
								channel.disconnect();
							}
							/*
								if(Channel.pool.size()==0){
							  thread=null;
							}
							*/
							break;
						case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							if(channel==null)
							{
								//break;
							}
							channel.setRecipient(buf.getInt());
							channel.setRemoteWindowSize(buf.getInt());
							channel.setRemotePacketSize(buf.getInt());
							break;
						case SSH_MSG_CHANNEL_OPEN_FAILURE:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							if(channel==null)
							{
								//break;
							}
							int reason_code=buf.getInt();
							//foo=buf.getString();  // additional textual information
							//foo=buf.getString();  // language tag
							channel.exitstatus=reason_code;
							channel._close=true;
							channel._eof_remote=true;
							channel.setRecipient(0);
							break;
						case SSH_MSG_CHANNEL_REQUEST:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							foo=buf.getString();
							bool reply=(buf.getByte()!=0);
							channel=Channel.getChannel(i, this);
							if(channel!=null)
							{
								byte reply_type=(byte)SSH_MSG_CHANNEL_FAILURE;
								if((new String(foo)).equals("exit-status"))
								{
									i=buf.getInt();             // exit-status
									channel.setExitStatus(i);
									//	    System.Console.WriteLine("exit-stauts: "+i);
									//          channel.close();
									reply_type=(byte)SSH_MSG_CHANNEL_SUCCESS;
								}
								if(reply)
								{
									packet.reset();
									buf.putByte(reply_type);
									buf.putInt(channel.getRecipient());
									write(packet);
								}
							}
							else
							{
							}
							break;
						case SSH_MSG_CHANNEL_OPEN:
							buf.getInt();
							buf.getShort();
							foo=buf.getString();
							String ctyp=new String(foo);
							//System.Console.WriteLine("type="+ctyp);
							if(!new String("forwarded-tcpip").equals(ctyp) &&
								!(new String("x11").equals(ctyp) && x11_forwarding))
							{
								System.Console.WriteLine("Session.run: CHANNEL OPEN "+ctyp);
								throw new IOException("Session.run: CHANNEL OPEN "+ctyp);
							}
							else
							{
								channel=Channel.getChannel(ctyp);
								addChannel(channel);
								channel.getData(buf);
								channel.init();

								packet.reset();
								buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
								buf.putInt(channel.getRecipient());
								buf.putInt(channel.id);
								buf.putInt(channel.lwsize);
								buf.putInt(channel.lmpsize);
								write(packet);
								Thread tmp=new Thread(channel);
								tmp.setName("Channel "+ctyp+" "+host);
								tmp.start();
								break;
							}
						case SSH_MSG_CHANNEL_SUCCESS:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							if(channel==null)
							{
								break;
							}
							channel.reply=1;
							break;
						case SSH_MSG_CHANNEL_FAILURE:
							buf.getInt();
							buf.getShort();
							i=buf.getInt();
							channel=Channel.getChannel(i, this);
							if(channel==null)
							{
								break;
							}
							channel.reply=0;
							break;
						case SSH_MSG_GLOBAL_REQUEST:
							buf.getInt();
							buf.getShort();
							foo=buf.getString();       // request name
							reply=(buf.getByte()!=0);
							if(reply)
							{
								packet.reset();
								buf.putByte((byte)SSH_MSG_REQUEST_FAILURE);
								write(packet);
							}
							break;
						case SSH_MSG_REQUEST_FAILURE:
						case SSH_MSG_REQUEST_SUCCESS:
							Thread t=grr.getThread();
							if(t!=null)
							{
								grr.setReply(msgType==SSH_MSG_REQUEST_SUCCESS? 1 : 0);
								t.interrupt();
							}
							break;
						default:
							System.Console.WriteLine("Session.run: unsupported type "+msgType);
							throw new IOException("Unknown SSH message type "+msgType);
					}
				}
			}
			catch(Exception e)
			{
				//System.Console.WriteLine("# Session.run");
				//e.printStackTrace();
			}
			try
			{
				disconnect();
			}
			catch(NullReferenceException e)
			{
				//System.Console.WriteLine("@1");
				//e.printStackTrace();
			}
			catch(Exception e)
			{
				//System.Console.WriteLine("@2");
				//e.printStackTrace();
			}
			_isConnected=false;
		}