Beispiel #1
0
        // check if a Command/TargetRpc/Rpc function & parameters are valid for weaving
        public bool ValidateRemoteCallAndParameters(MethodDefinition method, RemoteCallType callType)
        {
            if (method.IsAbstract)
            {
                logger.Error("Abstract Rpcs are currently not supported, use virtual method instead", method);
                return(false);
            }

            if (method.IsStatic)
            {
                logger.Error($"{method.Name} must not be static", method);
                return(false);
            }

            if (method.ReturnType.Is <System.Collections.IEnumerator>())
            {
                logger.Error($"{method.Name} cannot be a coroutine", method);
                return(false);
            }

            if (method.HasGenericParameters)
            {
                logger.Error($"{method.Name} cannot have generic parameters", method);
                return(false);
            }

            return(ValidateParameters(method, callType));
        }
Beispiel #2
0
        /// <summary>
        /// checks if return type if valid for rpc
        /// </summary>
        /// <exception cref="RpcException">Throws when parameter are invalid</exception>
        protected void ValidateReturnType(MethodDefinition md, RemoteCallType callType)
        {
            var returnType = md.ReturnType;

            if (returnType.Is(typeof(void)))
            {
                return;
            }

            // only ServerRpc allow UniTask
            if (callType == RemoteCallType.ServerRpc)
            {
                var unitaskType = typeof(UniTask <int>).GetGenericTypeDefinition();
                if (returnType.Is(unitaskType))
                {
                    return;
                }
            }


            if (callType == RemoteCallType.ServerRpc)
            {
                throw new RpcException($"Use UniTask<{md.ReturnType}> to return values from [ServerRpc]", md);
            }
            else
            {
                throw new RpcException($"[ClientRpc] must return void", md);
            }
        }
Beispiel #3
0
 /// <summary>
 /// checks if method parameters are valid for rpc
 /// </summary>
 /// <exception cref="RpcException">Throws when parameter are invalid</exception>
 protected void ValidateParameters(MethodReference method, RemoteCallType callType)
 {
     for (var i = 0; i < method.Parameters.Count; i++)
     {
         var param = method.Parameters[i];
         ValidateParameter(method, param, callType, i == 0);
     }
 }
Beispiel #4
0
        // check if a Command/TargetRpc/Rpc function & parameters are valid for weaving
        public static bool ValidateRemoteCallAndParameters(MethodDefinition method, RemoteCallType callType)
        {
            if (method.IsStatic)
            {
                Weaver.Error($"{method.Name} must not be static", method);
                return(false);
            }

            return(ValidateFunction(method) &&
                   ValidateParameters(method, callType));
        }
Beispiel #5
0
        // check if a Command/TargetRpc/Rpc function & parameters are valid for weaving
        public bool ValidateRemoteCallAndParameters(MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed)
        {
            if (method.IsStatic)
            {
                Log.Error($"{method.Name} must not be static", method);
                WeavingFailed = true;
                return(false);
            }

            return(ValidateFunction(method, ref WeavingFailed) &&
                   ValidateParameters(method, callType, ref WeavingFailed));
        }
Beispiel #6
0
 // check if all Command/TargetRpc/Rpc function's parameters are valid for weaving
 static bool ValidateParameters(MethodReference method, RemoteCallType callType)
 {
     for (int i = 0; i < method.Parameters.Count; ++i)
     {
         ParameterDefinition param = method.Parameters[i];
         if (!ValidateParameter(method, param, callType, i == 0))
         {
             return(false);
         }
     }
     return(true);
 }
Beispiel #7
0
        public static bool IsSenderConnection(ParameterDefinition param, RemoteCallType callType)
        {
            if (callType != RemoteCallType.Command)
            {
                return(false);
            }

            TypeReference type = param.ParameterType;

            return(type.Is <NetworkConnectionToClient>() ||
                   type.Resolve().IsDerivedFrom <NetworkConnectionToClient>());
        }
Beispiel #8
0
 // InvokeCmd/Rpc Delegate can all use the same function here
 // => invoke by index to save bandwidth (2 bytes instead of 4 bytes)
 internal static bool Invoke(ushort functionIndex, RemoteCallType remoteCallType, NetworkReader reader, NetworkBehaviour component, NetworkConnectionToClient senderConnection = null)
 {
     // IMPORTANT: we check if the message's componentIndex component is
     //            actually of the right type. prevents attackers trying
     //            to invoke remote calls on wrong components.
     if (GetInvoker(functionIndex, remoteCallType, out Invoker invoker) &&
         invoker.componentType.IsInstanceOfType(component))
     {
         // invoke function on this component
         invoker.function(component, reader, senderConnection);
         return(true);
     }
     return(false);
 }
        public static bool IsSenderConnection(ParameterDefinition param, RemoteCallType callType)
        {
            if (callType != RemoteCallType.Command)
            {
                return(false);
            }

            TypeReference type = param.ParameterType;

            const string ConnectionToClient   = "Mirror.NetworkConnectionToClient";
            bool         isConnectionToClient = type.FullName == ConnectionToClient || type.Resolve().IsDerivedFrom(ConnectionToClient);

            return(isConnectionToClient);
        }
Beispiel #10
0
 // note: no need to throw an error if not found.
 // an attacker might just try to call a cmd with an rpc's hash etc.
 // returning false is enough.
 static bool GetInvoker(ushort functionIndex, RemoteCallType remoteCallType, out Invoker invoker)
 {
     // valid index?
     if (functionIndex <= remoteCallDelegates.Count)
     {
         // get key by index
         int functionHash = remoteCallDelegates.Keys[functionIndex];
         invoker = remoteCallDelegates[functionHash];
         // check rpc type. don't allow calling cmds from rpcs, etc.
         return(invoker != null &&
                invoker.callType == remoteCallType);
     }
     invoker = null;
     return(false);
 }
        public static bool ProcessMethodsValidateParameters(MethodReference method, RemoteCallType callType)
        {
            for (int i = 0; i < method.Parameters.Count; ++i)
            {
                ParameterDefinition param = method.Parameters[i];

                bool valid = ValidateParameter(method, param, callType, i == 0);

                if (!valid)
                {
                    return(false);
                }
            }
            return(true);
        }
Beispiel #12
0
        // validate parameters for a remote function call like Rpc/Cmd
        bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam, ref bool WeavingFailed)
        {
            // need to check this before any type lookups since those will fail since generic types don't resolve
            if (param.ParameterType.IsGenericParameter)
            {
                Log.Error($"{method.Name} cannot have generic parameters", method);
                WeavingFailed = true;
                return(false);
            }

            bool isNetworkConnection = param.ParameterType.Is <NetworkConnection>();
            bool isSenderConnection  = IsSenderConnection(param, callType);

            if (param.IsOut)
            {
                Log.Error($"{method.Name} cannot have out parameters", method);
                WeavingFailed = true;
                return(false);
            }

            // if not SenderConnection And not TargetRpc NetworkConnection first param
            if (!isSenderConnection && isNetworkConnection && !(callType == RemoteCallType.TargetRpc && firstParam))
            {
                if (callType == RemoteCallType.Command)
                {
                    Log.Error($"{method.Name} has invalid parameter {param}, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", method);
                }
                else
                {
                    Log.Error($"{method.Name} has invalid parameter {param}. Cannot pass NetworkConnections", method);
                }
                WeavingFailed = true;
                return(false);
            }

            // sender connection can be optional
            if (param.IsOptional && !isSenderConnection)
            {
                Log.Error($"{method.Name} cannot have optional parameters", method);
                WeavingFailed = true;
                return(false);
            }

            return(true);
        }
Beispiel #13
0
        static bool CheckIfDelegateExists(Type componentType, RemoteCallType remoteCallType, RemoteCallDelegate func, int functionHash)
        {
            if (remoteCallDelegates.ContainsKey(functionHash))
            {
                // something already registered this hash.
                // it's okay if it was the same function.
                Invoker oldInvoker = remoteCallDelegates[functionHash];
                if (oldInvoker.AreEqual(componentType, remoteCallType, func))
                {
                    return(true);
                }

                // otherwise notify user. there is a rare chance of string
                // hash collisions.
                Debug.LogError($"Function {oldInvoker.componentType}.{oldInvoker.function.GetMethodName()} and {componentType}.{func.GetMethodName()} have the same hash.  Please rename one of them");
            }

            return(false);
        }
Beispiel #14
0
        /// <summary>
        /// check if a method is valid for rpc
        /// </summary>
        /// <exception cref="RpcException">Throws when method is invalid</exception>
        protected void ValidateMethod(MethodDefinition method, RemoteCallType callType)
        {
            if (method.IsAbstract)
            {
                throw new RpcException("Abstract Rpcs are currently not supported, use virtual method instead", method);
            }

            if (method.IsStatic)
            {
                throw new RpcException($"{method.Name} must not be static", method);
            }

            if (method.ReturnType.Is <System.Collections.IEnumerator>())
            {
                throw new RpcException($"{method.Name} cannot be a coroutine", method);
            }

            if (method.HasGenericParameters)
            {
                throw new RpcException($"{method.Name} cannot have generic parameters", method);
            }
        }
Beispiel #15
0
        // validate parameters for a remote function call like Rpc/Cmd
        bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam)
        {
            if (param.IsOut)
            {
                logger.Error($"{method.Name} cannot have out parameters", method);
                return(false);
            }

            if (param.ParameterType.IsGenericParameter)
            {
                logger.Error($"{method.Name} cannot have generic parameters", method);
                return(false);
            }

            if (IsNetworkConnection(param.ParameterType))
            {
                if (callType == RemoteCallType.ClientRpc && firstParam)
                {
                    // perfectly fine,  target rpc can receive a network connection as first parameter
                    return(true);
                }

                if (callType == RemoteCallType.ServerRpc)
                {
                    return(true);
                }

                logger.Error($"{method.Name} has invalid parameter {param}, Cannot pass NetworkConnections", method);
                return(false);
            }

            if (param.IsOptional)
            {
                logger.Error($"{method.Name} cannot have optional parameters", method);
                return(false);
            }

            return(true);
        }
Beispiel #16
0
        public void WriteArguments(ILProcessor worker, MethodDefinition method, VariableDefinition writer, ValueSerializer[] paramSerializers, RemoteCallType callType)
        {
            // write each argument
            // example result

            /*
             * writer.WritePackedInt32(someNumber)
             * writer.WriteNetworkIdentity(someTarget)
             */

            // NetworkConnection is not sent via the NetworkWriter so skip it here
            // skip first for NetworkConnection in TargetRpc
            var skipFirst = ClientRpcWithTarget(method, callType);

            var startingArg = skipFirst ? 1 : 0;

            for (var i = startingArg; i < method.Parameters.Count; i++)
            {
                // try/catch for each arg so that it will give error for each
                var param      = method.Parameters[i];
                var serializer = paramSerializers[i];
                WriteArgument(worker, writer, param, serializer);
            }
        }
Beispiel #17
0
        // validate parameters for a remote function call like Rpc/Cmd
        static bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam)
        {
            bool isNetworkConnection = param.ParameterType.Is <NetworkConnection>();
            bool isSenderConnection  = IsSenderConnection(param, callType);

            if (param.IsOut)
            {
                Weaver.Error($"{method.Name} cannot have out parameters", method);
                return(false);
            }


            // if not SenderConnection And not TargetRpc NetworkConnection first param
            if (!isSenderConnection && isNetworkConnection && !(callType == RemoteCallType.TargetRpc && firstParam))
            {
                if (callType == RemoteCallType.Command)
                {
                    Weaver.Error($"{method.Name} has invalid parameter {param}, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", method);
                }
                else
                {
                    Weaver.Error($"{method.Name} has invalid parameter {param}. Cannot pass NetworkConnections", method);
                }
                return(false);
            }

            // sender connection can be optional
            if (param.IsOptional && !isSenderConnection)
            {
                Weaver.Error($"{method.Name} cannot have optional parameters", method);
                return(false);
            }

            return(true);
        }
Beispiel #18
0
        /// <summary>
        /// checks if a parameter is valid for rpc
        /// </summary>
        /// <exception cref="RpcException">Throws when parameter are invalid</exception>
        private void ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam)
        {
            if (param.IsOut)
            {
                throw new RpcException($"{method.Name} cannot have out parameters", method);
            }

            if (IsNetworkPlayer(param.ParameterType))
            {
                if (callType == RemoteCallType.ClientRpc && firstParam)
                {
                    return;
                }

                if (callType == RemoteCallType.ServerRpc)
                {
                    return;
                }

                throw new RpcException($"{method.Name} has invalid parameter {param}, Cannot pass NetworkConnections", method);
            }

            // check networkplayer before optional, because networkplayer can be optional
            if (param.IsOptional)
            {
                throw new RpcException($"{method.Name} cannot have optional parameters", method);
            }
        }
Beispiel #19
0
        public static bool ReadArguments(MethodDefinition method, ILProcessor worker, RemoteCallType callType)
        {
            // read each argument
            // example result

            /*
             * CallCmdDoSomething(reader.ReadPackedInt32(), reader.ReadNetworkIdentity());
             */

            bool skipFirst = callType == RemoteCallType.TargetRpc &&
                             TargetRpcProcessor.HasNetworkConnectionParameter(method);

            // arg of calling  function, arg 0 is "this" so start counting at 1
            int argNum = 1;

            foreach (ParameterDefinition param in method.Parameters)
            {
                // NetworkConnection is not sent via the NetworkWriter so skip it here
                // skip first for NetworkConnection in TargetRpc
                if (argNum == 1 && skipFirst)
                {
                    argNum += 1;
                    continue;
                }
                // skip SenderConnection in Command
                if (IsSenderConnection(param, callType))
                {
                    argNum += 1;
                    continue;
                }


                MethodReference readFunc = Readers.GetReadFunc(param.ParameterType);

                if (readFunc == null)
                {
                    Weaver.Error($"{method.Name} has invalid parameter {param}.  Unsupported type {param.ParameterType},  use a supported Mirror type instead", method);
                    return(false);
                }

                worker.Append(worker.Create(OpCodes.Ldarg_1));
                worker.Append(worker.Create(OpCodes.Call, readFunc));

                // conversion.. is this needed?
                if (param.ParameterType.Is <float>())
                {
                    worker.Append(worker.Create(OpCodes.Conv_R4));
                }
                else if (param.ParameterType.Is <double>())
                {
                    worker.Append(worker.Create(OpCodes.Conv_R8));
                }
            }
            return(true);
        }
Beispiel #20
0
        public static bool WriteArguments(ILProcessor worker, MethodDefinition method, RemoteCallType callType)
        {
            // write each argument
            // example result

            /*
             * writer.WritePackedInt32(someNumber);
             * writer.WriteNetworkIdentity(someTarget);
             */

            bool skipFirst = callType == RemoteCallType.TargetRpc &&
                             TargetRpcProcessor.HasNetworkConnectionParameter(method);

            // arg of calling  function, arg 0 is "this" so start counting at 1
            int argNum = 1;

            foreach (ParameterDefinition param in method.Parameters)
            {
                // NetworkConnection is not sent via the NetworkWriter so skip it here
                // skip first for NetworkConnection in TargetRpc
                if (argNum == 1 && skipFirst)
                {
                    argNum += 1;
                    continue;
                }
                // skip SenderConnection in Command
                if (IsSenderConnection(param, callType))
                {
                    argNum += 1;
                    continue;
                }

                MethodReference writeFunc = Writers.GetWriteFunc(param.ParameterType);
                if (writeFunc == null)
                {
                    Weaver.Error($"{method.Name} has invalid parameter {param}", method);
                    return(false);
                }

                // use built-in writer func on writer object
                // NetworkWriter object
                worker.Append(worker.Create(OpCodes.Ldloc_0));
                // add argument to call
                worker.Append(worker.Create(OpCodes.Ldarg, argNum));
                // call writer extension method
                worker.Append(worker.Create(OpCodes.Call, writeFunc));
                argNum += 1;
            }
            return(true);
        }
        static bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam)
        {
            bool isNetworkConnection = param.ParameterType.FullName == Weaver.NetworkConnectionType.FullName;

            if (param.IsOut)
            {
                Weaver.Error($"{method.Name} cannot have out parameters", method);
                return(false);
            }


            // TargetRPC is an exception to this rule and can have a NetworkConnection as first parameter
            if (isNetworkConnection && !(callType == RemoteCallType.TargetRpc && firstParam))
            {
                Weaver.Error($"{method.Name} has invalid parameter {param}. Cannot pass NeworkConnections", method);
                return(false);
            }

            // sender connection can be optional
            if (param.IsOptional)
            {
                Weaver.Error($"{method.Name} cannot have optional parameters", method);
                return(false);
            }

            return(true);
        }
Beispiel #22
0
 // note: no need to throw an error if not found.
 // an attacker might just try to call a cmd with an rpc's hash etc.
 // returning false is enough.
 static bool GetInvokerForHash(int functionHash, RemoteCallType remoteCallType, out Invoker invoker) =>
 remoteCallDelegates.TryGetValue(functionHash, out invoker) &&
 invoker != null &&
 invoker.callType == remoteCallType;
Beispiel #23
0
 public bool AreEqual(Type componentType, RemoteCallType remoteCallType, RemoteCallDelegate invokeFunction) =>
 this.componentType == componentType &&
 this.callType == remoteCallType &&
 this.function == invokeFunction;
Beispiel #24
0
 private static bool ClientRpcWithTarget(MethodDefinition method, RemoteCallType callType)
 {
     return((callType == RemoteCallType.ClientRpc) &&
            HasNetworkPlayerParameter(method));
 }
Beispiel #25
0
        // pass full function name to avoid ClassA.Func & ClassB.Func collisions
        internal static int RegisterDelegate(Type componentType, string functionFullName, RemoteCallType remoteCallType, RemoteCallDelegate func, bool cmdRequiresAuthority = true)
        {
            // type+func so Inventory.RpcUse != Equipment.RpcUse
            int hash = functionFullName.GetStableHashCode();

            if (CheckIfDelegateExists(componentType, remoteCallType, func, hash))
            {
                return(hash);
            }

            // register invoker by hash
            remoteCallDelegates[hash] = new Invoker
            {
                callType             = remoteCallType,
                componentType        = componentType,
                function             = func,
                cmdRequiresAuthority = cmdRequiresAuthority
            };
            return(hash);
        }