Beispiel #1
0
        public DhtClient(int udpDhtPort, IDhtClientManager manager)
        {
            _manager = manager;

            IPEndPoint localEP;

            switch (Environment.OSVersion.Platform)
            {
            case PlatformID.Win32NT:
                if (Environment.OSVersion.Version.Major < 6)
                {
                    //below vista
                    _udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    _udpClient   = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

                    localEP = new IPEndPoint(IPAddress.Any, udpDhtPort);
                }
                else
                {
                    //vista & above
                    _udpListener = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
                    _udpListener.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);

                    _udpClient = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
                    _udpClient.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);

                    localEP = new IPEndPoint(IPAddress.IPv6Any, udpDhtPort);
                }
                break;

            case PlatformID.Unix:     //mono framework
                if (Socket.OSSupportsIPv6)
                {
                    _udpListener = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
                    _udpClient   = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);

                    localEP = new IPEndPoint(IPAddress.IPv6Any, udpDhtPort);
                }
                else
                {
                    _udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    _udpClient   = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

                    localEP = new IPEndPoint(IPAddress.Any, udpDhtPort);
                }

                break;

            default:     //unknown
                _udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                _udpClient   = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

                localEP = new IPEndPoint(IPAddress.Any, udpDhtPort);
                break;
            }

            try
            {
                _udpListener.Bind(localEP);
            }
            catch
            {
                localEP.Port = 0;

                _udpListener.Bind(localEP);
            }

            _localPort = (_udpListener.LocalEndPoint as IPEndPoint).Port;

            //init routing table
            _currentNode  = new CurrentNode(udpDhtPort);
            _routingTable = new KBucket(_currentNode);

            //bind udp client socket to random port
            if (_udpClient.AddressFamily == AddressFamily.InterNetwork)
            {
                _udpClient.Bind(new IPEndPoint(IPAddress.Any, 0));
            }
            else
            {
                _udpClient.Bind(new IPEndPoint(IPAddress.IPv6Any, 0));
            }

            //start reading udp response packets
            _udpClientThread = new Thread(ReadResponsePacketsAsync);
            _udpClientThread.IsBackground = true;
            _udpClientThread.Start(_udpClient);

            //start reading udp query packets
            _udpListenerThread = new Thread(ReadQueryPacketsAsync);
            _udpListenerThread.IsBackground = true;
            _udpListenerThread.Start(_udpListener);

            //start health timer
            _healthTimer = new Timer(HealthTimerCallback, null, QUERY_TIMEOUT, Timeout.Infinite);
        }
Beispiel #2
0
        private DhtRpcPacket Query(DhtRpcPacket packet, NodeContact contact)
        {
            Transaction transaction = new Transaction(contact.NodeID, contact.NodeEP);

            try
            {
                lock (_transactions)
                {
                    _transactions.Add(packet.TransactionID, transaction);
                }

                lock (transaction)
                {
                    lock (_sendBufferStream)
                    {
                        _sendBufferStream.Position = 0;
                        packet.WriteTo(_sendBufferStream);

                        if (_proxyEnabled)
                        {
                            _manager.SendDhtPacket(contact.NodeEP, _sendBufferStream.Buffer, 0, (int)_sendBufferStream.Position);
                        }
                        else
                        {
                            _udpClient.SendTo(_sendBufferStream.Buffer, 0, (int)_sendBufferStream.Position, SocketFlags.None, contact.NodeEP);
                        }
                    }

                    if (!Monitor.Wait(transaction, QUERY_TIMEOUT))
                    {
                        contact.IncrementRpcFailCount();
                        return(null);
                    }

                    //auto add contact or update last seen time
                    if (contact.NodeID == null)
                    {
                        contact = transaction.ResponsePacket.SourceNode;
                    }

                    KBucket     closestBucket = _routingTable.FindClosestBucket(contact.NodeID);
                    NodeContact bucketContact = closestBucket.FindContactInCurrentBucket(contact.NodeID);

                    if (bucketContact == null)
                    {
                        closestBucket.AddContactInCurrentBucket(contact);
                    }
                    else
                    {
                        bucketContact.UpdateLastSeenTime();
                    }

                    return(transaction.ResponsePacket);
                }
            }
            finally
            {
                lock (_transactions)
                {
                    _transactions.Remove(packet.TransactionID);
                }
            }
        }
Beispiel #3
0
        private object QueryFind(NodeContact[] initialContacts, BinaryID nodeID, RpcQueryType queryType)
        {
            List <NodeContact> availableContacts = new List <NodeContact>(initialContacts);
            List <NodeContact> respondedContacts = new List <NodeContact>();
            List <NodeContact> failedContacts    = new List <NodeContact>();

            NodeContact[] alphaContacts;
            int           alpha      = KADEMLIA_ALPHA;
            bool          finalRound = false;

            while (true)
            {
                //pick alpha contacts to query from available contacts
                lock (availableContacts)
                {
                    alphaContacts = PickClosestContacts(availableContacts, alpha);
                }

                if (alphaContacts.Length < 1)
                {
                    //no contacts available to query further

                    lock (respondedContacts)
                    {
                        if (respondedContacts.Count > KADEMLIA_K)
                        {
                            return(KBucket.GetClosestContacts(respondedContacts, nodeID, KADEMLIA_K));
                        }
                        else if (respondedContacts.Count > 0)
                        {
                            return(respondedContacts.ToArray());
                        }
                    }

                    return(null);
                }

                object              lockObj          = new object();
                List <NodeContact>  receivedContacts = new List <NodeContact>();
                List <PeerEndPoint> receivedPeers    = null;

                if (queryType == RpcQueryType.FIND_PEERS)
                {
                    receivedPeers = new List <PeerEndPoint>();
                }

                lock (lockObj)
                {
                    //query each alpha contact async
                    foreach (NodeContact alphaContact in alphaContacts)
                    {
                        ThreadPool.QueueUserWorkItem(QueryFindAsync, new object[] { lockObj, queryType, alphaContact, nodeID, availableContacts, respondedContacts, failedContacts, receivedContacts, receivedPeers });
                    }

                    //wait for any of the contact to return new contacts
                    if (Monitor.Wait(lockObj, QUERY_TIMEOUT))
                    {
                        //got reply!

                        if (queryType == RpcQueryType.FIND_PEERS)
                        {
                            lock (receivedPeers)
                            {
                                if (receivedPeers.Count > 0)
                                {
                                    return(receivedPeers.ToArray());
                                }
                            }
                        }

                        lock (receivedContacts)
                        {
                            if (receivedContacts.Count < 1)
                            {
                                //current round failed to return any new closer nodes

                                if (finalRound)
                                {
                                    if (queryType == RpcQueryType.FIND_PEERS)
                                    {
                                        return(null);
                                    }

                                    lock (respondedContacts)
                                    {
                                        if (respondedContacts.Count > KADEMLIA_K)
                                        {
                                            return(KBucket.GetClosestContacts(respondedContacts, nodeID, KADEMLIA_K));
                                        }
                                        else
                                        {
                                            return(respondedContacts.ToArray());
                                        }
                                    }
                                }
                                else
                                {
                                    //resend query to k closest node not already queried
                                    finalRound = true;
                                    alpha      = KADEMLIA_K;
                                }
                            }
                            else
                            {
                                finalRound = false;
                                alpha      = KADEMLIA_ALPHA;
                            }
                        }
                    }
                }
            }
        }
Beispiel #4
0
        public NodeContact[] GetKClosestContacts(BinaryNumber nodeID)
        {
            KBucket currentBucket = this;

            while (true)
            {
                NodeContact[] contacts    = currentBucket._contacts;
                KBucket       leftBucket  = currentBucket._leftBucket;
                KBucket       rightBucket = currentBucket._rightBucket;

                if ((contacts != null) || (leftBucket == null) || (rightBucket == null))
                {
                    #region find closest contacts from this bucket

                    KBucket       closestBucket   = currentBucket;
                    NodeContact[] closestContacts = null;

                    if (closestBucket._contactCount >= DhtNode.KADEMLIA_K)
                    {
                        closestContacts = closestBucket.GetAllContacts(false);

                        if (closestContacts.Length > DhtNode.KADEMLIA_K)
                        {
                            return(GetClosestContacts(closestContacts, nodeID, DhtNode.KADEMLIA_K));
                        }

                        if (closestContacts.Length == DhtNode.KADEMLIA_K)
                        {
                            return(closestContacts);
                        }

                        if (closestBucket._parentBucket == null)
                        {
                            return(closestContacts);
                        }
                    }

                    while (closestBucket._parentBucket != null)
                    {
                        KBucket parentBucket = closestBucket._parentBucket;

                        closestContacts = parentBucket.GetAllContacts(false);

                        if (closestContacts.Length > DhtNode.KADEMLIA_K)
                        {
                            return(GetClosestContacts(closestContacts, nodeID, DhtNode.KADEMLIA_K));
                        }

                        if (closestContacts.Length == DhtNode.KADEMLIA_K)
                        {
                            return(closestContacts);
                        }

                        closestBucket = parentBucket;
                    }

                    if (closestContacts == null)
                    {
                        closestContacts = closestBucket.GetAllContacts(false);
                    }

                    return(closestContacts);

                    #endregion
                }

                if ((leftBucket._bucketID & nodeID) == leftBucket._bucketID)
                {
                    currentBucket = leftBucket;
                }
                else
                {
                    currentBucket = rightBucket;
                }
            }
        }
Beispiel #5
0
        private void ReadQueryPacketsAsync(object parameter)
        {
            Socket udpListener = parameter as Socket;

            EndPoint        remoteEP;
            FixMemoryStream recvBufferStream = new FixMemoryStream(BUFFER_MAX_SIZE);
            FixMemoryStream sendBufferStream = new FixMemoryStream(BUFFER_MAX_SIZE);
            int             bytesRecv;

            if (udpListener.AddressFamily == AddressFamily.InterNetwork)
            {
                remoteEP = new IPEndPoint(IPAddress.Any, 0);
            }
            else
            {
                remoteEP = new IPEndPoint(IPAddress.IPv6Any, 0);
            }

            try
            {
                while (true)
                {
                    bytesRecv = udpListener.ReceiveFrom(recvBufferStream.Buffer, ref remoteEP);

                    if (bytesRecv > 0)
                    {
                        recvBufferStream.Position = 0;
                        recvBufferStream.SetLength(bytesRecv);

                        IPEndPoint remoteNodeEP = remoteEP as IPEndPoint;

                        if (NetUtilities.IsIPv4MappedIPv6Address(remoteNodeEP.Address))
                        {
                            remoteNodeEP = new IPEndPoint(NetUtilities.ConvertFromIPv4MappedIPv6Address(remoteNodeEP.Address), remoteNodeEP.Port);
                        }

                        try
                        {
                            DhtRpcPacket request  = new DhtRpcPacket(recvBufferStream, remoteNodeEP.Address);
                            DhtRpcPacket response = ProcessPacket(request);

                            //send response
                            if (response != null)
                            {
                                sendBufferStream.Position = 0;
                                response.WriteTo(sendBufferStream);
                                udpListener.SendTo(sendBufferStream.Buffer, 0, (int)sendBufferStream.Position, SocketFlags.None, remoteEP);
                            }

                            //if contact doesnt exists then add contact else update last seen time
                            KBucket     closestBucket = _routingTable.FindClosestBucket(request.SourceNode.NodeID);
                            NodeContact contact       = closestBucket.FindContactInCurrentBucket(request.SourceNode.NodeID);

                            if (contact == null)
                            {
                                //check if the closest bucket can accomodate another contact
                                if (!closestBucket.IsCurrentBucketFull(true))
                                {
                                    ThreadPool.QueueUserWorkItem(AddContactAfterPingAsync, request.SourceNode);
                                }
                            }
                            else
                            {
                                contact.UpdateLastSeenTime();
                            }
                        }
                        catch
                        { }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Write("DhtClient.ReadQueryPacketsAsync", ex);
            }
        }
Beispiel #6
0
        public bool RemoveContact(NodeContact contact)
        {
            KBucket currentBucket = this;

            while (true)
            {
                NodeContact[] contacts    = currentBucket._contacts;
                KBucket       leftBucket  = currentBucket._leftBucket;
                KBucket       rightBucket = currentBucket._rightBucket;

                lock (currentBucket)
                {
                    if ((contacts != null) || (leftBucket == null) || (rightBucket == null))
                    {
                        #region remove contact from this bucket

                        if (currentBucket._contactCount <= DhtNode.KADEMLIA_K)
                        {
                            return(false); //k-bucket is not full and replacement cache is empty
                        }
                        bool contactRemoved = false;

                        for (int i = 0; i < contacts.Length; i++)
                        {
                            if (contact.Equals(contacts[i]))
                            {
                                if (contacts[i].IsStale())
                                {
                                    //remove stale contact
                                    contacts[i] = null;
                                    currentBucket._contactCount--;
                                    currentBucket._lastChanged = DateTime.UtcNow;
                                    contactRemoved             = true;
                                }

                                break;
                            }
                        }

                        if (!contactRemoved)
                        {
                            return(false); //contact was not found or was not stale
                        }
                        //check parent bucket contact count and join the parent bucket
                        if (currentBucket._parentBucket != null)
                        {
                            JoinBucket(currentBucket._parentBucket);
                        }

                        return(true);

                        #endregion
                    }
                }

                if ((leftBucket._bucketID & contact.NodeID) == leftBucket._bucketID)
                {
                    currentBucket = leftBucket;
                }
                else
                {
                    currentBucket = rightBucket;
                }
            }
        }
Beispiel #7
0
        public bool AddContact(NodeContact contact)
        {
            KBucket currentBucket = this;

            while (true)
            {
                NodeContact[] contacts    = currentBucket._contacts;
                KBucket       leftBucket  = currentBucket._leftBucket;
                KBucket       rightBucket = currentBucket._rightBucket;

                lock (currentBucket)
                {
                    if ((contacts != null) || (leftBucket == null) || (rightBucket == null))
                    {
                        #region  add contact in this bucket

                        //search if contact already exists
                        for (int i = 0; i < contacts.Length; i++)
                        {
                            if (contact.Equals(contacts[i]))
                            {
                                return(false); //contact already exists
                            }
                        }

                        //try add contact
                        for (int i = 0; i < contacts.Length; i++)
                        {
                            if (contacts[i] == null)
                            {
                                contacts[i] = contact;
                                currentBucket._contactCount++;
                                currentBucket._lastChanged = DateTime.UtcNow;
                                return(true);
                            }
                        }

                        //k-bucket is full so contact was not added

                        //if current contact is not stale then find and replace with any existing stale contact
                        if (!contact.IsStale())
                        {
                            for (int i = 0; i < contacts.Length; i++)
                            {
                                if (contacts[i].IsStale())
                                {
                                    contacts[i] = contact;
                                    currentBucket._lastChanged = DateTime.UtcNow;
                                    return(true);
                                }
                            }
                        }

                        //no stale contact in this k-bucket to replace!
                        if (contacts[0].IsCurrentNode)
                        {
                            //split current bucket and add contact!
                            SplitBucket(currentBucket, contact);
                            return(true);
                        }
                        else
                        {
                            //k-bucket is full!
                            return(false);
                        }
                        #endregion
                    }
                }

                if ((leftBucket._bucketID & contact.NodeID) == leftBucket._bucketID)
                {
                    currentBucket = leftBucket;
                }
                else
                {
                    currentBucket = rightBucket;
                }
            }
        }
Beispiel #8
0
        private static void JoinBucket(KBucket parentBucket)
        {
            lock (parentBucket)
            {
                KBucket leftBucket  = parentBucket._leftBucket;
                KBucket rightBucket = parentBucket._rightBucket;

                if ((leftBucket == null) || (rightBucket == null))
                {
                    return;
                }

                lock (leftBucket)
                {
                    lock (rightBucket)
                    {
                        if ((leftBucket._contactCount + rightBucket._contactCount) > DhtNode.KADEMLIA_K * 2)
                        {
                            return; //child k-buckets have more contacts then parent can accomodate
                        }
                        NodeContact[] contacts     = new NodeContact[DhtNode.KADEMLIA_K * 2];
                        int           contactCount = 0;

                        if ((leftBucket._contacts[0] != null) && leftBucket._contacts[0].IsCurrentNode)
                        {
                            foreach (NodeContact contact in leftBucket._contacts)
                            {
                                if (contact != null)
                                {
                                    contacts[contactCount++] = contact;
                                }
                            }

                            foreach (NodeContact contact in rightBucket._contacts)
                            {
                                if (contact != null)
                                {
                                    contacts[contactCount++] = contact;
                                }
                            }
                        }
                        else
                        {
                            foreach (NodeContact contact in rightBucket._contacts)
                            {
                                if (contact != null)
                                {
                                    contacts[contactCount++] = contact;
                                }
                            }

                            foreach (NodeContact contact in leftBucket._contacts)
                            {
                                if (contact != null)
                                {
                                    contacts[contactCount++] = contact;
                                }
                            }
                        }

                        parentBucket._contacts     = contacts;
                        parentBucket._contactCount = contactCount;
                        parentBucket._leftBucket   = null;
                        parentBucket._rightBucket  = null;
                    }
                }
            }
        }
Beispiel #9
0
        private object QueryFind(NodeContact[] initialContacts, BinaryNumber nodeID, DhtRpcType queryType)
        {
            if (initialContacts.Length < 1)
            {
                return(null);
            }

            List <NodeContact>  seenContacts = new List <NodeContact>(initialContacts);
            List <NodeContact>  learnedNotQueriedContacts = new List <NodeContact>(initialContacts);
            List <NodeContact>  respondedContacts         = new List <NodeContact>();
            List <PeerEndPoint> receivedPeers             = null;
            int  alpha      = KADEMLIA_ALPHA;
            bool finalRound = false;
            bool checkTerminationCondition = false;

            if (queryType == DhtRpcType.FIND_PEERS)
            {
                receivedPeers = new List <PeerEndPoint>();
            }

            NodeContact previousClosestSeenContact = KBucket.GetClosestContacts(seenContacts, nodeID, 1)[0];

            while (true)
            {
                NodeContact[] alphaContacts;

                //pick alpha contacts to query from learned contacts
                lock (learnedNotQueriedContacts)
                {
                    alphaContacts = KBucket.GetClosestContacts(learnedNotQueriedContacts, nodeID, alpha);

                    //remove selected alpha contacts from learned not queries contacts list
                    foreach (NodeContact alphaContact in alphaContacts)
                    {
                        learnedNotQueriedContacts.Remove(alphaContact);
                    }
                }

                if (alphaContacts.Length < 1)
                {
                    checkTerminationCondition = true;
                }
                else
                {
                    object lockObj = new object();

                    lock (lockObj)
                    {
                        //query each alpha contact async
                        foreach (NodeContact alphaContact in alphaContacts)
                        {
                            Thread t = new Thread(delegate(object state)
                            {
                                DhtRpcPacket response;

                                if (queryType == DhtRpcType.FIND_NODE)
                                {
                                    response = Query(DhtRpcPacket.CreateFindNodePacketQuery(_currentNode, nodeID), alphaContact);
                                }
                                else
                                {
                                    response = Query(DhtRpcPacket.CreateFindPeersPacketQuery(_currentNode, nodeID), alphaContact);
                                }

                                if ((response == null) || (response.Type != queryType))
                                {
                                    //time out or error
                                    //ignore contact by removing from seen contacts list
                                    lock (seenContacts)
                                    {
                                        seenContacts.Remove(alphaContact);
                                    }

                                    return;
                                }

                                //got reply!
                                if ((queryType == DhtRpcType.FIND_PEERS) && (response.Peers.Length > 0))
                                {
                                    lock (receivedPeers)
                                    {
                                        foreach (PeerEndPoint peer in response.Peers)
                                        {
                                            if (!receivedPeers.Contains(peer))
                                            {
                                                receivedPeers.Add(peer);
                                            }
                                        }
                                    }
                                }

                                //add alpha contact to responded contacts list
                                lock (respondedContacts)
                                {
                                    if (!respondedContacts.Contains(alphaContact))
                                    {
                                        respondedContacts.Add(alphaContact);
                                    }
                                }

                                //add received contacts to learned contacts list
                                lock (seenContacts)
                                {
                                    lock (learnedNotQueriedContacts)
                                    {
                                        foreach (NodeContact contact in response.Contacts)
                                        {
                                            if (!seenContacts.Contains(contact))
                                            {
                                                seenContacts.Add(contact);
                                                learnedNotQueriedContacts.Add(contact);
                                            }
                                        }
                                    }
                                }

                                //no pulse for final round to wait for all k contacts to respond. this allows any failed node contact to be removed from seen contacts list during the wait.
                                lock (lockObj)
                                {
                                    Monitor.Pulse(lockObj);
                                }
                            });

                            t.IsBackground = true;
                            t.Start();
                        }

                        //wait for any of the node contact to return new contacts
                        if (Monitor.Wait(lockObj, QUERY_TIMEOUT))
                        {
                            //got reply or final round!

                            NodeContact currentClosestSeenContact;

                            lock (seenContacts)
                            {
                                currentClosestSeenContact = KBucket.GetClosestContacts(seenContacts, nodeID, 1)[0];
                            }

                            BinaryNumber previousDistance = nodeID ^ previousClosestSeenContact.NodeID;
                            BinaryNumber currentDistance  = nodeID ^ currentClosestSeenContact.NodeID;

                            if (previousDistance <= currentDistance)
                            {
                                //current round failed to return a node contact any closer than the closest already seen
                                if (finalRound)
                                {
                                    //final round over, check for termination condition
                                    checkTerminationCondition = true;
                                }
                                else
                                {
                                    //resend query to k closest node not already queried
                                    finalRound = true;
                                    alpha      = KADEMLIA_K;
                                }
                            }
                            else
                            {
                                //current closest seen contact is closer than previous closest seen contact
                                previousClosestSeenContact = currentClosestSeenContact;
                                finalRound = false;
                                alpha      = KADEMLIA_ALPHA;
                            }
                        }
                    }
                }

                if (checkTerminationCondition)
                {
                    checkTerminationCondition = false; //reset

                    if (queryType == DhtRpcType.FIND_PEERS)
                    {
                        //check only in final round to get most peers
                        lock (receivedPeers)
                        {
                            if (receivedPeers.Count > 0)
                            {
                                return(receivedPeers.ToArray());
                            }

                            return(null);
                        }
                    }

                    //lookup terminates when k closest seen contacts have responded
                    NodeContact[] kClosestSeenContacts;

                    lock (seenContacts)
                    {
                        kClosestSeenContacts = KBucket.GetClosestContacts(seenContacts, nodeID, KADEMLIA_K);
                    }

                    lock (respondedContacts)
                    {
                        bool success = true;

                        foreach (NodeContact contact in kClosestSeenContacts)
                        {
                            if (!respondedContacts.Contains(contact))
                            {
                                success = false;
                                break;
                            }
                        }

                        if (success)
                        {
                            return(kClosestSeenContacts);
                        }

                        if (alphaContacts.Length < 1)
                        {
                            return(KBucket.GetClosestContacts(respondedContacts, nodeID, KADEMLIA_K));
                        }
                    }
                }
            }
        }
Beispiel #10
0
        public bool RemoveContact(NodeContact contact)
        {
            KBucket currentBucket = this;

            while (true)
            {
                NodeContact[] contacts;
                KBucket       leftBucket;
                KBucket       rightBucket;

                lock (currentBucket)
                {
                    contacts    = currentBucket._contacts;
                    leftBucket  = currentBucket._leftBucket;
                    rightBucket = currentBucket._rightBucket;

                    if (contacts != null)
                    {
                        #region remove contact from this bucket

                        if (currentBucket._contactCount <= DhtNode.KADEMLIA_K)
                        {
                            return(false); //k-bucket is not full and replacement cache is empty
                        }
                        for (int i = 0; i < contacts.Length; i++)
                        {
                            if (contact.Equals(contacts[i]))
                            {
                                if (contacts[i].IsStale())
                                {
                                    //remove stale contact
                                    contacts[i] = null;
                                    currentBucket._lastChanged = DateTime.UtcNow;

                                    KBucket bucket = currentBucket;
                                    do
                                    {
                                        Interlocked.Decrement(ref bucket._contactCount);
                                        bucket = bucket._parentBucket;
                                    }while (bucket != null);

                                    return(true);
                                }

                                break;
                            }
                        }

                        return(false); //contact was not found or was not stale

                        #endregion
                    }
                }

                if ((leftBucket._bucketID & contact.NodeID) == leftBucket._bucketID)
                {
                    currentBucket = leftBucket;
                }
                else
                {
                    currentBucket = rightBucket;
                }
            }
        }