Shadowsocks-Net is a cross-platform version of Shadowsocks developed in C# (.NET Core).
Shadowsocks-Net plans to release multiple versions, the feature comparison is shown in the table below.
Completion of development |
Version | ss-local | ss-remote | Local HTTP1 |
Obfuscation | URL/IP filtering |
Server scheduling strategy |
GUI |
---|---|---|---|---|---|---|---|---|
90% | Minimal- cross-platform |
√ | √ | √ | ||||
10% | Windows | √ | √ | √ | √ | √ | √ | |
1% | Linux | √ | √ | √ | √ | √ |
The Minimal vesion is available for testing now, supported encryption algorithms:
aes-256-gcm, aes-192-gcm, aes-128-gcm, chacha20-ietf-poly1305, xchacha20-ietf-poly1305
Shadowsocks-Net encapsulates the network programming part simply so that the upper layer can focus on the socks5 protocol.
Since Shadowsocks mainly implements socks5 protocol, the upper layer code is now very thin.
Socks5 generally only do two things: 1. negotiation, 2. forwarding. Shadowsocks-Net tries to make developing Shadowsocks in C# more enjoyable and easier.
Master branch is a classical implementation of Shadowsocks. The pluggable-tunnel branch has a slightly different architecture, which gives flexibility to integrate multiplexed tunnels.
- Implement the unified encryption interface
IShadowsocksAeadCipher
orIShadowsocksStreamCipher
class MyCipher : IShadowsocksAeadCipher
{
//implementation
}
- Mark with
Cipher
attribute
[Cipher("my-cipher-name")]
class MyCipher : IShadowsocksAeadCipher
{
//implementation
}
MyCipher
is recognized now.
Obfuscation is similar to encryption, in Shadowsocks-Net, it works as a filter. The logic of obfuscation can be more complicated than encryption.
But as the other parts have been encapsulated, now only need to focus on reading and writing the network stream, and implement a ClientFilter
.
public interface IClientReaderFilter
{
ClientFilterResult OnReading(ClientFilterContext filterContext);
}
public interface IClientWriterFilter
{
ClientFilterResult OnWriting(ClientFilterContext filterContext);
}
Encryption, obfuscation and UDP encapsulation are all implemented by filters in Shadowsocks-Net. Filters are pluggable. Therefore, filters can also be used to interpret custom protocols.
The following filter inserts four bytes 0x12
, 0x34
, 0xAB
, 0xCD
into the beginning of the data each time before sending,
and correspondingly skips the first four bytes when receiving:
class TestClientFilter : ClientFilter
{
public override ClientFilterResult OnWriting(ClientFilterContext ctx)
{
byte[] data = ctx.Memory.ToArray();
byte[] newData = new byte[data.Length + 4];
newData[0] = 0x12;
newData[1] = 0x34;
newData[2] = 0xAB;
newData[3] = 0xCD;
Array.Copy(data, 0, newData, 4, data.Length);
return new ClientFilterResult(ctx.Client, newData, ...);
}
public override ClientFilterResult OnReading(ClientFilterContext ctx)
{
byte[] data = ctx.Memory.ToArray();
byte[] newData = data.Skip(4).ToArray();
return new ClientFilterResult(ctx.Client, newData, ...);
}
}
- Choose the proper
Category
andPriority
, which determine the order of the filter in the filter chain. The framework has preseted several categories:
public enum ClientFilterCategory
{
Obfuscation = 1,
Cipher = 2,
Encapsulation = 3,
Custom = 4
}
- Inherit
ClientFilter
public abstract class ClientFilter
{
public abstract ClientFilterResult OnReading(ClientFilterContext filterContext);
public abstract ClientFilterResult OnWriting(ClientFilterContext filterContext);
}
- Add filter to pipe
DuplexPipe.AddFilter(IClient client, IClientFilter filter);
A typical case: UdpEncapsulationFilter.cs.
As interfaces have already been abstracted by design, other communication protocols can now also be integrated.
Just implement IClient
and IServer
, no need to change other parts.
IClient
and IServer
are also simple:
public partial interface IClient : IPeer
{
ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default);
ValueTask<int> WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default);
IPEndPoint LocalEndPoint { get; }
void Close();
event EventHandler<ClientEventArgs> Closing;
}
public interface IServer : IPeer
{
void Listen();
void StopListen();
}
public interface IServer<TClient> : IServer
where TClient : IClient
{
Task<TClient> Accept();
}
For instance, in order to integrate the KCP protocol, we implemented KcpClient
and KcpServer
,
then Shadowsocks-Net uses KCP as the transport layer protocol.
Visual Studio 2019 Community, .NET Framework 4.6 (temporarily used to design winform), .NET Standard 2.1 & .NET 5.0.
Simply build the entire solution in Visual Studio.
This project is currently written 100% in C#, the core is .NET Standard 2.1 class library.
- ☑ Core rewrite
- ☐ Windows end
- ☐ Unified filter rule
- ☐ Target IP, domain check
- ☐ Linux version
Similar to using other versions of Shadowsocks.
The Minimal version has been tested on Windows and Debian10 x64, the parameters are configured through configuration file.
Take an example on Windows:
On server side, edit config.json
, run shadowsocks-net-remote.exe
:
{
//"server_host": null,
"server_port": 6666,
"use_ipv6": false,
"timeout": 5,
"password": "password1",
"method": "aes-128-gcm"
}
On local side, edit servers.json
and app-config.json
, run shadowsocks-net-local.exe
:
[
{
"remarks": "Test Server",
"server": "10.10.10.102",
"server_port": 6666,
"password": "password1",
"method": "aes-128-gcm",
"obfs": null,
"category": null
}
]
{
"Socks5Proxy": {
"Port": 2080,
"UseIPv6Address": false,
"UseLoopbackAddress": true
},
"HttpProxy": {
"Port": 8080,
"UseIPv6Address": false,
"UseLoopbackAddress": true
}
}
Resemble on Linux. Installing .NET Core Portal.
There is still a lot of code waiting to be added.
Local HTTP: Transform socks5 into HTTP.