} // Torrent file name /// <summary> /// Get a list of dictionaries from metainfo file that have been come under the main level dictionary /// key of "files". The output being a entry in the internal dictionary that contains a comma /// separated string of the fields (name, length, md5sum) for each list entry (file). Each file found is /// stored under the numeric key value representing the number of the dictionary within the list /// (ie. "0", "1"..... "N"). /// </summary> /// <param name="bNodeRoot">BNode root of list.</param> /// <param name="field">Field.</param> private void GetListOfDictionarys(BNodeBase bNodeRoot, string field) { BNodeBase fieldBytes = _Bencode.GetDictionaryEntry(bNodeRoot, field); if (fieldBytes is BNodeList bNodeList) { int fileNo = 0; foreach (var listItem in (bNodeList).list) { if (listItem is BNodeDictionary bNodeDictionary) { BNodeBase fileDictionaryItem = (bNodeDictionary); BNodeBase fileField = null; string fileEntry = String.Empty; fileField = _Bencode.GetDictionaryEntry(fileDictionaryItem, "path"); if (fileField != null) { string path = string.Empty; foreach (var file in ((BNodeList)(fileField)).list) { path += $"{Path.DirectorySeparatorChar}" + Encoding.ASCII.GetString(((BitTorrentLibrary.BNodeString)file).str); } fileEntry = path; } fileEntry += ","; fileEntry += _Bencode.GetDictionaryEntryString(fileDictionaryItem, "length"); fileEntry += ","; fileEntry += _Bencode.GetDictionaryEntryString(fileDictionaryItem, "md5string"); metaInfoDict[fileNo.ToString()] = Encoding.ASCII.GetBytes(fileEntry); fileNo++; } } } }
/// <summary> /// Return string representation of a given BNode dictionary entry. /// </summary> /// <returns>String value of a given byte encoded number or string.</returns> /// <param name="bNode">bNode - Entry to extract.</param> /// <param name="entryKey">entryKey - Key of dictionary entry to extract.</param> public string GetDictionaryEntryString(BNodeBase bNode, string entryKey) { BNodeBase entryNode; try { entryNode = GetDictionaryEntry(bNode, entryKey); if (entryNode != null) { if (entryNode is BNodeString bNodeString) { return(Encoding.ASCII.GetString((bNodeString).str)); } if (entryNode is BNodeNumber bNodeNumber) { return(Encoding.ASCII.GetString((bNodeNumber).number)); } } } catch (Exception ex) { Log.Logger.Debug(ex); throw new Exception("Could not get string from BNode structure." + ex.Message); } return(""); }
public void TestMultiFileTorrentWithErrorDecode() { Bencode bEncode = new Bencode(); byte[] expected = System.IO.File.ReadAllBytes(Constants.MultiFileWithErrorTorrent); Assert.Throws <Exception>(() => { BNodeBase torrentBase = bEncode.Decode(expected); }); }
public void TestDecodeThenEncodeTheSamString(string expected) { Bencode bEncode = new Bencode(); BNodeBase bNode = bEncode.Decode(Encoding.ASCII.GetBytes(expected)); byte[] actual = bEncode.Encode(bNode); Assert.Equal(Encoding.ASCII.GetBytes(expected), actual); }
/// <summary> /// Calculates the info hash for metainfo and stores in internal dictionary. /// </summary> /// <param name="bNodeRoot">BNode root of meta info.</param> private void CalculateInfoHash(BNodeBase bNodeRoot) { BNodeBase infoEncodedBytes = _Bencode.GetDictionaryEntry(bNodeRoot, "info"); if (infoEncodedBytes != null) { metaInfoDict["info hash"] = new SHA1CryptoServiceProvider().ComputeHash(_Bencode.Encode(infoEncodedBytes)); } }
public void TestMultiFileTorrentDecodeEncodeCheckTheSameAfter() { Bencode bEncode = new Bencode(); byte[] expected = System.IO.File.ReadAllBytes(Constants.MultiFileTorrent); BNodeBase torrentBase = bEncode.Decode(expected); byte[] actual = bEncode.Encode(torrentBase); Assert.Equal(expected, actual); }
/// <summary> /// Gets the bytes representing a string or number (characters of number). /// </summary> /// <param name="bNodeRoot">BNode root of string number.</param> /// <param name="field">Field.</param> private void GetStringOrNumeric(BNodeBase bNodeRoot, string field) { BNodeBase fieldBytes = _Bencode.GetDictionaryEntry(bNodeRoot, field); if (fieldBytes is BNodeNumber bNodeNumber) { metaInfoDict[field] = (bNodeNumber).number; } else if (fieldBytes is BNodeString bNodeString) { metaInfoDict[field] = (bNodeString).str; } }
/// <summary> /// Gets the list of strings from a BNode and create a comma separated string representing the /// list in the internal dictionary under the key value of field. /// </summary> /// <param name="bNodeRoot">BNode root of list of strings.</param> /// <param name="field">Field.</param> private void GetListOfStrings(BNodeBase bNodeRoot, string field) { BNodeBase fieldBytes = _Bencode.GetDictionaryEntry(bNodeRoot, field); if (fieldBytes is BNodeList bNodeList) { List <string> listString = new List <string>(); foreach (var innerList in (bNodeList).list) { if (innerList is BNodeList bNodeList2) { BNodeString stringItem = (BNodeString)(bNodeList2).list[0]; listString.Add(Encoding.ASCII.GetString(stringItem.str)); } } metaInfoDict[field] = Encoding.ASCII.GetBytes(string.Join(",", listString)); } }
/// <summary> /// Recursively parse BNode structure to produce Bencoding for it. /// </summary> /// <returns>Bencoded representation of BNode structure.</returns> /// <param name="bNode">Root <paramref name="bNode"/>.</param> private void EncodeFromBNode(BNodeBase bNode) { try { if (bNode is BNodeDictionary bNodeDictionary) { _workBuffer.Add((byte)'d'); foreach (var key in (bNodeDictionary).dict.Keys) { _workBuffer.AddRange(Encoding.ASCII.GetBytes($"{key.Length}:{key}")); EncodeFromBNode((bNodeDictionary).dict[key]); } _workBuffer.Add((byte)'e'); } else if (bNode is BNodeList bNodeList) { _workBuffer.Add((byte)'l'); foreach (var node in (bNodeList).list) { EncodeFromBNode(node); } _workBuffer.Add((byte)'e'); } else if (bNode is BNodeNumber bNodeNumber) { _workBuffer.Add((byte)'i'); _workBuffer.AddRange((bNodeNumber).number); _workBuffer.Add((byte)'e'); } else if (bNode is BNodeString bNodeString) { _workBuffer.AddRange(Encoding.ASCII.GetBytes($"{(bNodeString).str.Length}:")); _workBuffer.AddRange((bNodeString).str); } } catch (Exception ex) { Log.Logger.Debug(ex); throw new Exception("Failure to encode BNode structure." + ex.Message); } }
/// <summary> /// Get a BNode entry for a given dictionary key. Note that it recursively searches /// until the key is found in the BNode structure structure. /// </summary> /// <returns>BNode entry for dictionary key.</returns> /// <param name="bNode">bNode - Bnode root of dictionary.</param> /// <param name="entryKey">entryKey - Dictionary key of Bnode entry to return.</param> public BNodeBase GetDictionaryEntry(BNodeBase bNode, string entryKey) { BNodeBase bNodeEntry = null; try { if (bNode is BNodeDictionary bNodeDictionary) { if ((bNodeDictionary).dict.ContainsKey(entryKey)) { return((bNodeDictionary).dict[entryKey]); } foreach (var key in (bNodeDictionary).dict.Keys) { bNodeEntry = GetDictionaryEntry((bNodeDictionary).dict[key], entryKey); if (bNodeEntry != null) { break; } } } else if (bNode is BNodeList bNodeList) { foreach (var node in (bNodeList).list) { bNodeEntry = GetDictionaryEntry(node, entryKey); if (bNodeEntry != null) { break; } } } } catch (Exception ex) { Log.Logger.Debug(ex); throw new Exception("Could not get dictionary from BNode structure." + ex.Message); } return(bNodeEntry); }
/// <summary> /// Decode Bencoded torrent file and load internal dictionary from its contents /// for later retrieval by other modules. /// </summary> public void Parse() { try { BNodeBase bNodeRoot = _Bencode.Decode(_metaInfoData); GetStringOrNumeric(bNodeRoot, "announce"); GetListOfStrings(bNodeRoot, "announce-list"); GetStringOrNumeric(bNodeRoot, "comment"); GetStringOrNumeric(bNodeRoot, "created by"); GetStringOrNumeric(bNodeRoot, "creation date"); GetStringOrNumeric(bNodeRoot, "name"); GetStringOrNumeric(bNodeRoot, "piece length"); GetStringOrNumeric(bNodeRoot, "pieces"); GetStringOrNumeric(bNodeRoot, "private"); GetStringOrNumeric(bNodeRoot, "url-list"); if (_Bencode.GetDictionaryEntry(bNodeRoot, "files") == null) { GetStringOrNumeric(bNodeRoot, "length"); GetStringOrNumeric(bNodeRoot, "md5sum"); } else { GetListOfDictionarys(bNodeRoot, "files"); } CalculateInfoHash(bNodeRoot); foreach (var key in metaInfoDict.Keys) { if ((key != "pieces") && (key != "info") && (key != "info hash")) { Log.Logger.Error($"{key}={Encoding.ASCII.GetString(metaInfoDict[key])}"); } } } catch (Exception ex) { Log.Logger.Error(ex); throw new BitTorrentException(ex.Message); } }
/// <summary> /// Produce Bencoded output given the root of a BNode structure. /// </summary> /// <param name="bNode"></param> /// <returns></returns> public byte[] Encode(BNodeBase bNode) { _workBuffer.Clear(); EncodeFromBNode(bNode); return(_workBuffer.ToArray()); }
private readonly Bencode _Bencode; // Bencode encode/decode /// <summary> /// Decodes the announce request BEncoded response recieved from a tracker. /// </summary> /// <param name="announceResponse">Announce response.</param> /// <param name="decodedResponse">Response.</param> private void DecodeAnnounceResponse(Tracker tracker, byte[] announceResponse, ref AnnounceResponse decodedResponse) { if (announceResponse.Length != 0) { BNodeBase decodedAnnounce = _Bencode.Decode(announceResponse); decodedResponse.statusMessage = _Bencode.GetDictionaryEntryString(decodedAnnounce, "failure reason"); if (decodedResponse.statusMessage != "") { decodedResponse.failure = true; return; // If failure present then ignore rest of reply. } int.TryParse(_Bencode.GetDictionaryEntryString(decodedAnnounce, "complete"), out decodedResponse.complete); int.TryParse(_Bencode.GetDictionaryEntryString(decodedAnnounce, "incomplete"), out decodedResponse.incomplete); BNodeBase field = _Bencode.GetDictionaryEntry(decodedAnnounce, "peers"); if (field != null) { decodedResponse.peerList = new List <PeerDetails>(); if (field is BNodeString bNodeString) // Compact peer list reply { decodedResponse.peerList = tracker.GetCompactPeerList((bNodeString).str, 0); } else if (field is BNodeList bNodeList) // Non-compact peer list reply { foreach (var listItem in (bNodeList).list) { if (listItem is BNodeDictionary bNodeDictionary) { PeerDetails peer = new PeerDetails { infoHash = tracker.InfoHash }; BNodeBase peerDictionaryItem = (bNodeDictionary); BNodeBase peerField = _Bencode.GetDictionaryEntry(peerDictionaryItem, "ip"); if (peerField != null) { peer.ip = Encoding.ASCII.GetString(((BitTorrentLibrary.BNodeString)peerField).str); } if (peer.ip.Contains(":")) { peer.ip = peer.ip.Substring(peer.ip.LastIndexOf(":", StringComparison.Ordinal) + 1); } peerField = _Bencode.GetDictionaryEntry(peerDictionaryItem, "port"); if (peerField != null) { peer.port = int.Parse(Encoding.ASCII.GetString(((BitTorrentLibrary.BNodeNumber)peerField).number)); } if (peer.ip != tracker.Ip) // Ignore self in peers list { Log.Logger.Trace($"(Tracker) Peer {peer.ip} Port {peer.port} found."); decodedResponse.peerList.Add(peer); } } } } } int.TryParse(_Bencode.GetDictionaryEntryString(decodedAnnounce, "interval"), out decodedResponse.interval); int.TryParse(_Bencode.GetDictionaryEntryString(decodedAnnounce, "min interval"), out decodedResponse.minInterval); decodedResponse.trackerID = _Bencode.GetDictionaryEntryString(decodedAnnounce, "tracker id"); decodedResponse.statusMessage = _Bencode.GetDictionaryEntryString(decodedAnnounce, "warning message"); decodedResponse.announceCount++; } }