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"); }
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; } } }