sources/Google.Solutions.Ssh/Format/SshReader.cs (114 lines of code) (raw):

// // Copyright 2022 Google LLC // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // using Google.Solutions.Common.Format; using Google.Solutions.Common.Util; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; namespace Google.Solutions.Ssh.Format { /// <summary> /// Reader for SSH-structured data, see RFC4251 section 5. /// </summary> internal class SshReader : IDisposable { private readonly Stream stream; public SshReader(Stream stream) { Debug.Assert(stream.CanRead); this.stream = stream.ExpectNotNull(nameof(stream)); } //--------------------------------------------------------------------- // Publics. //--------------------------------------------------------------------- public byte ReadByte() { // // A byte represents an arbitrary 8 - bit value(octet).Fixed length // data is sometimes represented as an array of bytes, written // byte[n], where n is the number of bytes in the array. // var buffer = new byte[1]; if (this.stream.Read(buffer, 0, buffer.Length) == buffer.Length) { return buffer[0]; } else { throw new EndOfStreamException(); } } public bool ReadBoolean() { // // A boolean value is stored as a single byte.The value 0 // represents FALSE, and the value 1 represents TRUE. All non-zero // values MUST be interpreted as TRUE; however, applications MUST NOT // store values other than 0 and 1. // return ReadByte() == 1; } public uint ReadUint32() { // // Represents a 32 - bit unsigned integer. Stored as four bytes in the // order of decreasing significance (network byte order).For // example: the value 699921578 (0x29b7f4aa) is stored as 29 b7 f4 // aa. // var buffer = new byte[4]; if (this.stream.Read(buffer, 0, buffer.Length) == buffer.Length) { return BigEndian.DecodeUInt32(buffer, 0); } else { throw new EndOfStreamException(); } } public ulong ReadUint64() { // // Represents a 64 - bit unsigned integer. Stored as eight bytes in // the order of decreasing significance (network byte order). // var buffer = new byte[8]; if (this.stream.Read(buffer, 0, buffer.Length) == buffer.Length) { return BigEndian.DecodeUInt64(buffer, 0); } else { throw new EndOfStreamException(); } } public byte[] ReadByteString() { var length = ReadUint32(); var buffer = new byte[length]; if (this.stream.Read(buffer, 0, buffer.Length) == buffer.Length) { return buffer; } else { throw new EndOfStreamException(); } } public string ReadString() { // // Arbitrary length binary string.Strings are allowed to contain // arbitrary binary data, including null characters and 8 - bit // characters.They are stored as a uint32 containing its length // (number of bytes that follow) and zero (= empty string) or more // bytes that are the value of the string.Terminating null // characters are not used. // // Strings are also used to store text.In that case, US-ASCII is // used for internal names, and ISO-10646 UTF-8 for text that might // be displayed to the user.The terminating null character SHOULD // // NOT normally be stored in the string. For example: the US-ASCII // // string "testing" is represented as 00 00 00 07 t e s t i n g. The // UTF-8 mapping does not alter the encoding of US-ASCII characters. // return Encoding.ASCII.GetString(ReadByteString()); } public IEnumerable<byte> ReadMultiPrecisionInteger() { // // Represents multiple precision integers in two's complement format, // stored as a string, 8 bits per byte, MSB first. Negative numbers // have the value 1 as the most significant bit of the first byte of // the data partition. If the most significant bit would be set for // a positive number, the number MUST be preceded by a zero byte. // Unnecessary leading bytes with the value 0 or 255 MUST NOT be // included. The value zero MUST be stored as a string with zero // bytes of data. // var length = ReadUint32(); if (length == 0) { return new byte[] { 0 }; } else { var buffer = new byte[length]; if (this.stream.Read(buffer, 0, buffer.Length) == buffer.Length) { if (buffer[0] == 0) { return buffer.Skip(1); } else { return buffer; } } else { throw new EndOfStreamException(); } } } //--------------------------------------------------------------------- // IDisposable. //--------------------------------------------------------------------- protected virtual void Dispose(bool disposing) { this.stream.Dispose(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } }