⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssh2packet.cs

📁 Granados是一个基于.NET的SSH客户端库。同时支持SSH1和SSH2。实现了AES, Blowfish, TripleDES, RSA, DSA等加密验证算法。实现TCP协议连接。
💻 CS
字号:
/* ---------------------------------------------------------------------------
 *
 * Copyright (c) Routrek Networks, Inc.    All Rights Reserved..
 * 
 * This file is a part of the Granados SSH Client Library that is subject to
 * the license included in the distributed package.
 * You may not use this file except in compliance with the license.
 * 
 * ---------------------------------------------------------------------------
 */

using System;
using System.Collections;
using System.IO;
using System.Threading;
using System.Diagnostics;

using Routrek.SSHC;

namespace Routrek.SSHCV2
{
	/* SSH2 Packet Structure
	 * 
	 * uint32    packet_length
     * byte      padding_length
     * byte[n1]  payload; n1 = packet_length - padding_length - 1
     * byte[n2]  random padding; n2 = padding_length (max 255)
     * byte[m]   mac (message authentication code); m = mac_length
	 * 
	 * 4+1+n1+n2 must be a multiple of the cipher block size
	 */

	internal class SSH2Packet
	{
		private int _packetLength;
		private byte[] _payload;
		private byte[] _padding;
		private byte[] _mac;

		private const int MAX_PACKET_LENGTH = 0x80000; //there was the case that 64KB is insufficient

		public byte[] Data {
			get {
				return _payload;
			}
		}
		//constracts and appends mac
		public void CalcHash(MAC mac, int sequence) {
			byte[] buf = new byte[4+4+_packetLength];
			SSHUtil.WriteIntToByteArray(buf, 0, sequence);
			WriteTo(buf, 4, false);
			
			_mac = mac.Calc(buf);
		}
		public void WriteTo(AbstractSocket strm, Cipher cipher) {
			int bodylen = 4+_packetLength;
			byte[] buf = new byte[bodylen + (_mac==null? 0 : _mac.Length)];
			WriteTo(buf, 0, false);

			if(cipher!=null)
				cipher.Encrypt(buf, 0, bodylen, buf, 0);
			
			if(_mac!=null)
				Array.Copy(_mac, 0, buf, bodylen, _mac.Length);
			
			strm.Write(buf, 0, buf.Length);
			strm.Flush();
		}
		public void WriteTo(byte[] buf, int offset, bool includes_mac) {
			SSHUtil.WriteIntToByteArray(buf, offset, _packetLength);
			buf[offset+4] = (byte)_padding.Length;
			Array.Copy(_payload, 0, buf, offset+5, _payload.Length);
			Array.Copy(_padding, 0, buf, offset+5+_payload.Length, _padding.Length);
			if(includes_mac && _mac!=null)
				Array.Copy(_mac, 0, buf, offset+5+_payload.Length+_padding.Length, _mac.Length);
		}

		public static SSH2Packet FromPlainPayload(byte[] payload, int blocksize, Random rnd) {
			SSH2Packet p = new SSH2Packet();
			int r = 11 - payload.Length % blocksize;
			while(r < 4) r += blocksize;
			p._padding = new byte[r]; //block size is 8, and padding length is at least 4 bytes
			rnd.NextBytes(p._padding);
			p._payload = payload;
			p._packetLength = 1+payload.Length+p._padding.Length;
			return p;
		}
		//no decryption, no mac
		public static SSH2Packet FromPlainStream(byte[] buffer, int offset) {
			SSH2Packet p = new SSH2Packet();
			p._packetLength = SSHUtil.ReadInt32(buffer, offset);
			if(p._packetLength<=0 || p._packetLength>=MAX_PACKET_LENGTH) throw new SSHException(String.Format("packet size {0} is invalid", p._packetLength));
			offset += 4;

			byte pl = buffer[offset++];
			if(pl < 4) throw new SSHException(String.Format("padding length {0} is invalid", pl));
			p._payload = new byte[p._packetLength - 1 - pl];
			Array.Copy(buffer, offset, p._payload, 0, p._payload.Length);
			return p;
		}

		public static SSH2Packet FromDecryptedHead(byte[] head, byte[] buffer, int offset, Cipher cipher, int sequence, MAC mac) {

			SSH2Packet p = new SSH2Packet();
			p._packetLength = SSHUtil.ReadInt32(head, 0);
			if(p._packetLength<=0 || p._packetLength>=MAX_PACKET_LENGTH) throw new SSHException(String.Format("packet size {0} is invalid", p._packetLength));
			SSH2DataWriter buf = new SSH2DataWriter();
			buf.Write(sequence);
			buf.Write(head);
			if(p._packetLength > (cipher.BlockSize - 4)) {
				byte[] tmp = new byte[p._packetLength-(cipher.BlockSize - 4)];
				cipher.Decrypt(buffer, offset, tmp.Length, tmp, 0);
				offset += tmp.Length;
				buf.Write(tmp);
			}
			byte[] result = buf.ToByteArray();
			int padding_len = (int)result[8];
			if(padding_len<4) throw new SSHException("padding length is invalid");

			byte[] payload = new byte[result.Length-9-padding_len];
			Array.Copy(result, 9, payload, 0, payload.Length);
			p._payload = payload;

			if(mac!=null) {
				p._mac = mac.Calc(result);
				if(SSHUtil.memcmp(p._mac, 0, buffer, offset, mac.Size)!=0)
					throw new SSHException("MAC Error");
			}
			return p;
		}
	}

	internal interface ISSH2PacketHandler : IHandlerBase {
		void OnPacket(SSH2Packet packet);
	}

	internal class SynchronizedSSH2PacketHandler : SynchronizedHandlerBase, ISSH2PacketHandler {
		internal ArrayList _packets;

		internal SynchronizedSSH2PacketHandler() {
			_packets = new ArrayList();
		}

		public void OnPacket(SSH2Packet packet) {
			lock(this) {
				_packets.Add(packet);
				if(_packets.Count > 0)
					SetReady();
			}
		}
		public void OnError(Exception error, string msg) {
			base.SetError(msg);
		}
		public void OnClosed() {
			base.SetClosed();
		}

		public bool HasPacket {
			get {
				return _packets.Count>0;
			}
		}
		public SSH2Packet PopPacket() {
			lock(this) {
				if(_packets.Count==0)
					return null;
				else {
					SSH2Packet p = null;
					p = (SSH2Packet)_packets[0];
					_packets.RemoveAt(0);
					if(_packets.Count==0) _event.Reset();
					return p;
				}
			}
		}
	}
	internal class CallbackSSH2PacketHandler : ISSH2PacketHandler {
		internal SSH2Connection _connection;

		internal CallbackSSH2PacketHandler(SSH2Connection con) {
			_connection = con;
		}
		public void OnPacket(SSH2Packet packet) {
			_connection.AsyncReceivePacket(packet);
		}
		public void OnError(Exception error, string msg) {
			_connection.EventReceiver.OnError(error, msg);
		}
		public void OnClosed() {
			_connection.EventReceiver.OnConnectionClosed();
		}
	}

	internal class SSH2PacketBuilder : IByteArrayHandler {
		private ISSH2PacketHandler _handler;
		private byte[] _buffer;
		private byte[] _head;
		private int _readOffset;
		private int _writeOffset;
		private int _sequence;
		private Cipher _cipher;
		private MAC _mac;
		private ManualResetEvent _event;

		public SSH2PacketBuilder(ISSH2PacketHandler handler) {
			_handler = handler;
			_buffer = new byte[0x1000];
			_readOffset = 0;
			_writeOffset = 0;
			_sequence = 0;
			_cipher = null;
			_mac = null;
			_head = null;
		}
		public void SetSignal(bool value) {
			if(_event==null) _event = new ManualResetEvent(true);

			if(value)
				_event.Set();
			else
				_event.Reset();
		}

		public void SetCipher(Cipher c, MAC m) {
			_cipher = c;
			_mac = m;
		}
		public ISSH2PacketHandler Handler {
			get {
				return _handler;
			}
			set {
				_handler = value;
			}
		}

		public void OnData(byte[] data, int offset, int length) {
			try {
				while(_buffer.Length - _writeOffset < length)
					ExpandBuffer();
				Array.Copy(data, offset, _buffer, _writeOffset, length);
				_writeOffset += length;

				SSH2Packet p = ConstructPacket();
				while(p!=null) {
					_handler.OnPacket(p);
					p = ConstructPacket();
				}
				ReduceBuffer();
			}
			catch(Exception ex) {
				OnError(ex, ex.Message);
			}
		}
		//returns true if a new packet could be obtained
		private SSH2Packet ConstructPacket() {
			SSH2Packet packet = null;
			if(_event!=null && !_event.WaitOne(3000, false))
				throw new Exception("waithandle timed out");

			if(_cipher==null) {
				if(_writeOffset-_readOffset<4) return null;
				int len = SSHUtil.ReadInt32(_buffer, _readOffset);
				if(_writeOffset-_readOffset<4+len) return null;

				packet = SSH2Packet.FromPlainStream(_buffer, _readOffset);
				_readOffset += 4 + len;
				_sequence++;
			}
			else {
				if(_head==null) {
					if(_writeOffset-_readOffset<_cipher.BlockSize) return null;
					_head = new byte[_cipher.BlockSize];
					byte[] eh = new byte[_cipher.BlockSize];
					Array.Copy(_buffer, _readOffset, eh, 0, eh.Length);
					_readOffset += eh.Length;
					_cipher.Decrypt(eh, 0, eh.Length, _head, 0);
				}

				int len = SSHUtil.ReadInt32(_head, 0);
				if(_writeOffset-_readOffset < len+4-_head.Length+_mac.Size) return null;

				packet = SSH2Packet.FromDecryptedHead(_head, _buffer, _readOffset, _cipher, _sequence++, _mac);
				_readOffset += 4 + len - _head.Length + _mac.Size;
				_head = null;
			}

			return packet;
		}

		private void ExpandBuffer() {
			byte[] t = new byte[_buffer.Length*2];
			Array.Copy(_buffer, 0, t, 0, _buffer.Length);
			_buffer = t;
		}
		private void ReduceBuffer() {
			if(_readOffset==_writeOffset) {
				_readOffset = 0;
				_writeOffset = 0;
			}
			else {
				byte[] temp = new byte[_writeOffset - _readOffset];
				Array.Copy(_buffer, _readOffset, temp, 0, temp.Length);
				Array.Copy(temp, 0, _buffer, 0, temp.Length);
				_readOffset = 0;
				_writeOffset = temp.Length;
			}
		}

		public void OnError(Exception error, string msg) {
			_handler.OnError(error, msg);
		}
		public void OnClosed() {
			_handler.OnClosed();
			if(_event!=null) _event.Close();
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -