在套接字侦听器中检查请求是否通过 HTTP 或 HTTPS
Check if request comes over HTTP or HTTPS in socket listener
我有多线程异步套接字侦听器。我想检查请求是否安全。但我想在 AcceptCallBack 方法中检查,而不是 ReceiveCallBack。
我会这样做,因为我希望我的代码同时适用于 HTTP 和 HTTPS。如果请求来自 HTTPS,我将继续使用经过身份验证的 SslStream 而不是原始套接字。
这是我的代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
namespace LearnRequestType
{
class Whosebug
{
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 Class:
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 是 TCP 之上的协议,它们不适用于较低的协议级别,因此没有标志或任何可以说明使用哪种协议的信息。此外,HTTPS 只是包裹在 TLS/SSL 流中的普通 HTTP 流。
您将必须读取数据并根据数据确定使用的协议。或者您必须为 HTTP 和 HTTPS 使用单独的端口,这将使这变得微不足道。
要检测它是否是 TLS/SSL,您可以 窥视 几个字节并查看输入的内容。The TLS specification 表示 Client Hello 数据包以协议版本开头,作为两个 uint8
发送。由于 HTTP 请求始终将动词作为第一个,因此您可以轻松地检查前几个字节是否为字符,然后尝试 SSLStream
如果不是。
另请注意,如果您在套接字上启动 SSLStream
,它可能会从套接字读取数据,这会占用 HTTP 请求的开头,您无法正常处理它。
所以在你的接受回调中使用这样的东西:
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,您可以检查 this question on SO
我有多线程异步套接字侦听器。我想检查请求是否安全。但我想在 AcceptCallBack 方法中检查,而不是 ReceiveCallBack。
我会这样做,因为我希望我的代码同时适用于 HTTP 和 HTTPS。如果请求来自 HTTPS,我将继续使用经过身份验证的 SslStream 而不是原始套接字。
这是我的代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
namespace LearnRequestType
{
class Whosebug
{
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 Class:
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 是 TCP 之上的协议,它们不适用于较低的协议级别,因此没有标志或任何可以说明使用哪种协议的信息。此外,HTTPS 只是包裹在 TLS/SSL 流中的普通 HTTP 流。
您将必须读取数据并根据数据确定使用的协议。或者您必须为 HTTP 和 HTTPS 使用单独的端口,这将使这变得微不足道。
要检测它是否是 TLS/SSL,您可以 窥视 几个字节并查看输入的内容。The TLS specification 表示 Client Hello 数据包以协议版本开头,作为两个 uint8
发送。由于 HTTP 请求始终将动词作为第一个,因此您可以轻松地检查前几个字节是否为字符,然后尝试 SSLStream
如果不是。
另请注意,如果您在套接字上启动 SSLStream
,它可能会从套接字读取数据,这会占用 HTTP 请求的开头,您无法正常处理它。
所以在你的接受回调中使用这样的东西:
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,您可以检查 this question on SO