internal static void HandleClientRPCRequest(ulong clientId, Stream stream, string channelName, SecuritySendFlags security, Action <ulong> bufferCallback) { using (PooledBitReader reader = PooledBitReader.Get(stream)) { ulong networkId = reader.ReadUInt64Packed(); ushort behaviourId = reader.ReadUInt16Packed(); ulong hash = reader.ReadUInt64Packed(); ulong responseId = reader.ReadUInt64Packed(); if (SpawnManager.SpawnedObjects.ContainsKey(networkId)) { NetworkedBehaviour behaviour = SpawnManager.SpawnedObjects[networkId].GetBehaviourAtOrderIndex(behaviourId); if (behaviour == null) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("ClientRPCRequest message recieved for a non existant behaviour. NetworkId: " + networkId + ", behaviourIndex: " + behaviourId); } } else { object result = behaviour.OnRemoteClientRPC(hash, clientId, stream); using (PooledBitStream responseStream = PooledBitStream.Get()) { using (PooledBitWriter responseWriter = PooledBitWriter.Get(responseStream)) { responseWriter.WriteUInt64Packed(responseId); responseWriter.WriteObjectPacked(result); } InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_CLIENT_RPC_RESPONSE, channelName, responseStream, security, null); } } } else if (NetworkingManager.Singleton.IsServer || !NetworkingManager.Singleton.NetworkConfig.EnableMessageBuffering) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("ClientRPCRequest message recieved for a non existant object with id: " + networkId + ". This message is lost."); } } else { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("ClientRPCRequest message recieved for a non existant object with id: " + networkId + ". This message will be buffered and might be recovered."); } bufferCallback(networkId); } } }
/// <summary> /// Sends unnamed message to a list of clients /// </summary> /// <param name="clientIds">The clients to send to, sends to everyone if null</param> /// <param name="stream">The message stream containing the data</param> /// <param name="channel">The channel to send the data on</param> /// <param name="security">The security settings to apply to the message</param> public static void SendUnnamedMessage(List <ulong> clientIds, BitStream stream, string channel = null, SecuritySendFlags security = SecuritySendFlags.None) { if (!NetworkingManager.Singleton.IsServer) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning("Can not send unnamed messages to multiple users as a client"); } return; } InternalMessageSender.Send(MLAPIConstants.MLAPI_UNNAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, clientIds, stream, security, null); }
/// <summary> /// Sends unnamed message to a list of clients /// </summary> /// <param name="clientIds">The clients to send to, sends to everyone if null</param> /// <param name="buffer">The message stream containing the data</param> /// <param name="networkChannel">The channel to send the data on</param> public static void SendUnnamedMessage(List <ulong> clientIds, NetworkBuffer buffer, NetworkChannel networkChannel = NetworkChannel.Internal) { if (!NetworkManager.Singleton.IsServer) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning("Can not send unnamed messages to multiple users as a client"); } return; } InternalMessageSender.Send(NetworkConstants.UNNAMED_MESSAGE, networkChannel, clientIds, buffer); }
/// <summary> /// Sends a named message /// </summary> /// <param name="name">The message name to send</param> /// <param name="clientId">The client to send the message to</param> /// <param name="stream">The message stream containing the data</param> /// <param name="channel">The channel tos end the data on</param> /// <param name="security">The security settings to apply to the message</param> public static void SendNamedMessage(string name, ulong clientId, Stream stream, string channel = null, SecuritySendFlags security = SecuritySendFlags.None) { ulong hash = NetworkedBehaviour.HashMethodName(name); using (PooledBitStream messageStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(messageStream)) { writer.WriteUInt64Packed(hash); } messageStream.CopyFrom(stream); InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_NAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, messageStream, security, null); } }
internal static void HandleServerRPCRequest(ulong clientId, Stream stream, string channelName, SecuritySendFlags security) { using (PooledBitReader reader = PooledBitReader.Get(stream)) { ulong networkId = reader.ReadUInt64Packed(); ushort behaviourId = reader.ReadUInt16Packed(); ulong hash = reader.ReadUInt64Packed(); ulong responseId = reader.ReadUInt64Packed(); if (SpawnManager.SpawnedObjects.ContainsKey(networkId)) { NetworkedBehaviour behaviour = SpawnManager.SpawnedObjects[networkId].GetBehaviourAtOrderIndex(behaviourId); if (behaviour == null) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("ServerRPCRequest message recieved for a non existant behaviour. NetworkId: " + networkId + ", behaviourIndex: " + behaviourId); } } else { object result = behaviour.OnRemoteServerRPC(hash, clientId, stream); using (PooledBitStream responseStream = PooledBitStream.Get()) { using (PooledBitWriter responseWriter = PooledBitWriter.Get(responseStream)) { responseWriter.WriteUInt64Packed(responseId); responseWriter.WriteObjectPacked(result); } InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_SERVER_RPC_RESPONSE, channelName, responseStream, security, SpawnManager.SpawnedObjects[networkId]); } } } else { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("ServerRPCRequest message recieved for a non existant object with id: " + networkId + ". This message is lost."); } } } }
/// <summary> /// Generic Sending Method for Internal Messages /// TODO: Will need to open this up for discussion, but we will want to determine if this is how we want internal MLAPI command /// messages to be sent. We might want specific commands to occur during specific network update regions (see NetworkUpdate /// </summary> public void InternalMessagesSendAndFlush() { foreach (RpcFrameQueueItem queueItem in m_InternalMLAPISendQueue) { var PoolStream = queueItem.NetworkBuffer; if (NetworkManager.Singleton.IsListening) { switch (queueItem.QueueItemType) { case RpcQueueContainer.QueueItemType.CreateObject: { foreach (ulong clientId in queueItem.ClientNetworkIds) { InternalMessageSender.Send(clientId, NetworkConstants.ADD_OBJECT, queueItem.NetworkChannel, PoolStream); } PerformanceDataManager.Increment(ProfilerConstants.NumberOfRPCsSent, queueItem.ClientNetworkIds.Length); ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length); break; } case RpcQueueContainer.QueueItemType.DestroyObject: { foreach (ulong clientId in queueItem.ClientNetworkIds) { InternalMessageSender.Send(clientId, NetworkConstants.DESTROY_OBJECT, queueItem.NetworkChannel, PoolStream); } PerformanceDataManager.Increment(ProfilerConstants.NumberOfRPCsSent, queueItem.ClientNetworkIds.Length); ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length); break; } } } PoolStream.Dispose(); } m_InternalMLAPISendQueue.Clear(); }
/// <summary> /// Sends the named message /// </summary> /// <param name="name">The message name to send</param> /// <param name="clientIds">The clients to send to, sends to everyone if null</param> /// <param name="stream">The message stream containing the data</param> /// <param name="networkChannel">The channel to send the data on</param> public static void SendNamedMessage(string name, List <ulong> clientIds, Stream stream, NetworkChannel networkChannel = NetworkChannel.Internal) { ulong hash = 0; switch (NetworkManager.Singleton.NetworkConfig.RpcHashSize) { case HashSize.VarIntTwoBytes: hash = name.GetStableHash16(); break; case HashSize.VarIntFourBytes: hash = name.GetStableHash32(); break; case HashSize.VarIntEightBytes: hash = name.GetStableHash64(); break; } using (var messageBuffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(messageBuffer)) { writer.WriteUInt64Packed(hash); messageBuffer.CopyFrom(stream); if (!NetworkManager.Singleton.IsServer) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning("Can not send named messages to multiple users as a client"); } return; } InternalMessageSender.Send(NetworkConstants.NAMED_MESSAGE, networkChannel, clientIds, messageBuffer); PerformanceDataManager.Increment(ProfilerConstants.NamedMessageSent); } }
/// <summary> /// Sends the named message /// </summary> /// <param name="name">The message name to send</param> /// <param name="clientIds">The clients to send to, sends to everyone if null</param> /// <param name="stream">The message stream containing the data</param> /// <param name="channel">The channel to send the data on</param> /// <param name="security">The security settings to apply to the message</param> public static void SendNamedMessage(string name, List <ulong> clientIds, Stream stream, string channel = null, SecuritySendFlags security = SecuritySendFlags.None) { ulong hash = NetworkedBehaviour.HashMethodName(name); using (PooledBitStream messageStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(messageStream)) { writer.WriteUInt64Packed(hash); } messageStream.CopyFrom(stream); if (!NetworkingManager.Singleton.IsServer) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning("Can not send named messages to multiple users as a client"); } return; } if (clientIds == null) { for (int i = 0; i < NetworkingManager.Singleton.ConnectedClientsList.Count; i++) { InternalMessageSender.Send(NetworkingManager.Singleton.ConnectedClientsList[i].ClientId, MLAPIConstants.MLAPI_NAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, messageStream, security, null); } } else { for (int i = 0; i < clientIds.Count; i++) { InternalMessageSender.Send(clientIds[i], MLAPIConstants.MLAPI_NAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, messageStream, security, null); } } } }
// Runs on client internal static void HandleHailRequest(ulong clientId, Stream stream) { X509Certificate2 certificate = null; byte[] serverDiffieHellmanPublicPart = null; using (PooledBitReader reader = PooledBitReader.Get(stream)) { if (NetworkingManager.Singleton.NetworkConfig.EnableEncryption) { // Read the certificate if (NetworkingManager.Singleton.NetworkConfig.SignKeyExchange) { // Allocation justification: This runs on client and only once, at initial connection certificate = new X509Certificate2(reader.ReadByteArray()); if (CryptographyHelper.VerifyCertificate(certificate, NetworkingManager.Singleton.ConnectedHostname)) { // The certificate is not valid :( // Man in the middle. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid certificate. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } else { NetworkingManager.Singleton.NetworkConfig.ServerX509Certificate = certificate; } } // Read the ECDH // Allocation justification: This runs on client and only once, at initial connection serverDiffieHellmanPublicPart = reader.ReadByteArray(); // Verify the key exchange if (NetworkingManager.Singleton.NetworkConfig.SignKeyExchange) { int signatureType = reader.ReadByte(); byte[] serverDiffieHellmanPublicPartSignature = reader.ReadByteArray(); if (signatureType == 0) { RSACryptoServiceProvider rsa = certificate.PublicKey.Key as RSACryptoServiceProvider; if (rsa != null) { using (SHA256Managed sha = new SHA256Managed()) { if (!rsa.VerifyData(serverDiffieHellmanPublicPart, sha, serverDiffieHellmanPublicPartSignature)) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid RSA signature. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } } else { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("No RSA key found in certificate. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } else if (signatureType == 1) { DSACryptoServiceProvider dsa = certificate.PublicKey.Key as DSACryptoServiceProvider; if (dsa != null) { using (SHA256Managed sha = new SHA256Managed()) { if (!dsa.VerifyData(sha.ComputeHash(serverDiffieHellmanPublicPart), serverDiffieHellmanPublicPartSignature)) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid DSA signature. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } } else { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("No DSA key found in certificate. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } else { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid signature type. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } } } using (PooledBitStream outStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(outStream)) { if (NetworkingManager.Singleton.NetworkConfig.EnableEncryption) { // Create a ECDH key EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER); NetworkingManager.Singleton.clientAesKey = diffieHellman.GetSharedSecret(serverDiffieHellmanPublicPart); byte[] diffieHellmanPublicKey = diffieHellman.GetPublicKey(); writer.WriteByteArray(diffieHellmanPublicKey); } } // Send HailResponse InternalMessageSender.Send(NetworkingManager.Singleton.ServerClientId, MLAPIConstants.MLAPI_CERTIFICATE_HAIL_RESPONSE, "MLAPI_INTERNAL", outStream, SecuritySendFlags.None, null); } }
// Ran on server internal static void HandleHailResponse(ulong clientId, Stream stream) { if (!NetworkingManager.Singleton.PendingClients.ContainsKey(clientId) || NetworkingManager.Singleton.PendingClients[clientId].ConnectionState != PendingClient.State.PendingHail) { return; } if (!NetworkingManager.Singleton.NetworkConfig.EnableEncryption) { return; } using (PooledBitReader reader = PooledBitReader.Get(stream)) { if (NetworkingManager.Singleton.PendingClients[clientId].KeyExchange != null) { byte[] diffieHellmanPublic = reader.ReadByteArray(); NetworkingManager.Singleton.PendingClients[clientId].AesKey = NetworkingManager.Singleton.PendingClients[clientId].KeyExchange.GetSharedSecret(diffieHellmanPublic); if (NetworkingManager.Singleton.NetworkConfig.SignKeyExchange) { byte[] diffieHellmanPublicSignature = reader.ReadByteArray(); X509Certificate2 certificate = NetworkingManager.Singleton.NetworkConfig.ServerX509Certificate; RSACryptoServiceProvider rsa = certificate.PrivateKey as RSACryptoServiceProvider; if (rsa != null) { using (SHA256Managed sha = new SHA256Managed()) { byte[] clientHash = rsa.Decrypt(diffieHellmanPublicSignature, false); byte[] serverHash = sha.ComputeHash(diffieHellmanPublic); if (!CryptographyHelper.ConstTimeArrayEqual(clientHash, serverHash)) { //Man in the middle. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Signature doesnt match for the key exchange public part. Disconnecting"); } } NetworkingManager.Singleton.DisconnectClient(clientId); return; } } } else { throw new CryptographicException("[MLAPI] Only RSA certificates are supported. No valid RSA key was found"); } } } } NetworkingManager.Singleton.PendingClients[clientId].ConnectionState = PendingClient.State.PendingConnection; NetworkingManager.Singleton.PendingClients[clientId].KeyExchange = null; // Give to GC // Send greetings, they have passed all the handshakes using (PooledBitStream outStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(outStream)) { writer.WriteInt64Packed(DateTime.Now.Ticks); // This serves no purpose. } InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_GREETINGS, "MLAPI_INTERNAL", outStream, SecuritySendFlags.None, null, true); } }
/// <summary> /// Sends a unnamed message to a specific client /// </summary> /// <param name="clientId">The client to send the message to</param> /// <param name="stream">The message stream containing the data</param> /// <param name="channel">The channel tos end the data on</param> /// <param name="security">The security settings to apply to the message</param> public static void SendUnnamedMessage(ulong clientId, BitStream stream, string channel = null, SecuritySendFlags security = SecuritySendFlags.None) { InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_UNNAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, stream, security, null); }
/// <summary> /// Sends a unnamed message to a specific client /// </summary> /// <param name="clientId">The client to send the message to</param> /// <param name="buffer">The message stream containing the data</param> /// <param name="networkChannel">The channel tos end the data on</param> public static void SendUnnamedMessage(ulong clientId, NetworkBuffer buffer, NetworkChannel networkChannel = NetworkChannel.Internal) { InternalMessageSender.Send(clientId, NetworkConstants.UNNAMED_MESSAGE, networkChannel, buffer); PerformanceDataManager.Increment(ProfilerConstants.UnnamedMessageSent); }
/// <summary> /// Sends a unnamed message to a specific client /// </summary> /// <param name="clientId">The client to send the message to</param> /// <param name="buffer">The message stream containing the data</param> /// <param name="networkChannel">The channel tos end the data on</param> public static void SendUnnamedMessage(ulong clientId, NetworkBuffer buffer, NetworkChannel networkChannel = NetworkChannel.Internal) { InternalMessageSender.Send(clientId, NetworkConstants.UNNAMED_MESSAGE, networkChannel, buffer); }