Check if the request is coming through HTTP or HTTPS in the socket receiver

I have a multi-threaded asynchronous socket listener. I want to check if the request is safe or not. But I want to check that the AcceptCallBack method does not ReceiveCallBack.

I will do this because I want my code to work for both HTTP and HTTPS. If the request comes from HTTPS, I will simply continue with SslStream authentication instead of the raw socket.

Here is my code:

using System; using System.Net; using System.Net.Sockets; using System.Threading; using System.Text; namespace LearnRequestType { class StackOverFlow { private static readonly ManualResetEvent _manualResetEvent = new ManualResetEvent(false); private void StartListening() { IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 9002); if (localEndPoint != null) { Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); if (listener != null) { listener.Bind(localEndPoint); listener.Listen(10); Console.WriteLine("Socket listener is running..."); listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); } } } private void AcceptCallback(IAsyncResult ar) { _manualResetEvent.Set(); Socket listener = (Socket)ar.AsyncState; Socket handler = listener.EndAccept(ar); StateObject state = new StateObject(); state.workSocket = handler; // I want to understand if request comes from HTTP or HTTPS before this line. handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); } private void ReceiveCallback(IAsyncResult result) { StateObject state = (StateObject)result.AsyncState; Socket handler = state.workSocket; string clientIP = ((IPEndPoint)handler.RemoteEndPoint).Address.ToString(); int numBytesReceived = handler.EndReceive(result); if (!handler.Connected) { handler.Close(); return; } // Read incoming data... if (numBytesReceived > 0) { state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, numBytesReceived)); // Read incoming data line by line. string[] lines = state.sb.ToString().Split('\n'); if (lines[lines.Length - 1] == "<EOF>") { // We received all data. Do something... } else { // We didn't receive all data. Continue reading... handler.BeginReceive(state.buffer, 0, state.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), state); } } } } } public class StateObject { public Socket workSocket = null; public const int BufferSize = 256; public byte[] buffer = new byte[BufferSize]; public StringBuilder sb = new StringBuilder(); } 

If I change the AcceptCallBack and StateObject Class methods as follows:

 private void AcceptCallback(IAsyncResult ar) { _manualResetEvent.Set(); Socket listener = (Socket)ar.AsyncState; Socket handler = listener.EndAccept(ar); try { sslStream = new SslStream(new NetworkStream(handler, true)); // try to authenticate sslStream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Tls, true); state.workStream = sslStream; state.workStream.ReadTimeout = 100000; state.workStream.WriteTimeout = 100000; if (state.workStream.IsAuthenticated) { state.workStream.BeginRead(state.buffer, 0, StateObject.BufferSize, ReceiveCallback, state); } } catch (IOException ex) { // ıf we get handshake failed due to an unexpected packet format, this means incoming data is not HTTPS // Continue with socket not sslstream state.workSocket = handler; handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); } StateObject state = new StateObject(); state.workStream = handler; handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); } public class StateObject { public Socket workSocket = null; public SslStream workStream = null; public const int BufferSize = 1024; public byte[] buffer = new byte[BufferSize]; public StringBuilder sb = new StringBuilder(); } 

I can decide if the incoming data type is HTTP or HTTPS, but if it is HTTP, it will be processed by the catch block every time, so this will decrease the performance of the application.

Is there another way?

+6
source share
1 answer

If I understand correctly, you have one port where the client can use HTTP or HTTPS to connect, and you want to immediately know how the request was made, before any data is transmitted.

There is no way to find out before you receive data from the client. HTTP and HTTPS are protocols over TCP, they do not work at lower levels of the protocol, so there is no flag or anything that could tell which protocol is used. HTTPS is also a regular HTTP stream wrapped in a TLS / SSL stream.

You will need to read the data and determine based on which protocol is used. Or you should have separate ports for HTTP and HTTPS, which would make this trivial.

To determine if there is TLS / SSL, you can look at a few bytes and see what is included. The TLS specification says that the Hello client packet starts with a protocol version that ships as two uint8 s. Since the HTTP request will always have the verb as the first, you can easily check if the pair of the first bytes are characters or not, and then try SSLStream if it is not.

Also note that if you initiate an SSLStream on a socket, it can read from the socket, which will consume the start of the HTTP request, and you could not simply handle it as usual.

So, in your Accept callback, use something like this:

 Socket handler = listener.EndAccept(ar); byte[] tmp = new byte[2]; handler.Receive(tmp, 0, 2, SocketFlags.Peek); if (!Char.IsLetter((char)tmp[0]) || !Char.IsLetter((char)tmp[1])) { // Doesn't start with letters, so most likely not HTTP } else { // Starts with letters, should be HTTP } 

If you want to make sure it is TLS / SSL, you can check this question on SO

+4
source

Source: https://habr.com/ru/post/987559/


All Articles