C# Proxy Server: how to send/receive data with sockets? - c#

I'm trying to create a simple proxy server in C#. Below is a portion of my code where I created a socket with the host name that was parsed from the browser HTTP request. The full request is in the string "header" and is sent to the destination socket. Then the while loop receives the response from the socket and sends it to the client socket.
Socket destServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
destServerSocket.Connect(host, 80);
destServerSocket.Send(ASCIIEncoding.ASCII.GetBytes(header));
byte[] response = new byte[1];
while (destServerSocket.Receive(response) != 0)
{
client.Send(response);
}
destServerSocket.Disconnect(false);
destServerSocket.Dispose();
client.Disconnect(false);
client.Dispose();
Right now my proxy only works with simple html sites. Anything else and it just endlessly loads without ever displaying any content in the browser. How do I alter my code to allow my proxy server to work on websites that are more complex than basic html ones?

Related

Using Webclient ontop of already created TCP Socket

I need to communicate via a HTTP-Rest interface with a device.
I cannot create a connection to this device actively.
This device is creating a TCP-Socket with my program. So, I am listening on a port for this device.
After the Socket is created, I need to send HTTP-Requests to this device on top of that socket. I Am acting as HTTP-Client and the device is acting as HTTP-Server.
I cannot use WebClient for that as it creates its own TCP-Socket. But the socket as I said is already created.
My fist question would be: Is there still a way to use WebClient or another alternative to create the HTTP-Requests and read out the Responses?
If I cannot use Webclient I need to implement it by myself.
I saw that it is possible to use WebClient to create HTTP Header Bytes:
void SendHTTPRequest(string message)
{
//create Header Bytes
WebClient web = new WebClient();
web.Headers.Set(HttpRequestHeader.Connection, "keep-alive");
web.Headers.Set(HttpRequestHeader.Accept, "*/*");
web.Headers.Set(HttpRequestHeader.AcceptEncoding, "gzip, deflate, br");
//Built list with request bytes
List<byte> bytesToSend = new List<byte>();
bytesToSend.AddRange(Encoding.ASCII.GetBytes(message));
bytesToSend.AddRange(web.Headers.ToByteArray());
//Send request to the device
WriteBytes(bytesToSend.ToArray());
}
My Second Question would be: Is there a way to decode the HTTP-Headers out of the received bytes from the response? So that I only need to implement the write and read methods for the HTTP-Body.

How to get the requested target host from an SslStream as a server

With this code, for example:
private static void OnConnect(IAsyncResult ar)
{
var clientConnection = listener.EndAcceptTcpClient(ar);
listener.BeginAcceptTcpClient(OnConnect, ar.AsyncState);
try
{
// SSL the client
var clientStream = clientConnection.GetStream();
var clientSecureStream = new SslStream(clientStream, false);
clientSecureStream.AuthenticateAsServer(certificate);
...
...
I'm able to successfully establish an incoming SSL connection. The client that is connecting to this server specifies a target host when authenticating as a client:
https://learn.microsoft.com/en-us/dotnet/api/system.net.security.sslstream.authenticateasclient?view=netframework-4.7.2#System_Net_Security_SslStream_AuthenticateAsClient_System_String_
How do I retrieve the value of this target host in my code above?
I've looked into the RemoteCertificateValidationCallback parameter for the SslStream constructor, but the internet is telling me that this callback is only meant for client side, and not server side (I tried it and the targetHost argument is always an empty string).
I've searched here and online and I'm having trouble finding a solution...
Should an SslServer not be able to send different data depending on the target host? Does SslStream just not support this scenario? Am I missing some concept completely?

How to bind a specific port on a webclient object in C#

I am working on P2P application, I want to use the hole punching technique to connect peers.
here is my PHP third party Server Script.
<?php
echo "{yourIP:'".$_SERVER['REMOTE_ADDR']."',yourPort:".$_SERVER['REMOTE_PORT']."}";
?>
In my C# code I created a server socket which listens to 8765, when i want to get my public IP address and port number (in order to share with the peers), I send a request to php server using WebClient Object. The problem is the webclient object uses random local port to make request.
How to bind a webclient object to 8765 port? so that always the requests use this port as source port.
thanks!
Sorry for my poor english! :)
You will not be able to achieve this with WebClient, you can however solve it using HttpWebRequest:
public static IPEndPoint BindIPEndPointCallback(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
{
Console.WriteLine("BindIPEndpoint called");
return new IPEndPoint(IPAddress.Any,5000);
}
public static void Main()
{
HttpWebRequest request = (HttpWebRequest) WebRequest.Create("http://MyServer");
request.ServicePoint.BindIPEndPointDelegate = new BindIPEndPoint(BindIPEndPointCallback);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
}

HTTP over UDP adapter for Web API

I need to send some data from mobile clients to an ASP.NET ApiController. I cannot wait for an connection to be established (and I don't rely on the data to arrive), so I thought UDP is the way to go. On the server I need to listen on a UDP endpoint and redirect the request to my default HTTP endpoint.
This is what I have so far (shortened):
public class HttpOverUdpAdapter
{
public static UdpClient UpdListener;
public static void Start(int udpPort, tcpPort)
{
UpdListener = new UdpClient(udpPort);
new Thread(() =>
{
while (true)
{
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, udpPort);
byte[] bytes = UpdListener.Receive(ref endpoint);
TcpClient client = new TcpClient();
client.Connect(IPAddress.Loopback, tcpPort);
using (NetworkStream stream = client.GetStream())
{
stream.Write(bytes, 0, bytes.Length);
}
}
}).Start();
}
}
I get the following error when no matter what port I use:
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.
Apart from that, is this going to work out at all? Is it correct to start it in Application_Start()?
Remark: WCF is no alternative.
There is no such thing as "I don't rely on the data to arrive" with HTTP. HTTP consists of requests and responses, which are usually big enough so that multiple IP packets are needed. UDP does neither guarantee the delivery nor the order of the packets, so you might end up with corrupt requests which might result in no response (which might be ok for you) or in a response for another web resource (which is probably not ok). Also, the response can be corrupt too in an unpredictable way (which is probably not ok for you).
If you would a need a more reliable transport you would add a reliability layer on top of the UDP tunnel, which is neither simpler nor faster than simply using TCP directly.

how to send POST without waiting response C#

how to send POST without waiting response in C#?
How can I do it?
Help please!
Use one of the asynchronous methods in the WebClient class, for example UploadStringAsync.
The corresponding event (UploadStringCompleted for the example) will be triggered when the post is completed (or fails), but you don't have to hook up any handler for the event. You might want to do that, however, to check if there was any error.
An HTTP session is just a standard TCP session, and a POST is just a properly formatted piece of data that meets the HTTP specification. You can open a TCP connection to the web server and send a POST request and then close the TCP connection. You can check the HTTP specification (RFC 2616) to learn how to properly format a POST. You can find it at: http://www.w3.org/Protocols/rfc2616/rfc2616.html
A very basic POST would be sending the following over a TCP connection:
POST / HTTP/1.1
Host: www.thehost.com
Content-Length: 3
Content-Type: application/x-www-form-urlencoded
Hi!
Substituting the content, and the corresponding length with whatever you want to send, changing the / to the correct path and putting the hostname or IP of the host at Host:... and connecting the TCP session to that host of course.
A very very basic example:
using System.Net;
using System.Net.Sockets;
static void Main(string[] args)
{
string Hostname = "www.website.com";
TcpClient Client = new TcpClient(Hostname, 80);
Client.Client.Send(new ASCIIEncoding().GetBytes("POST / HTTP/1.1\nHost: "+Hostname+"\nConnection: close\n\n"));
Client.Close();
}
Changing www.website.com to the correct hostname (and the port if required).
UploadValuesAsync looks like a more complete solution, using something like this:
using (var client = new WebClient())
{
var values = new NameValueCollection();
// add values...
client.UploadValuesAsync(new System.Uri(rawUrl), "POST", values);
}
However note that I haven't tested this code as I was trying to solve a slightly different problem.
var wc = new WebClient();
wc.Headers["Content-type"] = "application/x-www-form-urlencoded";
wc.UploadStringAsync(uri, data);
wc.UploadStringCompleted += (sender, e) =>
{
// completed
};

Categories