示例#1
0
文件: Ditto.DHT.cs 项目: ottid/ditto
        public async Task <List <IPEndPoint> > GetPeers(byte[] infohash)
        {
            var visitedNodeAddresses = new HashSet <Int32>();

            var queries = 0;

            while (true)
            {
                var nodes = getKnownNodesByCloseness(infohash);

                Node closestNode = null;

                foreach (var node in nodes)
                {
                    if (visitedNodeAddresses.Contains(node.EP.Address.GetAddressBytes().Decode32BitInteger()))
                    {
                        continue;
                    }
                    else
                    {
                        visitedNodeAddresses.Add(node.EP.Address.GetAddressBytes().Decode32BitInteger());
                        closestNode = node;
                        break;
                    }
                }

                if (closestNode == null)
                {
                    logger.LogInformation($"Need new nodes to continue querying {infohash.ToHuman()} in the DHT (already visited {visitedNodeAddresses.Count}).");
                    await Task.Delay(5000);

                    continue;
                }

                if (queries++ > 32)
                {
                    throw new Exception("query count sanity limit exceeded");
                }

                var token = (lastToken = IncrementToken(lastToken));

                var result = new TaskCompletionSource <Dictionary <byte[], object> >();
                var key    = new QueryKey {
                    Token = token, EP = closestNode.EP
                };
                pendingQueries.Add(key, result);
                Task.Run(async() =>
                {
                    await Task.Delay(5000);
                    result.TrySetException(new Exception("get_peers timed out"));
                }).DoNotAwait();

                logger.LogInformation($"Sending get_peers for {key}...");
                sendGetPeers(closestNode.EP, token, infohash);

                Dictionary <byte[], object> results = null;
                try
                {
                    results = await result.Task;
                }
                catch (Exception ex)
                {
                    logger.LogWarning($"Query failed: {ex}.");
                    continue;
                }

                var response = results.GetDict("r");

                if (response.ContainsKey("nodes"))
                {
                    var compactNodes = response.GetBytes("nodes");

                    logger.LogInformation($"Got {compactNodes.Length / 26} closer nodes, pinging them all.");

                    var nodeCount = compactNodes.Length % 26;
                    for (var i = 0; i < compactNodes.Length; i += 26)
                    {
                        // we disregard the node ID here, since we'll ping all of them and get it then
                        var ep = decodeCompactEP(compactNodes.Slice(i + 20, i + 26));
                        Ping(ep).DoNotAwait();
                    }

                    await Task.Delay(2000);

                    continue;
                }
                else
                {
                    var compactPeers = response.GetList("values");

                    logger.LogInformation("Got peers!");

                    var peers = new List <IPEndPoint> {
                    };

                    foreach (var compactPeer_ in compactPeers)
                    {
                        var compactPeer = (byte[])compactPeer_;
                        peers.Add(decodeCompactEP(compactPeer));
                    }

                    return(peers);
                }
            }

            throw new Exception("this code path should not be possible");
        }
示例#2
0
文件: Ditto.DHT.cs 项目: ottid/ditto
        private void handleMessage(UDPSocket.ReceivedPacket message)
        {
            var value = Bencoding.DecodeDict(message.Data);

            var type = value.GetString("y");

            switch (type)
            {
            case "r":
            {
                var key = new QueryKey
                {
                    Token = value.GetBytes("t"),
                    EP    = message.Source
                };

                if (pendingQueries.ContainsKey(key))
                {
                    var responseSource = pendingQueries[key];
                    pendingQueries.Remove(key);

                    responseSource.TrySetResult(value);
                }
                else
                {
                    logger.LogDebug("Got unexpected response message.");
                }

                break;
            }

            case "e":
            {
                var key = new QueryKey
                {
                    Token = value.GetBytes("t"),
                    EP    = message.Source
                };

                logger.LogError($"Got error mesage for {key}.");

                if (pendingQueries.ContainsKey(key))
                {
                    var responseSource = pendingQueries[key];
                    pendingQueries.Remove(key);

                    var errors       = value.GetList("e");
                    var code         = (Int64)errors[0];
                    var errorMessage = ((byte[])errors[1]).FromASCII();

                    var exception = new Exception($"{code} {errorMessage}");
                    responseSource.TrySetException(new Exception[] { exception });
                }
                else
                {
                    logger.LogError($"But I don't even know {key}!");
                }

                break;
            }

            case "q":
            {
                logger.LogDebug($"Ignored query mesage from {message.Source}.");
                // do nothing because we're read-only
                break;
            }

            default:
            {
                logger.LogDebug($"Got unknown mesage from {message.Source}.");
                // maybe we could send an error?
                break;
            }
            }
        }