Http 隧道不适用于某些网站
Http tunnel does not work for some websites
我写了一个简单的http隧道代理服务器:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace TrafficMonitor
{
public class ProxyServer
{
const int BufferSize = 255;
public int Port { get; set; } = 9999;
private static Regex httpRegex = new Regex(@"^(?<method>[a-zA-Z]+)\s(?<url>.+)\sHTTP/(?<major>\d)\.(?<minor>\d+)$");
public async Task RunServerForEver()
{
var listner = new TcpListener(IPAddress.Any, Port);
listner.Start();
Console.WriteLine($"Listening on port {Port}");
try
{
while (true)
{
var client = await listner.AcceptTcpClientAsync();
ThreadPool.QueueUserWorkItem(HandleClient, client);
}
}
finally
{
listner.Stop();
Console.WriteLine("Server stopped");
}
}
void HandleClient(object state)
{
TcpClient client = (TcpClient)state;
Console.WriteLine($"New Connection Received: #{client.Client.Handle}");
try
{
byte[] bytes = new byte[BufferSize];
using (client)
using (var stream = client.GetStream())
{
int i = stream.Read(bytes, 0, bytes.Length);
var data = Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
if (!ParseHostPort(data, out string host, out int port))
{
client.Close();
return;
};
HandleClientIO(stream, host, port);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
bool ParseHostPort(string data, out string host, out int port)
{
host = null;
port = 0;
var lines = data.Split("\r\n", StringSplitOptions.RemoveEmptyEntries);
if (lines.Length == 0)
{
return false;
}
var connectMatch = httpRegex.Match(lines[0]);
if (!connectMatch.Success)
{
return false;
}
if (connectMatch.Groups["method"].Value != "CONNECT")
{
return false;
}
var url = connectMatch.Groups["url"].Value;
var uri = new Uri("http://" + url);
host = uri.Host;
port = uri.Port;
return true;
}
async Task SendMessage(NetworkStream stream, string data)
{
byte[] msg = Encoding.ASCII.GetBytes(data);
await stream.WriteAsync(msg, 0, msg.Length);
}
void HandleClientIO(NetworkStream stream, string host, int port)
{
TcpClient client = new TcpClient();
if (!client.ConnectAsync(host, port).Wait(10000))
{
throw new Exception("Could not connect to host: " + host + ":" + port);
}
SendMessage(stream, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\n\r\n").Wait();
using (var targetStream = client.GetStream())
{
Task send = SendClientData(stream, targetStream);
Task receive = ReceiveClientData(stream, targetStream);
Parallel.Invoke(
async () => await send,
async () => await receive
);
Task.WaitAll(send, receive);
}
}
async Task SendClientData(NetworkStream stream, NetworkStream targetStream)
{
await Task.Yield();
try
{
int r;
byte[] sendBuffer = new byte[BufferSize];
while ((r = await stream.ReadAsync(sendBuffer, 0, sendBuffer.Length)) != 0)
{
var data = Encoding.ASCII.GetString(sendBuffer, 0, r);
//Console.WriteLine("Received: {0}", data);
await targetStream.WriteAsync(sendBuffer, 0, r);
}
}
catch (Exception ex)
{
Console.WriteLine("Send Connection Failed: " + ex.Message);
}
}
async Task ReceiveClientData(NetworkStream stream, NetworkStream targetStream)
{
await Task.Yield();
try
{
byte[] receiveBuffer = new byte[BufferSize];
int i;
while ((i = await targetStream.ReadAsync(receiveBuffer, 0, receiveBuffer.Length)) != 0)
{
//var data2 = Encoding.ASCII.GetString(receiveBuffer, 0, i);
//Console.WriteLine("Remote: {0}", data2);
await stream.WriteAsync(receiveBuffer, 0, i);
}
}
catch (Exception ex)
{
Console.WriteLine("Receive Connection Failed: " + ex.Message);
}
}
}
}
并简单地在 main 中使用:
class Program
{
static void Main(string[] args)
{
var server = new ProxyServer();
server.RunServerForEver().Wait();
}
}
我执行代理并在 Chrome 和 Firefox 中对其进行测试,它适用于许多 https 和 http 网站。但不幸的是,有些网站不响应初始 SSL 握手,代理等待响应直到超时。
例如 'https://google.com' 有效,但 https://github.com/
无效。
可能与网站支持的TLS版本有关
尽管 TLS 1.0 和 TLS 1.1 已被弃用,google 仍然启用它们,而 github 则没有。
Here您可以检查您的 http 隧道服务器不能正常工作的网站的 TLS 版本,并将它们与正常工作的网站进行比较。
经过几个小时的尝试和错误后,我发现我应该使用更大的缓冲区大小!
更改此行解决了问题:
const int BufferSize = 8192;
我不知道为什么,但显然当以较小的缓冲区大小发送多个数据块时,SSL 握手不起作用。
我写了一个简单的http隧道代理服务器:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace TrafficMonitor
{
public class ProxyServer
{
const int BufferSize = 255;
public int Port { get; set; } = 9999;
private static Regex httpRegex = new Regex(@"^(?<method>[a-zA-Z]+)\s(?<url>.+)\sHTTP/(?<major>\d)\.(?<minor>\d+)$");
public async Task RunServerForEver()
{
var listner = new TcpListener(IPAddress.Any, Port);
listner.Start();
Console.WriteLine($"Listening on port {Port}");
try
{
while (true)
{
var client = await listner.AcceptTcpClientAsync();
ThreadPool.QueueUserWorkItem(HandleClient, client);
}
}
finally
{
listner.Stop();
Console.WriteLine("Server stopped");
}
}
void HandleClient(object state)
{
TcpClient client = (TcpClient)state;
Console.WriteLine($"New Connection Received: #{client.Client.Handle}");
try
{
byte[] bytes = new byte[BufferSize];
using (client)
using (var stream = client.GetStream())
{
int i = stream.Read(bytes, 0, bytes.Length);
var data = Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
if (!ParseHostPort(data, out string host, out int port))
{
client.Close();
return;
};
HandleClientIO(stream, host, port);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
bool ParseHostPort(string data, out string host, out int port)
{
host = null;
port = 0;
var lines = data.Split("\r\n", StringSplitOptions.RemoveEmptyEntries);
if (lines.Length == 0)
{
return false;
}
var connectMatch = httpRegex.Match(lines[0]);
if (!connectMatch.Success)
{
return false;
}
if (connectMatch.Groups["method"].Value != "CONNECT")
{
return false;
}
var url = connectMatch.Groups["url"].Value;
var uri = new Uri("http://" + url);
host = uri.Host;
port = uri.Port;
return true;
}
async Task SendMessage(NetworkStream stream, string data)
{
byte[] msg = Encoding.ASCII.GetBytes(data);
await stream.WriteAsync(msg, 0, msg.Length);
}
void HandleClientIO(NetworkStream stream, string host, int port)
{
TcpClient client = new TcpClient();
if (!client.ConnectAsync(host, port).Wait(10000))
{
throw new Exception("Could not connect to host: " + host + ":" + port);
}
SendMessage(stream, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\n\r\n").Wait();
using (var targetStream = client.GetStream())
{
Task send = SendClientData(stream, targetStream);
Task receive = ReceiveClientData(stream, targetStream);
Parallel.Invoke(
async () => await send,
async () => await receive
);
Task.WaitAll(send, receive);
}
}
async Task SendClientData(NetworkStream stream, NetworkStream targetStream)
{
await Task.Yield();
try
{
int r;
byte[] sendBuffer = new byte[BufferSize];
while ((r = await stream.ReadAsync(sendBuffer, 0, sendBuffer.Length)) != 0)
{
var data = Encoding.ASCII.GetString(sendBuffer, 0, r);
//Console.WriteLine("Received: {0}", data);
await targetStream.WriteAsync(sendBuffer, 0, r);
}
}
catch (Exception ex)
{
Console.WriteLine("Send Connection Failed: " + ex.Message);
}
}
async Task ReceiveClientData(NetworkStream stream, NetworkStream targetStream)
{
await Task.Yield();
try
{
byte[] receiveBuffer = new byte[BufferSize];
int i;
while ((i = await targetStream.ReadAsync(receiveBuffer, 0, receiveBuffer.Length)) != 0)
{
//var data2 = Encoding.ASCII.GetString(receiveBuffer, 0, i);
//Console.WriteLine("Remote: {0}", data2);
await stream.WriteAsync(receiveBuffer, 0, i);
}
}
catch (Exception ex)
{
Console.WriteLine("Receive Connection Failed: " + ex.Message);
}
}
}
}
并简单地在 main 中使用:
class Program
{
static void Main(string[] args)
{
var server = new ProxyServer();
server.RunServerForEver().Wait();
}
}
我执行代理并在 Chrome 和 Firefox 中对其进行测试,它适用于许多 https 和 http 网站。但不幸的是,有些网站不响应初始 SSL 握手,代理等待响应直到超时。
例如 'https://google.com' 有效,但 https://github.com/
无效。
可能与网站支持的TLS版本有关
尽管 TLS 1.0 和 TLS 1.1 已被弃用,google 仍然启用它们,而 github 则没有。
Here您可以检查您的 http 隧道服务器不能正常工作的网站的 TLS 版本,并将它们与正常工作的网站进行比较。
经过几个小时的尝试和错误后,我发现我应该使用更大的缓冲区大小!
更改此行解决了问题:
const int BufferSize = 8192;
我不知道为什么,但显然当以较小的缓冲区大小发送多个数据块时,SSL 握手不起作用。