HttpWebRequest through two proxies - c#

I have recently set up a website which uses geographic DNS to resolve the DNS to two different IP's depending on your location.
However, this means to monitor the websites, I need to make sure the site is available in both geographical locations. To do this I wrote a small program in .net to continually try and HttpWebRequest to get a small html file on the website once using the local internet settings and once using a proxy based in the region which will resolve the name to the second IP address.
This works fine on my laptop at home, however in the office, to connect to the internet on almost all machines you will need to go via a proxy, which means the proxy I set earlier no longer works.
What I need to be able to do is send the request through the office proxy, then through the proxy in the remote country and finally to the website.
Let me know if this is not clear enough!

First you need to ensure that both proxies are HTTPS and they both support CONNECT method, i.e. "proxy chaining". Design of usual HTTP protocol doesn't provide support for "proxy chaining".
The idea is to establish 2 CONNECT tunnels, one inside another.
The algorithm is the following:
Connect to the 1st proxy via TCP
Request CONNECT tunnel to 2nd proxy
Once tunnel is created, request tunnel to target host
Send request. Request will go to target host through proxy #1 and proxy #2.
Below is a sample code which I have tested on my box:
string host = "encrypted.google.com";
string proxy2 = "213.240.237.149";//host;
int proxyPort2 = 3128;//443;
string proxy = "180.183.236.63";//host;
int proxyPort = 3128;//443;
byte[] buffer = new byte[2048];
int bytes;
// Connect to the 1st proxy
TcpClient client = new TcpClient(proxy, proxyPort);
NetworkStream stream = client.GetStream();
// Establish tunnel to 2nd proxy
byte[] tunnelRequest = Encoding.UTF8.GetBytes(String.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost:{0}\r\n\r\n", proxy2, proxyPort2));
stream.Write(tunnelRequest, 0, tunnelRequest.Length);
stream.Flush();
// Read response to CONNECT request
// There should be loop that reads multiple packets
bytes = stream.Read(buffer, 0, buffer.Length);
Console.Write(Encoding.UTF8.GetString(buffer, 0, bytes));
// Establish tunnel to target host
tunnelRequest = Encoding.UTF8.GetBytes(String.Format("CONNECT {0}:443 HTTP/1.1\r\nHost:{0}\r\n\r\n", host));
stream.Write(tunnelRequest, 0, tunnelRequest.Length);
stream.Flush();
// Read response to CONNECT request
// There should be loop that reads multiple packets
bytes = stream.Read(buffer, 0, buffer.Length);
Console.Write(Encoding.UTF8.GetString(buffer, 0, bytes));
// Wrap into SSL stream
SslStream sslStream2 = new SslStream(stream);
sslStream2.AuthenticateAsClient(host);
// Send request
byte[] request = Encoding.UTF8.GetBytes(String.Format("GET https://{0}/ HTTP/1.1\r\nHost: {0}\r\n\r\n", host));
sslStream2.Write(request, 0, request.Length);
sslStream2.Flush();
// Read response
do
{
bytes = sslStream2.Read(buffer, 0, buffer.Length);
Console.Write(Encoding.UTF8.GetString(buffer, 0, bytes));
} while (bytes != 0);
client.Close();
Console.ReadKey();

Related

C# Telnet client give none/incomplete output

I have a problem with the basic implementation of telnet client in C#. When I try to connect to the remote computer in local network via command line (telnet 192.168.0.255 23), there are no problems, but with this code, I cannot get for me any logical output
(just chars "??%?? ?? ??'??¬?? ??").
TcpClient telnetClient = new TcpClient("192.168.0.255", 23);
NetworkStream telnetStream = telnetClient.GetStream();
byte[] data = new Byte[4];
string responseData = String.Empty;
int bytes = telnetStream.Read(data, 0, data.Length);
while (true){
responseData += Encoding.ASCII.GetString(data, 0, bytes);
bytes = telnetStream.Read(data, 0, data.Length);
if (!telnetStream.DataAvailable)
break;
}
Console.WriteLine(responseData);
telnetClient.Close();
I realize when I try to get output from different public servers (for example. telehack.com), I get just in some cases first line of output. But output, which is provided via command line is much much extensive.
Telnet connection to remote pc in LAN via telnet and c#
Connection to public pc via telnet and c#
Could you please tell me what I doing terribly wrong? :)
Thank you all for help.
The first bytes send via Telnet in the case of telehack.com are Command bytes (see https://en.wikipedia.org/wiki/Telnet#Telnet_data ). They Start with 0xFF and then the command. You can see them clearly in the example below in the debug window (numbers there are in decimal).
I tried this with your (extended) C# script:

Send data to remote server using Sockets

I want send data to remote server i know its IP address and Port number. Let say it is xxx.xxx.xx.xx and port number is 123. It works with TCP. I want to send first-last name, email, and host name where host name is the new name which i am going to give my machine. Server should give me response in the form of key value as 123-01234. Here I do not understand how i would send data to server using socket. I did work with just simple strings. I have one last more question, Have i need to make server program in it.
public void Connect(String server)
{
Int32 port = 123;
TcpClient client = new TcpClient(server, port);
string FNAME = "reet";
Byte[] data = System.Text.Encoding.ASCII.GetBytes(FNAME);
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
Console.WriteLine("Sent: {0}", FNAME);
data = new Byte[256];
String responseData = String.Empty;
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);
stream.Close();
client.Close();
}
To open a socket, you can either use the Socket type, or the TcpClient type (assuming it is TCP). The IP address and port are specified in the constructor for either. Then you need to decide whether you're going to use the Socket API, vs the NetworkStream API, to actually do the communications. If you're using a raw Socket (.Client on a TcpClient), then you use the Send and Receive methods (and the related async options). If you prefer a Stream, then that is .GetStream() on TcpClient, or new NetworkStream(socketInstance) for Socket, and Read / Write. With a Stream, you can of course wrap it in a StreamReader / StreamWriter if you want a simple text API, but it isn't clear whether your socket API is text or binary. You mention wanting to receive 123-01234, but that could be encoded many different ways, so ultimately you need to be very clear about what the socket API expects, at the byte level.

How to implement IPP gateway using C#?

I need to develop a Internet Printing Protocol gateway in .Net that will receive the print jobs fired from IOS using the AirPrint client. The gateway will receive the document fired and release it to the print queue. I am able to broadcast my print services using the SDK provided by Apple. However, when I listen on a port to receive network streams of a document, I am not able to detect the end of stream received as the client keeps on sending streams. My guess is we have to read the attributes and respond accordingly, but I have no idea of these attributes. Below is the code that I am currently using:
IPAddress ipAddress = IPAddress.Parse("10.0.0.13");
IPAddress tcpListener = new TcpListener(ipAddress, 631);
tcpListener.Start();
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
byte[] bytes = new byte[2560];
NetworkStream stream = tcpClient.GetStream();
stream.Read(bytes, 0, bytes.Length);
string mstrMessage = Encoding.ASCII.GetString(bytesReceived, 0, bytesReceived.Length);
string Continue = "HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " + dateTime + "\r\nPragma: no-cache\r\nTransfer-Encoding: chunked\r\nContent-Type: application/ipp\r\n\r\nattributes-charset utf-8 attributes-natural-language en-us compression-supported none printer-is-accepting-jobs true document-format-supported application/pdf\r\n\r\n0\r\n";
bytesSent = Encoding.ASCII.GetBytes(mstrResponse);
stream.Write(bytesSent, 0, bytesSent.Length);
}
You should check the stream.Read for the returning value.
If it isn't zero you have incoming bytes from the TcpClient:
var bytesLength = 0;
do
{
bytesLength = stream.Read(bytes, 0, bytes.Length);
if (bytesLength == 0) return;
}
while(bytesLength > 0);
You need to understand the level of communication. You're not even reading or writing proper IPP messages yet. 100 continue is purley HTTP related.
Even though Apples Spec for AirPrint is not publicly available there's still a lot of information online. In short: AirPrint is based on IPP. As for the supported PDL PDF is a good choice but not the only one. iOS first checks the printers capabilities. It's up to you what kind of (virtual) print-server you offer.
(In case you have a business case and require a remote developer, don't hestitate to contact us.)

Read ClientHello before AuthenticateAsServer

I'm developing a Server Application that needs to manage different SSL services.
Each service has its own Certificate (with, of course, different CN).
I wanna use SNI to provide the correct Certificate, but I'm struggling reading the ClientHello (where the SNI Extension is) before Authenticate the Stream.
That's the code:
byte[] ClientHello = new byte[3000];
Stream TargetStream = TargetTcpClient.GetStream();
//Whatever I write here let me be unable to successfully authenticate
//If I don't read anything (like in this way), it works perfectly
int ClientHelloN = 0; //TargetStream.ReadByte();// TargetStream.Read(ClientHello, 0, 3000);
/*SNI Logic to extract Server-name*/
Certificate = new X509Certificate2(/*PATH TO CORRECT CERTIFICATE*/);
SslStream SSLStream = new SslStream(TargetStream);
SSLStream.AuthenticateAsServer(Certificate);
If I directly Authenticate with a prefixed Certificate there's no problem, but when I try to read even just a byte from the Stream before authenticate, it loops forever in the AuthenticateAsServer method.
I know I'm a bit late to the party, but why not peek the TcpClient's underlying Socket?
byte[] peekBuffer = new byte[16];
Socket socket = targetTcpClient.Client;
int bytesAvailable = socket.Receive(peekBuffer, SocketFlags.Peek);
if (bytesAvailable > 0)
{
if (peekBuffer[0] == 0x16) // ClientHello?
{
SslStream secureStream = new SslStream(targetTcpClient.GetStream());
}
}
Once you read data from TargetStream it's gone and your SslStream will be missing the beginning of the handshake.
I believe what you will need to do is wrap the Stream that you get from TargetTcpClient.GetStream() with your own Stream subclass. In your subclass you can read bytes from the stream to parse the Client Hello, but you should save all those bytes in a buffer. Your subclass should override Stream.Read() - when your override is called if there is anything in the buffer then return that, otherwise forward the call to the wrapped stream. The approach is similar to this answer except that subclasses StreamReader and only buffers a single char.

Sending a image file using a socket in C# (Large Data)

i want to send a large data (image) approx . 1MB file though a socket connection
Question 1
following code snippet of the socket client which i currently use to send a a text message .. how i can modify this to send a file ?
NetworkStream serverStream = clientSocket.GetStream();
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(richTextBox1.Text+"$");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
Question 2 : What modifications that required in both socket client and server to send and get large files ?
For large data portions you will probably need some kind of transmitting by portions. In your snippet you get all of the data in the array which couldn't be possible if file is large enough (or at least not suitable if it is not just one file to send).
Sending side will be something like that:
const int bufsize = 8192;
var buffer = new byte[bufsize];
NetworkStream ns = socket.GetStream();
using (var s = File.OpenRead("path"))
{
int actuallyRead;
while ((actuallyRead = s.Read(buffer, 0, bufsize)) > 0)
{
ns.Write(buffer, 0, actuallyRead);
}
}
ns.Flush();
Receiving site is just symmetric.
A socket doesn't care if you send text or binary data. Or how much you send. If there's any trouble then it is at the receiving end, code you didn't post. A classic mistake is to forget that a NetworkStream is a stream and not a sequence of packets. The Read() call at the receiving end can return any number of bytes. It won't be the number of bytes you wrote in the Write() call, depending on how routers in between the two machines broke up the IP packets and how much data is buffered in the receiver. You are probably getting away with calling Read only once because the string is short. That is definitely not going to work when you send a lot of data.
You need a protocol to help the receiver figure out when it received all the data. A simple way to do this is by first sending the length of the data. The receiver can then first read that length, then know how long to keep calling Read() to get the rest of the data.
You can arbitrarily extend this protocol by, say, sending the name of the file. Etcetera. Although that by the time you're done, you'd be close to having re-invented FTP.
If all you want to do is to send an image and you don't need any metadata, you can use code like this:
Server:
var listener = new TcpListener(address, port);
listener.Start();
using (var incoming = listener.AcceptTcpClient())
using (var networkStream = incoming.GetStream())
using (var fileStream = File.OpenWrite(imagePath))
{
networkStream.CopyTo(fileStream);
}
listener.Stop();
Client:
var client = new TcpClient();
client.Connect(address, port);
using (var networkStream = client.GetStream())
using (var fileStream = File.OpenRead(imagePath))
{
fileStream.CopyTo(networkStream);
}
client.Close();
If you want to compress the file, you can use GZipStream.
This code uses CopyTo() method, which is available in .Net 4, but you can write it yourself in earlier versions.

Categories