UDP relay implementation do not work as I expected - c#

I am trying to implement the relay of UDP communication.
For example, I have two terminals:
192.168.10.5
192.168.10.6
I would like to monitor between the two by way of 192.168.10.3.
My environment:
Unity 5.1.2-f1 on MacOS X 10.8.5 using C#
Following is my code snippet related to the UDP communication.
void Monitor() {
UdpClient client = new UdpClient (port);
client.Client.ReceiveTimeout = 1000; // msec
client.Client.Blocking = false;
while (ToggleComm.isOn) {
try {
IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
byte[] data = client.Receive(ref anyIP);
string text = Encoding.ASCII.GetString(data);
if (text.Length == 0) {
Thread.Sleep(20);
continue;
}
string fromIP = anyIP.Address.ToString();
// send to the other
if (fromIP.Equals(ipadr1)) {
client.Send(data, data.Length, ipadr2, port);
Debug.Log("from: " + ipadr1 + " to " + ipadr2 + data);
} else {
client.Send(data, data.Length, ipadr1, port);
Debug.Log("from: " + ipadr2 + " to " + ipadr1 + data);
}
}
catch (Exception err) {
}
// without this sleep, on android, the app will freeze at Unity splash screen
Thread.Sleep(200);
}
client.Close ();
}
With the code above,
I can send from 192.168.10.5 to 192.168.10.3
I can send from 192.168.10.3 to 192.168.10.6 (relay)
I can receive at 192.168.10.3 from 192.168.10.6
I cannot receive at 192.168.10.5 from 192.168.10.3
where 192.168.10.6 works as echo server, returning received string back.
My debug print on the relay says
from: 192.168.10.5 to 192.168.10.6
from: 192.168.10.6 to 192.168.10.5
It seems that the relay (192.168.10.3) send to 192.168.10.5, but 192.168.10.5 cannot receive the relayed one.

Following code worked (not elegant yet, though).
I had misunderstanding about port of the sender and receiver.
void Monitor() {
UdpClient client = new UdpClient (port);
client.Client.ReceiveTimeout = 300; // msec
client.Client.Blocking = false;
string fromPort = "6000"; // at first 6000 is set as dummy
while (ToggleComm.isOn) {
try {
IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
byte[] data = client.Receive(ref anyIP);
string text = Encoding.ASCII.GetString(data);
if (text.Length == 0) {
Thread.Sleep(20);
continue;
}
string fromIP = anyIP.Address.ToString();
// send to the other
if (fromIP.Equals(ipadr1)) {
fromPort = anyIP.Port.ToString(); // store the port
client.Send(data, data.Length, ipadr2, port);
Debug.Log("1 from: " + fromIP + "(" + fromPort
+ ") to " + ipadr2 + "(" + port.ToString() + ")");
} else {
client.Send(data, data.Length, ipadr1, Convert.ToInt32(fromPort));
Debug.Log("2 from: " + fromIP + "(" + "..."
+ ") to " + ipadr1 + "(" + fromPort + ")");
}
}
catch (Exception err) {
}
// without this sleep, on android, the app will freeze at Unity splash screen
Thread.Sleep(200);
}
client.Close ();
}
I put the unity project at github (v0.3)

Related

c# HTTPS proxy (just relaying from browser -> proxy -> website -> proxy -> browser)

so I have this code where I relay all connections to the destination server, I get the response back from it in a byte[] called result, but when dealing with SSL/HTTPS the end server always return an empty response
public static byte[] ForwardRequest(byte[] requestBytes)
{
byte[] result = new byte[0];
try
{
string requestString = Encoding.UTF8.GetString(requestBytes);
string[] requestLines = requestString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
string host = "";
int port = 80;
if (requestString.Contains("CONNECT"))
{
string connectLine = requestLines[0];
string hostWithPort = connectLine.Split(' ')[1];
host = hostWithPort.Split(':')[0];
port = Convert.ToInt32(hostWithPort.Split(':')[1]);
}
else
{
host = requestLines[1].Split(' ')[1];
if (host.Contains(":"))
{
string[] split = host.Split(':');
host = split[0];
port = Convert.ToInt32(split[1]);
}
}
Console.WriteLine("HOST:" + host + "#PORT:" + port);
TcpClient client = new TcpClient();
client.Connect(host, port);
if(client.Connected == true && requestString.Contains("CONNECT"))
{
Console.WriteLine("client connected to ssl");
}
Network.WriteBytes(client, requestBytes);
result = Network.ReadBytes(client, 10000);
if (requestString.Contains("CONNECT"))
{
string resultString = Encoding.UTF8.GetString(result);
Console.WriteLine("############BROWSER REQUEST################" + Environment.NewLine + requestString + Environment.NewLine + "############BROWSER REQUEST################");
Console.WriteLine("############WEBSITE RESPONSE###############" + Environment.NewLine + resultString + Environment.NewLine + "############WEBSITE RESPONSE###############");
Console.WriteLine("responseLenght: " + result.Length);
}
}
catch (Exception ex)
{
Console.WriteLine("Network.Forward() -> " + ex.Message);
}
return result;
}
I thought that it should work because I'm just sending raw data from browser to server and back. Again, all the other code works fine because with HTTP websites it works.
this is my console window after sending trying to talk to HTTPS

TcpListener AcceptSocket() accepts two connections for each single GET request

I have this web server program up and running, using TcpListener. The problem is that for each get request made through web browser (or chrome Postman extension), it captures two requests.
namespace Server
{
class WebServer2
{
private TcpListener listener;
private int port = 8080;
public WebServer2()
{
try
{
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), port);
listener.Start();
Console.WriteLine("Listening...");
//start the thread which calls the method 'StartListen'
Thread th = new Thread(new ThreadStart(StartListen));
th.Start();
}
catch (Exception e)
{
Console.WriteLine("An Exception Occurred While Listening: " + e.ToString());
}
}
//Recieve Request
public void StartListen()
{
int iStartPos = 0;
String sRequest;
String sRequestedFile;
String sResponse = "";
while (true)
{
//Accept a new connection
Socket socket = listener.AcceptSocket();
if (socket.Connected)
{
Console.WriteLine("\nClient Connected");
Console.WriteLine("----------------");
//Receive data from the client
Byte[] bReceive = new Byte[1024];
int i = socket.Receive(bReceive, bReceive.Length, 0);
//Convert Byte to String
string sBuffer = Encoding.ASCII.GetString(bReceive);
//Only GET Request is accepted
if (sBuffer.Substring(0, 3) != "GET")
{
Console.WriteLine("Not a Get Request.");
socket.Close();
continue;
}
// Look for HTTP request
iStartPos = sBuffer.IndexOf("HTTP", 1);
// Get the HTTP text and version e.g. it will return "HTTP/1.1"
string sHttpVersion = sBuffer.Substring(iStartPos, 8);
// Extract the Requested Type and Requested file/directory
sRequest = sBuffer.Substring(0, iStartPos - 1);
//If file name not provided
if (sRequest.IndexOf(".") < 1)
{
Console.WriteLine("File name not Provided!");
socket.Close();
continue;
}
//Extract the requested file name
iStartPos = sRequest.LastIndexOf("/") + 1;
sRequestedFile = sRequest.Substring(iStartPos);
Console.WriteLine("Requested File: " + sRequestedFile);
int iTotBytes = 0;
sResponse = "";
FileStream fs = new FileStream(sRequestedFile, FileMode.Open, FileAccess.Read, FileShare.Read);
BinaryReader reader = new BinaryReader(fs);
byte[] bytes = new byte[fs.Length];
int read;
while ((read = reader.Read(bytes, 0, bytes.Length)) != 0)
{
// Read from the file and write the data to the network
sResponse = sResponse + Encoding.ASCII.GetString(bytes, 0, read);
iTotBytes = iTotBytes + read;
}
reader.Close();
fs.Close();
SendHeader(sHttpVersion, "text/html", iTotBytes, " 200 OK", ref socket);
SendToBrowser(bytes, ref socket);
socket.Send(bytes, bytes.Length, 0);
socket.Close();
}
}
}
// Overloaded Function, takes string, convert to bytes and calls
// overloaded sendToBrowserFunction.
public void SendToBrowser(String sData, ref Socket socket)
{
SendToBrowser(Encoding.ASCII.GetBytes(sData), ref socket);
}
/// Sends data to the browser (client)
public void SendToBrowser(Byte[] bSendData, ref Socket socket)
{
int numBytes = 0;
try
{
if (socket.Connected)
{
if ((numBytes = socket.Send(bSendData, bSendData.Length, 0)) == -1)
Console.WriteLine("Socket Error");
}
else
Console.WriteLine("Connection Dropped!");
}
catch (Exception e)
{
Console.WriteLine("Error: {0} ", e);
}
}
// This function send the Header Information to the client (Browser)
public void SendHeader(string sHttpVersion, string sMIMEHeader, int iTotBytes, string sStatusCode, ref Socket socket)
{
String sBuffer = "";
Byte[] bSendData;
if (sStatusCode.Equals("404") || sStatusCode.Equals("400"))
{
sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
sBuffer = sBuffer + "Server: MyServer\r\n";
sBuffer = sBuffer + "Content-Length: " + 0 + "\r\n\r\n";
bSendData = Encoding.ASCII.GetBytes(sBuffer);
SendToBrowser(bSendData, ref socket);
}
else
{
sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
sBuffer = sBuffer + "Server: MyServer\r\n";
sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n";
sBuffer = sBuffer + "Accept-Ranges: bytes\r\n";
sBuffer = sBuffer + "Content-Length: " + iTotBytes + "\r\n\r\n";
bSendData = Encoding.ASCII.GetBytes(sBuffer);
SendToBrowser(bSendData, ref socket);
}
}
}
}
Single request made by chrome against http://localhost:8080/page1.html
Request made by Postman Extension
The funny thing is, everything works find when I send request through my client program (using TcpClient).
I tested your server and got similar results:
Chrome automatically asks for a favicon.ico file for every request. This generates the extra GET request to your server.
Firefox doesn't do this and simply generates one request per refresh.
Then there is a second problem I experienced, which is the one in your Postman screenshot.
Chrome (including the Postman Extension running inside it) reconnects
without sending anything, so the server "hangs" until you get a
response filled with \0 (nul-characters).
This is part of Chrome's prediction service to load subsequent requests faster. You could disable this behavior under Settings (advanced) > Privacy:
In your server implementation, this results in the "Not a Get Request" output if no subsequent GET request follows (instead, Chrome sends \0 to signal its not sending anything anyway).
Note: I didn't experience any of this in the actual Postman application.
While the server waits for chrome to send a request, it cannot service any other requests. To prevent this from happening, consider using an asynchronous approach.

TCP IP Server with acknowledgement and receive data again

I have developed TCP/IP Server in C# listening on port and a GPS device is sending the data to server.
Initially, the device sends the IMEI number to server and server acknowledges with 01. After receiving the acknowledgement by device, a new packet of data is sent to server.
I am able to get IMEI number by TCP Server and after sending acknowledgement, i am not able to receive new packet data from client. I am including my code below also. Please let me know where i am wrong.
I have tested using hercules tcp server tool and next data packet is receiving successfully but from my application it is not working.
try
{
IPAddress ipAdress = IPAddress.Parse(ConfigurationManager.AppSettings["Server"].ToString());
// You can use local IP as far as you use the
//same in client
// Initializes the Listener
TcpListener myList = new TcpListener(ipAdress, int.Parse(ConfigurationManager.AppSettings["Port"].ToString()));
for (; ; )
{ // Run forever, accepting and servicing connections
//Start Listeneting at the specified port
myList.Start();
Console.WriteLine("Server running - Port: 8000");
Console.WriteLine("Local end point:" + myList.LocalEndpoint);
Console.WriteLine("Waiting for connections...");
Socket socket = myList.AcceptSocket();
// When accepted
Console.WriteLine("Connection accepted from " + socket.RemoteEndPoint);
byte[] b = new byte[1000];
int k = socket.Receive(b);
Console.WriteLine("echoed {0} bytes.", k);
Console.WriteLine("Reading IMEI....");
string hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, b.Length));
File.WriteAllText(#"C:\ClientFiles\testIMEI", hexStr);
ASCIIEncoding asen = new ASCIIEncoding();
string response = "01";
Console.WriteLine("Ackowledgement Data - " + response);
//
int sentBytes = socket.Send(asen.GetBytes(response));
Console.WriteLine("Acknowledgement data sent!\n");
int count = 0;
while (socket.Poll(-1, SelectMode.SelectRead))
{
b = new byte[1000];
k = socket.Receive(b);
if (k > 0)
{
count++;
Console.WriteLine("Reading Client Data - Count - " + count.ToString() + " and lenght " + k.ToString());
hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, b.Length));
File.WriteAllText(#"C:\ClientFiles\testData" + count.ToString(), hexStr);
}
}
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
myList.Stop();
}
catch (Exception ex)
{
File.WriteAllText(#"C:\ClientFiles\Error.txt", ex.Message + "****" + ex.InnerException);
}
I don't see any error in the program, except these comments:
// Don't use the b.Length in this command:
string hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, b.Length));
// use k instead, the numbers actually read, not the length of the buffer.
string hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, k));
while (socket.Poll(-1, SelectMode.SelectRead))
{
// b has been allocated, don't need this
// b = new byte[1000];
k = socket.Receive(b);
if (k > 0)
{
count++;
Console.WriteLine("Reading Client Data - Count - " + count.ToString() + " and lenght " + k.ToString();
// use k not Length
hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, k /*b.Length*/));
File.WriteAllText(#"C:\ClientFiles\testData" + count.ToString(), hexStr);
}
}
Do you know if the Poll command ever returns? May be it raises an error and you are missing that one. Check it out.

Troubleshooting a simple proxy server

I've written a simple proxy server in C# using synchronous methods and classes of System.Net and System.Net.Sockets. Most of the things are done, except one minor glitch.
When I browse a url from the proxy client (say www.google.com), it opens fine. When I do a keyword search that too goes fine. However, when I click on a search-result, the client again sees the google.com home-page on the screen!! However, the client browser url bar is still showing the search-result url which is clicked.
Perhaps this is happenning because google result page doesn't provide a direct link to the result url, but a link back to google itself, and I think the relay logic in my code is not able to handle that. Could you help me with this? My code is as follows:
(The ThreadHandleClient() method is the important one that handles all the client requests and relays requests and responses)
public void Start(IPAddress ip, int port)
{
try
{
TcpListener listener = new TcpListener(ip, port);
listener.Start(100);
while (!stopFlag)
{
Socket client = listener.AcceptSocket();
IPEndPoint rep = (IPEndPoint)client.RemoteEndPoint;
Thread th = new Thread(ThreadHandleClient);
th.Start(client);
}
listener.Stop();
}
catch (Exception ex)
{
Debug.Print("START: " + ex.Message);
}
}
public void Stop()
{
stopFlag = true;
}
public void ThreadHandleClient(object o)
{
try
{
Socket client = (Socket)o;
Debug.Print("lingerstate=" + client.LingerState.Enabled.ToString() + " timeout=" + client.LingerState.LingerTime.ToString());
NetworkStream ns = new NetworkStream(client);
//RECEIVE CLIENT DATA
byte[] buffer = new byte[2048];
int rec = 0, sent = 0, transferred = 0, rport = 0;
string data = "";
do
{
rec = ns.Read(buffer, 0, buffer.Length);
data += Encoding.ASCII.GetString(buffer, 0, rec);
} while (rec == buffer.Length);
//PARSE DESTINATION AND SEND REQUEST
string line = data.Replace("\r\n", "\n").Split(new string[] { "\n" }, StringSplitOptions.None)[0];
Uri uri = new Uri(line.Split(new string[] { " " }, StringSplitOptions.None)[1]);
Debug.Print("CLIENT REQUEST RECEIVED: " + uri.OriginalString);
if (uri.Scheme == "https")
{
rport = 443;
Debug.Print("HTTPS - 443");
//rq = HttpVersion + " 200 Connection established\r\nProxy-Agent: Prahlad`s Proxy Server\r\n\r\n";
//ClientSocket.BeginSend(Encoding.ASCII.GetBytes(rq), 0, rq.Length, SocketFlags.None, new AsyncCallback(this.OnOkSent), ClientSocket);
}
else
{
rport = 80;
Debug.Print("HTTP - 443");
}
IPHostEntry rh = Dns.GetHostEntry(uri.Host);
Socket webserver = new Socket(rh.AddressList[0].AddressFamily, SocketType.Stream, ProtocolType.IP);
webserver.Connect(new IPEndPoint(rh.AddressList[0], rport));
byte[] databytes = Encoding.ASCII.GetBytes(data);
webserver.Send(databytes, databytes.Length, SocketFlags.None);
Debug.Print("SENT TO SERVER. WILL NOW RELAY: " + data);
//START RELAY
buffer = new byte[2048];
bool firstTime = true;
rec = 0;
data = "";
do
{
transferred = 0;
do
{
if (webserver.Poll((firstTime ? 9000 : 2000) * 1000, SelectMode.SelectRead))
{
rec = webserver.Receive(buffer, buffer.Length, SocketFlags.None);
Debug.Print("RECEIVED FROM WEBSERVER[" + rec.ToString() + "]: " + Encoding.ASCII.GetString(buffer, 0, rec));
firstTime = false;
sent = client.Send(buffer, rec, SocketFlags.None);
Debug.Print("SENT TO CLIENT[" + sent.ToString() + "]: " + Encoding.ASCII.GetString(buffer, 0, rec));
transferred += rec;
}
else
{
Debug.Print("No data polled from webserver");
}
} while (rec == buffer.Length);
Debug.Print("loop-1 finished");
//if (transferred == 0)
// break;
//transferred = 0;
rec = 0;
do
{
if (client.Poll(1000 * 1000, SelectMode.SelectRead))
{
rec = client.Receive(buffer, buffer.Length, SocketFlags.None);
Debug.Print("RECEIVED FROM CLIENT: " + Encoding.ASCII.GetString(buffer, 0, rec));
sent = webserver.Send(buffer, rec, SocketFlags.None);
Debug.Print("SENT TO WEBSERVER[" + sent.ToString() + "]: " + Encoding.ASCII.GetString(buffer, 0, rec));
transferred += rec;
}
else
{
Debug.Print("No data polled from client");
}
} while (rec == buffer.Length);
Debug.Print("loop-2 finished");
} while (transferred > 0);
Debug.Print("LOOP ENDS. EXITING THREAD");
client.Close();
webserver.Close();
}
catch (Exception ex)
{
Debug.Print("Error occured: " + ex.Message);
}
finally {
Debug.Print("Client thread closed");
}
}
After some trial and error, I found the solution, hence I'm answering my own question. The issue with my relay logic was that it used to relay both ways i.e. from webserver->client and client->webserver.
I realized that relaying the second part could lead to trouble in redirects, because in redirects the url part of the header ("GET http://debian.org HTTP/1.1") would suddenly change, so that it will be different from the web-server being relayed!! For example, the google redirect pointed to debian.org, whereas the proxy will still relay traffic from google.com and not debian.org.
To avoid this situation, I removed the second relay part (i.e. client->webserver). If a clients sends a new request, it better be handled in a separate thread, so that it would be directed to the proper url host. So, all I had to do was comment out the second inner loop in my relay logic:
//rec = 0;
//do
//{
// if (client.Poll(3000 * 1000, SelectMode.SelectRead))
// {
// rec = client.Receive(buffer, buffer.Length, SocketFlags.None);
// Debug.Print("RECEIVED FROM CLIENT: " + Encoding.ASCII.GetString(buffer, 0, rec));
// sent = webserver.Send(buffer, rec, SocketFlags.None);
// Debug.Print("SENT TO WEBSERVER[" + sent.ToString() + "]: " + Encoding.ASCII.GetString(buffer, 0, rec));
// transferred += rec;
// }
// else
// {
// Debug.Print("No data polled from client");
// }
//} while (rec == buffer.Length);
//Debug.Print("loop-2 finished");

between html5 and C# web socket communication

trying to send string data to windows c# application with this code;
try {
var ws = new WebSocket('ws://192.168.1.77:10048');
ws.onopen = function () {
ws.send("Sunucuya mesaj"); // I WANT TO SEND THIS MESSAGE TO SERVER!
};
ws.onclose = function () {
alert('Bağlantı kapandı.');
};
}
catch (e) {
alert(e);
}
and trying to get data from windows c# application with this code;
static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8181));
serverSocket.Listen(128);
serverSocket.BeginAccept(null, 0, OnAccept, null);
private static void OnAccept(IAsyncResult result)
{
byte[] buffer = new byte[1024];
try
{
Socket client = null;
string headerResponse = "";
if (serverSocket != null && serverSocket.IsBound)
{
client = serverSocket.EndAccept(result);
var i = client.Receive(buffer);
headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
// write received data to the console
Console.WriteLine(headerResponse);
}
if (client != null)
{
var key = headerResponse.Replace("ey:", "`")
.Split('`')[1] // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
.Replace("\r", "").Split('\n')[0] // dGhlIHNhbXBsZSBub25jZQ==
.Trim();
// key should now equal dGhlIHNhbXBsZSBub25jZQ==
var test1 = AcceptKey(ref key);
var newLine = "\r\n";
var response = "HTTP/1.1 101 Switching Protocols" + newLine
+ "Upgrade: websocket" + newLine
+ "Connection: Upgrade" + newLine
+ "Sec-WebSocket-Accept: " + test1 + newLine + newLine
//+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
//+ "Sec-WebSocket-Version: 13" + newLine
;
// which one should I use? none of them fires the onopen method
client.Send(System.Text.Encoding.UTF8.GetBytes(response));
var i = client.Receive(buffer); // wait for client to send a message
// once the message is received decode it in different formats
Console.WriteLine(Convert.ToBase64String(buffer).Substring(0, i));
Console.WriteLine("\n\nPress enter to send data to client");
Console.Read();
var subA = SubArray<byte>(buffer, 0, i);
client.Send(subA);
Thread.Sleep(10000);//wait for message to be send
}
}
catch (SocketException exception)
{
throw exception;
}
finally
{
if (serverSocket != null && serverSocket.IsBound)
{
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}
}
with this code i can communicate between client and server but i can't get ws.send("Sunucuya mesaj"); message from server that send from client. headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i); in this row you can see the header that is getting from server;
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 192.168.1.77:8181
Origin: http://localhost
Sec-WebSocket-Key: dHI34r7feV/Ar4G0/fONCg==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
How can i get data that is coming from client from server-side?
Thanks...
Messages between client and server are not plain text. See the data framing section of the protocol spec for details on how to encode/decode messages.
There are several liberally licensed open source C# servers you can take examples from. Such as Fleck

Categories

Resources