Exemple #1
0
        /// <summary>
        /// Performs an identity verification process for the client's identity using the already opened connection to the server.
        /// </summary>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> VerifyIdentityAsync()
        {
            log.Trace("()");

            bool res = false;

            if (await StartConversationAsync())
            {
                ProxProtocolMessage requestMessage = MessageBuilder.CreateVerifyIdentityRequest(serverChallenge);
                if (await SendMessageAsync(requestMessage))
                {
                    ProxProtocolMessage responseMessage = await ReceiveMessageAsync();

                    if (CheckResponseMessage(requestMessage, responseMessage))
                    {
                        res = true;
                    }
                    else
                    {
                        log.Warn("Received unexpected or invalid message.");
                    }
                }
                else
                {
                    log.Warn("Unable to send message.");
                }
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #2
0
        /// <summary>
        /// Sends StartNeighborhoodInitializationRequest to the server and reads a response.
        /// </summary>
        /// <param name="PrimaryPort">Primary interface port of the requesting proximity server.</param>
        /// <param name="NeighborPort">Neighbors interface port of the requesting proximity server.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> StartNeighborhoodInitializationAsync(uint PrimaryPort, uint NeighborPort)
        {
            log.Trace("()");

            bool   res    = false;
            Config config = (Config)Base.ComponentDictionary[ConfigBase.ComponentName];
            ProxProtocolMessage requestMessage = MessageBuilder.CreateStartNeighborhoodInitializationRequest(PrimaryPort, NeighborPort, config.ExternalServerAddress);

            if (await SendMessageAsync(requestMessage))
            {
                ProxProtocolMessage responseMessage = await ReceiveMessageAsync();

                if (CheckResponseMessage(requestMessage, responseMessage))
                {
                    res = true;
                }
                else
                {
                    if (lastResponseStatus != Status.ErrorBusy)
                    {
                        log.Warn("Received unexpected or invalid message.");
                    }
                }
            }
            else
            {
                log.Warn("Unable to send message.");
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #3
0
        /// <summary>
        /// Sends ListRolesRequest to the server and reads a response.
        /// </summary>
        /// <returns>ListRolesResponse message if the function succeeds, null otherwise.</returns>
        public async Task <ListRolesResponse> SendListRolesRequest()
        {
            log.Trace("()");

            ListRolesResponse   res            = null;
            ProxProtocolMessage requestMessage = MessageBuilder.CreateListRolesRequest();

            if (await SendMessageAsync(requestMessage))
            {
                ProxProtocolMessage responseMessage = await ReceiveMessageAsync();

                if (CheckResponseMessage(requestMessage, responseMessage))
                {
                    res = responseMessage.Response.SingleResponse.ListRoles;
                }
                else
                {
                    log.Warn("Received unexpected or invalid message.");
                }
            }
            else
            {
                log.Warn("Unable to send message.");
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #4
0
        /// <summary>
        /// Sends StopNeighborhoodUpdatesRequest to the server and reads a response.
        /// </summary>
        /// <param name="RequestMessage">Request message to send.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> SendStopNeighborhoodUpdatesAsync(ProxProtocolMessage RequestMessage)
        {
            log.Trace("()");

            bool res = false;
            ProxProtocolMessage requestMessage = RequestMessage;

            if (await SendMessageAsync(requestMessage))
            {
                ProxProtocolMessage responseMessage = await ReceiveMessageAsync();

                if (CheckResponseMessage(requestMessage, responseMessage))
                {
                    res = true;
                }
                else
                {
                    if (lastResponseStatus != Status.ErrorNotFound)
                    {
                        log.Warn("Received unexpected or invalid message.");
                    }
                }
            }
            else
            {
                log.Warn("Unable to send message.");
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #5
0
        /// <summary>
        /// Reads and parses protocol message from the network stream.
        /// </summary>
        /// <returns>Parsed protocol message or null if the function fails.</returns>
        public async Task <ProxProtocolMessage> ReceiveMessageAsync()
        {
            log.Trace("()");

            ProxProtocolMessage res = null;

            using (CancellationTokenSource readTimeoutTokenSource = new CancellationTokenSource(60000),
                   timeoutShutdownTokenSource = CancellationTokenSource.CreateLinkedTokenSource(readTimeoutTokenSource.Token, shutdownCancellationToken))
            {
                RawMessageReader messageReader = new RawMessageReader(Stream);
                RawMessageResult rawMessage    = await messageReader.ReceiveMessageAsync(timeoutShutdownTokenSource.Token);

                if (rawMessage.Data != null)
                {
                    res = (ProxProtocolMessage)CreateMessageFromRawData(rawMessage.Data);
                    if (res.MessageTypeCase == Message.MessageTypeOneofCase.Response)
                    {
                        lastResponseStatus  = res.Response.Status;
                        lastResponseDetails = res.Response.Details;
                    }
                }
            }

            ForceDisconnect = res == null;

            log.Trace("(-):ForceDisconnect={0}", ForceDisconnect, res != null ? "Message" : "null");
            return(res);
        }
Exemple #6
0
        /// <summary>
        /// Generates client's challenge and creates start conversation request with it.
        /// </summary>
        /// <returns>StartConversationRequest message that is ready to be sent to the server.</returns>
        public ProxProtocolMessage CreateStartConversationRequest()
        {
            clientChallenge = new byte[ProxMessageBuilder.ChallengeDataSize];
            Crypto.Rng.GetBytes(clientChallenge);
            ProxProtocolMessage res = MessageBuilder.CreateStartConversationRequest(clientChallenge);

            return(res);
        }
Exemple #7
0
        /// <summary>
        /// Verifies whether the server successfully signed the correct start conversation challenge.
        /// </summary>
        /// <param name="StartConversationResponse">StartConversationResponse received from the server.</param>
        /// <returns>true if the signature is valid, false otherwise.</returns>
        public bool VerifyServerChallengeSignature(ProxProtocolMessage StartConversationResponse)
        {
            log.Trace("()");

            byte[] receivedChallenge = StartConversationResponse.Response.ConversationResponse.Start.ClientChallenge.ToByteArray();
            bool   res = ByteArrayComparer.Equals(receivedChallenge, clientChallenge) &&
                         MessageBuilder.VerifySignedConversationResponseBodyPart(StartConversationResponse, receivedChallenge, serverKey);

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #8
0
        /// <summary>
        /// Processes PingRequest message from client.
        /// <para>Simply copies the payload to a new ping response message.</para>
        /// </summary>
        /// <param name="Client">Client that sent the request.</param>
        /// <param name="RequestMessage">Full request message.</param>
        /// <returns>Response message to be sent to the client.</returns>
        public ProxProtocolMessage ProcessMessagePingRequest(IncomingClient Client, ProxProtocolMessage RequestMessage)
        {
            log.Trace("()");

            ProxMessageBuilder messageBuilder = Client.MessageBuilder;
            PingRequest        pingRequest    = RequestMessage.Request.SingleRequest.Ping;

            ProxProtocolMessage res = messageBuilder.CreatePingResponse(RequestMessage, pingRequest.Payload.ToByteArray(), ProtocolHelper.GetUnixTimestampMs());

            log.Trace("(-):*.Response.Status={0}", res.Response.Status);
            return(res);
        }
Exemple #9
0
        /// <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)
        {
            ProxMessageBuilder mb = new ProxMessageBuilder(0, new List <SemVer>()
            {
                SemVer.V100
            }, null);
            ProxProtocolMessage response = mb.CreateErrorProtocolViolationResponse(new ProxProtocolMessage(new Message()
            {
                Id = 0x0BADC0DE
            }));

            await Client.SendMessageAsync(response);
        }
Exemple #10
0
        /// <summary>
        /// Starts conversation with the server the client is connected to and checks whether the server response contains expected values.
        /// </summary>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> StartConversationAsync()
        {
            log.Trace("()");

            bool res = false;

            ProxProtocolMessage requestMessage = CreateStartConversationRequest();

            if (await SendMessageAsync(requestMessage))
            {
                ProxProtocolMessage responseMessage = await ReceiveMessageAsync();

                if (CheckResponseMessage(requestMessage, responseMessage))
                {
                    try
                    {
                        SemVer receivedVersion = new SemVer(responseMessage.Response.ConversationResponse.Start.Version);
                        bool   versionOk       = receivedVersion.Equals(new SemVer(MessageBuilder.Version));

                        bool pubKeyLenOk = responseMessage.Response.ConversationResponse.Start.PublicKey.Length == ProtocolHelper.NetworkIdentifierLength;
                        bool challengeOk = responseMessage.Response.ConversationResponse.Start.Challenge.Length == ProxMessageBuilder.ChallengeDataSize;

                        serverKey       = responseMessage.Response.ConversationResponse.Start.PublicKey.ToByteArray();
                        serverId        = Crypto.Sha256(serverKey);
                        serverChallenge = responseMessage.Response.ConversationResponse.Start.Challenge.ToByteArray();
                        bool challengeVerifyOk = VerifyServerChallengeSignature(responseMessage);

                        res = versionOk && pubKeyLenOk && challengeOk && challengeVerifyOk;
                    }
                    catch
                    {
                        log.Warn("Received unexpected or invalid message.");
                    }
                }
                else
                {
                    log.Warn("Received unexpected or invalid message.");
                }
            }
            else
            {
                log.Warn("Unable to send message.");
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #11
0
        /// <summary>
        /// Processes VerifyIdentityRequest message from client.
        /// <para>It verifies the identity's public key against the signature of the challenge provided during the start of the conversation.
        /// If everything is OK, the status of the conversation is upgraded to Verified.</para>
        /// </summary>
        /// <param name="Client">Client that sent the request.</param>
        /// <param name="RequestMessage">Full request message.</param>
        /// <returns>Response message to be sent to the client.</returns>
        public ProxProtocolMessage ProcessMessageVerifyIdentityRequest(IncomingClient Client, ProxProtocolMessage RequestMessage)
        {
            log.Trace("()");

            ProxProtocolMessage res = null;

            if (!CheckSessionConditions(Client, RequestMessage, ServerRole.Client | ServerRole.Neighbor, ClientConversationStatus.ConversationStarted, out res))
            {
                log.Trace("(-):*.Response.Status={0}", res.Response.Status);
                return(res);
            }


            ProxMessageBuilder    messageBuilder        = Client.MessageBuilder;
            VerifyIdentityRequest verifyIdentityRequest = RequestMessage.Request.ConversationRequest.VerifyIdentity;

            byte[] challenge = verifyIdentityRequest.Challenge.ToByteArray();
            if (StructuralEqualityComparer <byte[]> .Default.Equals(challenge, Client.AuthenticationChallenge))
            {
                if (messageBuilder.VerifySignedConversationRequestBody(RequestMessage, verifyIdentityRequest, Client.PublicKey))
                {
                    log.Debug("Identity '{0}' successfully verified its public key.", Client.IdentityId.ToHex());
                    Client.ConversationStatus = ClientConversationStatus.Verified;
                    res = messageBuilder.CreateVerifyIdentityResponse(RequestMessage);
                }
                else
                {
                    log.Warn("Client's challenge signature is invalid.");
                    res = messageBuilder.CreateErrorInvalidSignatureResponse(RequestMessage);
                }
            }
            else
            {
                log.Warn("Challenge provided in the request does not match the challenge created by the proximity server.");
                res = messageBuilder.CreateErrorInvalidValueResponse(RequestMessage, "challenge");
            }

            log.Trace("(-):*.Response.Status={0}", res.Response.Status);
            return(res);
        }
Exemple #12
0
        /// <summary>
        /// Processes ListRolesRequest message from client.
        /// <para>Obtains a list of role servers and returns it in the response.</para>
        /// </summary>
        /// <param name="Client">Client that sent the request.</param>
        /// <param name="RequestMessage">Full request message.</param>
        /// <returns>Response message to be sent to the client.</returns>
        public ProxProtocolMessage ProcessMessageListRolesRequest(IncomingClient Client, ProxProtocolMessage RequestMessage)
        {
            log.Trace("()");

            ProxProtocolMessage res = null;

            if (!CheckSessionConditions(Client, RequestMessage, ServerRole.Primary, null, out res))
            {
                log.Trace("(-):*.Response.Status={0}", res.Response.Status);
                return(res);
            }

            ProxMessageBuilder messageBuilder   = Client.MessageBuilder;
            ListRolesRequest   listRolesRequest = RequestMessage.Request.SingleRequest.ListRoles;

            List <Iop.Proximityserver.ServerRole> roles = GetRolesFromServerComponent();

            res = messageBuilder.CreateListRolesResponse(RequestMessage, roles);

            log.Trace("(-):*.Response.Status={0}", res.Response.Status);
            return(res);
        }
        /// <summary>
        /// Checks whether the create activity request is valid.
        /// <para>This function does not verify the uniqueness of the activity identifier.
        /// It also does not verify whether a closer proximity server exists.</para>
        /// </summary>
        /// <param name="CreateActivityRequest">Create activity request part of the client's request message.</param>
        /// <param name="Client">Client that sent the request.</param>
        /// <param name="RequestMessage">Full request message from client.</param>
        /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param>
        /// <returns>true if the create activity request can be applied, false otherwise.</returns>
        public static bool ValidateCreateActivityRequest(CreateActivityRequest CreateActivityRequest, IncomingClient Client, ProxProtocolMessage RequestMessage, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("()");

            bool res = false;

            ErrorResponse = null;
            string details = null;

            if (CreateActivityRequest == null)
            {
                CreateActivityRequest = new CreateActivityRequest();
            }
            if (CreateActivityRequest.Activity == null)
            {
                CreateActivityRequest.Activity = new ActivityInformation();
            }
            if (CreateActivityRequest.Activity.ProfileServerContact == null)
            {
                CreateActivityRequest.Activity.ProfileServerContact = new ServerContactInfo();
            }

            SignedActivityInformation signedActivity = new SignedActivityInformation()
            {
                Activity  = CreateActivityRequest.Activity,
                Signature = RequestMessage.Request.ConversationRequest.Signature
            };


            if (ValidateSignedActivityInformation(signedActivity, Client.PublicKey, Client.MessageBuilder, RequestMessage, "", false, out ErrorResponse))
            {
                byte[] ownerPubKey = CreateActivityRequest.Activity.OwnerPublicKey.ToByteArray();

                if (!ByteArrayComparer.Equals(Client.PublicKey, ownerPubKey))
                {
                    log.Debug("Client's public key '{0}' does not match activity owner's public key '{1}'.", Client.PublicKey.ToHex(), ownerPubKey.ToHex());
                    details = "activity.ownerPublicKey";
                }


                if (details == null)
                {
                    int index = 0;
                    foreach (ByteString serverId in CreateActivityRequest.IgnoreServerIds)
                    {
                        if (serverId.Length != ProtocolHelper.NetworkIdentifierLength)
                        {
                            log.Debug("Ignored server ID #{0} is not a valid network ID as its length is {1} bytes.", index, serverId.Length);
                            details = "ignoreServerIds";
                            break;
                        }

                        index++;
                    }
                }

                if (details == null)
                {
                    res = true;
                }
                else
                {
                    ErrorResponse = Client.MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, details);
                }
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #14
0
        /// <summary>
        /// Verifies that client's request was not sent against the protocol rules - i.e. that the role server
        /// that received the message is serving the role the message was designed for and that the conversation
        /// status with the clients matches the required status for the particular message.
        /// </summary>
        /// <param name="Client">Client that sent the request.</param>
        /// <param name="RequestMessage">Full request message.</param>
        /// <param name="RequiredRole">Server role required for the message, or null if all roles servers can handle this message.</param>
        /// <param name="RequiredConversationStatus">Required conversation status for the message, or null for single messages.</param>
        /// <param name="ResponseMessage">If the verification fails, this is filled with error response to be sent to the client.</param>
        /// <returns>true if the function succeeds (i.e. required conditions are met and the message can be processed), false otherwise.</returns>
        public bool CheckSessionConditions(IncomingClient Client, ProxProtocolMessage RequestMessage, ServerRole?RequiredRole, ClientConversationStatus?RequiredConversationStatus, out ProxProtocolMessage ResponseMessage)
        {
            log.Trace("(RequiredRole:{0},RequiredConversationStatus:{1})", RequiredRole != null ? RequiredRole.ToString() : "null", RequiredConversationStatus != null ? RequiredConversationStatus.Value.ToString() : "null");

            bool res = false;

            ResponseMessage = null;

            string requestName = RequestMessage.Request.ConversationTypeCase == Request.ConversationTypeOneofCase.SingleRequest ? "single request " + RequestMessage.Request.SingleRequest.RequestTypeCase.ToString() : "conversation request " + RequestMessage.Request.ConversationRequest.RequestTypeCase.ToString();

            // RequiredRole contains one or more roles and the current server has to have at least one of them.
            if ((RequiredRole == null) || ((roleServer.Roles & (uint)RequiredRole.Value) != 0))
            {
                if (RequiredConversationStatus == null)
                {
                    res = true;
                }
                else
                {
                    switch (RequiredConversationStatus.Value)
                    {
                    case ClientConversationStatus.NoConversation:
                    case ClientConversationStatus.ConversationStarted:
                        res = Client.ConversationStatus == RequiredConversationStatus.Value;
                        if (!res)
                        {
                            log.Warn("Client sent {0} but the conversation status is {1}.", requestName, Client.ConversationStatus);
                            ResponseMessage = Client.MessageBuilder.CreateErrorBadConversationStatusResponse(RequestMessage);
                        }
                        break;

                    case ClientConversationStatus.Verified:
                        res = Client.ConversationStatus == RequiredConversationStatus.Value;
                        if (!res)
                        {
                            log.Warn("Client sent {0} but the conversation status is {1}.", requestName, Client.ConversationStatus);
                            ResponseMessage = Client.MessageBuilder.CreateErrorUnauthorizedResponse(RequestMessage);
                        }
                        break;


                    case ClientConversationStatus.ConversationAny:
                        res = (Client.ConversationStatus == ClientConversationStatus.ConversationStarted) ||
                              (Client.ConversationStatus == ClientConversationStatus.Verified);

                        if (!res)
                        {
                            log.Warn("Client sent {0} but the conversation status is {1}.", requestName, Client.ConversationStatus);
                            ResponseMessage = Client.MessageBuilder.CreateErrorBadConversationStatusResponse(RequestMessage);
                        }
                        break;

                    default:
                        log.Error("Unknown conversation status '{0}'.", Client.ConversationStatus);
                        ResponseMessage = Client.MessageBuilder.CreateErrorInternalResponse(RequestMessage);
                        break;
                    }
                }
            }
            else
            {
                log.Warn("Received {0} on server without {1} role(s) (server roles are {2}).", requestName, RequiredRole.Value, roleServer.Roles);
                ResponseMessage = Client.MessageBuilder.CreateErrorBadRoleResponse(RequestMessage);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Validates incoming update item during the neighborhood initialization process with in-memory database of activities from the neighbor.
        /// </summary>
        /// <param name="AddItem">Update item to validate.</param>
        /// <param name="Index">Index of the update item in the message.</param>
        /// <param name="ActivityDatabase">In-memory temporary database of activities from the neighbor server that were already received and processed.</param>
        /// <param name="MessageBuilder">Client's network message builder.</param>
        /// <param name="RequestMessage">Full request message received by the client.</param>
        /// <param name="ErrorResponse">If the validation fails, this is filled with response message to be sent to the neighbor.</param>
        /// <returns>true if the validation is successful, false otherwise.</returns>
        public static bool ValidateInMemorySharedActivityActivityInformation(SharedActivityAddItem AddItem, int Index, Dictionary <string, NeighborActivity> ActivityDatabase, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("(Index:{0})", Index);

            bool res = false;

            ErrorResponse = null;

            if (AddItem == null)
            {
                AddItem = new SharedActivityAddItem();
            }
            if (AddItem.SignedActivity == null)
            {
                AddItem.SignedActivity = new SignedActivityInformation();
            }
            if (AddItem.SignedActivity.Activity == null)
            {
                AddItem.SignedActivity.Activity = new ActivityInformation();
            }

            byte[] ownerIdentityPubKey = AddItem.SignedActivity.Activity.OwnerPublicKey.ToByteArray();
            if (ValidateSignedActivityInformation(AddItem.SignedActivity, ownerIdentityPubKey, MessageBuilder, RequestMessage, Index.ToString() + ".add.signedActivity.", true, out ErrorResponse))
            {
                string details = null;
                if (ActivityDatabase.Count >= ActivityBase.MaxPrimaryActivities)
                {
                    log.Debug("Target server already sent too many activities.");
                    details = "add";
                }

                if (details == null)
                {
                    byte[]           identityId = Crypto.Sha256(ownerIdentityPubKey);
                    NeighborActivity na         = new NeighborActivity()
                    {
                        ActivityId      = AddItem.SignedActivity.Activity.Id,
                        OwnerIdentityId = identityId
                    };

                    string activityFullId = na.GetFullId();
                    if (ActivityDatabase.ContainsKey(activityFullId))
                    {
                        log.Debug("Activity full ID '{0}' (public key '{1}') already exists.", activityFullId, ownerIdentityPubKey.ToHex());
                        details = "add.signedActivity.activity.id";
                    }
                }

                if (details == null)
                {
                    res = true;
                }
                else
                {
                    ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, Index.ToString() + "." + details);
                }
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Validates incoming SharedActivityDeleteItem update item.
        /// </summary>
        /// <para>This function does not verify whether the activity exists.</para>
        /// <param name="DeleteItem">Delete item to validate.</param>
        /// <param name="Index">Item index in the update message.</param>
        /// <param name="UsedFullActivityIdsInBatch">List of activity full IDs of already validated items of this batch.</param>
        /// <param name="MessageBuilder">Client's network message builder.</param>
        /// <param name="RequestMessage">Full request message received by the client.</param>
        /// <param name="ErrorResponse">If the validation fails, this is filled with response message to be sent to the neighbor.</param>
        /// <returns>true if the validation is successful, false otherwise.</returns>
        public static bool ValidateSharedActivityDeleteItem(SharedActivityDeleteItem DeleteItem, int Index, HashSet <string> UsedFullActivityIdsInBatch, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("(Index:{0})", Index);

            bool res = false;

            ErrorResponse = null;

            if (DeleteItem == null)
            {
                DeleteItem = new SharedActivityDeleteItem();
            }

            string details = null;

            uint activityId = DeleteItem.Id;

            // 0 is not a valid activity identifier.
            if (activityId == 0)
            {
                log.Debug("Invalid activity ID '{0}'.", activityId);
                details = "delete.id";
            }

            if (details == null)
            {
                byte[] identityId      = DeleteItem.OwnerNetworkId.ToByteArray();
                bool   identityIdValid = identityId.Length == ProtocolHelper.NetworkIdentifierLength;
                if (identityIdValid)
                {
                    NeighborActivity na = new NeighborActivity()
                    {
                        ActivityId      = DeleteItem.Id,
                        OwnerIdentityId = identityId
                    };

                    string activityFullId = na.GetFullId();
                    if (!UsedFullActivityIdsInBatch.Contains(activityFullId))
                    {
                        UsedFullActivityIdsInBatch.Add(activityFullId);
                    }
                    else
                    {
                        log.Debug("Activity full ID '{0}' already processed in this request.", activityFullId);
                        details = "delete.id";
                    }
                }
                else
                {
                    log.Debug("Invalid owner network ID length '{0}'.", identityId.Length);
                    details = "delete.ownerNetworkId";
                }
            }


            if (details == null)
            {
                res = true;
            }
            else
            {
                ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, Index.ToString() + "." + details);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Validates incoming SharedActivityChangeItem update item.
        /// <para>This function does not verify whether the activity exists.
        /// It also does not verify whether the new activity start time and expiration time are correct if they are not both changed.</para>
        /// </summary>
        /// <param name="ChangeItem">Change item to validate.</param>
        /// <param name="Index">Item index in the update message.</param>
        /// <param name="UsedFullActivityIdsInBatch">List of activity full IDs of already validated items of this batch.</param>
        /// <param name="MessageBuilder">Client's network message builder.</param>
        /// <param name="RequestMessage">Full request message received by the client.</param>
        /// <param name="ErrorResponse">If the validation fails, this is filled with response message to be sent to the neighbor.</param>
        /// <returns>true if the validation is successful, false otherwise.</returns>
        public static bool ValidateSharedActivityChangeItem(SharedActivityChangeItem ChangeItem, int Index, HashSet <string> UsedFullActivityIdsInBatch, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("(Index:{0})", Index);

            bool res = false;

            ErrorResponse = null;

            if (ChangeItem == null)
            {
                ChangeItem = new SharedActivityChangeItem();
            }
            if (ChangeItem.SignedActivity == null)
            {
                ChangeItem.SignedActivity = new SignedActivityInformation();
            }
            if (ChangeItem.SignedActivity.Activity == null)
            {
                ChangeItem.SignedActivity.Activity = new ActivityInformation();
            }


            byte[] ownerIdentityPubKey = ChangeItem.SignedActivity.Activity.OwnerPublicKey.ToByteArray();
            if (ValidateSignedActivityInformation(ChangeItem.SignedActivity, ownerIdentityPubKey, MessageBuilder, RequestMessage, Index.ToString() + ".change.signedActivity.", true, out ErrorResponse))
            {
                string details = null;

                byte[] identityId = Crypto.Sha256(ownerIdentityPubKey);

                NeighborActivity na = new NeighborActivity()
                {
                    ActivityId      = ChangeItem.SignedActivity.Activity.Id,
                    OwnerIdentityId = identityId
                };

                string activityFullId = na.GetFullId();
                if (!UsedFullActivityIdsInBatch.Contains(activityFullId))
                {
                    UsedFullActivityIdsInBatch.Add(activityFullId);
                }
                else
                {
                    log.Debug("Activity full ID '{0}' already processed in this request.", activityFullId);
                    details = "change.signedActivity.activity.id";
                }

                if (details == null)
                {
                    res = true;
                }
                else
                {
                    ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, Index.ToString() + "." + details);
                }
            }


            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Validates incoming SharedActivityUpdateItem update item.
        /// </summary>
        /// <param name="UpdateItem">Update item to validate.</param>
        /// <param name="Index">Item index in the update message.</param>
        /// <param name="SharedActivitiesCount">Number of activities the neighbor already shares with the activity server.</param>
        /// <param name="UsedFullActivityIdsInBatch">List of activity full IDs of already validated items of this batch.</param>
        /// <param name="MessageBuilder">Client's network message builder.</param>
        /// <param name="RequestMessage">Full request message received by the client.</param>
        /// <param name="ErrorResponse">If the validation fails, this is filled with response message to be sent to the neighbor.</param>
        /// <returns>true if the validation is successful, false otherwise.</returns>
        public static bool ValidateSharedActivityUpdateItem(SharedActivityUpdateItem UpdateItem, int Index, int SharedActivitiesCount, HashSet <string> UsedFullActivityIdsInBatch, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("(Index:{0},SharedActivitiesCount:{1})", Index, SharedActivitiesCount);

            bool res = false;

            ErrorResponse = null;

            switch (UpdateItem.ActionTypeCase)
            {
            case SharedActivityUpdateItem.ActionTypeOneofCase.Add:
                res = ValidateSharedActivityAddItem(UpdateItem.Add, Index, SharedActivitiesCount, UsedFullActivityIdsInBatch, MessageBuilder, RequestMessage, out ErrorResponse);
                break;

            case SharedActivityUpdateItem.ActionTypeOneofCase.Change:
                res = ValidateSharedActivityChangeItem(UpdateItem.Change, Index, UsedFullActivityIdsInBatch, MessageBuilder, RequestMessage, out ErrorResponse);
                break;

            case SharedActivityUpdateItem.ActionTypeOneofCase.Delete:
                res = ValidateSharedActivityDeleteItem(UpdateItem.Delete, Index, UsedFullActivityIdsInBatch, MessageBuilder, RequestMessage, out ErrorResponse);
                break;

            case SharedActivityUpdateItem.ActionTypeOneofCase.Refresh:
                res = true;
                break;

            default:
                ErrorResponse = MessageBuilder.CreateErrorProtocolViolationResponse(RequestMessage);
                res           = false;
                break;
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Checks whether the activity search request is valid.
        /// </summary>
        /// <param name="ActivitySearchRequest">Activity search request part of the client's request message.</param>
        /// <param name="MessageBuilder">Client's network message builder.</param>
        /// <param name="RequestMessage">Full request message from client.</param>
        /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param>
        /// <returns>true if the search request is valid, false otherwise.</returns>
        public static bool ValidateActivitySearchRequest(ActivitySearchRequest ActivitySearchRequest, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("()");

            bool res = false;

            ErrorResponse = null;
            string details = null;

            int responseResultLimit = ProxMessageProcessor.ActivitySearchMaxResponseRecords;
            int totalResultLimit    = ProxMessageProcessor.ActivitySearchMaxTotalRecords;

            bool maxResponseRecordCountValid = (1 <= ActivitySearchRequest.MaxResponseRecordCount) &&
                                               (ActivitySearchRequest.MaxResponseRecordCount <= responseResultLimit);

            if (!maxResponseRecordCountValid)
            {
                log.Debug("Invalid maxResponseRecordCount value '{0}'.", ActivitySearchRequest.MaxResponseRecordCount);
                details = "maxResponseRecordCount";
            }

            if (details == null)
            {
                bool maxTotalRecordCountValid = (1 <= ActivitySearchRequest.MaxTotalRecordCount) &&
                                                (ActivitySearchRequest.MaxTotalRecordCount <= totalResultLimit) &&
                                                (ActivitySearchRequest.MaxResponseRecordCount <= ActivitySearchRequest.MaxTotalRecordCount);
                if (!maxTotalRecordCountValid)
                {
                    log.Debug("Invalid maxTotalRecordCount value '{0}'.", ActivitySearchRequest.MaxTotalRecordCount);
                    details = "maxTotalRecordCount";
                }
            }

            if ((details == null) && (ActivitySearchRequest.OwnerNetworkId.Length > 0))
            {
                bool ownerNetworkIdValid = ActivitySearchRequest.OwnerNetworkId.Length == ProtocolHelper.NetworkIdentifierLength;
                if (!ownerNetworkIdValid)
                {
                    log.Debug("Invalid owner network ID length '{0}'.", ActivitySearchRequest.OwnerNetworkId.Length);
                    details = "ownerNetworkId";
                }
            }

            if ((details == null) && (ActivitySearchRequest.Type != null))
            {
                bool typeValid = Encoding.UTF8.GetByteCount(ActivitySearchRequest.Type) <= ProxMessageBuilder.MaxActivitySearchTypeLengthBytes;
                if (!typeValid)
                {
                    log.Debug("Invalid type value length '{0}'.", ActivitySearchRequest.Type.Length);
                    details = "type";
                }
            }

            if ((details == null) && ((ActivitySearchRequest.StartNotAfter != 0) || (ActivitySearchRequest.ExpirationNotBefore != 0)))
            {
                DateTime?startNotAfter       = null;
                DateTime?expirationNotBefore = null;

                if (ActivitySearchRequest.StartNotAfter != 0)
                {
                    startNotAfter = ProtocolHelper.UnixTimestampMsToDateTime(ActivitySearchRequest.StartNotAfter);
                    bool startNotAfterValid = startNotAfter != null;
                    if (!startNotAfterValid)
                    {
                        log.Debug("Start not after {0} is not a valid timestamp value.", ActivitySearchRequest.StartNotAfter);
                        details = "startNotAfter";
                    }
                }

                if ((details == null) && (ActivitySearchRequest.ExpirationNotBefore != 0))
                {
                    expirationNotBefore = ProtocolHelper.UnixTimestampMsToDateTime(ActivitySearchRequest.ExpirationNotBefore);
                    bool expirationNotBeforeValid = expirationNotBefore != null;
                    if (!expirationNotBeforeValid)
                    {
                        log.Debug("Expiration not before {0} is not a valid timestamp value.", ActivitySearchRequest.ExpirationNotBefore);
                        details = "expirationNotBefore";
                    }
                    else if (ActivitySearchRequest.StartNotAfter != 0)
                    {
                        expirationNotBeforeValid = ActivitySearchRequest.StartNotAfter <= ActivitySearchRequest.ExpirationNotBefore;
                        if (!expirationNotBeforeValid)
                        {
                            log.Debug("Expiration not before {0} is smaller than start not after {1}.", ActivitySearchRequest.StartNotAfter, ActivitySearchRequest.ExpirationNotBefore);
                            details = "expirationNotBefore";
                        }
                    }
                }
            }

            if ((details == null) && (ActivitySearchRequest.Latitude != GpsLocation.NoLocationLocationType))
            {
                GpsLocation locLat  = new GpsLocation(ActivitySearchRequest.Latitude, 0);
                GpsLocation locLong = new GpsLocation(0, ActivitySearchRequest.Longitude);
                if (!locLat.IsValid())
                {
                    log.Debug("Latitude '{0}' is not a valid GPS latitude value.", ActivitySearchRequest.Latitude);
                    details = "latitude";
                }
                else if (!locLong.IsValid())
                {
                    log.Debug("Longitude '{0}' is not a valid GPS longitude value.", ActivitySearchRequest.Longitude);
                    details = "longitude";
                }
            }

            if ((details == null) && (ActivitySearchRequest.Latitude != GpsLocation.NoLocationLocationType))
            {
                bool radiusValid = ActivitySearchRequest.Radius > 0;
                if (!radiusValid)
                {
                    log.Debug("Invalid radius value '{0}'.", ActivitySearchRequest.Radius);
                    details = "radius";
                }
            }

            if ((details == null) && (ActivitySearchRequest.ExtraData != null))
            {
                bool validLength    = (Encoding.UTF8.GetByteCount(ActivitySearchRequest.ExtraData) <= ProxMessageBuilder.MaxActivitySearchExtraDataLengthBytes);
                bool extraDataValid = RegexTypeValidator.ValidateRegex(ActivitySearchRequest.ExtraData);
                if (!validLength || !extraDataValid)
                {
                    log.Debug("Invalid extraData regular expression filter.");
                    details = "extraData";
                }
            }

            if (details == null)
            {
                res = true;
            }
            else
            {
                ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, details);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #20
0
        /// <summary>
        /// Checks whether the incoming response is a reponse to a specific request and if the reported status is OK.
        /// <para>This function sets the last error.</para>
        /// </summary>
        /// <param name="RequestMessage">Request message for which the response was received.</param>
        /// <param name="ResponseMessage">Response message received.</param>
        /// <returns>true if the response is valid and its status is OK.</returns>
        public bool CheckResponseMessage(ProxProtocolMessage RequestMessage, ProxProtocolMessage ResponseMessage)
        {
            log.Trace("()");

            bool res = false;

            if (ResponseMessage != null)
            {
                if (ResponseMessage.Id == RequestMessage.Id)
                {
                    if (ResponseMessage.MessageTypeCase == Message.MessageTypeOneofCase.Response)
                    {
                        Response response = ResponseMessage.Response;
                        if (response.Status == Status.Ok)
                        {
                            switch (response.ConversationTypeCase)
                            {
                            case Response.ConversationTypeOneofCase.SingleResponse:
                                if (RequestMessage.Request.ConversationTypeCase == Request.ConversationTypeOneofCase.SingleRequest)
                                {
                                    SingleRequest.RequestTypeOneofCase requestType = RequestMessage.Request.SingleRequest.RequestTypeCase;
                                    if (response.SingleResponse.ResponseTypeCase.ToString() == requestType.ToString())
                                    {
                                        res = true;
                                    }
                                    else
                                    {
                                        log.Debug("Single response type {0} does not match single request type {1}.", response.SingleResponse.ResponseTypeCase, requestType);
                                    }
                                }
                                else
                                {
                                    log.Debug("Response message conversation type {0} does not match request message conversation type {1}.", response.ConversationTypeCase, RequestMessage.Request.ConversationTypeCase);
                                }
                                break;

                            case Response.ConversationTypeOneofCase.ConversationResponse:
                                if (RequestMessage.Request.ConversationTypeCase == Request.ConversationTypeOneofCase.ConversationRequest)
                                {
                                    ConversationRequest.RequestTypeOneofCase requestType = RequestMessage.Request.ConversationRequest.RequestTypeCase;
                                    if (response.ConversationResponse.ResponseTypeCase.ToString() == requestType.ToString())
                                    {
                                        res = true;
                                    }
                                    else
                                    {
                                        log.Debug("Conversation response type {0} does not match conversation request type {1}.", response.ConversationResponse.ResponseTypeCase, requestType);
                                    }
                                }
                                else
                                {
                                    log.Debug("Response message conversation type {0} does not match request message conversation type {1}.", response.ConversationTypeCase, RequestMessage.Request.ConversationTypeCase);
                                }
                                break;

                            default:
                                log.Error("Invalid response conversation type {0}.", ResponseMessage.Response.ConversationTypeCase);
                                break;
                            }
                        }
                        else
                        {
                            log.Debug("Response message status is {0}.", ResponseMessage.Response.Status);
                        }
                    }
                    else
                    {
                        log.Debug("Received message is not response, its message type is {0}.", ResponseMessage.MessageTypeCase);
                    }
                }
                else
                {
                    log.Debug("Response message ID {0} does not match request message ID {1}.", ResponseMessage.Id, RequestMessage.Id);
                }
            }
            else
            {
                log.Debug("Response message is null.");
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Checks whether a signed activity information is valid.
        /// </summary>
        /// <param name="SignedActivity">Signed activity information to check.</param>
        /// <param name="OwnerIdentityPublicKey">Public key of the activity's owner identity.</param>
        /// <param name="MessageBuilder">Client's network message builder.</param>
        /// <param name="RequestMessage">Full request message from client.</param>
        /// <param name="ErrorPrefix">Prefix to add to the validation error details.</param>
        /// <param name="InvalidSignatureToDetails">If set to true, invalid signature error will be reported as invalid value in signature field.</param>
        /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param>
        /// <returns>true if the signed activity information is valid and signed correctly by the given identity, false otherwise.</returns>
        public static bool ValidateSignedActivityInformation(SignedActivityInformation SignedActivity, byte[] OwnerIdentityPublicKey, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, string ErrorPrefix, bool InvalidSignatureToDetails, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("()");
            ErrorResponse = null;

            if (SignedActivity == null)
            {
                SignedActivity = new SignedActivityInformation();
            }
            if (SignedActivity.Activity == null)
            {
                SignedActivity.Activity = new ActivityInformation();
            }

            bool res = false;

            if (ValidateActivityInformation(SignedActivity.Activity, OwnerIdentityPublicKey, MessageBuilder, RequestMessage, ErrorPrefix + "activity.", out ErrorResponse))
            {
                // ActivityBase.InternalInvalidActivityType is a special internal type of activity that we use to prevent problems in the network.
                // This is not an elegant solution.
                // See NeighborhoodActionProcessor.NeighborhoodActivityUpdateAsync case NeighborhoodActionType.AddActivity for more information.
                if (SignedActivity.Activity.Type != ActivityBase.InternalInvalidActivityType)
                {
                    byte[] signature = SignedActivity.Signature.ToByteArray();
                    byte[] data      = SignedActivity.Activity.ToByteArray();

                    if (Ed25519.Verify(signature, data, OwnerIdentityPublicKey))
                    {
                        res = true;
                    }
                    else
                    {
                        ErrorResponse = InvalidSignatureToDetails ? MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, ErrorPrefix + "signature") : MessageBuilder.CreateErrorInvalidSignatureResponse(RequestMessage);
                    }
                }
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #22
0
        /// <summary>
        /// Processes StartConversationRequest message from client.
        /// <para>Initiates a conversation with the client provided that there is a common version of the protocol supported by both sides.</para>
        /// </summary>
        /// <param name="Client">Client that sent the request.</param>
        /// <param name="RequestMessage">Full request message.</param>
        /// <returns>Response message to be sent to the client.</returns>
        public ProxProtocolMessage ProcessMessageStartConversationRequest(IncomingClient Client, ProxProtocolMessage RequestMessage)
        {
            log.Trace("()");

            ProxProtocolMessage res = null;

            if (!CheckSessionConditions(Client, RequestMessage, null, ClientConversationStatus.NoConversation, out res))
            {
                log.Trace("(-):*.Response.Status={0}", res.Response.Status);
                return(res);
            }


            ProxMessageBuilder       messageBuilder           = Client.MessageBuilder;
            StartConversationRequest startConversationRequest = RequestMessage.Request.ConversationRequest.Start;

            byte[] clientChallenge = startConversationRequest.ClientChallenge.ToByteArray();
            byte[] pubKey          = startConversationRequest.PublicKey.ToByteArray();

            if (clientChallenge.Length == ProxMessageBuilder.ChallengeDataSize)
            {
                if ((0 < pubKey.Length) && (pubKey.Length <= ProtocolHelper.MaxPublicKeyLengthBytes))
                {
                    SemVer version;
                    if (GetCommonSupportedVersion(startConversationRequest.SupportedVersions, out version))
                    {
                        Client.PublicKey  = pubKey;
                        Client.IdentityId = Crypto.Sha256(Client.PublicKey);

                        if (clientList.AddNetworkPeerWithIdentity(Client))
                        {
                            Client.MessageBuilder.SetProtocolVersion(version);

                            byte[] challenge = new byte[ProxMessageBuilder.ChallengeDataSize];
                            Crypto.Rng.GetBytes(challenge);
                            Client.AuthenticationChallenge = challenge;
                            Client.ConversationStatus      = ClientConversationStatus.ConversationStarted;

                            log.Debug("Client {0} conversation status updated to {1}, selected version is '{2}', client public key set to '{3}', client identity ID set to '{4}', challenge set to '{5}'.",
                                      Client.RemoteEndPoint, Client.ConversationStatus, version, Client.PublicKey.ToHex(), Client.IdentityId.ToHex(), Client.AuthenticationChallenge.ToHex());

                            res = messageBuilder.CreateStartConversationResponse(RequestMessage, version, Config.Configuration.Keys.PublicKey, Client.AuthenticationChallenge, clientChallenge);
                        }
                        else
                        {
                            res = messageBuilder.CreateErrorInternalResponse(RequestMessage);
                        }
                    }
                    else
                    {
                        log.Warn("Client and server are incompatible in protocol versions.");
                        res = messageBuilder.CreateErrorUnsupportedResponse(RequestMessage);
                    }
                }
                else
                {
                    log.Warn("Client send public key of invalid length of {0} bytes.", pubKey.Length);
                    res = messageBuilder.CreateErrorInvalidValueResponse(RequestMessage, "publicKey");
                }
            }
            else
            {
                log.Warn("Client send clientChallenge, which is {0} bytes long, but it should be {1} bytes long.", clientChallenge.Length, ProxMessageBuilder.ChallengeDataSize);
                res = messageBuilder.CreateErrorInvalidValueResponse(RequestMessage, "clientChallenge");
            }

            log.Trace("(-):*.Response.Status={0}", res.Response.Status);
            return(res);
        }
        /// <summary>
        /// Validates ActivityInformation structure by itself. This function does not touch database and does not validate
        /// anything that depends on the context.
        /// </summary>
        /// <param name="Activity">Description of the activity to validate.</param>
        /// <param name="OwnerIdentityPublicKey">Public key of the activity's owner identity.</param>
        /// <param name="MessageBuilder">Network message builder of the client who sent this activity description.</param>
        /// <param name="RequestMessage">Full request message from client.</param>
        /// <param name="ErrorPrefix">Prefix to add to the validation error details.</param>
        /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param>
        /// <returns>true if the activity information is valid, false otherwise.</returns>
        public static bool ValidateActivityInformation(ActivityInformation Activity, byte[] OwnerIdentityPublicKey, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, string ErrorPrefix, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("()");

            bool res = false;

            ErrorResponse = null;
            string details = null;

            if (Activity == null)
            {
                Activity = new ActivityInformation();
            }
            if (Activity.ProfileServerContact == null)
            {
                Activity.ProfileServerContact = new ServerContactInfo();
            }

            SemVer version = new SemVer(Activity.Version);

            // Currently only supported version is 1.0.0.
            if (!version.Equals(SemVer.V100))
            {
                log.Debug("Unsupported version '{0}'.", version);
                details = "version";
            }


            if (details == null)
            {
                uint activityId = Activity.Id;

                // 0 is not a valid activity identifier.
                if (activityId == 0)
                {
                    log.Debug("Invalid activity ID '{0}'.", activityId);
                    details = "id";
                }
            }

            if (details == null)
            {
                byte[] pubKey      = Activity.OwnerPublicKey.ToByteArray();
                bool   pubKeyValid = (0 < pubKey.Length) && (pubKey.Length <= ProtocolHelper.MaxPublicKeyLengthBytes) && ByteArrayComparer.Equals(OwnerIdentityPublicKey, pubKey);
                if (!pubKeyValid)
                {
                    log.Debug("Invalid public key '{0}' does not match identity public key '{1}'.", pubKey.ToHex(), OwnerIdentityPublicKey.ToHex());
                    details = "ownerPublicKey";
                }
            }


            if (details == null)
            {
                ServerContactInfo sci    = Activity.ProfileServerContact;
                bool      networkIdValid = sci.NetworkId.Length == ProtocolHelper.NetworkIdentifierLength;
                IPAddress ipAddress      = IPAddressExtensions.IpFromBytes(sci.IpAddress.ToByteArray());
                bool      ipAddressValid = (ipAddress != null) && (Config.Configuration.TestModeEnabled || !ipAddress.IsReservedOrLocal());
                bool      portValid      = (1 <= sci.PrimaryPort) && (sci.PrimaryPort <= 65535);

                if (!networkIdValid || !ipAddressValid || !portValid)
                {
                    log.Debug("Profile server contact's network ID is {0}, IP address is {1}, port is {2}.", networkIdValid ? "valid" : "invalid", ipAddressValid ? "valid" : "invalid", portValid ? "valid" : "invalid");

                    if (!networkIdValid)
                    {
                        details = "profileServerContact.networkId";
                    }
                    else if (!ipAddressValid)
                    {
                        details = "profileServerContact.ipAddress";
                    }
                    else if (!portValid)
                    {
                        details = "profileServerContact.primaryPort";
                    }
                }
            }

            if (details == null)
            {
                string activityType = Activity.Type;
                if (activityType == null)
                {
                    activityType = "";
                }

                int  byteLen           = Encoding.UTF8.GetByteCount(activityType);
                bool activityTypeValid = (0 < byteLen) && (byteLen <= ProxMessageBuilder.MaxActivityTypeLengthBytes);
                if (!activityTypeValid)
                {
                    log.Debug("Activity type too long or zero length ({0} bytes, limit is {1}).", byteLen, ProxMessageBuilder.MaxActivityTypeLengthBytes);
                    details = "type";
                }
            }

            if (details == null)
            {
                GpsLocation locLat  = new GpsLocation(Activity.Latitude, 0);
                GpsLocation locLong = new GpsLocation(0, Activity.Longitude);
                if (!locLat.IsValid())
                {
                    log.Debug("Latitude '{0}' is not a valid GPS latitude value.", Activity.Latitude);
                    details = "latitude";
                }
                else if (!locLong.IsValid())
                {
                    log.Debug("Longitude '{0}' is not a valid GPS longitude value.", Activity.Longitude);
                    details = "longitude";
                }
            }

            if (details == null)
            {
                uint precision      = Activity.Precision;
                bool precisionValid = (0 <= precision) && (precision <= ProxMessageBuilder.MaxLocationPrecision);
                if (!precisionValid)
                {
                    log.Debug("Precision '{0}' is not an integer between 0 and {1}.", precision, ProxMessageBuilder.MaxLocationPrecision);
                    details = "precision";
                }
            }


            if (details == null)
            {
                DateTime?startTime      = ProtocolHelper.UnixTimestampMsToDateTime(Activity.StartTime);
                DateTime?expirationTime = ProtocolHelper.UnixTimestampMsToDateTime(Activity.ExpirationTime);

                if (startTime == null)
                {
                    log.Debug("Invalid activity start time timestamp '{0}'.", Activity.StartTime);
                    details = "startTime";
                }
                else if (expirationTime == null)
                {
                    log.Debug("Invalid activity expiration time timestamp '{0}'.", Activity.ExpirationTime);
                    details = "expirationTime";
                }
                else
                {
                    if (startTime > expirationTime)
                    {
                        log.Debug("Activity expiration time has to be greater than or equal to its start time.");
                        details = "expirationTime";
                    }
                    else if (expirationTime.Value > DateTime.UtcNow.AddHours(ActivityBase.MaxActivityLifeTimeHours))
                    {
                        log.Debug("Activity expiration time {0} is more than {1} hours in the future.", expirationTime.Value.ToString("yyyy-MM-dd HH:mm:ss"), ActivityBase.MaxActivityLifeTimeHours);
                        details = "expirationTime";
                    }
                }
            }

            if (details == null)
            {
                string extraData = Activity.ExtraData;
                if (extraData == null)
                {
                    extraData = "";
                }


                // Extra data is semicolon separated 'key=value' list, max ActivityBase.MaxActivityExtraDataLengthBytes bytes long.
                int byteLen = Encoding.UTF8.GetByteCount(extraData);
                if (byteLen > ProxMessageBuilder.MaxActivityExtraDataLengthBytes)
                {
                    log.Debug("Extra data too large ({0} bytes, limit is {1}).", byteLen, ProxMessageBuilder.MaxActivityExtraDataLengthBytes);
                    details = "extraData";
                }
            }

            if (details == null)
            {
                res = true;
            }
            else
            {
                ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, ErrorPrefix + details);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemple #24
0
        /// <summary>
        /// Processing of a message received from a client.
        /// </summary>
        /// <param name="Client">TCP client who send the message.</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(ClientBase Client, IProtocolMessage IncomingMessage)
        {
            IncomingClient      client          = (IncomingClient)Client;
            ProxProtocolMessage incomingMessage = (ProxProtocolMessage)IncomingMessage;
            ProxMessageBuilder  messageBuilder  = client.MessageBuilder;

            bool res = false;

            log.Debug("()");
            try
            {
                // Update time until this client's connection is considered inactive.
                client.NextKeepAliveTime = DateTime.UtcNow.AddMilliseconds(client.KeepAliveIntervalMs);
                log.Trace("Client ID {0} NextKeepAliveTime updated to {1}.", client.Id.ToHex(), client.NextKeepAliveTime.ToString("yyyy-MM-dd HH:mm:ss"));

                log.Trace("Received message type is {0}, message ID is {1}.", incomingMessage.MessageTypeCase, incomingMessage.Id);
                switch (incomingMessage.MessageTypeCase)
                {
                case Message.MessageTypeOneofCase.Request:
                {
                    ProxProtocolMessage responseMessage = messageBuilder.CreateErrorProtocolViolationResponse(incomingMessage);
                    Request             request         = incomingMessage.Request;
                    log.Trace("Request conversation type is {0}.", request.ConversationTypeCase);
                    switch (request.ConversationTypeCase)
                    {
                    case Request.ConversationTypeOneofCase.SingleRequest:
                    {
                        SingleRequest singleRequest = request.SingleRequest;
                        SemVer        version       = new SemVer(singleRequest.Version);
                        log.Trace("Single request type is {0}, version is {1}.", singleRequest.RequestTypeCase, version);

                        if (!version.IsValid())
                        {
                            responseMessage.Response.Details = "version";
                            break;
                        }

                        switch (singleRequest.RequestTypeCase)
                        {
                        case SingleRequest.RequestTypeOneofCase.Ping:
                            responseMessage = ProcessMessagePingRequest(client, incomingMessage);
                            break;

                        case SingleRequest.RequestTypeOneofCase.ListRoles:
                            responseMessage = ProcessMessageListRolesRequest(client, incomingMessage);
                            break;

                        default:
                            log.Warn("Invalid request type '{0}'.", singleRequest.RequestTypeCase);
                            break;
                        }

                        break;
                    }

                    case Request.ConversationTypeOneofCase.ConversationRequest:
                    {
                        ConversationRequest conversationRequest = request.ConversationRequest;
                        log.Trace("Conversation request type is {0}.", conversationRequest.RequestTypeCase);
                        if (conversationRequest.Signature.Length > 0)
                        {
                            log.Trace("Conversation signature is '{0}'.", conversationRequest.Signature.ToByteArray().ToHex());
                        }
                        else
                        {
                            log.Trace("No signature provided.");
                        }

                        switch (conversationRequest.RequestTypeCase)
                        {
                        case ConversationRequest.RequestTypeOneofCase.Start:
                            responseMessage = ProcessMessageStartConversationRequest(client, incomingMessage);
                            break;

                        case ConversationRequest.RequestTypeOneofCase.VerifyIdentity:
                            responseMessage = ProcessMessageVerifyIdentityRequest(client, incomingMessage);
                            break;

                        default:
                            log.Warn("Invalid request type '{0}'.", conversationRequest.RequestTypeCase);
                            // Connection will be closed in ReceiveMessageLoop.
                            break;
                        }

                        break;
                    }

                    default:
                        log.Error("Unknown conversation type '{0}'.", request.ConversationTypeCase);
                        // Connection will be closed in ReceiveMessageLoop.
                        break;
                    }

                    if (responseMessage != null)
                    {
                        // Send response to client.
                        res = await client.SendMessageAsync(responseMessage);

                        if (res)
                        {
                            // If the message was sent successfully to the target, we close the connection in case it was a protocol violation error response.
                            if (responseMessage.MessageTypeCase == Message.MessageTypeOneofCase.Response)
                            {
                                res = responseMessage.Response.Status != Status.ErrorProtocolViolation;
                            }
                        }
                    }
                    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}', conversation type is {2}.", response.Status, response.Details, response.ConversationTypeCase);

                    // Find associated request. If it does not exist, disconnect the client as it
                    // send a response without receiving a request. This is protocol violation,
                    // but as this is a reponse, we have no how to inform the client about it,
                    // so we just disconnect it.
                    UnfinishedRequest unfinishedRequest = client.GetAndRemoveUnfinishedRequest(incomingMessage.Id);
                    if ((unfinishedRequest != null) && (unfinishedRequest.RequestMessage != null))
                    {
                        ProxProtocolMessage requestMessage = (ProxProtocolMessage)unfinishedRequest.RequestMessage;
                        Request             request        = requestMessage.Request;
                        // We now check whether the response message type corresponds with the request type.
                        // This is only valid if the status is Ok. If the message types do not match, we disconnect
                        // for the protocol violation again.
                        bool typeMatch       = false;
                        bool isErrorResponse = response.Status != Status.Ok;
                        if (!isErrorResponse)
                        {
                            if (response.ConversationTypeCase == Response.ConversationTypeOneofCase.SingleResponse)
                            {
                                typeMatch = (request.ConversationTypeCase == Request.ConversationTypeOneofCase.SingleRequest) &&
                                            ((int)response.SingleResponse.ResponseTypeCase == (int)request.SingleRequest.RequestTypeCase);
                            }
                            else
                            {
                                typeMatch = (request.ConversationTypeCase == Request.ConversationTypeOneofCase.ConversationRequest) &&
                                            ((int)response.ConversationResponse.ResponseTypeCase == (int)request.ConversationRequest.RequestTypeCase);
                            }
                        }
                        else
                        {
                            typeMatch = true;
                        }

                        if (typeMatch)
                        {
                            // Now we know the types match, so we can rely on request type even if response is just an error.
                            switch (request.ConversationTypeCase)
                            {
                            case Request.ConversationTypeOneofCase.SingleRequest:
                            {
                                SingleRequest singleRequest = request.SingleRequest;
                                switch (singleRequest.RequestTypeCase)
                                {
                                default:
                                    log.Warn("Invalid conversation type '{0}' of the corresponding request.", request.ConversationTypeCase);
                                    // Connection will be closed in ReceiveMessageLoop.
                                    break;
                                }

                                break;
                            }

                            case Request.ConversationTypeOneofCase.ConversationRequest:
                            {
                                ConversationRequest conversationRequest = request.ConversationRequest;
                                switch (conversationRequest.RequestTypeCase)
                                {
                                default:
                                    log.Warn("Invalid type '{0}' of the corresponding request.", conversationRequest.RequestTypeCase);
                                    // Connection will be closed in ReceiveMessageLoop.
                                    break;
                                }
                                break;
                            }

                            default:
                                log.Error("Unknown conversation type '{0}' of the corresponding request.", request.ConversationTypeCase);
                                // Connection will be closed in ReceiveMessageLoop.
                                break;
                            }
                        }
                        else
                        {
                            log.Warn("Message type of the response ID {0} does not match the message type of the request ID {1}, the connection will be closed.", incomingMessage.Id, unfinishedRequest.RequestMessage.Id);
                            // Connection will be closed in ReceiveMessageLoop.
                        }
                    }
                    else
                    {
                        log.Warn("No unfinished request found for incoming response ID {0}, the connection will be closed.", incomingMessage.Id);
                        // Connection will be closed in ReceiveMessageLoop.
                    }

                    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.
            }

            if (res && client.ForceDisconnect)
            {
                log.Debug("Connection to the client will be forcefully closed.");
                res = false;
            }

            log.Debug("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Checks whether the update activity request is valid.
        /// <para>This function does not verify whether the activity exists.
        /// It also does not verify whether a closer proximity server exists to which the activity should be migrated.
        /// It also does not verify whether the changed activity's start time will be before expiration time.</para>
        /// </summary>
        /// <param name="UpdateActivityRequest">Update activity request part of the client's request message.</param>
        /// <param name="Client">Client that sent the request.</param>
        /// <param name="RequestMessage">Full request message from client.</param>
        /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param>
        /// <returns>true if the update activity request can be applied, false otherwise.</returns>
        public static bool ValidateUpdateActivityRequest(UpdateActivityRequest UpdateActivityRequest, IncomingClient Client, ProxProtocolMessage RequestMessage, out ProxProtocolMessage ErrorResponse)
        {
            log.Trace("()");

            bool res = false;

            ErrorResponse = null;
            string details = null;

            if (UpdateActivityRequest == null)
            {
                UpdateActivityRequest = new UpdateActivityRequest();
            }
            if (UpdateActivityRequest.Activity == null)
            {
                UpdateActivityRequest.Activity = new ActivityInformation();
            }

            SignedActivityInformation signedActivity = new SignedActivityInformation()
            {
                Activity  = UpdateActivityRequest.Activity,
                Signature = RequestMessage.Request.ConversationRequest.Signature
            };

            ProxMessageBuilder messageBuilder = Client.MessageBuilder;

            if (ValidateSignedActivityInformation(signedActivity, Client.PublicKey, messageBuilder, RequestMessage, "", false, out ErrorResponse))
            {
                if (details == null)
                {
                    int index = 0;
                    foreach (ByteString serverId in UpdateActivityRequest.IgnoreServerIds)
                    {
                        if (serverId.Length != ProtocolHelper.NetworkIdentifierLength)
                        {
                            log.Debug("Ignored server ID #{0} is not a valid network ID as its length is {1} bytes.", index, serverId.Length);
                            details = "ignoreServerIds";
                            break;
                        }

                        index++;
                    }
                }

                if (details == null)
                {
                    res = true;
                }
                else
                {
                    ErrorResponse = messageBuilder.CreateErrorInvalidValueResponse(RequestMessage, details);
                }
            }

            log.Trace("(-):{0}", res);
            return(res);
        }