public static void ReceiveHandshake(IO.Stream stream, ref ByteField20 infoDigest) { // read in protocol string byte[] protocolVersionLength = new byte[1]; if (stream.Read(protocolVersionLength, 0, protocolVersionLength.Length) != protocolVersionLength.Length) { throw new System.Exception("Invalid handshake protocol"); } if (protocolVersionLength[0] != 19) { throw new System.Exception("Invalid handshake protocol"); } byte[] protocolBytes = new byte[protocolVersionLength[0]]; if (stream.Read(protocolBytes, 0, protocolBytes.Length) != protocolBytes.Length) { throw new System.Exception("Invalid handshake protocol"); } string protocol = System.Text.ASCIIEncoding.ASCII.GetString(protocolBytes, 0, protocolBytes.Length); if (protocol != protocolString) { throw new System.Exception("Invalid handshake protocol"); } // 8 zeros byte[] zeroes = new byte[8]; if (stream.Read(zeroes, 0, zeroes.Length) != zeroes.Length) { throw new System.Exception("Invalid handshake protocol"); } // SHA digest stream.Read(infoDigest.Data, 0, infoDigest.Data.Length); }
public Session(ByteField20 localPeerId) { mLocalPeerId = localPeerId; if (Config.ActiveConfig.ProxyURL != "") // set web proxy { System.Net.WebRequest.DefaultWebProxy = new Net.WebProxy(Config.ActiveConfig.ProxyURL); } SetAllowUnsafeHeaderParsing20(); this.StartServer(); }
public static void SendHandshake(IO.Stream stream, ByteField20 infoDigest) { stream.WriteByte((byte)protocolString.Length); stream.Write(System.Text.ASCIIEncoding.ASCII.GetBytes(protocolString), 0, protocolString.Length); // 8 zeros stream.Write(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 8); // SHA digest stream.Write(infoDigest.Data, 0, infoDigest.Data.Length); }
public Torrent FindTorrent(ByteField20 infoDigest) { lock (this.mTorrents) { foreach (Torrent torrent in this.mTorrents) { if (torrent.Metainfo.InfoDigest.Equals(infoDigest)) { return(torrent); } } return(null); } }
/// <summary> /// Helper thread method to start the connection to peers /// </summary> /// <param name="state"></param> private void StartPeerConnectionThread(System.IAsyncResult result) { object[] objs = (object[])result.AsyncState; Sockets.Socket socket = (Sockets.Socket)objs[0]; PeerInformation peerinfo = (PeerInformation)objs[1]; ByteField20 infoDigest = new ByteField20(), peerId = new ByteField20(); Sockets.NetworkStream netStream; try { socket.EndConnect(result); netStream = new Sockets.NetworkStream(socket, true); // send handshake info PeerProtocol.SendHandshake(netStream, this.infofile.InfoDigest); PeerProtocol.ReceiveHandshake(netStream, ref infoDigest); PeerProtocol.SendPeerId(netStream, this.mSession.LocalPeerID); if (!PeerProtocol.ReceivePeerId(netStream, ref peerId)) { // NAT check socket.Close(); return; } // check info digest matches and we are not attempting to connect to ourself if (infoDigest.Equals(this.infofile.InfoDigest) && !peerId.Equals(this.mSession.LocalPeerID)) { peerinfo.ID = peerId; this.AddPeer(socket, netStream, peerinfo); } else // info digest doesn't match, close the connection { socket.Close(); } } catch (System.Exception e) { Config.LogException(e); // die if the connection failed if (socket != null) { socket.Close(); } return; } }
/// <summary></summary> /// <returns>True if the objects are equal</returns> public override bool Equals(object obj) { if (obj is ByteField20) { ByteField20 sha = (ByteField20)obj; for (int i = 0; i < this.data.Length; ++i) { if (this.data[i] != sha.data[i]) { return(false); } } return(true); } else { return(false); } }
private static ByteField20 DefaultCalculatePeerId() { // calculate our peer id string peerIdString = "-TN" + string.Format("{0:00}", Config.MajorVersionNumber) + string.Format("{0:00}", Config.MinorVersionNumber) + "-"; ByteField20 peerId = new ByteField20(); System.Array.Copy(System.Text.ASCIIEncoding.ASCII.GetBytes(peerIdString), 0, peerId.Data, 0, peerIdString.Length); const string peerChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; System.Random rand = new System.Random(); for (int i = peerIdString.Length; i < 20; ++i) { peerId.Data[i] = (byte)peerChars[rand.Next(0, peerChars.Length - 1)]; } return(peerId); }
public static void ReceiveHandshake(IO.Stream stream, ref ByteField20 infoDigest) { // read in protocol string byte[] protocolVersionLength = new byte[1]; if ( stream.Read( protocolVersionLength, 0, protocolVersionLength.Length ) != protocolVersionLength.Length ) throw new System.Exception( "Invalid handshake protocol" ); if ( protocolVersionLength[0] != 19 ) throw new System.Exception( "Invalid handshake protocol" ); byte[] protocolBytes = new byte[ protocolVersionLength[ 0 ] ]; if ( stream.Read( protocolBytes, 0, protocolBytes.Length ) != protocolBytes.Length ) throw new System.Exception( "Invalid handshake protocol" ); string protocol = System.Text.ASCIIEncoding.ASCII.GetString(protocolBytes, 0, protocolBytes.Length); if (protocol != protocolString) throw new System.Exception( "Invalid handshake protocol" ); // 8 zeros byte[] zeroes = new byte[ 8 ]; if ( stream.Read( zeroes, 0, zeroes.Length ) != zeroes.Length ) throw new System.Exception( "Invalid handshake protocol" ); // SHA digest stream.Read(infoDigest.Data, 0, infoDigest.Data.Length); }
/// <summary> /// Parses the response from the tracker, and updates the peer list /// </summary> /// <param name="stream">IO stream from response</param> private void ParseTrackerResponse(IO.Stream stream) { this.peerList.Clear(); /* * // because the response stream does not support seeking, we copy the contents to a memorystream * // to send to the bencoder. This shouldnt cause too much of a performance penalty as it shouldnt * // be too large anyway. * byte[] data = new byte[ 1024 ]; * IO.MemoryStream responseStream = new IO.MemoryStream(); * int dataRead = 0; * * while ((dataRead = stream.Read(data, 0, data.Length)) > 0) * { * responseStream.Write(data, 0, dataRead); * } * * responseStream.Seek(0, IO.SeekOrigin.Begin); */ /// BEncode.Dictionary dic = BEncode.NextDictionary(stream); // note: sometimes IPs can be duplicated in quick disconnection, so there is a check for any duplications if (dic.Contains("failure reason")) { throw new IO.IOException("Tracker connection failed: " + dic.GetString("failure reason")); } else { this.updateInterval = dic.GetInteger("interval"); BEncode.Element peers = dic["peers"]; if (peers is BEncode.List) { // peer list comes as a list of dictionaries BEncode.List dicList = (BEncode.List)peers; foreach (BEncode.Dictionary dicPeer in dicList) { ByteField20 peerId = new ByteField20(dicPeer.GetBytes("peer id")); string peerIp = dicPeer.GetString("ip"); int port = dicPeer.GetInteger("port"); PeerInformation peerinfo = new PeerInformation(peerIp, port, peerId); if (!this.peerList.Contains(peerinfo)) { this.peerList.Add(peerinfo); } } } else if (peers is BEncode.String) { // else its compressed (this is pretty common) byte[] compactPeers = ((BEncode.String)peers).Data; for (int i = 0; i < compactPeers.Length; i += 6) { int ip1 = 0xFF & compactPeers[i]; int ip2 = 0xFF & compactPeers[i + 1]; int ip3 = 0xFF & compactPeers[i + 2]; int ip4 = 0xFF & compactPeers[i + 3]; int po1 = 0xFF & compactPeers[i + 4]; int po2 = 0xFF & compactPeers[i + 5]; string peerIp = ip1 + "." + ip2 + "." + ip3 + "." + ip4; int port = (po1 * 256) + po2; PeerInformation peerinfo = new PeerInformation(peerIp, port); if (!this.peerList.Contains(peerinfo)) { this.peerList.Add(peerinfo); } } } else { throw new TrackerException("Unexcepted error"); } } }
/// <summary> /// This *must* be called before any operations are performed. This should be in the constructor, but as it can be a time-consuming process /// it was decided against. This analyzes the file to look for which pieces are downloaded, or if the torrent has just started it will create the /// new, empty files. /// </summary> /// <param name="callback">Callback delegate to inform the caller of the progress</param> public void CheckIntegrity( bool forced, CheckIntegrityCallback callback) { // if their already is a piece file in the application directory, load it from there. (unless it is forced where is has to do it) if ( forced || !GetPieceInfoFromFile(callback)) { int dataPosition = 0, filePosition = 0; int i = 0; byte[] data = new byte[this.infofile.GetPieceLength(0)]; Crypto.SHA1 sha = Crypto.SHA1CryptoServiceProvider.Create(); for (int currentFile=0; currentFile<this.infofile.FileCount; ++currentFile) { int fileLength = this.infofile.GetFileLength(currentFile); string path = this.infofile.GetFileName(currentFile); IO.FileInfo fileInfo = new IO.FileInfo(path); if (!fileInfo.Exists) { // create the file if it does not exist this.CreateEmptyFile(fileInfo, fileLength); int fileDataLeft = 0; if (dataPosition > 0) { // if dataPosition does not equal zero, meaning we have some of a piece from the last file. This automatically fails, // and we move onto the next piece. i++; fileDataLeft = fileLength - (this.infofile.GetPieceLength(0) - dataPosition); } else fileDataLeft = fileLength - filePosition; int numPieces = fileDataLeft / this.infofile.GetPieceLength(0); i += numPieces; if (fileDataLeft % this.infofile.GetPieceLength(0) > 0) { // set the next file's filePosition, and fail the next piece filePosition = this.infofile.GetPieceLength(i) - (fileDataLeft % this.infofile.GetPieceLength(i)); i++; } else { filePosition = 0; } dataPosition = 0; if (callback != null) callback(this, i, false, ((float)(i+1)/(float)this.infofile.PieceCount) * 100.0f); // move onto next file continue; } else { // check the length, otherwise truncate it if (fileInfo.Length != fileLength) this.TruncateFile(fileInfo, fileLength); // open the file, start checking. IO.FileStream fstream = fileInfo.OpenRead(); while (filePosition < fileLength) { int dataToRead = System.Math.Min(fileLength - filePosition, this.infofile.GetPieceLength(i) - dataPosition); byte[] tempData = new byte[dataToRead]; fstream.Read(tempData, 0, tempData.Length); if (dataToRead + dataPosition >= this.infofile.GetPieceLength(i)) { // piece finished System.Array.Copy(tempData, 0, data, dataPosition, dataToRead); sha.ComputeHash(data, 0, this.infofile.GetPieceLength(i)); ByteField20 final = new ByteField20(sha.Hash); bool good = final.Equals( this.infofile.GetSHADigest(i) ); if (!good) // if piece is good we can subtract it from the bytes left to download numBytesLeft += this.infofile.GetPieceLength(i); this.piecesDownloaded.Set(i, good); if (callback != null) callback(this, i, good, ((float)(i+1)/(float)this.infofile.PieceCount) * 100.0f); i++; dataPosition = 0; } else { System.Array.Copy(tempData, 0, data, dataPosition, dataToRead); dataPosition += dataToRead; } filePosition += dataToRead; } filePosition = 0; fstream.Close(); } } } if (this.PercentChanged != null) this.PercentChanged(this, this.PercentComplete); }
private void OnAccept(System.IAsyncResult result) { Sockets.Socket socket; try { // Accept connections from other peers, find the appropriate torrent and add the peer to it socket = this.mListener.EndAccept(result); } catch (System.Exception) { if (this.mListener != null) { this.mListener.Close(); } this.mListener = null; return; } try { ByteField20 infoDigest = new ByteField20(), peerId = new ByteField20(); Sockets.NetworkStream netStream = new Sockets.NetworkStream(socket, true); PeerProtocol.ReceiveHandshake(netStream, ref infoDigest); Torrent torrent = this.FindTorrent(infoDigest); if (torrent != null) { // found it, finish handshaking and add the peer to the list PeerProtocol.SendHandshake(netStream, torrent.Metainfo.InfoDigest); PeerProtocol.SendPeerId(netStream, mLocalPeerId); if (!PeerProtocol.ReceivePeerId(netStream, ref peerId)) { // NAT check, discard socket.Close(); } else { if (!peerId.Equals(mLocalPeerId)) // make sure we aren't connecting to ourselves { Net.IPEndPoint endPoint = (Net.IPEndPoint)socket.RemoteEndPoint; PeerInformation peerInformation = new PeerInformation(endPoint.Address.ToString(), endPoint.Port, peerId); // add the peer to the torrent torrent.AddPeer(socket, netStream, peerInformation); } else { socket.Close(); } } } else { socket.Close(); } } catch (System.Exception e) { Config.LogException(e); socket.Close(); } this.mListener.BeginAccept(new System.AsyncCallback(OnAccept), null); }
/// <summary> /// This *must* be called before any operations are performed. This should be in the constructor, but as it can be a time-consuming process /// it was decided against. This analyzes the file to look for which pieces are downloaded, or if the torrent has just started it will create the /// new, empty files. /// </summary> /// <param name="callback">Callback delegate to inform the caller of the progress</param> public void CheckIntegrity(bool forced, CheckIntegrityCallback callback) { // if their already is a piece file in the application directory, load it from there. (unless it is forced where is has to do it) if (forced || !GetPieceInfoFromFile(callback)) { int dataPosition = 0, filePosition = 0; int i = 0; byte[] data = new byte[this.infofile.GetPieceLength(0)]; Crypto.SHA1 sha = Crypto.SHA1CryptoServiceProvider.Create(); for (int currentFile = 0; currentFile < this.infofile.FileCount; ++currentFile) { int fileLength = this.infofile.GetFileLength(currentFile); string path = this.infofile.GetFileName(currentFile); IO.FileInfo fileInfo = new IO.FileInfo(path); if (!fileInfo.Exists) { // create the file if it does not exist this.CreateEmptyFile(fileInfo, fileLength); int fileDataLeft = 0; if (dataPosition > 0) { // if dataPosition does not equal zero, meaning we have some of a piece from the last file. This automatically fails, // and we move onto the next piece. i++; fileDataLeft = fileLength - (this.infofile.GetPieceLength(0) - dataPosition); } else { fileDataLeft = fileLength - filePosition; } int numPieces = fileDataLeft / this.infofile.GetPieceLength(0); i += numPieces; if (fileDataLeft % this.infofile.GetPieceLength(0) > 0) { // set the next file's filePosition, and fail the next piece filePosition = this.infofile.GetPieceLength(i) - (fileDataLeft % this.infofile.GetPieceLength(i)); i++; } else { filePosition = 0; } dataPosition = 0; if (callback != null) { callback(this, i, false, ((float)(i + 1) / (float)this.infofile.PieceCount) * 100.0f); } // move onto next file continue; } else { // check the length, otherwise truncate it if (fileInfo.Length != fileLength) { this.TruncateFile(fileInfo, fileLength); } // open the file, start checking. IO.FileStream fstream = fileInfo.OpenRead(); while (filePosition < fileLength) { int dataToRead = System.Math.Min(fileLength - filePosition, this.infofile.GetPieceLength(i) - dataPosition); byte[] tempData = new byte[dataToRead]; fstream.Read(tempData, 0, tempData.Length); if (dataToRead + dataPosition >= this.infofile.GetPieceLength(i)) { // piece finished System.Array.Copy(tempData, 0, data, dataPosition, dataToRead); sha.ComputeHash(data, 0, this.infofile.GetPieceLength(i)); ByteField20 final = new ByteField20(sha.Hash); bool good = final.Equals(this.infofile.GetSHADigest(i)); if (!good) // if piece is good we can subtract it from the bytes left to download { numBytesLeft += this.infofile.GetPieceLength(i); } this.piecesDownloaded.Set(i, good); if (callback != null) { callback(this, i, good, ((float)(i + 1) / (float)this.infofile.PieceCount) * 100.0f); } i++; dataPosition = 0; } else { System.Array.Copy(tempData, 0, data, dataPosition, dataToRead); dataPosition += dataToRead; } filePosition += dataToRead; } filePosition = 0; fstream.Close(); } } } if (this.PercentChanged != null) { this.PercentChanged(this, this.PercentComplete); } }
public static bool ReceivePeerId(IO.Stream stream, ref ByteField20 peerId) { return(stream.Read(peerId.Data, 0, peerId.Data.Length) > 0); }
/// <summary>Constructs a MetainfoFile</summary> /// <param name="istream">Stream to read data from</param> public MetainfoFile(IO.Stream istream) { BEncode.Dictionary mainDictionary = (BEncode.Dictionary)BEncode.NextElement(istream); this.announceUrl = mainDictionary.GetString(new BEncode.String("announce")); if (mainDictionary.Contains("comment")) { this.comment = mainDictionary.GetString("comment"); } if (mainDictionary.Contains("created by")) { this.createdBy = mainDictionary.GetString("created by"); } if (mainDictionary.Contains("creation date")) { int creation = mainDictionary.GetInteger("creation date"); this.creationDate = new System.DateTime(1970, 1, 1, 0, 0, 0); this.creationDate = this.creationDate.AddSeconds(creation); } BEncode.Dictionary infoDictionary = mainDictionary.GetDictionary("info"); this.name = infoDictionary.GetString("name"); this.pieceLength = infoDictionary.GetInteger("piece length"); this.pieceFileName = this.name.ToLower().Replace(' ', '_'); // Get SHA digests byte[] pieces = infoDictionary.GetBytes("pieces"); int numPieces = pieces.Length / 20; this.shaDigestList.Capacity = numPieces; for (int i = 0; i < numPieces; ++i) { this.shaDigestList.Add(new ByteField20(pieces, i * 20)); } // Get filenames and lengths if (infoDictionary.Contains("length")) { // one file this.fileList.Add(name); int fileLength = infoDictionary.GetInteger("length"); this.fileLengthList.Add(fileLength); this.totalSize = fileLength; } else { // multiple files - a list of dictionaries containing the filename and length BEncode.List files = infoDictionary.GetList("files"); this.fileList.Capacity = this.fileLengthList.Capacity = files.Count; this.totalSize = 0; foreach (BEncode.Dictionary fileDic in files) { BEncode.List pathList = fileDic.GetList("path"); string path = this.name + IO.Path.DirectorySeparatorChar; for (int i = 0; i < pathList.Count - 1; ++i) { path += pathList[i].ToString() + IO.Path.DirectorySeparatorChar; } path += pathList[pathList.Count - 1]; this.fileList.Add(path); int fileLength = fileDic.GetInteger("length"); this.fileLengthList.Add(fileLength); this.totalSize += fileLength; } } // calculate the SHA-1 digest of the info dictionary - this is required for the tracker protocol istream.Seek(infoDictionary.Position, IO.SeekOrigin.Begin); byte[] infoData = new byte[infoDictionary.Length]; istream.Read(infoData, 0, infoData.Length); this.infoDigest = ByteField20.ComputeSHAHash(infoData); }
public static void SendPeerId(IO.Stream netStream, ByteField20 peerId) { netStream.Write(peerId.Data, 0, peerId.Data.Length); }
private void OnAccept(System.IAsyncResult result) { Sockets.Socket socket; try { // Accept connections from other peers, find the appropriate torrent and add the peer to it socket = this.mListener.EndAccept(result); } catch (System.Exception) { if (this.mListener != null) this.mListener.Close(); this.mListener = null; return; } try { ByteField20 infoDigest = new ByteField20(), peerId = new ByteField20(); Sockets.NetworkStream netStream = new Sockets.NetworkStream(socket, true); PeerProtocol.ReceiveHandshake(netStream, ref infoDigest); Torrent torrent = this.FindTorrent(infoDigest); if (torrent != null) { // found it, finish handshaking and add the peer to the list PeerProtocol.SendHandshake(netStream, torrent.Metainfo.InfoDigest); PeerProtocol.SendPeerId(netStream, mLocalPeerId ); if ( !PeerProtocol.ReceivePeerId( netStream, ref peerId )) { // NAT check, discard socket.Close(); } else { if ( !peerId.Equals( mLocalPeerId ) ) // make sure we aren't connecting to ourselves { Net.IPEndPoint endPoint = (Net.IPEndPoint)socket.RemoteEndPoint; PeerInformation peerInformation = new PeerInformation( endPoint.Address.ToString(), endPoint.Port, peerId ); // add the peer to the torrent torrent.AddPeer( socket, netStream, peerInformation ); } else socket.Close(); } } else socket.Close(); } catch (System.Exception e) { Config.LogException( e ); socket.Close(); } this.mListener.BeginAccept(new System.AsyncCallback(OnAccept), null); }
private static ByteField20 DefaultCalculatePeerId() { // calculate our peer id string peerIdString = "-TN" + string.Format("{0:00}", Config.MajorVersionNumber) + string.Format("{0:00}", Config.MinorVersionNumber) + "-"; ByteField20 peerId = new ByteField20(); System.Array.Copy(System.Text.ASCIIEncoding.ASCII.GetBytes(peerIdString), 0, peerId.Data, 0, peerIdString.Length); const string peerChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; System.Random rand = new System.Random(); for (int i=peerIdString.Length; i<20; ++i) { peerId.Data[i] = (byte)peerChars[rand.Next(0, peerChars.Length-1)]; } return peerId; }
public Session( ByteField20 localPeerId ) { mLocalPeerId = localPeerId; if ( Config.ActiveConfig.ProxyURL != "" ) // set web proxy { System.Net.WebRequest.DefaultWebProxy = new Net.WebProxy( Config.ActiveConfig.ProxyURL ); } SetAllowUnsafeHeaderParsing20(); this.StartServer(); }
public Torrent FindTorrent(ByteField20 infoDigest) { lock ( this.mTorrents ) { foreach ( Torrent torrent in this.mTorrents ) { if (torrent.Metainfo.InfoDigest.Equals(infoDigest)) { return torrent; } } return null; } }
/// <summary>Constructs a MetainfoFile</summary> /// <param name="istream">Stream to read data from</param> public MetainfoFile(IO.Stream istream) { BEncode.Dictionary mainDictionary = (BEncode.Dictionary)BEncode.NextElement(istream); this.announceUrl = mainDictionary.GetString(new BEncode.String("announce")); if (mainDictionary.Contains("comment")) this.comment = mainDictionary.GetString("comment"); if (mainDictionary.Contains("created by")) this.createdBy = mainDictionary.GetString("created by"); if (mainDictionary.Contains("creation date")) { int creation = mainDictionary.GetInteger("creation date"); this.creationDate = new System.DateTime(1970, 1, 1, 0, 0, 0); this.creationDate = this.creationDate.AddSeconds(creation); } BEncode.Dictionary infoDictionary = mainDictionary.GetDictionary("info"); this.name = infoDictionary.GetString("name"); this.pieceLength = infoDictionary.GetInteger("piece length"); this.pieceFileName = this.name.ToLower().Replace(' ', '_'); // Get SHA digests byte[] pieces = infoDictionary.GetBytes("pieces"); int numPieces = pieces.Length / 20; this.shaDigestList.Capacity = numPieces; for (int i=0; i<numPieces; ++i) { this.shaDigestList.Add( new ByteField20(pieces, i*20) ); } // Get filenames and lengths if (infoDictionary.Contains("length")) { // one file this.fileList.Add(name); int fileLength = infoDictionary.GetInteger("length"); this.fileLengthList.Add(fileLength); this.totalSize = fileLength; } else { // multiple files - a list of dictionaries containing the filename and length BEncode.List files = infoDictionary.GetList("files"); this.fileList.Capacity = this.fileLengthList.Capacity = files.Count; this.totalSize = 0; foreach (BEncode.Dictionary fileDic in files) { BEncode.List pathList = fileDic.GetList("path"); string path = this.name + IO.Path.DirectorySeparatorChar; for (int i=0; i<pathList.Count-1; ++i) { path += pathList[i].ToString() + IO.Path.DirectorySeparatorChar; } path += pathList[ pathList.Count-1 ]; this.fileList.Add(path); int fileLength = fileDic.GetInteger("length"); this.fileLengthList.Add(fileLength); this.totalSize += fileLength; } } // calculate the SHA-1 digest of the info dictionary - this is required for the tracker protocol istream.Seek(infoDictionary.Position, IO.SeekOrigin.Begin); byte[] infoData = new byte[ infoDictionary.Length ]; istream.Read(infoData, 0, infoData.Length); this.infoDigest = ByteField20.ComputeSHAHash(infoData); }
/// <summary> /// Parses the response from the tracker, and updates the peer list /// </summary> /// <param name="stream">IO stream from response</param> private void ParseTrackerResponse(IO.Stream stream) { this.peerList.Clear(); /* // because the response stream does not support seeking, we copy the contents to a memorystream // to send to the bencoder. This shouldnt cause too much of a performance penalty as it shouldnt // be too large anyway. byte[] data = new byte[ 1024 ]; IO.MemoryStream responseStream = new IO.MemoryStream(); int dataRead = 0; while ((dataRead = stream.Read(data, 0, data.Length)) > 0) { responseStream.Write(data, 0, dataRead); } responseStream.Seek(0, IO.SeekOrigin.Begin); */ /// BEncode.Dictionary dic = BEncode.NextDictionary(stream); // note: sometimes IPs can be duplicated in quick disconnection, so there is a check for any duplications if (dic.Contains("failure reason")) { throw new IO.IOException("Tracker connection failed: " + dic.GetString("failure reason")); } else { this.updateInterval = dic.GetInteger("interval"); BEncode.Element peers = dic["peers"]; if (peers is BEncode.List) { // peer list comes as a list of dictionaries BEncode.List dicList = (BEncode.List)peers; foreach (BEncode.Dictionary dicPeer in dicList) { ByteField20 peerId = new ByteField20(dicPeer.GetBytes("peer id")); string peerIp = dicPeer.GetString("ip"); int port = dicPeer.GetInteger("port"); PeerInformation peerinfo = new PeerInformation(peerIp, port, peerId); if (!this.peerList.Contains(peerinfo)) this.peerList.Add(peerinfo); } } else if (peers is BEncode.String) { // else its compressed (this is pretty common) byte[] compactPeers = ((BEncode.String)peers).Data; for (int i=0; i<compactPeers.Length; i += 6) { int ip1 = 0xFF & compactPeers[i]; int ip2 = 0xFF & compactPeers[i+1]; int ip3 = 0xFF & compactPeers[i+2]; int ip4 = 0xFF & compactPeers[i+3]; int po1 = 0xFF & compactPeers[i+4]; int po2 = 0xFF & compactPeers[i+5]; string peerIp = ip1 + "." + ip2 + "." + ip3 + "." + ip4; int port = (po1 * 256) + po2; PeerInformation peerinfo = new PeerInformation(peerIp, port); if (!this.peerList.Contains(peerinfo)) this.peerList.Add(peerinfo); } } else throw new TrackerException("Unexcepted error"); } }
/// <summary> /// Helper thread method to start the connection to peers /// </summary> /// <param name="state"></param> private void StartPeerConnectionThread(System.IAsyncResult result) { object[] objs = (object[])result.AsyncState; Sockets.Socket socket = (Sockets.Socket)objs[0]; PeerInformation peerinfo = (PeerInformation)objs[1]; ByteField20 infoDigest = new ByteField20(), peerId = new ByteField20(); Sockets.NetworkStream netStream; try { socket.EndConnect(result); netStream = new Sockets.NetworkStream(socket, true); // send handshake info PeerProtocol.SendHandshake(netStream, this.infofile.InfoDigest); PeerProtocol.ReceiveHandshake(netStream, ref infoDigest); PeerProtocol.SendPeerId(netStream, this.mSession.LocalPeerID ); if (!PeerProtocol.ReceivePeerId(netStream, ref peerId)) { // NAT check socket.Close(); return; } // check info digest matches and we are not attempting to connect to ourself if ( infoDigest.Equals( this.infofile.InfoDigest ) && !peerId.Equals( this.mSession.LocalPeerID ) ) { peerinfo.ID = peerId; this.AddPeer(socket, netStream, peerinfo); } else // info digest doesn't match, close the connection socket.Close(); } catch (System.Exception e) { Config.LogException(e); // die if the connection failed if (socket != null) socket.Close(); return; } }
public static bool ReceivePeerId(IO.Stream stream, ref ByteField20 peerId) { return stream.Read(peerId.Data, 0, peerId.Data.Length) > 0; }