private void CheckMetadata(BDictionary metadata) { if (metadata == null) { throw new InvalidMetaException("Invalid metadata."); } if (!metadata.ContainsKey("announce")) { throw new InvalidMetaException("Invalid metadata, 'announce' not found."); } if (!metadata.ContainsKey("info")) { throw new InvalidMetaException("Invalid metadata, 'info' not found."); } }
/// <summary> /// Parses file info for multi-file torrents. /// </summary> /// <param name="info">The 'info'-dictionary of a torrent.</param> /// <param name="encoding"></param> /// <returns>The file info.</returns> protected virtual MultiFileInfoList ParseMultiFileInfo(BDictionary info, Encoding encoding) { if (!info.ContainsKey(TorrentInfoFields.Files)) { return(null); } var list = new MultiFileInfoList { DirectoryName = info.Get <BString>(TorrentInfoFields.Name)?.ToString(encoding), DirectoryNameUtf8 = info.Get <BString>(TorrentInfoFields.NameUtf8)?.ToString(encoding) }; var fileInfos = info.Get <BList>(TorrentInfoFields.Files).Cast <BDictionary>() .Select(x => new MultiFileInfo { FileSize = x.Get <BNumber>(TorrentFilesFields.Length), Path = x.Get <BList>(TorrentFilesFields.Path)?.AsStrings(encoding).ToList(), PathUtf8 = x.Get <BList>(TorrentFilesFields.PathUtf8)?.AsStrings(Encoding.UTF8).ToList(), Md5Sum = x.Get <BString>(TorrentFilesFields.Md5Sum)?.ToString(encoding) }); list.AddRange(fileInfos); return(list); }
private void LoadMetadata() { BDictionary metadata = ReadMetaInfo(); var announce = metadata["announce"] as BString; var info = metadata["info"] as BDictionary; //CheckMetadata(announce, info); var pieceSize = info["piece length"] as BInteger; List <byte[]> checksumList = GetRawChecksums(info, pieceSize); BString name = GetNameFromInfo(info); List <FileData> decodedFiles = DecodeFiles(info, name); if (metadata.ContainsKey("announce-list")) { var announceList = (BList)metadata["announce-list"]; Trackers = DecodeTrackerList(announceList); } Name = name; Files = decodedFiles; Tracker = announce; Checksums = checksumList; PieceSize = (int)pieceSize.InnerValue; PieceCount = Checksums.Count; TotalLength = (int)Files.Sum(f => f.Length); InfoHash = new InfoHash(ComputeInfoHash(info)); EnumerableTrackers = CreateTrackerList(Tracker, Trackers); }
public static MessageType TryParseMessage(byte[] messageBytes, out BDictionary message) { if (BObject.TryParse(messageBytes, out BObject bObject)) { if (bObject is BDictionary dictionary) { message = dictionary; if (message.TryGetValue("y", out BString type)) { if (!message.ContainsKey <BString>("t")) { return(MessageType.Invalid); } switch (type.Text) { case "q": return(MessageType.Query); case "r": return(MessageType.Response); case "e": return(MessageType.Error); default: return(MessageType.Unknown); } } } } message = null; return(MessageType.Invalid); }
private static void EnsureFields(IList <string> requiredFields, BDictionary data, string message = null) { message = message ?? "Torrent is missing required field."; foreach (var field in requiredFields.Where(field => !data.ContainsKey(field))) { throw new InvalidTorrentException(message, field); } }
static Guid GetSession(BDictionary message) { Guid session; if (message.ContainsKey("session")) { session = Guid.Parse(message["session"].ToString()); } else { session = NewSession(); } return(session); }
/// <summary> /// Parses file info for single-file torrents. /// </summary> /// <param name="info">The 'info'-dictionary of a torrent.</param> /// <param name="encoding"></param> /// <returns>The file info.</returns> protected virtual SingleFileInfo ParseSingleFileInfo(BDictionary info, Encoding encoding) { if (!info.ContainsKey(TorrentInfoFields.Length)) { return(null); } return(new SingleFileInfo { FileName = info.Get <BString>(TorrentInfoFields.Name)?.ToString(encoding), FileSize = info.Get <BNumber>(TorrentInfoFields.Length), Md5Sum = info.Get <BString>(TorrentInfoFields.Md5Sum)?.ToString(encoding) }); }
private List <FileData> DecodeFiles(BDictionary info, BString name) { var decodedFileList = new List <FileData>(); if (info.ContainsKey("files")) { DecodeMultipleFiles(info, decodedFileList); } else { DecodeSingleFile(info, name, decodedFileList); } return(decodedFileList); }
/// <summary> /// Checks the torrent data for required fields and throws an exception if any are missing or invalid. /// </summary> /// <param name="data">The torrent data.</param> /// <exception cref="InvalidTorrentException">The torrent data is missing required fields or otherwise invalid.</exception> protected void EnsureValidTorrentData(BDictionary data) { /* * NOTE: The 'announce' field is technically required according to the specification, * but is not really required for DHT and PEX. */ if (!data.ContainsKey(TorrentFields.Info)) { throw new InvalidTorrentException("Torrent is missing 'info'-dictionary.", TorrentFields.Info); } var info = data.Get <BDictionary>(TorrentFields.Info); var requiredFields = new List <string> { TorrentInfoFields.PieceLength, TorrentInfoFields.Pieces, TorrentInfoFields.Name }; // Single-file torrents must have either the 'length' field or the 'files' field, but not both if (info.ContainsKey(TorrentInfoFields.Length) && info.ContainsKey(TorrentInfoFields.Files)) { throw new InvalidTorrentException( $"Torrent 'info'-dictionary cannot contain both '{TorrentInfoFields.Length}' and '{TorrentInfoFields.Files}'."); } if (!info.ContainsKey(TorrentInfoFields.Length)) { requiredFields.Add(TorrentInfoFields.Files); } EnsureFields(requiredFields, info, "Torrent is missing required field in 'info'-dictionary."); if (info.ContainsKey(TorrentInfoFields.Files)) { var filesData = info.Get <BList>(TorrentInfoFields.Files).AsType <BDictionary>(); var requiredFileFields = new[] { TorrentFilesFields.Length, TorrentFilesFields.Path }; EnsureFields(requiredFileFields, filesData, "Torrent is missing required field in 'info.files' dictionaries."); } }
/// <summary> /// Parses any extra fields from the root or the 'info'-dictionary /// that are not otherwise represented in a <see cref="Torrent"/>. /// </summary> /// <param name="root"></param> /// <param name="encoding"></param> /// <returns></returns> protected virtual BDictionary ParseAnyExtraFields(BDictionary root, Encoding encoding) { var extraFields = ParseExtraRootFields(root); if (!root.ContainsKey(TorrentFields.Info)) { return(extraFields); } var info = root.Get <BDictionary>(TorrentFields.Info); var extraInfoFields = ParseExtraInfoFields(info); if (extraInfoFields.Any()) { extraFields.Add(TorrentFields.Info, extraInfoFields); } FixEncoding(extraFields, encoding); return(extraFields); }
private void GetPeers(Node node, int selfRecursionLevel = 0) { try { /* BEndode get_peers Response * * "t" -> <transId>, * "y" -> "r", * "r" -> { "id" -> <nodeId> , "token" -> <token> , "nodes" -> "nodeId + host + port...", "values" -> [host + port, ...] } * */ if (options.Verbosity > 0) { Log($"[{node.distance}] [{node.host}] [REQ ]"); } requested++; BDictionary bResponse = GetResponse(node); if (bResponse == null || !bResponse.ContainsKey("y") || ((BString)bResponse["y"]).ToString() != "r") { node.status = Node.Status.FAILED; if (!options.Beggar.Stats.BoostMode) { lock (rememberBadNodes) rememberBadNodes.Add(node.host); } if (bResponse != null) { bResponse.Clear(); } if (options.Verbosity > 0) { Log($"[{node.distance}] [{node.host}] [RESP] Failed"); } return; } if (options.Verbosity > 0) { Log($"[{node.distance}] [{node.host}] [RESP]"); } bResponse = (BDictionary)bResponse["r"]; // r -> Nodes if (bResponse.ContainsKey("nodes")) { byte[] curNodes = ((BString)bResponse["nodes"]).Value.ToArray(); for (int i = 0; i < curNodes.Length; i += 26) { byte[] curNodeId = Utils.ArraySub(ref curNodes, (uint)i, 20, false); short curDistance = isWeirdStrategy ? CalculateDistance(curNodeId) : CalculateDistance2(curNodeId); string curIP = (new IPAddress(Utils.ArraySub(ref curNodes, (uint)i + 20, 4))).ToString(); UInt16 curPort = (UInt16)BitConverter.ToInt16(Utils.ArraySub(ref curNodes, (uint)i + 24, 2, true), 0); if (curPort < 100) { continue; // Drop fake } if (i > (5 * 26) && curDistance > minBucketDistance) { break; // Avoid collecting too many nodes out of distance from a single node } if (options.Verbosity > 0) { Log($"[{node.distance}] [{node.host}] [NODE] [{curDistance}] {curIP}:{curPort}"); } lock (lockerNodes) { if (!bucketNodesPointer.ContainsKey(curIP) && !rememberBadNodes.Contains(curIP)) { AddNewNode(curIP, curPort, curDistance); } } } } // r -> Peers if (bResponse.ContainsKey("values")) { int newPeers = 0; BList values = (BList)bResponse["values"]; if (values.Count == 0) { node.status = Node.Status.REQUESTED; responded++; bResponse.Clear(); return; } Dictionary <string, int> curPeers = new Dictionary <string, int>(); if (isWeirdStrategy) { weirdPeers += values.Count; } else { normalPeers += values.Count; } //node.hasPeers = true; havePeers++; foreach (IBObject cur in values) { byte[] value = ((BString)cur).Value.ToArray(); string curIP = (new IPAddress(Utils.ArraySub(ref value, 0, 4))).ToString(); UInt16 curPort = (UInt16)BitConverter.ToInt16(Utils.ArraySub(ref value, 4, 2, true), 0); if (curPort < 500) { continue; // Drop fake / Avoid DDOS } //if (options.Verbosity > 0) Log($"[{node.distance}] [{node.host}] [PEER] {curIP}:{curPort}"); curPeers[curIP] = curPort; } if (curPeers.Count > 0) { options.Beggar.FillPeers(curPeers, BitSwarm.PeersStorage.DHT); } //if (options.Verbosity > 0) Log($"[{node.distance}] [{node.host}] [NEW PEERS] {newPeers}"); // Re-requesting same Node with Peers > 99 (max returned peers are 100?) // Possible fake/random peers (escape recursion? 30 loops?) | NOTE: we loosing sync with scheduler because of that if (status == Status.RUNNING && values.Count > 99) { if (selfRecursionLevel > 30) { if (options.Verbosity > 0) { Log($"[{node.distance}] [{node.host}] [RE-REQUEST LIMIT EXCEEDED]"); } } else { selfRecursionLevel++; if (options.Verbosity > 0) { Log($"[{node.distance}] [{node.host}] [RE-REQUEST {selfRecursionLevel}] {newPeers}"); } Thread.Sleep(10); GetPeers(node, selfRecursionLevel); } } } node.status = Node.Status.REQUESTED; responded++; bResponse.Clear(); } catch (Exception e) { node.status = Node.Status.FAILED; if (options.Verbosity > 0) { Log($"[{node.distance}] [{node.host}] [ERROR] {e.Message}\r\n{e.StackTrace}"); } } }
public bool SendEventToTracker(NetworkManager networkManager, string eventType, bool stopOnFail) { IsScrapeUpdated = false; string urlEvent = GenerateUrlString(eventType); Uri uriEvent = new Uri(urlEvent); try { Tracker = networkManager.MakeWebRequest(uriEvent, Client.HttpProtocol, Client.Headers); if (Tracker != null && Tracker.Dico != null) { BDictionary dico = Tracker.Dico; BString failure = dico.Get <BString>("failure reason"); if (failure != null) { log.Warn($"Tracker Error: {failure}"); if (stopOnFail) { log.Info("Stopped because of tracker error!!!"); return(false); } } else { foreach (BString key in dico.Keys) { if (key != "peers") { log.Info(key + ": " + dico[key]); } } BNumber interval = dico.Get <BNumber>("interval"); if (interval != null) { } BNumber complete = dico.Get <BNumber>("complete"); BNumber incomplete = dico.Get <BNumber>("incomplete"); Seeders = (int)complete.Value; Leechers = (int)incomplete.Value; } if (dico.ContainsKey("peers")) { HasInitialPeers = true; if (dico["peers"] is BString) { Encoding encoding = Encoding.GetEncoding(0x6faf); byte[] buffer = encoding.GetBytes(dico["peers"].ToString()); using (MemoryStream ms = new MemoryStream(buffer)) using (BinaryReader br = new BinaryReader(ms)) { PeerList list = new PeerList(); for (int num1 = 0; num1 < buffer.Length; num1 += 6) { list.Add(new Peer(br.ReadBytes(4), br.ReadInt16())); } br.Close(); log.Info($"Peers : {list}"); } } else if (dico["peers"] is BList) { BList bList = dico.Get <BList>("peers"); PeerList pList = new PeerList(); foreach (object objList in bList) { if (objList is BDictionary) { BDictionary dictionary = (BDictionary)objList; pList.Add(new Peer(dictionary["ip"].ToString(), dictionary["port"].ToString(), dictionary["peer id"].ToString())); } } log.Info($"Peers : {pList}"); } else { log.Info($"Peers(x) : {dico["peers"]}"); } } } } catch (Exception ex) { log.Error(ex); return(false); } return(true); }