VERSION | METHODS_COUNT | METHODS |
---|---|---|
1字节 | 1字节 | 1到255字节,长度由METHODS_COUNT值决定 |
0x05 | 0x03 | 0x00 0x01 0x02 |
各字段含义
METHODS列表
VERSION | METHOD |
---|---|
1字节 | 1字节 |
0x05 | 0x00 |
VERSION | METHOD |
---|---|
1字节 | 1字节 |
0x05 | 0x02 |
VERSION | USERNAME_LENGTH | USERNAME | PASSWORD_LENGTH | PASSWORD |
---|---|---|---|---|
1字节 | 1字节 | 1到255字节 | 1字节 | 1到255字节 |
0x01 | 0x01 | 0x0a | 0x01 | 0x0a |
各字段含义
VERSION | STATUS |
---|---|
1字节 | 1字节 |
0x01 | 0x00 |
各字段含义
VERSION | COMMAND | RSV | ADDRESS_TYPE | DST.ADDR | DST.PORT |
---|---|---|---|---|---|
1字节 | 1字节 | 1字节 | 1字节 | 1-255字节 | 2字节 |
各字段含义
VERSION | RESPONSE | RSV | ADDRESS_TYPE | DST.ADDR | DST.PORT |
---|---|---|---|---|---|
1字节 | 1字节 | 1字节 | 1字节 | 1-255字节 | 2字节 |
各字段含义
第3步成功后,进入数据转发阶段
RSV | FRAG | ADDRESS_TYPE | DST.ADDR | DST.PORT | DATA |
---|---|---|---|---|---|
2字节 | 1字节 | 1字节 | 可变长 | 2字节 | 可变长 |
各字段含义
///
/// 客户端和目标服务器的连接
///
internal class UserAndToken
{
public TcpClient Client { get; set; }
public NetworkStream ClientStream { get; set; }
public TcpClient TargetClient { get; set; }
public NetworkStream TargetStream { get; set; }
}
///
/// socks5地址类型
///
public enum Socks5AddressType : byte
{
///
/// IPV4
///
IPV4 = 0x01,
///
/// 域名
///
Domain = 0x03,
///
/// IPV6
///
IPV6 = 0x04
}
///
/// socks5命令类型
///
public enum Socks5CommandType : byte
{
///
/// 连接
///
Connect = 0x01,
///
/// 绑定
///
Bind = 0x02,
///
/// UDP转发
///
Udp = 0x03
}
using System.Buffers.Binary;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Server
{
public class TcpClientSocks5Server
{
static ushort port = 8300;
static async Task Main(string[] args)
{
try
{
TcpListener listener = new TcpListener(IPAddress.Any, port);
listener.Start();
Console.WriteLine($"Socks5服务已启动,监听端口:{port}");
while (true)
{
//接受连接创建客户端
TcpClient tcpClient = await listener.AcceptTcpClientAsync();
HandleClientAsync(tcpClient);
}
}
catch (Exception e)
{
Console.WriteLine($"Main的异常:{e.Message}");
}
}
///
/// 接收处理客户端的数据
///
///
///
private static async Task HandleClientAsync(TcpClient tcpClient)
{
try
{
UserAndToken token = new UserAndToken()
{
Client = tcpClient,
ClientStream = tcpClient.GetStream(),
};
//握手阶段,读取到客户端发送的协议,可以根据协议进行指定的连接
byte[] buffer = new byte[10];
await token.ClientStream.ReadAsync(buffer, 0, buffer.Length);
//判断协议
if (buffer[0] == 5)
{
int AuthMethod = 0;
//设置不需要认证
await token.ClientStream.WriteAsync(new byte[] { 0x05, 0x00 });
//不需要认证,直接到第三步
if (AuthMethod != 0)
{
//处理认证
}
#region 处理命令阶段
byte[] Commandbuffer = new byte[1024];
await token.ClientStream.ReadAsync(Commandbuffer, 0, Commandbuffer.Length);
Socks5CommandType socks5CommandType = (Socks5CommandType)Commandbuffer[1];
Socks5AddressType addressType = (Socks5AddressType)Commandbuffer[3];
//获取目标服务器IP和端口
string targetHost = null;
ushort TargetPort = 0;
switch (addressType)
{
case Socks5AddressType.IPV4:
{
targetHost = new IPAddress(Commandbuffer.AsSpan(4, 4)).ToString();
TargetPort = BinaryPrimitives.ReadUInt16BigEndian(Commandbuffer.AsSpan(8, 2));
}
break;
case Socks5AddressType.Domain:
{
byte length = Commandbuffer[4];
targetHost = Encoding.UTF8.GetString(Commandbuffer.AsSpan(5, length));
TargetPort = BinaryPrimitives.ReadUInt16BigEndian(Commandbuffer.AsSpan(5 + length, 2));
}
break;
case Socks5AddressType.IPV6:
{
targetHost = new IPAddress(Commandbuffer.AsSpan(4, 16)).ToString();
TargetPort = BinaryPrimitives.ReadUInt16BigEndian(Commandbuffer.AsSpan(20, 2));
}
break;
}
byte[] portArray = BitConverter.GetBytes(port);
// 如果您需要确保使用特定的字节顺序(例如,大端或小端),可以使用以下方式进行转换:
if (BitConverter.IsLittleEndian)
{
// 如果当前系统是小端字节序,反转字节数组以得到大端字节序
Array.Reverse(portArray);
}
if (socks5CommandType == Socks5CommandType.Connect)
{
try
{
token.TargetClient = new TcpClient(targetHost, TargetPort);
token.TargetStream = token.TargetClient.GetStream();
// 返回客户端连接成功
await token.ClientStream.WriteAsync(new byte[] { 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, portArray[0], portArray[1] }, 0, 10);
}
catch (SocketException ex)
{
//访问目标服务器失败则不断开客户端和代理服务器的连接,仅返回失败原因
if (SocketError.HostNotFound == ex.SocketErrorCode)
{
// 返回客户端连接失败原因:目标服务器无法访问(主机名无效)
await token.ClientStream.WriteAsync(new byte[] { 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, portArray[0], portArray[1] }, 0, 10);
}
else
{
// 返回客户端连接失败原因:连接目标服务器被拒绝
await token.ClientStream.WriteAsync(new byte[] { 0x05, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, portArray[0], portArray[1] }, 0, 10);
}
}
}
else
{
// 返回客户端连接失败原因:代理服务器规则集不允许连接
await token.ClientStream.WriteAsync(new byte[] { 0x05, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, portArray[0], portArray[1] }, 0, 10);
//关闭和客户端的连接通道
token.Client.Close();
token.Client.Dispose();
}
#endregion
#region 处理转发
if (token.Client.Connected && token.TargetClient.Connected)
{
//两个Task不停地互相转发数据
await Task.WhenAny(
PipeAsync(token.ClientStream, token.TargetStream),
PipeAsync(token.TargetStream, token.ClientStream)
);
}
#endregion
}
else
{
token.Client.Close();
token.Client.Dispose();
}
}
catch (Exception e)
{
Console.WriteLine($"HandleClientAsync的异常:{e.Message}");
}
}
///
/// 转发数据
///
/// 源Stream
/// 目标Stream
///
static async Task PipeAsync(NetworkStream source, NetworkStream target)
{
byte[] buffer = new byte[1024];
int bytesRead;
try
{
//一直循环,在没有stream传输的时候等待
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await target.WriteAsync(buffer, 0, bytesRead);
}
}
catch (Exception e)
{
Console.WriteLine($"PipeAsync的异常:{e.Message}");
}
}
}
}
SOCKS5代理连接到目标网站的过程通常包括以下四个阶段: