/// <summary>
        /// Checks if the the relay has been destroyed already and changes its status to Destroyed.
        /// </summary>
        /// <returns>true if the relay has been destroyed already, false otherwise.</returns>
        public async Task <bool> TestAndSetDestroyed()
        {
            log.Trace("()");

            bool res = false;

            await lockObject.WaitAsync();

            res    = status == RelayConnectionStatus.Destroyed;
            status = RelayConnectionStatus.Destroyed;

            lockObject.Release();

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Disposes the instance of the class if it has not been disposed yet and <paramref name="Disposing"/> is set.
        /// </summary>
        /// <param name="Disposing">Indicates whether the method was invoked from the IDisposable.Dispose implementation or from the finalizer.</param>
        protected virtual void Dispose(bool Disposing)
        {
            if (disposed)
            {
                return;
            }

            if (Disposing)
            {
                lock (disposingLock)
                {
                    status = RelayConnectionStatus.Destroyed;
                    CancelTimeoutTimer();

                    disposed = true;
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Processes ApplicationServiceSendMessageRequest message from a client.
        /// <para>
        /// Relay received message from one client and sends it to the other one. If this is the first request
        /// a client sends after it connects to clAppService port, the request's message is ignored and the reply is sent
        /// to the client as the other client is confirmed to join the relay.</para>
        /// </summary>
        /// <param name="Client">Client that sent the message.</param>
        /// <param name="RequestMessage">Full request message.</param>
        /// <param name="Token">Sender's relay token.</param>
        /// <returns>Response message to be sent to the client.</returns>
        public async Task <PsProtocolMessage> ProcessIncomingMessage(IncomingClient Client, PsProtocolMessage RequestMessage, Guid Token)
        {
            log.Trace("()");

            PsProtocolMessage res = null;
            bool destroyRelay     = false;

            await lockObject.WaitAsync();

            bool isCaller = callerToken.Equals(Token);

            IncomingClient otherClient = isCaller ? callee : caller;

            log.Trace("Received message over relay '{0}' in status {1} with client ID {2} being {3} and the other client ID {4} is {5}.",
                      id, status, Client.Id.ToHex(), isCaller ? "caller" : "callee", otherClient != null ? otherClient.Id.ToHex() : "N/A", isCaller ? "callee" : "caller");

            switch (status)
            {
            case RelayConnectionStatus.WaitingForCalleeResponse:
            {
                if (!isCaller)
                {
                    // We have received a message from callee, but we did not receive its IncomingCallNotificationResponse.
                    // This may be OK if this message has been sent by callee and it just not has been processed before
                    // the callee connected to clAppService port and sent us the initialization message.
                    // In this case we will try to wait a couple of seconds and see if we receive IncomingCallNotificationResponse.
                    // If yes, we continue as if we processed the message in the right order.
                    // In all other cases, this is a fatal error and we have to destroy the relay.
                    lockObject.Release();

                    bool statusChanged = false;
                    log.Warn("Callee sent initialization message before we received IncomingCallNotificationResponse. We will wait to see if it arrives soon.");
                    for (int i = 0; i < 5; i++)
                    {
                        log.Warn("Attempt #{0}, waiting 1 second.", i + 1);
                        await Task.Delay(1000);

                        await lockObject.WaitAsync();

                        log.Warn("Attempt #{0}, checking relay status.", i + 1);
                        if (status != RelayConnectionStatus.WaitingForCalleeResponse)
                        {
                            log.Warn("Attempt #{0}, relay status changed to {1}.", i + 1, status);
                            statusChanged = true;
                        }

                        lockObject.Release();

                        if (statusChanged)
                        {
                            break;
                        }
                    }

                    await lockObject.WaitAsync();

                    if (statusChanged)
                    {
                        // Status of relay has change, which means either it has been destroyed already, or the IncomingCallNotificationResponse
                        // message we were waiting for arrived. In any case, we call this method recursively, but it can not happen that we would end up here again.
                        lockObject.Release();

                        log.Trace("Calling ProcessIncomingMessage recursively.");
                        res = await ProcessIncomingMessage(Client, RequestMessage, Token);

                        await lockObject.WaitAsync();
                    }
                    else
                    {
                        log.Trace("Message received from caller and relay status is WaitingForCalleeResponse and IncomingCallNotificationResponse did not arrive, closing connection to client, destroying relay.");
                        res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                        Client.ForceDisconnect = true;
                        destroyRelay           = true;
                    }
                }
                else
                {
                    log.Trace("Message received from caller and relay status is WaitingForCalleeResponse, closing connection to client, destroying relay.");
                    res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                    Client.ForceDisconnect = true;
                    destroyRelay           = true;
                }
                break;
            }

            case RelayConnectionStatus.WaitingForFirstInitMessage:
            {
                log.Debug("Received an initialization message from the first client ID '{0}' on relay '{1}', waiting for the second client.", Client.Id.ToHex(), id);
                CancelTimeoutTimerLocked();

                if (Client.Relay == null)
                {
                    Client.Relay = this;

                    // Other peer is not connected yet, so we put this request on hold and wait for the other client.
                    if (isCaller)
                    {
                        caller = Client;
                    }
                    else
                    {
                        callee = Client;
                    }

                    status = RelayConnectionStatus.WaitingForSecondInitMessage;
                    log.Trace("Relay '{0}' status changed to {1}.", id, status);

                    pendingMessage = RequestMessage;
                    timeoutTimer   = new Timer(TimeoutCallback, status, SecondAppServiceInitializationMessageDelayMaxSeconds * 1000, Timeout.Infinite);

                    // res remains null, which is OK as the request is put on hold until the other client joins the channel.
                }
                else
                {
                    // Client already sent us the initialization message, this is protocol violation error, destroy the relay.
                    // Since the relay should be upgraded to WaitingForSecondInitMessage status, this can happen
                    // only if a client does not use a separate connection for each clAppService session, which is forbidden.
                    log.Debug("Client ID {0} on relay '{1}' probably uses a single connection for two relays. Both relays will be destroyed.", Client.Id.ToHex(), id);
                    res          = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                    destroyRelay = true;
                }
                break;
            }

            case RelayConnectionStatus.WaitingForSecondInitMessage:
            {
                log.Debug("Received an initialization message from the second client on relay '{0}'.", id);
                CancelTimeoutTimerLocked();

                if (Client.Relay == null)
                {
                    Client.Relay = this;

                    // Other peer is connected already, so we just inform it by sending response to its initial ApplicationServiceSendMessageRequest.
                    if (isCaller)
                    {
                        caller = Client;
                    }
                    else
                    {
                        callee = Client;
                    }

                    status = RelayConnectionStatus.Open;
                    log.Trace("Relay '{0}' status changed to {1}.", id, status);

                    PsProtocolMessage otherClientResponse = otherClient.MessageBuilder.CreateApplicationServiceSendMessageResponse(pendingMessage);
                    pendingMessage = null;
                    if (await otherClient.SendMessageAsync(otherClientResponse))
                    {
                        // And we also send reply to the second client that the channel is now ready for communication.
                        res = Client.MessageBuilder.CreateApplicationServiceSendMessageResponse(RequestMessage);
                    }
                    else
                    {
                        log.Warn("Unable to send message to other client ID {0}, closing connection to client and destroying the relay.", otherClient.Id.ToHex());
                        res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                        Client.ForceDisconnect = true;
                        destroyRelay           = true;
                    }
                }
                else
                {
                    // Client already sent us the initialization message, this is error, destroy the relay.
                    log.Debug("Client ID {0} on relay '{1}' sent a message before receiving a reply to its initialization message. Relay will be destroyed.", Client.Id.ToHex(), id);
                    res          = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                    destroyRelay = true;
                }

                break;
            }


            case RelayConnectionStatus.Open:
            {
                if (Client.Relay == this)
                {
                    // Relay is open, this means that all incoming messages are sent to the other client.
                    byte[]              messageForOtherClient = RequestMessage.Request.SingleRequest.ApplicationServiceSendMessage.Message.ToByteArray();
                    PsProtocolMessage   otherClientMessage    = otherClient.MessageBuilder.CreateApplicationServiceReceiveMessageNotificationRequest(messageForOtherClient);
                    RelayMessageContext context = new RelayMessageContext(this, RequestMessage);
                    if (await otherClient.SendMessageAndSaveUnfinishedRequestAsync(otherClientMessage, context))
                    {
                        // res is null, which is fine, the sender is put on hold and we will get back to it once the recipient confirms that it received the message.
                        log.Debug("Message from client ID {0} has been relayed to other client ID {1}.", Client.Id.ToHex(), otherClient.Id.ToHex());
                    }
                    else
                    {
                        log.Warn("Unable to relay message to other client ID {0}, closing connection to client and destroying the relay.", otherClient.Id.ToHex());
                        res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                        Client.ForceDisconnect = true;
                        destroyRelay           = true;
                    }
                }
                else
                {
                    // This means that the client used a single clAppService port connection for two different relays, which is forbidden.
                    log.Warn("Client ID {0} mixed relay '{1}' with relay '{2}', closing connection to client and destroying both relays.", otherClient.Id.ToHex(), Client.Relay.id, id);
                    res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                    Client.ForceDisconnect = true;
                    destroyRelay           = true;
                }

                break;
            }

            case RelayConnectionStatus.Destroyed:
            {
                log.Trace("Relay has been destroyed, closing connection to client.");
                res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                Client.ForceDisconnect = true;
                break;
            }

            default:
                log.Trace("Relay status is '{0}', closing connection to client, destroying relay.", status);
                res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                Client.ForceDisconnect = true;
                destroyRelay           = true;
                break;
            }

            lockObject.Release();

            if (destroyRelay)
            {
                Server serverComponent = (Server)Base.ComponentDictionary[Server.ComponentName];
                await serverComponent.RelayList.DestroyNetworkRelay(this);

                if ((this != Client.Relay) && (Client.Relay != null))
                {
                    await serverComponent.RelayList.DestroyNetworkRelay(Client.Relay);
                }
            }

            log.Trace("(-)");
            return(res);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Handles situation when the callee replied to the incoming call notification request.
        /// </summary>
        /// <param name="ResponseMessage">Full response message from the callee.</param>
        /// <param name="Request">Unfinished call request message of the caller that corresponds to the response message.</param>
        /// <returns></returns>
        public async Task <bool> CalleeRepliedToIncomingCallNotification(PsProtocolMessage ResponseMessage, UnfinishedRequest Request)
        {
            log.Trace("()");

            bool res = false;

            bool              destroyRelay        = false;
            IncomingClient    clientToSendMessage = null;
            PsProtocolMessage messageToSend       = null;

            await lockObject.WaitAsync();


            if (status == RelayConnectionStatus.WaitingForCalleeResponse)
            {
                CancelTimeoutTimerLocked();

                // The caller is still connected and waiting for an answer to its call request.
                if (ResponseMessage.Response.Status == Status.Ok)
                {
                    // The callee is now expected to connect to clAppService with its token.
                    // We need to inform caller that the callee accepted the call.
                    // This is option 4) from ProcessMessageCallIdentityApplicationServiceRequestAsync.
                    messageToSend       = caller.MessageBuilder.CreateCallIdentityApplicationServiceResponse(pendingMessage, callerToken.ToByteArray());
                    clientToSendMessage = caller;
                    pendingMessage      = null;

                    caller = null;
                    callee = null;
                    status = RelayConnectionStatus.WaitingForFirstInitMessage;
                    log.Debug("Relay '{0}' status has been changed to {1}.", id, status);

                    /// Install timeoutTimer to expire if the first client does not connect to clAppService port
                    /// and send its initialization message within reasonable time.
                    timeoutTimer = new Timer(TimeoutCallback, RelayConnectionStatus.WaitingForFirstInitMessage, FirstAppServiceInitializationMessageDelayMaxSeconds * 1000, Timeout.Infinite);

                    res = true;
                }
                else
                {
                    // The callee rejected the call or reported other error.
                    // These are options 3) and 2) from ProcessMessageCallIdentityApplicationServiceRequestAsync.
                    if (ResponseMessage.Response.Status == Status.ErrorRejected)
                    {
                        log.Debug("Callee ID '{0}' rejected the call from caller identity ID '{1}', relay '{2}'.", callee.Id.ToHex(), caller.Id.ToHex(), id);
                        messageToSend = caller.MessageBuilder.CreateErrorRejectedResponse(pendingMessage);
                    }
                    else
                    {
                        log.Warn("Callee ID '0} sent error response '{1}' for call request from caller identity ID {2}, relay '{3}'.",
                                 callee.Id.ToHex(), ResponseMessage.Response.Status, caller.Id.ToHex(), id);

                        messageToSend = caller.MessageBuilder.CreateErrorNotAvailableResponse(pendingMessage);
                    }

                    clientToSendMessage = caller;
                    destroyRelay        = true;
                }
            }
            else
            {
                // The relay has probably been destroyed, or something bad happened to it.
                // We take no action here regardless of what the callee's response is.
                // If it rejected the call, there is nothing to be done since we do not have
                // any connection to the caller anymore.
                log.Debug("Relay status is {0}, nothing to be done.", status);
            }

            lockObject.Release();


            if (messageToSend != null)
            {
                if (await clientToSendMessage.SendMessageAsync(messageToSend))
                {
                    log.Debug("Response to call initiation request sent to the caller ID {0}.", clientToSendMessage.Id.ToHex());
                }
                else
                {
                    log.Debug("Unable to reponse to call initiation request to the caller ID {0}.", clientToSendMessage.Id.ToHex());
                }
            }

            if (destroyRelay)
            {
                Server serverComponent = (Server)Base.ComponentDictionary[Server.ComponentName];
                await serverComponent.RelayList.DestroyNetworkRelay(this);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Callback routine that is called once the timeoutTimer expires.
        /// <para>
        /// If relay status is WaitingForCalleeResponse, the callee has to reply to the incoming call notification
        /// within a reasonable time. If it does the timer is cancelled. If it does not, the timeout occurs.
        /// </para>
        /// <para>
        /// If relay status is WaitingForFirstInitMessage, both clients are expected to connect to clAppService port
        /// and send an initial message over that service. The timeoutTimer expires when none of the clients
        /// connects to clAppService port and sends its initialization message within a reasonable time.
        /// </para>
        /// <para>
        /// Then if relay status is WaitingForSecondInitMessage, the profile server receives a message from the first client
        /// on clAppService port, it starts the timer again, which now expires if the second client does not connect
        /// and send its initial message within a reasonable time.
        /// </para>
        /// </summary>
        /// <param name="state">Status of the relay when the timer was installed.</param>
        private async void TimeoutCallback(object State)
        {
            LogDiagnosticContext.Start();

            RelayConnectionStatus previousStatus = (RelayConnectionStatus)State;

            log.Trace("(State:{0})", previousStatus);

            IncomingClient    clientToSendMessage = null;
            PsProtocolMessage messageToSend       = null;
            bool destroyRelay = false;

            await lockObject.WaitAsync();

            if (timeoutTimer != null)
            {
                switch (status)
                {
                case RelayConnectionStatus.WaitingForCalleeResponse:
                {
                    // The caller requested the call and the callee was notified.
                    // The callee failed to send us response on time, this is situation 2)
                    // from ProcessMessageCallIdentityApplicationServiceRequestAsync.
                    // We send ERROR_NOT_AVAILABLE to the caller and destroy the relay.
                    log.Debug("Callee failed to reply to the incoming call notification, closing relay.");

                    clientToSendMessage = caller;
                    messageToSend       = caller.MessageBuilder.CreateErrorNotAvailableResponse(pendingMessage);
                    break;
                }

                case RelayConnectionStatus.WaitingForFirstInitMessage:
                {
                    // Neither client joined the channel on time, nothing to do, just destroy the relay.
                    log.Debug("None of the clients joined the relay on time, closing relay.");
                    break;
                }

                case RelayConnectionStatus.WaitingForSecondInitMessage:
                {
                    // One client is waiting for the other one to join, but the other client failed to join on time.
                    // We send ERROR_NOT_FOUND to the waiting client and close its connection.
                    log.Debug("{0} failed to join the relay on time, closing relay.", callee != null ? "Caller" : "Callee");

                    clientToSendMessage = callee != null ? callee : caller;
                    messageToSend       = clientToSendMessage.MessageBuilder.CreateErrorNotFoundResponse(pendingMessage);
                    break;
                }

                default:
                    log.Debug("Time out triggered while the relay status was {0}.", status);
                    break;
                }

                // In case of any timeouts, we just destroy the relay.
                destroyRelay = true;
            }
            else
            {
                log.Debug("Timeout timer of relay '{0}' has been destroyed, no action taken.", id);
            }

            lockObject.Release();


            if (messageToSend != null)
            {
                if (!await clientToSendMessage.SendMessageAsync(messageToSend))
                {
                    log.Warn("Unable to send message to the client ID {0} in relay '{1}', maybe it is not connected anymore.", clientToSendMessage.Id.ToHex(), id);
                }
            }

            if (destroyRelay)
            {
                Server serverComponent = (Server)Base.ComponentDictionary[Server.ComponentName];
                await serverComponent.RelayList.DestroyNetworkRelay(this);
            }

            log.Trace("(-)");

            LogDiagnosticContext.Stop();
        }
Exemplo n.º 6
0
        /// <summary>
        /// Processes ApplicationServiceSendMessageRequest message from a client.
        /// <para>
        /// Relay received message from one client and sends it to the other one. If this is the first request
        /// a client sends after it connects to clAppService port, the request's message is ignored and the reply is sent
        /// to the client as the other client is confirmed to join the relay.</para>
        /// </summary>
        /// <param name="Client">Client that sent the message.</param>
        /// <param name="RequestMessage">Full request message.</param>
        /// <param name="Token">Sender's relay token.</param>
        /// <returns>Response message to be sent to the client.</returns>
        public async Task <Message> ProcessIncomingMessage(Client Client, Message RequestMessage, Guid Token)
        {
            log.Trace("()");

            Message res          = null;
            bool    destroyRelay = false;

            await lockObject.WaitAsync();

            bool isCaller = callerToken.Equals(Token);

            Client otherClient = isCaller ? callee : caller;

            log.Trace("Received message over relay '{0}' in status {1} with client ID '0x{2:X16}' being {3} and the other client ID '0x{4:X16}' is {5}.",
                      id, status, Client.Id, isCaller ? "caller" : "callee", otherClient != null ? otherClient.Id : 0xFFFFFFFFFFFFFFFF, isCaller ? "callee" : "caller");

            switch (status)
            {
            case RelayConnectionStatus.WaitingForFirstInitMessage:
            {
                log.Debug("Received an initialization message from the first client ID '0x{0:X16}' on relay '{1}', waiting for the second client.", Client.Id, id);
                CancelTimeoutTimerLocked();

                if (Client.Relay == null)
                {
                    Client.Relay = this;

                    // Other peer is not connected yet, so we put this request on hold and wait for the other client.
                    if (isCaller)
                    {
                        caller = Client;
                    }
                    else
                    {
                        callee = Client;
                    }

                    status = RelayConnectionStatus.WaitingForSecondInitMessage;
                    log.Trace("Relay '{0}' status changed to {1}.", id, status);

                    pendingMessage = RequestMessage;
                    timeoutTimer   = new Timer(TimeoutCallback, status, SecondAppServiceInitializationMessageDelayMaxSeconds * 1000, Timeout.Infinite);

                    // res remains null, which is OK as the request is put on hold until the other client joins the channel.
                }
                else
                {
                    // Client already sent us the initialization message, this is protocol violation error, destroy the relay.
                    // Since the relay should be upgraded to WaitingForSecondInitMessage status, this can happen
                    // only if a client does not use a separate connection for each clAppService session, which is forbidden.
                    log.Debug("Client ID '0x{0:X16}' on relay '{1}' probably uses a single connection for two relays. Both relays will be destroyed.", Client.Id, id);
                    res          = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                    destroyRelay = true;
                }
                break;
            }

            case RelayConnectionStatus.WaitingForSecondInitMessage:
            {
                log.Debug("Received an initialization message from the second client on relay '{0}'.", id);
                CancelTimeoutTimerLocked();

                if (Client.Relay == null)
                {
                    Client.Relay = this;

                    // Other peer is connected already, so we just inform it by sending response to its initial ApplicationServiceSendMessageRequest.
                    if (isCaller)
                    {
                        caller = Client;
                    }
                    else
                    {
                        callee = Client;
                    }

                    status = RelayConnectionStatus.Open;
                    log.Trace("Relay '{0}' status changed to {1}.", id, status);

                    Message otherClientResponse = otherClient.MessageBuilder.CreateApplicationServiceSendMessageResponse(pendingMessage);
                    pendingMessage = null;
                    if (await otherClient.SendMessageAsync(otherClientResponse))
                    {
                        // And we also send reply to the second client that the channel is now ready for communication.
                        res = Client.MessageBuilder.CreateApplicationServiceSendMessageResponse(RequestMessage);
                    }
                    else
                    {
                        log.Warn("Unable to send message to other client ID '0x{0:X16}', closing connection to client and destroying the relay.", otherClient.Id);
                        res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                        Client.ForceDisconnect = true;
                        destroyRelay           = true;
                    }
                }
                else
                {
                    // Client already sent us the initialization message, this is error, destroy the relay.
                    log.Debug("Client ID '0x{0:X16}' on relay '{1}' sent a message before receiving a reply to its initialization message. Relay will be destroyed.", Client.Id, id);
                    res          = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                    destroyRelay = true;
                }

                break;
            }


            case RelayConnectionStatus.Open:
            {
                if (Client.Relay == this)
                {
                    // Relay is open, this means that all incoming messages are sent to the other client.
                    byte[]              messageForOtherClient = RequestMessage.Request.SingleRequest.ApplicationServiceSendMessage.Message.ToByteArray();
                    Message             otherClientMessage    = otherClient.MessageBuilder.CreateApplicationServiceReceiveMessageNotificationRequest(messageForOtherClient);
                    RelayMessageContext context = new RelayMessageContext(this, RequestMessage);
                    if (await otherClient.SendMessageAndSaveUnfinishedRequestAsync(otherClientMessage, context))
                    {
                        // res is null, which is fine, the sender is put on hold and we will get back to it once the recipient confirms that it received the message.
                        log.Debug("Message from client ID '0x{0:X16}' has been relayed to other client ID '0x{1:X16}'.", Client.Id, otherClient.Id);
                    }
                    else
                    {
                        log.Warn("Unable to relay message to other client ID '0x{0:X16}', closing connection to client and destroying the relay.", otherClient.Id);
                        res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                        Client.ForceDisconnect = true;
                        destroyRelay           = true;
                    }
                }
                else
                {
                    // This means that the client used a single clAppService port connection for two different relays, which is forbidden.
                    log.Warn("Client ID '0x{0:X16}' mixed relay '{1}' with relay '{2}', closing connection to client and destroying both relays.", otherClient.Id, Client.Relay.id, id);
                    res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                    Client.ForceDisconnect = true;
                    destroyRelay           = true;
                }

                break;
            }

            case RelayConnectionStatus.Destroyed:
            {
                log.Trace("Relay has been destroyed, closing connection to client.");
                res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                Client.ForceDisconnect = true;
                break;
            }

            default:
                log.Trace("Relay status is '{0}', closing connection to client, destroying relay.", status);
                res = Client.MessageBuilder.CreateErrorNotFoundResponse(RequestMessage);
                Client.ForceDisconnect = true;
                destroyRelay           = true;
                break;
            }

            lockObject.Release();

            if (destroyRelay)
            {
                Server     serverComponent = (Server)Base.ComponentDictionary["Network.Server"];
                ClientList clientList      = serverComponent.GetClientList();
                await clientList.DestroyNetworkRelay(this);

                if (this != Client.Relay)
                {
                    await clientList.DestroyNetworkRelay(Client.Relay);
                }
            }

            log.Trace("(-)");
            return(res);
        }