/// <summary> /// Deserializes a handshake instance from the specified byte array. /// </summary> /// <param name="data">An array of bytes containing handshake data.</param> /// <returns>An initialized instance of the Handshake class deserialized /// from the specified byte array.</returns> /// <exception cref="SerializationException">Thrown if the specified byte /// array does not contain valid handshake data.</exception> public static Handshake Deserialize(byte[] data) { Handshake hs = new Handshake(); using (var ms = new MemoryStream(data)) { using (var r = new BinaryReader(ms)) { try { hs.MessageId = (HandshakeType) r.ReadByte(); hs.MajorVersion = r.ReadByte(); hs.MinorVersion = r.ReadByte(); // The payload size is in network byte order (big endian). hs.PayloadSize = r.ReadUInt16(true); } catch (Exception e) { throw new SerializationException("The specified byte array contains " + "invalid data.", e); } } } // According to specification, version _must_ be 1.0 if (hs.MajorVersion != majorVersion || hs.MinorVersion != minorVersion) { throw new SerializationException("Unexpected handshake version: " + hs.MajorVersion + "." + hs.MinorVersion); } // When the handshake message has a MessageId of HandshakeError, the // AuthPayload field _must_ have a length of 8 bytes. if (hs.MessageId == HandshakeType.HandshakeError && hs.PayloadSize != 8) { throw new SerializationException("Unexpected payload size. Expected " + "8, but was: " + hs.PayloadSize); } return hs; }
/// <summary> /// Reads the client's handshake from the specified buffer. /// </summary> /// <param name="buffer">A byte array from which the handshake data /// will be read.</param> /// <param name="offset">The zero-based index in the buffer at which to /// begin reading bytes.</param> /// <param name="count">The number of bytes to read from buffer.</param> /// <returns>True if the handshake has been read completely, otherwise /// false.</returns> bool ReadHandshake(byte[] buffer, int offset, int count) { // Accumulate data into buffer until 5 bytes have been read. int read = Math.Min(count, 5 - handshakeData.Length); handshakeData.Append(buffer, offset, read); if (handshakeData.Length == 5) { // We're now expecting the payload data. state = FilterStreamState.ReadingPayload; handshake = Handshake.Deserialize(handshakeData.ToArray()); handshakeData.Clear(); // Append rest of buffer to payloadData. payloadData.Append(buffer, offset + read, count - read); return true; } // We haven't read 5 bytes yet. return false; }
/// <summary> /// Reads the server response from the underlying inner stream. /// </summary> void ReadServerResponse() { // We get a base64-encoded ASCII string back from the server. string base64 = ReadLine(innerStream); HandshakeType type = HandshakeType.HandshakeInProgress; byte[] decoded; try { // Strip off "+ " continuation command; IMAP and POP3 both use this syntax // whereas SMTP uses "334 ". base64 = Regex.Replace(base64, @"^(\+|334)\s", String.Empty); decoded = Convert.FromBase64String(base64); } catch (FormatException) { // If the server didn't respond with base64-data, something must have gone // wrong and we should gracefully shut down. type = HandshakeType.HandshakeError; decoded = errorCode; } // Prepare a new handshake to hand to the NegotiateStream instance. Handshake hs = new Handshake(type, (ushort) decoded.Length); receivedData = new ByteBuilder() .Append(hs.Serialize()) .Append(decoded) .ToArray(); receivedConsumed = 0; }