在套接字监听器中检查请求是通过HTTP还是HTTPS发送的

本文关键字:还是 HTTP HTTPS 监听器 套接字 检查 请求 | 更新日期: 2023-09-27 17:49:47

我有多线程异步套接字监听器。我想检查请求是否安全。但我想在AcceptCallBack方法中检查那个而不是ReceiveCallBack。

我将这样做,因为我希望我的代码工作的HTTP和HTTPS。如果请求来自HTTPS,我将继续使用经过身份验证的SslStream而不是原始套接字。

下面是我的代码:
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();
}

如果我像这样改变AcceptCallBack方法和StateObject类:

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();
}

我可以决定传入的数据类型是HTTP还是HTTPS,但如果是HTTP,每次都会被catch块处理,所以它会降低应用程序的性能。

还有别的办法吗?

在套接字监听器中检查请求是通过HTTP还是HTTPS发送的

如果我理解正确的话,您有一个客户端可以使用HTTP或HTTPS连接的单一端口,并且您希望在传输任何数据之前立即知道请求是如何发出的。

在从客户端接收数据之前没有办法知道这一点。HTTP和HTTPS是TCP之上的协议,它们不能在较低的协议级别上工作,所以没有标志或任何东西可以说明使用哪个协议。另外,HTTPS只是普通的HTTP流封装在TLS/SSL流中。

您必须读取数据并根据使用的协议确定。或者,您必须为HTTP和HTTPS提供单独的端口,这将使此问题变得微不足道。

要检测它是否是TLS/SSL,你可以偷看几个字节,看看进来的是什么。TLS规范规定客户端Hello数据包以协议版本开始,作为两个uint8发送。由于HTTP请求总是将动词作为第一个,因此您可以轻松检查前几个字节是否为字符,如果不是,则尝试SSLStream

还请注意,如果您在套接字上启动SSLStream,它可能会从套接字中读取,这将消耗HTTP请求的开头,并且您无法正常处理它。

所以在你的Accept回调中使用这样的东西:

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
}

如果你想确保是TLS/SSL,你可以在SO

检查这个问题