private bool EncryptionRegisterFromWire(Peer toPassTo, PacketBase packet) { EncryptionRequest eq = packet as EncryptionRequest; if (eq == null) { ClassLogger.LogError("Recieved encryption request with null packet."); return(false); } if (!EncryptionFactory.ContainsKey(eq.EncryptionByteType)) { ClassLogger.LogError("Failed to establish encryption from Peer ID: " + toPassTo.UniqueConnectionId + " with EncryptionByte: " + eq.EncryptionByteType); return(false); } EncryptionBase newEncryptionObj = EncryptionFactory[eq.EncryptionByteType](); toPassTo.EncryptionRegister.Register(newEncryptionObj, eq.EncryptionByteType); bool result = toPassTo.EncryptionRegister[eq.EncryptionByteType] .SetNetworkInitRequiredData(eq.EncryptionInitInfo); if (result) { EncryptionRequest encryptionResponse = new EncryptionRequest(eq.EncryptionByteType, newEncryptionObj.NetworkInitRequiredData()); toPassTo.SendMessage(Packet.OperationType.Response, encryptionResponse, (byte)InternalPacketCode.EncryptionRequest, Packet.DeliveryMethod.ReliableUnordered, 0, 0, true); } return(false); }
private bool ProcessEncryptionResponse(Peer peer, PacketBase packet) { EncryptionRequest eq = packet as EncryptionRequest; if (eq == null) { ClassLogger.LogError("Recieved encryption request with null packet."); return(false); } if (!peer.EncryptionRegister.HasKey(eq.EncryptionByteType)) { ClassLogger.LogError("Recieved an encryption request response from the server for ByteType: {0} but the client is unaware of that type." , eq.EncryptionByteType); return(false); } //TODO: Verify that this is working. Callback at one point was not working. //This will set the server's init info. In the case of the default, for example, it will set the Diffiehelmman public key. //With this the server and client have established a shared secret and can now pass messages, with the IV, to eachother securely. //In the case of a custom method it is User defined and should be referenced. bool result = peer.EncryptionRegister[eq.EncryptionByteType].SetNetworkInitRequiredData(eq.EncryptionInitInfo); Action callback = peer.EncryptionRegister[eq.EncryptionByteType].OnEstablished; if (callback != null) { callback(); } return(result); }
private bool HandleInternalMessage(Peer toPassTo, LidgrenTransferPacket packet) { if (packet.isEncrypted) { if (toPassTo.EncryptionRegister.HasKey(packet.EncryptionMethodByte)) { if (!packet.Decrypt(toPassTo.EncryptionRegister[packet.EncryptionMethodByte])) { ClassLogger.LogError("Failed to decrypt package from Peer ID: " + toPassTo.UniqueConnectionId + " with EncryptionByte: " + packet.EncryptionMethodByte); return(false); } } } PacketBase deserializedPacketBase = InternalPacketConstructor(packet); switch (packet.OperationType) { case Packet.OperationType.Request: return(this.ProcessInternalRequest((InternalPacketCode)packet.PacketCode, deserializedPacketBase, toPassTo)); case Packet.OperationType.Response: return(this.ProcessInternalResponse((InternalPacketCode)packet.PacketCode, deserializedPacketBase, toPassTo)); case Packet.OperationType.Event: throw new LoggableException("GladNet currently does not support internal events.", null, LogType.Error); default: return(false); } }
public virtual bool DispatchMessage(Peer toPassTo, NetIncomingMessage msg, bool isInternal) { if (msg == null) { throw new LoggableException("When dispatched NetBuffer was found to be null.", null, LogType.Error); } LidgrenTransferPacket packet = this.BuildTransferPacket(msg); if (toPassTo == null) { return(false); } if (!this.SerializerRegister.HasKey(packet.SerializerKey)) { ClassLogger.LogError("Recieved a packet that cannot be handled due to not having a serializer registered with byte code: " + packet.SerializerKey); return(false); } if (packet == null) { ClassLogger.LogError("Lidgren packet built to null."); return(false); } if (toPassTo == null) { ClassLogger.LogError("When attempted to dispatch the Peer passed was found to be null."); return(false); } #if UNITYDEBUG || DEBUG this.ClassLogger.LogDebug("About to handle packet. Encrypted: " + packet.isEncrypted.ToString() + " EncryptionCode: " + packet.EncryptionMethodByte); #endif if (!isInternal) { if (packet.isEncrypted) { return(DispatchEncryptedMessage(toPassTo, packet, msg.DeliveryMethod)); } else { return(Dispatch(toPassTo, packet, msg.DeliveryMethod)); } } else { return(HandleInternalMessage(toPassTo, packet)); } }
private void ServiceLidgrenMessage(NetIncomingMessage msg) { if (msg == null) { return; } switch (msg.MessageType) { case NetIncomingMessageType.StatusChanged: try { HandleStatusChange((NetConnectionStatus)msg.ReadByte()); } catch (NetException e) { #if UNITYDEBUG ClassLogger.LogError("Malformed packet recieved. Packet indicated that it was a status change but had no info."); #else //TODO: What shall we do when the packet is malformed here? #endif } catch (LoggableException e) { //Checking this because it can cause some nasty GC to make these string adds. if (ClassLogger.isStateEnabled(LogType.Debug)) { ClassLogger.LogDebug(e.Message + " Inner: " + e.InnerException != null ? e.InnerException.Message : ""); } } break; //We can take advantage of using the same logic for both cases. //All that changes is we till the handler it is an internal message for a Data message type. case NetIncomingMessageType.ExternalHighlevelMessage: case NetIncomingMessageType.Data: try { this.NetworkMessageHandler.DispatchMessage(this, msg, msg.MessageType == NetIncomingMessageType.Data); } catch (LoggableException e) { //Checking this because it can cause some nasty GC to make these string adds. if (ClassLogger.isStateEnabled(LogType.Debug)) { ClassLogger.LogDebug(e.Message + " Inner: " + e.InnerException != null ? e.InnerException.Message : ""); } } break; } }
public void StartListener() { if (networkThread != null) { ClassLogger.LogError("Attempted to start listener while listener is spinning."); #if !UNITYDEBUG && !UNITYRELEASE //TODO: Better exception throwing throw new Exception("Attempted to start listener while listener is spinning."); #endif } //If we hit this point we need to make a network thread to poll the lidgren client and process incoming client data. networkThread = new Thread(new ThreadStart(NetworkListenerThreadMethod)); networkThread.Start(); }
private bool DispatchEncryptedMessage(Peer toPassTo, LidgrenTransferPacket packet, NetDeliveryMethod method) { if (!toPassTo.EncryptionRegister.HasKey(packet.EncryptionMethodByte)) { ClassLogger.LogError("Failed to decrypt packet. Client requested unregistered method: " + packet.EncryptionMethodByte); return(false); } try { switch (packet.OperationType) { case Packet.OperationType.Event: EventPackage ep = this.GeneratePackage <EventPackage>(packet, toPassTo.EncryptionRegister[packet.EncryptionMethodByte]); if (ep != null) { toPassTo.PackageRecieve(ep, new MessageInfo(packet, method)); } return(true); case Packet.OperationType.Request: RequestPackage rqp = this.GeneratePackage <RequestPackage>(packet, toPassTo.EncryptionRegister[packet.EncryptionMethodByte]); if (rqp != null) { toPassTo.PackageRecieve(rqp, new MessageInfo(packet, method)); } return(true); case Packet.OperationType.Response: ResponsePackage rp = this.GeneratePackage <ResponsePackage>(packet, toPassTo.EncryptionRegister[packet.EncryptionMethodByte]); if (rp != null) { toPassTo.PackageRecieve(rp, new MessageInfo(packet, method)); } return(true); default: return(false); } } catch (LoggableException e) { ClassLogger.LogError(e.Message + e.InnerException != null ? " Inner: " + e.InnerException : ""); return(false); } }
//TODO: Totally refactor this garbage public void StartPipeListener(string clientHandleString) { #if DEBUGBUILD ClassLogger.LogDebug("Started pipe listener to launcher with handle: " + clientHandleString); #endif Task.Factory.StartNew(() => { try { using (PipeStream ps = new AnonymousPipeClientStream(PipeDirection.In, clientHandleString)) { using (StreamReader reader = new StreamReader(ps, Encoding.Default)) { string message; while (true) { message = reader.ReadLine(); if (message != null) { #if DEBUGBUILD ClassLogger.LogDebug("Recieved message via Launcher pipe: " + message); #endif if (message.Contains("[SHUTDOWN]")) { if (message == "[SHUTDOWN] " + Process.GetCurrentProcess().Id.ToString()) { isReady = false; break; } } } } } } } catch (Exception e) { ClassLogger.LogError(e.Message); } }, TaskCreationOptions.LongRunning); }
private bool Dispatch(Peer toPassTo, IPackage packet, NetDeliveryMethod method) { try { //TODO: Refactor switch ((Packet.OperationType)packet.OperationType) { case Packet.OperationType.Event: EventPackage ePackage = GeneratePackage <EventPackage>(packet); if (ePackage != null) { toPassTo.PackageRecieve(ePackage, new MessageInfo(method)); } return(true); case Packet.OperationType.Request: //ClassLogger.LogDebug("Hit request"); RequestPackage rqPackage = GeneratePackage <RequestPackage>(packet); if (rqPackage != null) { //ClassLogger.LogDebug("About to call peer method"); toPassTo.PackageRecieve(rqPackage, new MessageInfo(method)); } return(true); case Packet.OperationType.Response: ResponsePackage rPackage = GeneratePackage <ResponsePackage>(packet); if (rPackage != null) { toPassTo.PackageRecieve(rPackage, new MessageInfo(method)); } return(true); default: return(false); } } catch (LoggableException e) { ClassLogger.LogError(e.Message + e.InnerException != null ? " Inner: " + e.InnerException : ""); return(false); } }
public bool Connect(string ip, int port, string hailMessage, string appName) { if (isConnected) { this.Disconnect(); } NetPeerConfiguration config = new NetPeerConfiguration(appName); config.AcceptIncomingConnections = false; if (ip == null || appName == null || ip.Length == 0) { ClassLogger.LogError("Connection to remote host must have a valid appname and IP address."); #if UNITYDEBUG || UNITYRELEASE return(false); #else throw new NullReferenceException("Connection to remote host must have a valid appname and IP address."); #endif } //This should reduce GC which is always terrible for Unity. config.UseMessageRecycling = true; internalLidgrenClient = new NetClient(config); internalLidgrenClient.Start(); NetOutgoingMessage msg = GenerateClientHail(hailMessage); IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ip), port); NetConnection connection = internalLidgrenClient.Connect(endPoint, msg); this.SetConnectionDetails(connection, endPoint, connection.RemoteUniqueIdentifier); _isConnected = true; Interlocked.Exchange(ref timeNowByPoll, NetTime.Now < 3 ? 0 : NetTime.Now); Interlocked.Exchange(ref timeNowByPollComparer, NetTime.Now < 3 ? 0 : NetTime.Now); return(true); }
private PacketBase InternalPacketConstructor(IPackage package) { SerializerBase serializer = this.SerializerRegister[package.SerializerKey]; if (serializer == null) { ClassLogger.LogError("Failed to deserialize internal message with serializer key: " + package.SerializerKey); return(null); } PacketBase packet = this.Converter.PacketConstructor(package.InternalByteRepresentation, serializer); if (packet == null) { ClassLogger.LogError("Recieved a null internal package. Code: {0} SerializerKey: {1}", package.PacketCode, package.SerializerKey); } return(packet); }
private NetOutgoingMessage GenerateClientHail(string hail) { if (internalLidgrenClient != null) { NetOutgoingMessage msg = internalLidgrenClient.CreateMessage(); //This indicates we're a pure client connection. It is a reserved connection type value. msg.Write(hail); msg.Write((byte)0); return(msg); } else { #if UNITYDEBUG || UNITYRELEASE ClassLogger.LogError("Internal lidgren client is null. Do not invoke HailMessageGeneration via reflection."); return(null); #else throw new NullReferenceException("internalLidgrenClient is null for some reason."); #endif } }
//This is not needed by the client /// <summary> /// Registers an encryption object. This will request that the encryption be established by sending a message to the server. /// Do not try to register an encryption object if you are not connected. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="encryptorInstance"></param> /// <returns></returns> public bool Register <T>(T encryptorInstance, Action OnSuccess) where T : EncryptionBase { if (encryptorInstance == null) { ClassLogger.LogError("Cannot register a null encryption object."); return(false); } if (this.EncryptionRegister.HasKey(encryptorInstance.EncryptionTypeByte)) { ClassLogger.LogError("Tried to register an already known encryption object."); return(false); } encryptorInstance.OnEstablished += OnSuccess; this.EncryptionRegister.Register(encryptorInstance, encryptorInstance.EncryptionTypeByte); PacketBase packet = new EncryptionRequest(encryptorInstance.EncryptionTypeByte, encryptorInstance.NetworkInitRequiredData()); return(this.SendMessage(Packet.OperationType.Request, packet, (byte)InternalPacketCode.EncryptionRequest, Packet.DeliveryMethod.ReliableUnordered, 0, 0, true) != Packet.SendResult.FailedNotConnected); }
private void RegisterProtobufPackets(Func <Type, bool> registerAsDefaultFunc) { if (RecieverListener == null) { ClassLogger.LogError("The IListener instance passed in on connection is a null reference."); return; } try { //Registering the empty packet Packet.Register(typeof(EmptyPacket), true); Packet.Register(typeof(EncryptionRequest), true); Packet.SetupProtoRuntimePacketInheritance(); this.RecieverListener.RegisterProtobufPackets(registerAsDefaultFunc); Packet.LockInProtobufnet(); } catch (LoggableException e) { ClassLogger.LogError(e.Message + e.InnerException != null ? e.InnerException.Message : ""); throw; } }
//This is not needed by the client /// <summary> /// Registers an encryption object. This will request that the encryption be established by sending a message to the server. /// Do not try to register an encryption object if you are not connected. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="encryptorInstance"></param> /// <returns></returns> public bool Register <T>(T encryptorInstance) where T : EncryptionBase { if (encryptorInstance == null) { ClassLogger.LogError("Cannot register a null encryption object."); return(false); } if (this.EncryptionRegister.HasKey(encryptorInstance.EncryptionTypeByte)) { ClassLogger.LogError("Tried to register an already known encryption object."); return(false); } if (!isConnected || RecieverListener == null) { ClassLogger.LogError("Cannot register encryption objects when not connected."); return(false); } //Set the callback for when the server acknowledges our encryption request. encryptorInstance.OnEstablished += () => { lock (networkIncomingEnqueueSyncObj) { this.networkPackageQueue.Enqueue(() => { RecieverListener.OnStatusChange(StatusChange.EncryptionEstablished); }); } }; this.EncryptionRegister.Register(encryptorInstance, encryptorInstance.EncryptionTypeByte); PacketBase packet = new EncryptionRequest(encryptorInstance.EncryptionTypeByte, encryptorInstance.NetworkInitRequiredData()); return(this.SendMessage(Packet.OperationType.Request, packet, (byte)InternalPacketCode.EncryptionRequest, Packet.DeliveryMethod.ReliableUnordered, 0, 0, true) != Packet.SendResult.FailedNotConnected); }
private void NetworkListenerThreadMethod() { #if UNITYDEBUG || DEBUG ClassLogger.LogDebug("Started network thread."); #endif if (internalLidgrenClient == null || internalLidgrenClient == null) { ClassLogger.LogError("Cannot start listening before connecting."); #if !UNITYDEBUG && !UNITYRELEASE ClassLogger.LogError("Cannot start listening before connecting."); throw new NullReferenceException("Cannot start listening before connecting. Internally a client object is null."); #endif } NetIncomingMessage msg; try { while (_isConnected) { msg = internalLidgrenClient.WaitMessage(20); //TODO: May not be thread safe due to NetTime.Now //This checks to see if Poll was called in the last 3 seconds. If it was not it stops the network thread basically. if (Interlocked.Exchange(ref timeNowByPollComparer, timeNowByPoll) < NetTime.Now - 3) { #if UNITYDEBUG || DEBUG ClassLogger.LogDebug("Stopping network thread."); #endif _isConnected = false; //Should be thread safe and fine to do. this.Disconnect(); } if (msg != null) { try { ServiceLidgrenMessage(msg); //Recycling the message reduces GC which can be make or break for Unity. this.internalLidgrenClient.Recycle(msg); } //We can catch nullreferences here without affecting exceptions external to the library //This is because we have a Queue<Action> and thus we can't possibly catch the exception coming from the main thread via the lambda generated Action instance. catch (NullReferenceException e) { ClassLogger.LogError("Error occurred during polling: " + e.Message + " StackTrace: " + e.StackTrace + " Source: " + e.Source); //If we hit this point it generally means that the object has gone out of scope for some reason without disconnecting (usually in Unity going from playmode //to the editor so at this point we should just catch it and let the application and thread die. _isConnected = false; } } } } catch (LoggableException e) { ClassLogger.LogError(e.Message + e.InnerException != null ? "Inner: " + e.InnerException : ""); } catch (Exception e) { ClassLogger.LogError(e.Message + e.Data); throw; } Disconnect(); networkThread = null; }
private void MessagePoll(NetPeer peer) { //TODO: Examine if this will cause latency issues for handling messages //This is done so that CPU usage isn't too high. It blocks the thread and waits for a message //Nothing but GladNet should be executing on the main thread anyway. NetIncomingMessage msg = peer.WaitMessage(10); //TODO: Refactor if (msg != null && msg.SenderConnection != null) { #if DEBUGBUILD ClassLogger.LogDebug("Recieved a message from client ID: " + msg.SenderConnection.RemoteUniqueIdentifier); #endif switch (msg.MessageType) { case NetIncomingMessageType.ConnectionApproval: ClassLogger.LogDebug("Hit connection approval"); if (this.NetworkMessageHandler.TryReadHailMessage(msg, ExpectedClientHailMessage)) { ClassLogger.LogDebug("About to approve."); try { msg.SenderConnection.Approve(); this.TryRegisterApprovedConnection(msg.SenderConnection, msg.SenderConnection.RemoteHailMessage.ReadByte()); } catch (NetException e) { #if DEBUGBUILD ClassLogger.LogError("Failed to read connection type byte from hail message packet. Exception: " + e.Message); #endif } } else { ClassLogger.LogWarn("Client failed to satisfy hailmessage. Expected: " + ExpectedClientHailMessage); } break; case NetIncomingMessageType.StatusChanged: //Malicious user could try to send a fake StatusChange without the extra byte so try to catch NetException. try { if (Clients.HasKey(msg.SenderConnection.RemoteUniqueIdentifier)) { this.ReadStatusChange((NetConnectionStatus)msg.ReadByte(), Clients[msg.SenderConnection.RemoteUniqueIdentifier]); } else if (ServerConnections.HasKey(msg.SenderConnection.RemoteUniqueIdentifier)) { this.ReadStatusChange((NetConnectionStatus)msg.ReadByte(), ServerConnections[msg.SenderConnection.RemoteUniqueIdentifier]); } else { //If this point is reached it indicates that the status change is not from a registered connection and //this could indicate potentially a subserver connecting or maybe a client message before hail approval. ReadStatusChange((NetConnectionStatus)msg.ReadByte(), msg.SenderConnection); } } catch (NetException e) { #if DEBUGBUILD ClassLogger.LogError("NetConnection ID: " + msg.SenderConnection.RemoteUniqueIdentifier + " sent a potentially malicious StatusChange update. Error: " + e.Message); #endif } break; case NetIncomingMessageType.ExternalHighlevelMessage: OnRecieveMessage(msg, false); break; case NetIncomingMessageType.Data: OnRecieveMessage(msg, true); break; } peer.Recycle(msg); } }