public static byte[] GetHandshakeResponse(ByteBuffer input) { int position = (int)input.Position; if (input[position + 4] == 0) { //Using old style handshake byte[] output = new byte[2 * RtmpProtocolDecoder.HandshakeSize + 1]; output[0] = 0x03; int tick = System.Environment.TickCount; output[1] = (byte)((tick >> 24) & 0xff); output[2] = (byte)((tick >> 16) & 0xff); output[3] = (byte)((tick >> 8) & 0xff); output[4] = (byte)(tick & 0xff); HandshakePadBytes.CopyTo(output, 5); input.Read(output, RtmpProtocolDecoder.HandshakeSize + 1, RtmpProtocolDecoder.HandshakeSize); return output; } else { //This method is broken by FP 10.0.32.18 //int index = (input[8] + input[9] + input[10] + input[11]) % 728 + 12; int index = 0; if (input[position + 5] == 0 && input[position + 6] == 3 && input[position + 7] == 2) index = ((input[position + 772] & 0x0ff) + (input[position + 773] & 0x0ff) + (input[position + 774] & 0x0ff) + (input[position + 775] & 0x0ff)) % 728 + 776; else index = ((input[position + 8] & 0x0ff) + (input[position + 9] & 0x0ff) + (input[position + 10] & 0x0ff) + (input[position + 11] & 0x0ff)) % 728 + 12; byte[] part = new byte[32]; for (int i = 0; i < 32; ++i) { part[i] = input[position + index + i]; } HMACSHA256 hmacsha256 = new HMACSHA256(); hmacsha256.Key = SecretKey; byte[] newKey = hmacsha256.ComputeHash(part); byte[] randBytes = new byte[RtmpProtocolDecoder.HandshakeSize - 32]; Random random = new Random(); random.NextBytes(randBytes); hmacsha256.Key = newKey; byte[] hashedBytes = hmacsha256.ComputeHash(randBytes); byte[] output = new byte[2 * RtmpProtocolDecoder.HandshakeSize + 1]; output[0] = 0x03; HandshakeServerBytes.CopyTo(output, 1); //randBytes.CopyTo(output, RtmpProtocolDecoder.HandshakeSize + 1); randBytes.CopyTo(output, 1 + HandshakeServerBytes.Length); //hashedBytes.CopyTo(output, RtmpProtocolDecoder.HandshakeSize + 1 + randBytes.Length); hashedBytes.CopyTo(output, 1 + HandshakeServerBytes.Length + randBytes.Length); input.Skip(RtmpProtocolDecoder.HandshakeSize); return output; } }
static ISharedObjectMessage DecodeFlexSharedObject(ByteBuffer stream) { // Unknown byte, always 0? stream.Skip(1); RtmpReader reader = new RtmpReader(stream); string name = reader.ReadString(); // Read version of SO to modify int version = reader.ReadInt32(); // Read persistence informations bool persistent = reader.ReadInt32() == 2; // Skip unknown bytes reader.ReadInt32(); SharedObjectMessage so = new FlexSharedObjectMessage(null, name, version, persistent); DecodeSharedObject(so, stream, reader); return so; }
static void EncodeSharedObject(RtmpContext context, ISharedObjectMessage so, ByteBuffer output) { RtmpWriter writer = new RtmpWriter(output); //Set legacy collection flag from context writer.UseLegacyCollection = context.UseLegacyCollection; writer.UseLegacyThrowable = context.UseLegacyThrowable; writer.WriteUTF(so.Name); // SO version writer.WriteInt32(so.Version); // Encoding (this always seems to be 2 for persistent shared objects) writer.WriteInt32(so.IsPersistent ? 2 : 0); // unknown field writer.WriteInt32(0); int mark, len = 0; foreach(ISharedObjectEvent sharedObjectEvent in so.Events) { byte type = SharedObjectTypeMapping.ToByte(sharedObjectEvent.Type); switch(sharedObjectEvent.Type) { case SharedObjectEventType.SERVER_CONNECT: case SharedObjectEventType.CLIENT_INITIAL_DATA: case SharedObjectEventType.CLIENT_CLEAR_DATA: writer.WriteByte(type); writer.WriteInt32(0); break; case SharedObjectEventType.SERVER_DELETE_ATTRIBUTE: case SharedObjectEventType.CLIENT_DELETE_DATA: case SharedObjectEventType.CLIENT_UPDATE_ATTRIBUTE: writer.WriteByte(type); mark = (int)output.Position; output.Skip(4); // we will be back writer.WriteUTF(sharedObjectEvent.Key); len = (int)output.Position - mark - 4; output.PutInt(mark, len); break; case SharedObjectEventType.SERVER_SET_ATTRIBUTE: case SharedObjectEventType.CLIENT_UPDATE_DATA: if (sharedObjectEvent.Key == null) { // Update multiple attributes in one request IDictionary initialData = sharedObjectEvent.Value as IDictionary; foreach(DictionaryEntry entry in initialData) { writer.WriteByte(type); mark = (int)output.Position; output.Skip(4); // we will be back string key = entry.Key as string; object value = entry.Value; writer.WriteUTF(key); writer.WriteData(context.ObjectEncoding, value); len = (int)output.Position - mark - 4; output.PutInt(mark, len); } } else { writer.WriteByte(type); mark = (int)output.Position; output.Skip(4); // we will be back writer.WriteUTF(sharedObjectEvent.Key); writer.WriteData(context.ObjectEncoding, sharedObjectEvent.Value); //writer.WriteData(sharedObjectEvent.Value); len = (int)output.Position - mark - 4; output.PutInt(mark, len); } break; case SharedObjectEventType.CLIENT_SEND_MESSAGE: case SharedObjectEventType.SERVER_SEND_MESSAGE: // Send method name and value writer.WriteByte(type); mark = (int)output.Position; output.Skip(4); // we will be back // Serialize name of the handler to call writer.WriteData(context.ObjectEncoding, sharedObjectEvent.Key); //writer.WriteUTF(sharedObjectEvent.Key); // Serialize the arguments foreach(object arg in sharedObjectEvent.Value as IList) { writer.WriteData(context.ObjectEncoding, arg); } //writer.WriteData(sharedObjectEvent.Value as IList); len = (int)output.Position - mark - 4; //output.PutInt(mark, len); output.PutInt(mark, len); break; case SharedObjectEventType.CLIENT_STATUS: writer.WriteByte(type); mark = (int)output.Position; output.Skip(4); // we will be back writer.WriteUTF(sharedObjectEvent.Key); writer.WriteUTF(sharedObjectEvent.Value as string); len = (int)output.Position - mark - 4; output.PutInt(mark, len); break; case SharedObjectEventType.SERVER_DISCONNECT: writer.WriteByte(type); output.PutInt((int)output.Position, 0); break; default: #if !SILVERLIGHT _log.Error("Unknown event " + sharedObjectEvent.Type.ToString()); #endif writer.WriteByte(type); mark = (int)output.Position; output.Skip(4); // we will be back if (sharedObjectEvent.Key != null) { writer.WriteUTF(sharedObjectEvent.Key); writer.WriteData(context.ObjectEncoding, sharedObjectEvent.Value); } len = (int)output.Position - mark - 4; output.PutInt(mark, len); break; } } }
/// <summary> /// Decodes handshake message. /// </summary> /// <param name="context">RTMP protocol state.</param> /// <param name="stream">Buffer to be decoded.</param> /// <returns>Buffer with handshake response.</returns> public static object DecodeHandshake(RtmpContext context, ByteBuffer stream) { long remaining = stream.Remaining; if(context.Mode == RtmpMode.Server) { if(context.State == RtmpState.Connect) { if(remaining < HandshakeSize + 1) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSInitBuffering, remaining)); #endif context.SetBufferDecoding(HandshakeSize + 1); return null; } else { #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug("Handshake 1st phase"); #endif stream.Get();// skip the header byte byte[] handshake = RtmpHandshake.GetHandshakeResponse(stream); context.SetHandshake(handshake); context.State = RtmpState.Handshake; return handshake; } } if(context.State == RtmpState.Handshake) { //if (log.IsDebugEnabled) // log.Debug("Handshake reply"); if(remaining < HandshakeSize) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSReplyBuffering, remaining)); #endif context.SetBufferDecoding(HandshakeSize); return null; } else { // Skip first 8 bytes when comparing the handshake, they seem to be changed when connecting from a Mac client. if (!context.ValidateHandshakeReply(stream, 8, HandshakeSize - 8)) { #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug("Handshake reply validation failed, disconnecting client."); #endif stream.Skip(HandshakeSize); context.State = RtmpState.Error; throw new HandshakeFailedException("Handshake validation failed"); } stream.Skip(HandshakeSize); context.State = RtmpState.Connected; context.ContinueDecoding(); return null; } } } else { //Client mode if(context.State == RtmpState.Connect) { int size = (2 * HandshakeSize) + 1; if(remaining < size) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSInitBuffering, remaining)); #endif context.SetBufferDecoding(size); return null; } else { ByteBuffer hs = ByteBuffer.Allocate(size); ByteBuffer.Put(hs, stream, size); hs.Flip(); context.State = RtmpState.Handshake; return hs; } } } return null; }