/// <summary> /// Reads and decodes message from the stream from LOC server. /// </summary> /// <param name="CancellationToken">Cancallation token for async calls.</param> /// <param name="CheckProtocolViolation">If set to true, the function checks whether a protocol violation occurred and if so, it sends protocol violation error to the peer.</param> /// <returns>Received message of null if the function fails.</returns> public async Task <LocProtocolMessage> ReceiveMessageAsync(CancellationToken CancellationToken, bool CheckProtocolViolation = false) { log.Trace("()"); LocProtocolMessage res = null; RawMessageResult rawMessage = await messageReader.ReceiveMessageAsync(CancellationToken); if (rawMessage.Data != null) { res = (LocProtocolMessage)LocMessageBuilder.CreateMessageFromRawData(rawMessage.Data); } else { log.Debug("Connection to LOC server has been terminated."); } if (CheckProtocolViolation) { if ((res == null) || rawMessage.ProtocolViolation) { await messageProcessor.SendProtocolViolation(this); } } log.Trace("(-):{0}", res != null ? "LocProtocolMessage" : "null"); return(res); }
/// <summary> /// Sends ERROR_PROTOCOL_VIOLATION to client with message ID set to 0x0BADC0DE. /// </summary> /// <param name="Client">Client to send the error to.</param> public async Task SendProtocolViolation(ClientBase Client) { LocMessageBuilder mb = new LocMessageBuilder(0, new List <SemVer> { SemVer.V100 }); LocProtocolMessage response = mb.CreateErrorProtocolViolationResponse(new LocProtocolMessage(new Message() { Id = 0x0BADC0DE })); await Client.SendMessageAsync(response); }
/// <summary> /// Initialize the object. /// </summary> /// <param name="ServerEndPoint">LOC server address and port.</param> public LocClient(IPEndPoint ServerEndPoint, IMessageProcessor MessageProcessor, ComponentShutdown ShutdownSignaling) : base(ServerEndPoint, false) { log = new Logger("IopServerCore.Network.LOC.LocClient"); log.Trace("(ServerEndPoint:'{0}')", ServerEndPoint); config = (ConfigBase)Base.ComponentDictionary[ConfigBase.ComponentName]; messageBuilder = new LocMessageBuilder(0, new List <SemVer> { SemVer.V100 }); messageProcessor = MessageProcessor; shutdownSignaling = ShutdownSignaling; log.Trace("(-)"); }
/// <summary> /// Reads messages from the client stream and processes them in a loop until the client disconnects /// or until an action (such as a protocol violation) that leads to disconnecting of the client occurs. /// </summary> /// <param name="Client">TCP client.</param> /// <param name="MessageBuilder">Client's message builder.</param> public async Task ReceiveMessageLoop(TcpClient Client, LocMessageBuilder MessageBuilder) { log.Trace("()"); try { NetworkStream stream = Client.GetStream(); RawMessageReader messageReader = new RawMessageReader(stream); while (!isShutdown) { RawMessageResult rawMessage = await messageReader.ReceiveMessageAsync(shutdownCancellationTokenSource.Token); bool disconnect = rawMessage.Data == null; bool protocolViolation = rawMessage.ProtocolViolation; if (rawMessage.Data != null) { LocProtocolMessage message = (LocProtocolMessage)LocMessageBuilder.CreateMessageFromRawData(rawMessage.Data); if (message != null) { disconnect = !await ProcessMessageAsync(Client, MessageBuilder, message); } else { protocolViolation = true; } } if (protocolViolation) { await SendProtocolViolation(Client); break; } if (disconnect) { break; } } } catch (Exception e) { log.Error("Exception occurred: {0}", e.ToString()); } log.Trace("(-)"); }
/// <summary> /// Sends a message to the client over the open network stream. /// </summary> /// <param name="Client">TCP client.</param> /// <param name="Message">Message to send.</param> /// <returns>true if the message was sent successfully to the target recipient.</returns> private async Task <bool> SendMessageInternalAsync(TcpClient Client, LocProtocolMessage Message) { log.Trace("()"); bool res = false; string msgStr = Message.ToString(); log.Trace("Sending message:\n{0}", msgStr); byte[] responseBytes = LocMessageBuilder.MessageToByteArray(Message); await StreamWriteLock.WaitAsync(); try { NetworkStream stream = Client.GetStream(); if (stream != null) { await stream.WriteAsync(responseBytes, 0, responseBytes.Length); res = true; } else { log.Info("Connection to the client has been terminated."); } } catch (IOException) { log.Info("Connection to the client has been terminated."); } finally { StreamWriteLock.Release(); } log.Trace("(-):{0}", res); return(res); }
/// <summary> /// Handler for each client that connects to the TCP server. /// </summary> /// <param name="Client">Client that is connected to TCP server.</param> private async void ClientHandlerAsync(TcpClient Client) { LogDiagnosticContext.Start(); log.Debug("(Client.Client.RemoteEndPoint:{0})", Client.Client.RemoteEndPoint); connectedProfileServer = Client; connectedProfileServerMessageBuilder = new LocMessageBuilder(0, new List <SemVer>() { SemVer.V100 }); await ReceiveMessageLoop(Client, connectedProfileServerMessageBuilder); connectedProfileServerWantsUpdates = false; connectedProfileServer = null; Client.Dispose(); log.Debug("(-)"); LogDiagnosticContext.Stop(); }
/// <summary> /// Converts an IoP Network protocol message to a binary format. /// </summary> /// <param name="Message">IoP Network protocol message.</param> /// <returns>Binary representation of the message to be sent over the network.</returns> public override byte[] MessageToByteArray(IProtocolMessage Message) { return(LocMessageBuilder.MessageToByteArray(Message)); }
/// <summary> /// Constructs ProtoBuf message from raw data read from the network stream. /// </summary> /// <param name="Data">Raw data to be decoded to the message.</param> /// <returns>ProtoBuf message or null if the data do not represent a valid message.</returns> public override IProtocolMessage CreateMessageFromRawData(byte[] Data) { return((IProtocolMessage)LocMessageBuilder.CreateMessageFromRawData(Data)); }
/// <summary> /// Processes DeregisterServiceRequest message from client. /// <para>Removes information about the profile server's NodeProfile.</para> /// </summary> /// <param name="Client">TCP client that sent the request.</param> /// <param name="MessageBuilder">Client's message builder.</param> /// <param name="RequestMessage">Full request message.</param> /// <returns>Response message to be sent to the client.</returns> public LocProtocolMessage ProcessMessageDeregisterServiceRequest(TcpClient Client, LocMessageBuilder MessageBuilder, LocProtocolMessage RequestMessage) { log.Trace("()"); LocProtocolMessage res = MessageBuilder.CreateDeregisterServiceResponse(RequestMessage); DeregisterServiceRequest deregisterServiceRequest = RequestMessage.Request.LocalService.DeregisterService; profileServer.Uninitialize(); log.Trace("(-):*.Response.Status={0}", res.Response.Status); return(res); }
/// <summary> /// Processes RegisterServiceRequest message from client. /// <para>Obtains information about the profile server's NodeProfile.</para> /// </summary> /// <param name="Client">TCP client that sent the request.</param> /// <param name="MessageBuilder">Client's message builder.</param> /// <param name="RequestMessage">Full request message.</param> /// <returns>Response message to be sent to the client.</returns> public LocProtocolMessage ProcessMessageRegisterServiceRequest(TcpClient Client, LocMessageBuilder MessageBuilder, LocProtocolMessage RequestMessage) { log.Trace("()"); LocProtocolMessage res = MessageBuilder.CreateRegisterServiceResponse(RequestMessage); RegisterServiceRequest registerServiceRequest = RequestMessage.Request.LocalService.RegisterService; byte[] serverId = registerServiceRequest.Service.ServiceData.ToByteArray(); if ((registerServiceRequest.Service.Type == ServiceType.Profile) && (serverId.Length == 32)) { profileServer.SetNetworkId(serverId); } else { log.Error("Received register service request is invalid."); } log.Trace("(-):*.Response.Status={0}", res.Response.Status); return(res); }
/// <summary> /// Processes GetNeighbourNodesByDistanceLocalRequest message from client. /// <para>Obtains information about the profile server's neighborhood and initiates sending updates to it.</para> /// </summary> /// <param name="Client">TCP client that sent the request.</param> /// <param name="MessageBuilder">Client's message builder.</param> /// <param name="RequestMessage">Full request message.</param> /// <param name="KeepAlive">This is set to true if KeepAliveAndSendUpdates in the request was set.</param> /// <returns>Response message to be sent to the client.</returns> public LocProtocolMessage ProcessMessageGetNeighbourNodesByDistanceLocalRequest(TcpClient Client, LocMessageBuilder MessageBuilder, LocProtocolMessage RequestMessage, out bool KeepAlive) { log.Trace("()"); LocProtocolMessage res = null; GetNeighbourNodesByDistanceLocalRequest getNeighbourNodesByDistanceLocalRequest = RequestMessage.Request.LocalService.GetNeighbourNodes; KeepAlive = getNeighbourNodesByDistanceLocalRequest.KeepAliveAndSendUpdates; List <NodeInfo> neighborList = new List <NodeInfo>(); lock (neighborsLock) { foreach (ProfileServer ps in neighbors.Values) { NodeInfo ni = ps.GetNodeInfo(); neighborList.Add(ni); } } res = MessageBuilder.CreateGetNeighbourNodesByDistanceLocalResponse(RequestMessage, neighborList); log.Trace("(-):*.Response.Status={0}", res.Response.Status); return(res); }
/// <summary> /// Processing of a message received from a client. /// </summary> /// <param name="Client">TCP client.</param> /// <param name="MessageBuilder">Client's message builder.</param> /// <param name="IncomingMessage">Full ProtoBuf message to be processed.</param> /// <returns>true if the conversation with the client should continue, false if a protocol violation error occurred and the client should be disconnected.</returns> public async Task <bool> ProcessMessageAsync(TcpClient Client, LocMessageBuilder MessageBuilder, LocProtocolMessage IncomingMessage) { bool res = false; log.Debug("()"); try { log.Trace("Received message type is {0}, message ID is {1}.", IncomingMessage.MessageTypeCase, IncomingMessage.Id); switch (IncomingMessage.MessageTypeCase) { case Message.MessageTypeOneofCase.Request: { LocProtocolMessage responseMessage = MessageBuilder.CreateErrorProtocolViolationResponse(IncomingMessage); Request request = IncomingMessage.Request; bool setKeepAlive = false; SemVer version = new SemVer(request.Version); log.Trace("Request type is {0}, version is {1}.", request.RequestTypeCase, version); switch (request.RequestTypeCase) { case Request.RequestTypeOneofCase.LocalService: { log.Trace("Local service request type is {0}.", request.LocalService.LocalServiceRequestTypeCase); switch (request.LocalService.LocalServiceRequestTypeCase) { case LocalServiceRequest.LocalServiceRequestTypeOneofCase.RegisterService: responseMessage = ProcessMessageRegisterServiceRequest(Client, MessageBuilder, IncomingMessage); break; case LocalServiceRequest.LocalServiceRequestTypeOneofCase.DeregisterService: responseMessage = ProcessMessageDeregisterServiceRequest(Client, MessageBuilder, IncomingMessage); break; case LocalServiceRequest.LocalServiceRequestTypeOneofCase.GetNeighbourNodes: responseMessage = ProcessMessageGetNeighbourNodesByDistanceLocalRequest(Client, MessageBuilder, IncomingMessage, out setKeepAlive); break; default: log.Error("Invalid local service request type '{0}'.", request.LocalService.LocalServiceRequestTypeCase); break; } break; } default: log.Error("Invalid request type '{0}'.", request.RequestTypeCase); break; } if (responseMessage != null) { // Send response to client. res = await SendMessageAsync(Client, responseMessage); if (res) { // If the message was sent successfully to the target, we close the connection only in case of protocol violation error. if (responseMessage.MessageTypeCase == Message.MessageTypeOneofCase.Response) { res = responseMessage.Response.Status != Status.ErrorProtocolViolation; } } if (res && setKeepAlive) { connectedProfileServerWantsUpdates = true; log.Debug("Profile server '{0}' is now connected to its LOC server and waiting for updates.", profileServer.Name); } } else { // If there is no response to send immediately to the client, // we want to keep the connection open. res = true; } break; } case Message.MessageTypeOneofCase.Response: { Response response = IncomingMessage.Response; log.Trace("Response status is {0}, details are '{1}', response type is {2}.", response.Status, response.Details, response.ResponseTypeCase); switch (response.ResponseTypeCase) { case Response.ResponseTypeOneofCase.LocalService: { log.Trace("Local service response type is {0}.", response.LocalService.LocalServiceResponseTypeCase); switch (response.LocalService.LocalServiceResponseTypeCase) { case LocalServiceResponse.LocalServiceResponseTypeOneofCase.NeighbourhoodUpdated: // Nothing to be done here. res = true; break; default: log.Error("Invalid local service response type '{0}'.", response.LocalService.LocalServiceResponseTypeCase); break; } break; } default: log.Error("Unknown response type '{0}'.", response.ResponseTypeCase); // Connection will be closed in ReceiveMessageLoop. break; } break; } default: log.Error("Unknown message type '{0}', connection to the client will be closed.", IncomingMessage.MessageTypeCase); await SendProtocolViolation(Client); // Connection will be closed in ReceiveMessageLoop. break; } } catch (Exception e) { log.Error("Exception occurred, connection to the client will be closed: {0}", e.ToString()); await SendProtocolViolation(Client); // Connection will be closed in ReceiveMessageLoop. } log.Debug("(-):{0}", res); return(res); }