예제 #1
0
 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.");
     }
 }
예제 #2
0
        /// <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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
            }
        }
예제 #6
0
        static Guid GetSession(BDictionary message)
        {
            Guid session;

            if (message.ContainsKey("session"))
            {
                session = Guid.Parse(message["session"].ToString());
            }
            else
            {
                session = NewSession();
            }
            return(session);
        }
예제 #7
0
        /// <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)
            });
        }
예제 #8
0
        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);
        }
예제 #9
0
        /// <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.");
            }
        }
예제 #10
0
        /// <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);
        }
예제 #11
0
        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}");
                }
            }
        }
예제 #12
0
        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);
        }