How to Relay SSL(HTTPS) response through Proxy? - c#

I have to trace the HTTP traffic .It work without Https. I try for the https, it successfully get the response via Sslstream through the certificate. When i forward ssl response for the relay but it doesn’t getting the response.
Please let me how to relay SSL response?
Please check the following code for the same.
Thanks
public void ThreadHandleHTTPClient(object o)
{
try{
Socket client = (Socket)o;
Stream ns = new NetworkStream(client);
//Stream outStrem = ns;
//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];
string e=line.Split(new string[] { " " }, StringSplitOptions.None)[1];
Uri uri = new Uri(e);
if (uri.Scheme == "https" || line.Contains("CONNECT"))
{
rport = 443;
//Make tunnel for the HTTPS which is authentic host.
SslStream sslStream = sslTunnel(ns, uri.OriginalString, "1.0");
ns = sslStream;
string remoteUri = "https://" + uri.Scheme;
StreamReader clientStreamReader = new StreamReader(sslStream);
string httpCmd = clientStreamReader.ReadLine();
string[] splitBuffer = httpCmd.Split(_spaceSplit, 3);
remoteUri = remoteUri + splitBuffer[1];
myQuery = string.Empty;
//Read the SSL Stream save command in myQuery variable.
readRequestHeadersEx(clientStreamReader);
data = myQuery;
data = splitBuffer[0]+" ";
data += remoteUri;
data += " ";
data += splitBuffer[2];
data += "\r\n";
data += myQuery;
data += "\r\n";
line = data.Replace("\r\n", "\n").Split(new string[] { "\n" }, StringSplitOptions.None)[0];
uri = new Uri(line.Split(new string[] { " " }, StringSplitOptions.None)[1]);
}
else
{
rport = 80;
}
IPHostEntry rh = Dns.GetHostEntry(uri.Host);
Socket remoteserver = new Socket(rh.AddressList[0].AddressFamily, SocketType.Stream, ProtocolType.IP);
remoteserver.Connect(new IPEndPoint(rh.AddressList[0], rport));
byte[] databytes = Encoding.ASCII.GetBytes(data);
remoteserver.Send(databytes, databytes.Length, SocketFlags.None);
//START RELAY
buffer = new byte[2048];
rec = 0;
data = "";
do
{
transferred = 0;
do
{
rec = remoteserver.Receive(buffer, buffer.Length, SocketFlags.None);
sent = client.Send(buffer, rec, SocketFlags.None);
transferred += rec;
data += Encoding.ASCII.GetString(buffer, 0, rec);
} while (rec == buffer.Length);
if (transferred == 0)
break;
} while (transferred > 0);
client.Close();
}
catch (Exception ex)
{
}
}
public void Start(IPAddress ip, int port)
{
TcpListener listener = new TcpListener(ip, port);
listener.Start(100);
while (true)
{
Socket client = listener.AcceptSocket();
Thread th = new Thread(ThreadHandleHTTPClient);
th.Start(client);
}
listener.Stop();
}

Related

C# create HttpClient based on existing Stream

I have established a secure connection to a web server using BouncyCastle TlsClientProtocol and it gave me a System.IO.Stream which I can read and write to, the following code works:
MyTlsSession sess = new MyTlsSession("api.telegram.org", 443);
using (var stream = sess.Stream())
{
var hdr = new StringBuilder();
hdr.AppendLine("GET / HTTP/1.1");
hdr.AppendLine("Host: api.telegram.org");
hdr.AppendLine();
var dataToSend = Encoding.ASCII.GetBytes(hdr.ToString());
stream.Write(dataToSend, 0, dataToSend.Length);
int totalRead = 0;
string response = "";
byte[] buff = new byte[1024];
do
{
totalRead = stream.Read(buff, 0, buff.Length);
response += Encoding.ASCII.GetString(buff, 0, totalRead);
} while (totalRead == buff.Length);
Console.WriteLine(response);
}
How can I instantiate a System.Net.Http.HttpClient on top of this Stream to have a higher-level API instead of dealing with raw bytes?
I don't use built-in TLS capabilities of .NET because of an outdated OS (doesn't have the required cipher suites).
public static string CallTCP(string domainName, int port)
{
try
{
byte[] data = new byte[1024];
string result;
TcpClient client;
Logger.Verbose("CallTCP Connecting", ">>" + domainName);
client = new TcpClient(domainName, port);
NetworkStream ns = client.GetStream();
int recv = ns.Read(data, 0, data.Length);
result = Encoding.ASCII.GetString(data, 0, recv);
Logger.Verbose("CallTCP Connect\t", "<<" + domainName + "->" + result);
ns.Close();
client.Close();
return result;
}
catch (Exception e)
{
Logger.Error("CallTCP", "Error: \t" + e.Message.ToString());
Logger.Error("CallTCP", "Error: \t" + e.StackTrace.ToString());
return null;
}
}

TCP Socket recieves data wrong in C#

I'm trying to send files from a Socket to another Socket. The sockets are running in two different applications, a client application and a server application. This is happening on the same machine now when testing it. The client first sends to the server when it is ready to receive first info about the file that will be sent to it like filename and file size in bytes in a 2056 bytes message. Then it sends a message each time it is ready to receive a new 2048 buffer of file content.
In short, this is the communication flow for each file:
client -"CLIENT_READY_TO_RECEIVE_FILE_INFO"-------> server
client <-"File name+file size of file coming next"- server
client -"CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER" -> server
client <-"2048 byte buffer of file content"-------- server
...the same flow is repeated until all files are sent to the client.
My problem is that the client receives the file info message wrong, although I have double checked that the server sends it corretly. They both use ASCII encoding. Here is my code:
Client code (receives the files):
private void fetchFilesThreadMethod()
{
String localHostName = Dns.GetHostName();
IPAddress[] localIPs = Dns.GetHostAddresses(localHostName);
IPAddress localIP = localIPs[2];
int port = 1305;
IPEndPoint localEP = new IPEndPoint(localIP,port);
fetchFilesSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
fetchFilesSocket.Bind(localEP);
fetchFilesSocket.Listen(1);
fetchFilesSocket = fetchFilesSocket.Accept();
while (true)
{
byte[] receivedFileInfo = new byte[2056];
byte[] receivedFileCont = new byte[2048];
byte[] comMessage = new byte[100];
byte[] comMessageBytes;
String fileInfoStr = String.Empty;
String fileNameStr = String.Empty; ;
String fileExtStr = String.Empty;
String comMessageStr = String.Empty;
String fileSizeStr = String.Empty;
ulong fileSize = 0;
ulong lastBytesSize = 0;
comMessageStr = "CLIENT_READY_TO_RECEIVE_FILE_INFO";
comMessageBytes = Encoding.ASCII.GetBytes(comMessageStr);
for (int i = 0; i < comMessageBytes.Length; i++)
comMessage[i] = comMessageBytes[i];
Console.WriteLine("Bytes available to be flushed by client: " + fetchFilesSocket.Available);
if (fetchFilesSocket.Available > 0)
{
Console.WriteLine(fetchFilesSocket.Available + " bytes flushed by client.");
byte[] flusher = new byte[fetchFilesSocket.Available];
fetchFilesSocket.Receive(flusher, 0, fetchFilesSocket.Available, SocketFlags.None);
}
fetchFilesSocket.Send(comMessage, 0, 100, SocketFlags.None);
Console.WriteLine("Client sent ready to receive file info.");
fetchFilesSocket.Receive(receivedFileInfo,0,2056, SocketFlags.None);
fileInfoStr = Encoding.ASCII.GetString(receivedFileInfo);
Console.WriteLine("Received file info:" + fileInfoStr);
fileNameStr = fileInfoStr.Split(new String[] { "ENDN" }, StringSplitOptions.None)[0];
Console.WriteLine("Received file name:" + fileNameStr);
fileExtStr = fileNameStr.Split('.').Last();
Console.WriteLine("Received file extension:" + fileExtStr);
fileSizeStr = fileInfoStr.Split(new String[] {"ENDS"},StringSplitOptions.None)[0];
fileSizeStr = fileSizeStr.Split(new String[] {"ENDN"},StringSplitOptions.None).Last();
Console.WriteLine("File size string:" + fileSizeStr);
fileSize = Convert.ToUInt64(fileSizeStr,10);
Console.WriteLine("Received file size:" + fileSize);
lastBytesSize = fileSize % 2048;
ulong byteCount = 0;
bool keepReceiving = true;
ulong buffersReceived = 0;
while (keepReceiving)
{
comMessageStr = "CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER";
comMessageBytes = Encoding.ASCII.GetBytes(comMessageStr);
for (int i = 0; i < comMessageBytes.Length; i++)
comMessage[i] = comMessageBytes[i];
fetchFilesSocket.Send(comMessage, 0, 100, SocketFlags.None);
Console.WriteLine("Console sent ready to receive buffer message.");
if (fileSize - byteCount >= 2048)
{
fetchFilesSocket.Receive(receivedFileCont, 0, 2048, SocketFlags.None);
buffersReceived++;
Console.WriteLine("Buffers received:" + buffersReceived);
byteCount = byteCount + 2048;
}
else
{
fetchFilesSocket.Receive(receivedFileCont, 0, 2048, SocketFlags.None); buffersReceived++;
byteCount = byteCount + 2048;
Console.WriteLine("Buffers received:" + buffersReceived);
keepReceiving = false;
}
Console.WriteLine("Bytes received " + byteCount + "/" + fileSize);
//Console.WriteLine("Received bytes in current file:" + byteCount);
}
Console.WriteLine("File received.");
}
}
Server code (sends the files):
private void fetchThreadMethod(Object commandArgs)
{
if (fetchSocket == null)
{
fetchSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
int port = 1304;
IPEndPoint fetchSocketEP = new IPEndPoint(localIP,port);
fetchSocket.Bind(fetchSocketEP);
}
if (!fetchSocket.Connected)
try
{
fetchSocket.Connect(remoteIP, 1305);
if (fetchSocket.Connected)
Console.WriteLine("Server fetch socket connected.");
}catch(Exception e)
{
Console.WriteLine("Something went wrong when connecting the server fetch socket.");
}
FileCollector fCollector = new FileCollector();
String userName = Environment.GetEnvironmentVariable("USERPROFILE");
String targetDirectory = userName + "\\" + commandArgs;
Console.WriteLine("Path sent to file collector:" + targetDirectory);
fCollector.startFileCollector(targetDirectory);
List<FileNode> collectedFiles = fCollector.getCollectedFiles();
String comMessageStr = String.Empty;
foreach (FileNode fNode in collectedFiles)
{
comMessageStr = String.Empty;
byte[] sentFileInfo = new byte[2056];
byte[] sentFileCont = new byte[2048];
byte[] comMessage = new byte[100];
String fileName = fNode.getFileName();
String formattedFileInfo = fileName;
formattedFileInfo += "ENDN";
ulong fileSize = 0;
FileStream fStream = null;
try
{
fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
FileInfo fInfo = new FileInfo(fileName);
fileSize = (ulong) fInfo.Length;
if (fileSize == 0)
continue;
formattedFileInfo += fileSize.ToString() + "ENDS";
}
catch (Exception e)
{
Console.WriteLine("Could not read from file:" + fileName);
deniedAccessFiles.Add(fileName);
continue;
}
byte[] fileInfoBytes = Encoding.ASCII.GetBytes(formattedFileInfo);
for (int i = 0; i < fileInfoBytes.Length; i++)
sentFileInfo[i] = fileInfoBytes[i];
while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_FILE_INFO"))
{
Console.WriteLine("Server waiting for file info ready message from client.");
fetchSocket.Receive(comMessage,0,100,SocketFlags.None);
comMessageStr = Encoding.ASCII.GetString(comMessage);
comMessageStr = comMessageStr.Substring(0,33);
Console.WriteLine("Received parsed message from client:" + comMessageStr);
}
Console.WriteLine("Server received file info ready message from client.");
comMessageStr = String.Empty;
Console.WriteLine("formattedFileInfo:" + formattedFileInfo);
Console.WriteLine("Sent file info:" + Encoding.ASCII.GetString(sentFileInfo));
fetchSocket.Send(sentFileInfo, 0, 2056, SocketFlags.None);
int readByte = 0;
ulong byteCount = 0;
ulong buffersSent = 0;
while (readByte != -1)
{
if (byteCount == 2048)
{
while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER"))
{
Console.WriteLine("Server waiting for ready for buffer message from client.");
fetchSocket.Receive(comMessage, 100, SocketFlags.None);
comMessageStr = Encoding.ASCII.GetString(comMessage);
comMessageStr = comMessageStr.Substring(0,38);
Console.WriteLine("Received parsed message from client 1:" + comMessageStr);
}
Console.WriteLine("Server received ready for buffer message from client.");
fetchSocket.Send(sentFileCont, 0, 2048, SocketFlags.None);
comMessageStr = String.Empty;
buffersSent++;
Console.WriteLine("Buffers sent:" + buffersSent);
byteCount = 0;
}
else
{
readByte = fStream.ReadByte();
if (readByte != -1)
{
sentFileCont[byteCount] = Convert.ToByte(readByte);
byteCount++;
}
}
}
while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER"))
{
Console.WriteLine("Server waiting for ready for buffer message from client.");
fetchSocket.Receive(comMessage, 100, SocketFlags.None);
comMessageStr = Encoding.ASCII.GetString(comMessage);
comMessageStr = comMessageStr.Substring(0, 38);
Console.WriteLine("Received parsed message from client 2:" + comMessageStr);
}
Console.WriteLine("Server received ready for buffer message from client.");
fetchSocket.Send(sentFileCont, 0, 2048, SocketFlags.None);
buffersSent++;
Console.WriteLine("Buffers sent:" + buffersSent);
comMessageStr = String.Empty;
}
}
Console outputs:
My recommendation would be to utilise the TCPListener and TCPClient classes provided within System.Net.Sockets they really simplify sending messages over TCP/IP.
Server side you need something like this:
class MyTcpListener
{
public static void Listen()
{
TcpListener server = null;
byte[] bytes = new byte[256];
try
{
// Set the TcpListener on port 13000.
const int port = 13000;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Enter the listening loop.
while (true)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
string data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine($"Received: {data}");
// Process the data sent by the client.
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
// Send back a response.
stream.Write(msg, 0, msg.Length);
Console.WriteLine($"Sent: {data}");
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Console.WriteLine($"SocketException: {e}");
}
finally
{
// Stop listening for new clients.
server?.Stop();
}
}
}
Obviously you'll want to adapt it to your program's structure. I took this from here but edited it slightly.
And on your client side you want to use something like this:
class MyTCPClient
{
static void Connect(String server, String message)
{
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
int port = 13000;
TcpClient client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array. Any encoding can be used as long as it's consistent with the server.
byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
NetworkStream stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Console.WriteLine($"Sent: {message}");
// Receive the TcpServer.response. This is all optional and can be removed if you aren't recieving a response.
// Buffer to store the response bytes.
data = new byte[256];
// String to store the response ASCII representation.
// Read the first batch of the TcpServer response bytes.
int bytes = stream.Read(data, 0, data.Length);
string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {responseData}");
// Close everything.
stream?.Close();
client?.Close();
}
catch (ArgumentNullException e)
{
Console.WriteLine($"ArgumentNullException: {e}");
}
catch (SocketException e)
{
Console.WriteLine($"SocketException: {e}");
}
Console.WriteLine("\n Press Enter to continue...");
Console.Read();
}
}
}
Again, this needs to be adapted to your structure and I took it from here. The response stuff can all be removed if you aren't expecting a response from your server.
These two classes are extremely resilient and abstract away all of the complicated stuff, the size of the data buffers can also be changed to whatever size you need.

JS: Call API from server side

The following JavaScript code is working fine on old i9 browser but not on the latest one. Now I want to call API from server side as this code is not working due to cross domain cores issue.
var xmlHttpDevice = new XMLHttpRequest();
xmlHttpDevice.open("DEVICEINFO", "http://127.0.0.1:" + PortNumber + "/getDeviceInfo", true);
xmlHttpDevice.onload = function (e) {
if (xmlHttpDevice.readyState === 4) {
if (xmlHttpDevice.status === 200) {
alert(xmlHttpDevice.responseText);
} else {
alert(xmlHttpDevice.statusText);
}
}
};
xmlHttpDevice.onerror = function (e) {
console.error(xmlHttpDevice.statusText);
};
var params = "rdverb=DEVICEINFO&URL=''";
xmlHttpDevice.send(params);
my server side code :
TcpClient socket = new TcpClient();
try
{
// Call EndGetContext to complete the asynchronous operation.
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerRequest request = context.Request;
string strPortNumber = string.Empty;
string strRDVerb = "";
int PortNumberStartRange = 11099;
result.AsyncWaitHandle.WaitOne();
if (context.Request.InputStream != null)
{
var body = new StreamReader(context.Request.InputStream).ReadToEnd();
GetPostedData(ref strPortNumber, ref strRDVerb, body);
}
else
{
strPortNumber = "12345";
strRDVerb = "RDSERVICE";
}
if (strRDVerb != "RDSERVICE" && strRDVerb != "DEVICEINFO")
{
strRDVerb = "CAPTURE";
}
//var body = new StreamReader(context.Request.InputStream).ReadToEnd();
string response = string.Empty;
//Get the stream that will be used to send/receive data
ExecuteRecoveryCode(ref socket, PortNumberStartRange);
NetworkStream ns = socket.GetStream();
//Write the HTTP Header info to the stream
StreamWriter sw = new StreamWriter(ns);
if (strRDVerb == "DEVICEINFO")
{
var message = "rdverb=DEVICEINFO&URL=''";
//var data = System.Text.Encoding.ASCII.GetBytes(message);
sw.Write(message, 0, message.Length);
sw.WriteLine(string.Format(strRDVerb + " /getDeviceInfo HTTP/1.1"));
sw.WriteLine(string.Format("HOST:127.0.0.1:11100"));
}
sw.Flush();
//Save the data that lives in the stream
string packet = string.Empty;
StreamReader sr = new StreamReader(ns);
int count = 0;
string EndString = string.Empty;
GetServiceMethod(strRDVerb, ref count, ref EndString);
for (int i = 0; i < count; i++)
{
packet = sr.ReadLine();
response += packet;
}
HttpListenerResponse resp = context.Response;
//byte[] buffer = System.Text.Encoding.UTF8.GetBytes("<HTML><BODY> " + response + EndString + "</BODY></HTML>");
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(response + EndString);
resp.StatusDescription = response;
resp.ContentLength64 = buffer.Length;
System.IO.Stream output = resp.OutputStream;
resp.StatusCode = (int)HttpStatusCode.OK;
output.Write(buffer, 0, buffer.Length);
output.Close();
resp.Close();
}
catch (Exception ex)
{
//throw ex;
}
finally
{
socket.Close();
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}

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");

Downloading web site using sockets

I'm trying to download source of the site using sockets. Currently i can download headers and after that i just terminate connection because i don't know how long should I receive data. This is the code:
private void HandleConnect(SocketAsyncEventArgs e)
{
if (e.ConnectSocket != null)
{
// simply start sending
bool completesAsynchronously = e.ConnectSocket.SendAsync(e);
// check if the completed event will be raised.
// if not, invoke the handler manually.
if (!completesAsynchronously)
{
SocketAsyncEventArgs_Completed(e.ConnectSocket, e);
}
}
}
private void HandleReceive(SocketAsyncEventArgs e)
{
string responseL = Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length);
response += responseL;
temp += responseL;
string[] lines = Regex.Split(response, "\r\n\r\n");
if (lines.Length > 1 && header == "")
{
header = lines[0].ToString() + "\r\n";
lines[0] = "";
response = lines.ToString();
}
if (header == "")
{
bool completesAsynchronously = e.ConnectSocket.ReceiveAsync(e);
}
else
{
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
_callback(false, this);
});
}
}
I was trying to search for \r\n but it didn't help :/
Please help!
Thank you in advance :)
I use this code to send headers to the site and then read its content. I hope you find it useful.
ReadStateObject stateObject; //Info below
mytcpclient = new TcpClient();
mytcpclient.Connect(host, port);
mysocket = mytcpclient.Client;
SendHeader(mysocket);//Info below
ns = mytcpclient.GetStream();
if (ns.CanRead)
{
stateObject = new ReadStateObject(ns, 1024);
ns.BeginRead(stateObject.ReadBuffer, 0, stateObject.ReadBuffer.Length, new AsyncCallback(ReadCallBack), stateObject);
}
StateObject is small class used to represent the AsyncState object in BeginRead method:
class ReadStateObject
{
public NetworkStream Stream {get; set;}
public byte[] ReadBuffer;
public ReadStateObject(NetworkStream _stream, int bufferSize)
{
Stream = _stream;
ReadBuffer = new byte[bufferSize];
}
}
And this is a Callback Method used in BeginRead method.
private void ReadCallBack(IAsyncResult result)
{
ReadStateObject stateObject = (ReadStateObject)result.AsyncState;
NetworkStream myNetworkStream = stateObject.Stream;
int numberofbytesread = 0;
StringBuilder sb = new StringBuilder();
numberofbytesread = myNetworkStream.EndRead(result);
sb.Append(Encoding.ASCII.GetString(stateObject.ReadBuffer, 0, numberofbytesread));
/*It seems, if there is no delay, the DataAvailable may not be true even when there are still data to be received from the site, so I added this delay. Any suggestions, how to avoid this are welcome*/
Thread.Sleep(500);
while (myNetworkStream.DataAvailable)
{
byte[] mydata = new byte[1024];
numberofbytesread = myNetworkStream.Read(mydata, 0, mydata.Length);
sb.Append(Encoding.ASCII.GetString(mydata, 0, numberofbytesread));
}
Console.Writeln(sb.ToString());
mytcpclient.Close();
}
And this is where Headers are sent to the site
public void SendHeader(Socket mySocket)
{
String sBuffer = "";
sBuffer = sBuffer + "GET /"+pathquery+" HTTP/1.1" + "\r\n";
sBuffer = sBuffer + "Host: "+ hostname + "\r\n";
sBuffer = sBuffer + "Content-Type: text/html\r\n";
sBuffer = sBuffer + "\r\n";
Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer);
mySocket.Send(Encoding.ASCII.GetBytes(sBuffer), Encoding.ASCII.GetBytes(sBuffer).Length, 0);
}
Maybe, you should use WebClient or HttpWebRequest instead of sockets.
Using sockets and interpreting Http protocol can be painful.

Categories

Resources