C#. Torrent client. Peer handshake. Strange peer responses - c#

I'm developing my own torrent client(for self-improvement) and during the step of peer handshake I'm constantly retrieving "incorrect" responses like that one:
by specification(https://wiki.theory.org/BitTorrentSpecification#Handshake) I need to send the request following the next pattern:
//handshake: <info_hash><peer_id> as a byte array.
code to generate peer handshake byte array(GenerateHandShakeMessage method):
public class PeerHandShake
{
//pstrlen
public const string ProtocolStringLength = "19";
//pstr
public const string ProtocolString = "BitTorrent protocol";
public byte[] InfoHash { get; set; }
public string PeerId { get; set; }
public PeerHandShake(string peerId, byte[] infoHash)
{
PeerId = peerId;
InfoHash = infoHash;
}
//handshake: <pstrlen><pstr><reserved><info_hash><peer_id>
public byte[] GenerateHandShakeMessage()
{
var handshakeMessage = new List<byte>();
var protocolPart = Encoding.ASCII.GetBytes($"{ProtocolStringLength}{ProtocolString}");
handshakeMessage.AddRange(protocolPart);
//Supported version is 1.0 that is why all reserved 8 bytes are zeros
var reservedPart = Encoding.ASCII.GetBytes(new[] { '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00' });
handshakeMessage.AddRange(reservedPart);
handshakeMessage.AddRange(InfoHash);
var peerPart = Encoding.ASCII.GetBytes($"{PeerId}");
handshakeMessage.AddRange(peerPart);
return handshakeMessage.ToArray();
}
}
This is how am I sending it and receiving the response(this is just handshake test code):
public static void SendRequest(string endpoint, int port, byte[] message)
{
try
{
var listener = new TcpClient();
listener.Connect(endpoint, port);
var stream = listener.GetStream();
stream.Write(message, 0, message.Length);
listener.Client.Send(message);
var data = new byte[1000];
var responseData = string.Empty;
var bytes = stream.Read(data, 0, data.Length);
responseData = Encoding.ASCII.GetString(data, 0, bytes);
listener.Client.Close();
listener.Close();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
I'm sending the 69 bytes which as ASCII string would be displayed as:
"19BitTorrent protocol\0\0\0\0\0\0\0\0???fq?[??w?Tb?\u000e/5E??-PT-0001-01678496022"
And I supposed to receive a string which is following the same pattern however I do receive something like that(and quite often the response is different):
"?\u0001)R?)Q0???q????_??\u0010\u0001?b???;\v??\u0005?G??P3?}??\r ;?%8?a???+\u0010?\f??H?\u001f\u0010\n?OSA\u001e[\u001eR;?D?E]^?y?\u0013TN?u7I?8\u0016?\u0003\u001c\a??t?\u000f}*?Tv\u001aW(-?^?u%\u001e??J??]5?I?????\u0005\n??H?;}T??1?9?\u001e??????????U$?P???6DZ??\u0012?\u0014#4<\u001a?S?Pt???{K??r???o?(A{?\b< ?y???4?w\u0016?W)?N?f??q???"o\u000fR?\u0015???l??????\u007f\u0001"
When I was running the same torrent file via uTorrent with WireShark monitoring, I noticed that uTorrent is accepting such handshakes.
Could somebody point me, what do I missed?
PS: infohash is generated correctly, because I'm able to get the peers' ip addresses from tracker with it.
PPS: I would appreciate any help, because I'm stuck with it for a long time :(

The handshake begins with a byte set to 19base10 or 0x13. Since you're sending the string "19" (2 bytes) your handshake is already incorrect and the peer apparently tries to respond with something else, perhaps the obfuscated protocol handshake.

Related

TcpClient does not flush writes

I have this code to send String messages to a server. These messages are supposed to be sent separately, but the Stream of the TcpClient is sending the messages in one bug chunk after the Stream is closed.
What can I do to send separated messages with this code:
public void sendData()
{
const int PORT_NO = 12900;
const string SERVER_IP = "127.0.0.1";
TcpClient client = new TcpClient(SERVER_IP, PORT_NO);
try
{
client.NoDelay = true;
NetworkStream nwStream = client.GetStream();
sendText(nwStream, "Text-1"); // Here is it supposed to send "Text-1"
sendText(nwStream, "Text-2");
sendText(nwStream, "Text-3");
}
finally
{
client.Close(); // But, all messages are sent here!
}
}
private void sendText(NetworkStream nwStream, String text)
{
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(text);
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
nwStream.Flush();
}
With this code the Server ends up receiveing: Text-1Text-2Text-3 in one message and it is supposed to receive 3 different messages. What is wrong here?

C# good practice waiting for TCP response

While making a c# application for remote controlling cisco routers using TCP, I got the problem of waiting for a response from the router.
For the application I have to connect to a Cisco router using a TCP connection. After the connection has been made a networkstream will push my command to the Cisco router. To let the router process the command, I am using Thread.Sleep. This is not the best solution.
Here is the complete code to get a idea what my program is doing.
string IPAddress = "192.168.1.1";
string message = "show running-config"; // command to run
int bytes;
string response = "";
byte[] responseInBytes = new byte[4096];
var client = new TcpClient();
client.ConnectAsync(IPAddress, 23).Wait(TimeSpan.FromSeconds(2));
if (client.Connected == true)
{
client.ReceiveTimeout = 3;
client.SendTimeout = 3;
byte[] messageInBytes = Encoding.ASCII.GetBytes(message);
NetworkStream stream = client.GetStream();
Console.WriteLine();
stream.Write(messageInBytes, 0, messageInBytes.Count()); //send data to router
Thread.Sleep(50); // temporary way to let the router fill his tcp response
bytes = stream.Read(responseInBytes, 0, responseInBytes.Length);
response = Encoding.ASCII.GetString(responseInBytes, 0, bytes);
return response; //whole command output
}
return null;
What is a good and reliable way to get the full response.
Thanks for any help or command.
More info:
The networksteam is always filled with something, most of the time it is filled with the cisco IOS login page. The biggest problem is to determine when the router is done filling up the response.
The response I most of the time get:
"??\u0001??\u0003??\u0018??\u001f\r\n\r\nUser Access Verification\r\n\r\nUsername: "
The return data will be diffent every time because it will be a result of a cisco command. This can vary from a short string to a very long string.
mrmathijs95 -
When reading from NetworkStream with Stream.Read it not 100% sure that you will read all expected data. Stream.Read can return when only few packet arrived and not waiting for others.
To be sure that you get all data use BinaryReader for reading.
BinaryReader.Read method will block current thread until all expected data arrived
private string GetResponse(string message)
{
const int RESPONSE_LENGTH = 4096;
byte[] messageInBytes = Encoding.ASCII.GetBytes(message);
bool leaveStreamOpen = true;
using(var writer = new BinaryWriter(client.GetStream()))
{
writer.Write(messageInBytes);
}
using(var reader = New BinaryReader(client.GetStream()))
{
byte[] bytes = reader.Read(RESPONSE_LENGTH );
return Encoding.ASCII.GetString(bytes);
}
}
Don't use Thread.Sleep. I would async/await the entire thing, given that you don't always know what the data is based on your recent edit. This is how I would do it (untested):
public class Program
{
// call Foo write with "show running-config"
}
public class Foo
{
private TcpClient _client;
private ConcurrentQueue<string> _responses;
private Task _continualRead;
private CancellationTokenSource _readCancellation;
public Foo()
{
this._responses = new ConcurrentQueue<string>();
this._readCancellation = new CancellationTokenSource();
this._continualRead = Task.Factory.StartNew(this.ContinualReadOperation, this._readCancellation.Token, this._readCancellation.Token);
}
public async Task<bool> Connect(string ip)
{
this._client = new TcpClient
{
ReceiveTimeout = 3, // probably shouldn't be 3ms.
SendTimeout = 3 // ^
};
int timeout = 1000;
return await this.AwaitTimeoutTask(this._client.ConnectAsync(ip, 23), timeout);
}
public async void StreamWrite(string message)
{
var messageBytes = Encoding.ASCII.GetBytes(message);
var stream = this._client.GetStream();
if (await this.AwaitTimeoutTask(stream.WriteAsync(messageBytes, 0, messageBytes.Length), 1000))
{
//write success
}
else
{
//write failure.
}
}
public async void ContinualReadOperation(object state)
{
var token = (CancellationToken)state;
var stream = this._client.GetStream();
var byteBuffer = new byte[4096];
while (!token.IsCancellationRequested)
{
int bytesLastRead = 0;
if (stream.DataAvailable)
{
bytesLastRead = await stream.ReadAsync(byteBuffer, 0, byteBuffer.Length, token);
}
if (bytesLastRead > 0)
{
var response = Encoding.ASCII.GetString(byteBuffer, 0, bytesLastRead);
this._responses.Enqueue(response);
}
}
}
private async Task<bool> AwaitTimeoutTask(Task task, int timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
public void GetResponses()
{
//Do a TryDequeue etc... on this._responses.
}
}
I didn't expose the read cancellation publicly, but you could add this method to cancel the read operation:
public void Cancel()
{
this._readCancellation.Cancel();
}
And then dispose of your client and all that fun stuff.
Lastly, because you said there's always data available on the stream, where you're doing the read you may have to do some logic on the number of bytes last read to offset yourself within the stream if the data doesn't clear. You'll know if the responses you're getting is always the same.
This is the working code for me.
It uses the solution of Fabio,
combined with a while loop to check every X miliseconds if the response has changed.
client.ReceiveTimeout = 3;
client.SendTimeout = 3;
byte[] messageInBytes = Encoding.ASCII.GetBytes(message);
NetworkStream stream = client.GetStream();
Console.WriteLine();
using (var writer = new BinaryWriter(client.GetStream(),Encoding.ASCII,true))
{
writer.Write(messageInBytes);
}
using (var reader = new BinaryReader(client.GetStream(),Encoding.ASCII, true))
{
while (itIsTheEnd == false)
{
bytes = reader.Read(responseInBytes, 0, responseInBytes.Count());
if (lastBytesArray == responseInBytes)
{
itIsTheEnd = true;
}
lastBytesArray = responseInBytes;
Thread.Sleep(15);
}
}
response = Encoding.ASCII.GetString(responseInBytes);
Thanks for everyone who suggested a solution.
And thanks to Fabio for the given solution.

Very basic web server in C#

I'm wanting to run a little socket server in C# to be accessed by a browser. I have a socket listener up and running on a specific port, and am trying to access it via the browser:
class WebSocketServer
{
private static string output = string.Empty;
public void CreateListener()
{
TcpListener tcpListener = null;
var ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
try
{
tcpListener = new TcpListener(ipAddress, 1313);
tcpListener.Start();
output = "Waiting for a connection";
}
catch (Exception e)
{
throw;
}
var socketHelper = new SocketHelper();
while (true)
{
Thread.Sleep(10);
TcpClient tcpClient = tcpListener.AcceptTcpClient();
byte[] bytes = new byte[256];
var stream = tcpClient.GetStream();
stream.Read(bytes, 0, bytes.Length);
socketHelper.ProcessMessage(tcpClient, stream, bytes);
}
}
}
class SocketHelper
{
private static int counter = 0;
public void ProcessMessage(TcpClient tcpClient, NetworkStream stream, byte[] bytesReceived)
{
// handle message received and send response to client
var msg = Encoding.ASCII.GetString(bytesReceived);
string response = string.Empty;
if (msg.Substring(0, 10) == "GET / HTTP")
{
response = "";// html page
}
byte[] bytesToSend = Encoding.ASCII.GetBytes(response);
stream.Write(bytesToSend, 0, bytesToSend.Length);
}
The browser appears to connect to it, but it never seems to display the html - what's wrong? I'm eventually wanting to be able to serve up JSON data via a REST interface. In addition, is there a much easier solution to (I assume) this common problem.

Netty server and C# Socket Client. Receive on client doesn't work

I want to receive message on C# client from Netty server. I use sync C# socket and protobuf.
I send message to server and it's ok. But I can't receive response.
Netty server uses ProtobufDecoder. Server ChannelInboundHandler has this part of code:
public void channelRead0(final ChannelHandlerContext ctx, Object msg) {
...
Set<String> keys = jedis.keys("tanks*");
String allKeys = "";
for(String key: keys){
allKeys+=key+";";
}
ctx.write(allKeys);
ctx.flush();
}
C# client code is:
const string server = "localhost";
const int port = 8080;
var tcpClient = new TcpClient(server, port);
_networkStream = tcpClient.GetStream();
var stream = new MemoryStream();
Serializer.Serialize(stream, tankDataObject);
var data = stream.ToArray();
_networkStream.Write(data, 0, data.Length);
data = new Byte[10000];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = _networkStream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
// Close everything.
_networkStream.Close();
tcpClient.Close();
Client doesn't receive any bytes or receive empty array if I call ctx.close() on server. Any help will be appreciated. Thank you.

Problem calculating byte length when sending data over sockets

I'm writing a client/server application. It seems to work well when connecting using the loopback address, but when connecting over the LAN or internet I'm getting a very strange problem that I just can't figure out.
All data sent between the server and client is contained within a class called 'DataPacket'. This class is then serialized to binary so that it can be sent using sockets.
Before a packet is sent, a 4 byte header is attached which contains the byte length of the packet so that the receiver knows how many bytes to expect before it can be deserialized. The problem is that at some point, the receiver thinks it is waiting for much more data than what is actually sent in the header. Sometimes this figure is in the millions of bytes, which is causing serious problems.
This is how the data is sent:
public override void Send(DataPacket packet)
{
byte[] content = packet.Serialize();
byte[] header = BitConverter.GetBytes(content.Length);
List<Byte> bytes = new List<Byte>();
bytes.AddRange(header);
bytes.AddRange(content);
if (socket.Connected)
{
socket.BeginSend(bytes.ToArray(), 0, bytes.Count, SocketFlags.None, OnSendPacket, socket);
}
}
On the server side, each client is assigned a StateObject - this is a class that holds the users socket, buffer, and any data that has been received so far that is incomplete.
This is how the StateObject class looks:
public class StateObject
{
public const int HeaderSize = 4;
public const int BufferSize = 8192;
public Socket Socket { get; private set; }
public List<Byte> Data { get; set; }
public byte[] Header { get; set; }
public byte[] Buffer { get; set; }
public StateObject(Socket sock)
{
Socket = sock;
Data = new List<Byte>();
Header = new byte[HeaderSize];
Buffer = new byte[BufferSize];
}
}
This is how the StateObject class is used:
private void OnClientConnect(IAsyncResult result)
{
Socket serverSock = (Socket)result.AsyncState;
Socket clientSock = serverSock.EndAccept(result);
StateObject so = new StateObject(clientSock);
so.Socket.BeginReceive(so.Buffer, 0, StateObject.BufferSize, SocketFlags.None, OnReceiveData, so);
}
When some data is received from a client, EndReceive is called. If StateObject.Data is empty then it is assumed that this is the first part of a new packet, therefore the header is checked. If the number of bytes received is less than the size shown in the header then I call BeginReceive again, re-using the same StateObject.
Here is the code for OnReceiveData:
private void OnReceiveData(IAsyncResult result)
{
StateObject state = (StateObject)result.AsyncState;
int bytes = state.Socket.EndReceive(result);
if (bytes > 0)
{
state.ProcessReceivedData(bytes);
if (state.CheckDataOutstanding())
{
//Wait until all data has been received.
WaitForData(state);
}
else
{
ThreadPool.QueueUserWorkItem(OnPacketReceived, state);
//Continue receiving data from client.
WaitForData(new StateObject(state.Socket));
}
}
}
As can be seen above, first I call state.ProcessReceivedData() - this is where I detach the header from the data, and then move the data from the buffer:
public void ProcessReceivedData(int byteCount)
{
int offset = 0;
if (Data.Count == 0)
{
//This is the first part of the message so get the header.
System.Buffer.BlockCopy(Buffer, 0, Header, 0, HeaderSize);
offset = HeaderSize;
}
byte[] temp = new byte[byteCount - offset];
System.Buffer.BlockCopy(Buffer, offset, temp, 0, temp.Length);
Data.AddRange(temp);
}
I then call CheckDataOutstanding() to compare the number of bytes received with the size in the header. If these match then I can safely deserialize the data:
public bool CheckDataOutstanding()
{
int totalBytes = BitConverter.ToInt32(Header, 0);
int receivedBytes = Data.Count;
int outstanding = totalBytes - receivedBytes;
return outstanding > 0;
}
The problem is that somehow the value in the header is ending up at a different value to what it was sent as. This seems to happen randomly, sometimes on the server and other times on the client. Due to the randomness and the general difficulty in debugging both server and client accross the network I'm finding it nearly impossible to figure this out.
Is there something obvious or stupid that I'm missing here?
What happens if you get only part of the header? That is, suppose you got three bytes in the first read instead of four or more? Your ProcessReceivedData function will need to account for that and not try to extract the header size until you have at least four bytes.

Categories

Resources