Skip to content

Commit bac7aa4

Browse files
committed
Add socks5server class
1 parent e47bc93 commit bac7aa4

File tree

9 files changed

+294
-4
lines changed

9 files changed

+294
-4
lines changed

Socksy.Core.Test/Fixtures.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Socksy.Core.Test
8+
{
9+
internal static class Fixtures
10+
{
11+
private static int _lastTempPort = 54678;
12+
13+
public static IPEndPoint GetLocalendpointWithTemplatePortNumber()
14+
{
15+
return new IPEndPoint(IPAddress.Loopback, _lastTempPort++);
16+
}
17+
}
18+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using Socksy.Core.Network;
2+
3+
namespace Socksy.Core.Test.Network;
4+
5+
public class NetHelperTests
6+
{
7+
private static int _lastPort = 54000;
8+
private static int TempPort => _lastPort++;
9+
10+
[Fact]
11+
public async void CreateTcpConnectionTo_WithValidEndpoint_EstablishesTcpConnection()
12+
{
13+
//Arrange
14+
var randomPort = TempPort;
15+
var localIP = IPAddress.Loopback;
16+
var endpoint = new IPEndPoint(localIP, randomPort);
17+
var listener = new TcpListener(endpoint);
18+
19+
//Act
20+
listener.Start();
21+
var listenerAcceptingClientResult = listener.AcceptTcpClientAsync();
22+
23+
var client = await NetHelper.CreateTcpConnectionTo(endpoint);
24+
25+
_ = await listenerAcceptingClientResult;
26+
27+
28+
//Assert
29+
Assert.True(client.Connected);
30+
}
31+
32+
[Fact]
33+
public async void CreateTcpConnectionTo_WithValidEndpointAndEnoughTimeout_EstablishesTcpConnection()
34+
{
35+
//Arrange
36+
var timeout = 2000;
37+
var randomPort = TempPort;
38+
var localIP = IPAddress.Loopback;
39+
var endpoint = new IPEndPoint(localIP, randomPort);
40+
var listener = new TcpListener(endpoint);
41+
42+
//Act
43+
listener.Start();
44+
var listenerAcceptingClientResult = listener.AcceptTcpClientAsync();
45+
46+
var client = await NetHelper.CreateTcpConnectionTo(endpoint, timeout);
47+
48+
_ = await listenerAcceptingClientResult;
49+
50+
51+
//Assert
52+
Assert.True(client.Connected);
53+
}
54+
55+
[Fact]
56+
public async void CreateTcpConnectionTo_AfterTimeoutReaches_ThrowsException()
57+
{
58+
//Arrange
59+
var timeout = 10;
60+
var randomPort = 54679;
61+
var localIP = IPAddress.Loopback;
62+
var endpoint = new IPEndPoint(localIP, randomPort);
63+
64+
//Act
65+
var sut = () => NetHelper.CreateTcpConnectionTo(endpoint, timeout);
66+
67+
//Assert
68+
await Assert.ThrowsAsync<OperationCanceledException>(sut);
69+
}
70+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
namespace Socksy.Core.Test;
2+
3+
public class Socks5ServerTests
4+
{
5+
[Fact]
6+
public void ServerListensForIncommingConnectionsAfterBeingStarted()
7+
{
8+
//Arrange
9+
var endpoint = Fixtures.GetLocalendpointWithTemplatePortNumber();
10+
var sut = new Socks5Server(endpoint);
11+
12+
//Act
13+
sut.Start();
14+
15+
//Assert
16+
Assert.True(sut.IsListening);
17+
}
18+
19+
[Fact]
20+
public async void ServerStopsAfterCallingStopMethod()
21+
{
22+
//Arrange
23+
var delayMs = 50;
24+
var endpoint = Fixtures.GetLocalendpointWithTemplatePortNumber();
25+
var sut = new Socks5Server(endpoint);
26+
27+
//Act
28+
sut.Start();
29+
_ = Task.Delay(delayMs).ContinueWith((t) => sut.Stop());
30+
await sut;
31+
32+
//Assert
33+
Assert.False(sut.IsListening);
34+
}
35+
36+
[Fact]
37+
public async void ServerStopsAfterBeingDisposed()
38+
{
39+
//Arrange
40+
var delayMs = 50;
41+
var endpoint = Fixtures.GetLocalendpointWithTemplatePortNumber();
42+
var sut = new Socks5Server(endpoint);
43+
44+
//Act
45+
sut.Start();
46+
_ = Task.Delay(delayMs).ContinueWith((t) => sut.Dispose());
47+
await sut;
48+
49+
//Assert
50+
Assert.False(sut.IsListening);
51+
}
52+
}

Socksy.Core.Test/Socksy.Core.Test.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1616
<PrivateAssets>all</PrivateAssets>
1717
</PackageReference>
18-
<PackageReference Include="coverlet.collector" Version="3.1.2">
19-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
20-
<PrivateAssets>all</PrivateAssets>
21-
</PackageReference>
2218
</ItemGroup>
2319

2420
<ItemGroup>

Socksy.Core/Network/NetHelper.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace Socksy.Core.Network;
2+
3+
internal static class NetHelper
4+
{
5+
internal static async Task<TcpClient> CreateTcpConnectionTo(IPEndPoint endPoint)
6+
{
7+
var tcpClient = new TcpClient();
8+
await tcpClient.ConnectAsync(endPoint);
9+
return tcpClient;
10+
}
11+
12+
internal static async Task<TcpClient> CreateTcpConnectionTo(IPEndPoint endPoint, int timeOut)
13+
{
14+
using var ctSource = new CancellationTokenSource();
15+
ctSource.CancelAfter(TimeSpan.FromMilliseconds(timeOut));
16+
var tcpClient = new TcpClient();
17+
await tcpClient.ConnectAsync(endPoint, ctSource.Token);
18+
return tcpClient;
19+
}
20+
21+
internal static IPAddress? ResolveHost(string hostName)
22+
{
23+
var res = Dns.GetHostEntry(hostName);
24+
return res.AddressList.FirstOrDefault();
25+
}
26+
27+
internal static IPAddress? ResolveHost(string hostName, AddressFamily addressFamily)
28+
{
29+
var res = Dns.GetHostEntry(hostName, addressFamily);
30+
return res.AddressList.FirstOrDefault();
31+
}
32+
}

Socksy.Core/Network/TcpServer.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System.Runtime.CompilerServices;
2+
3+
namespace Socksy.Core.Network;
4+
5+
internal class TcpServer : IDisposable
6+
{
7+
private readonly TcpListener _listener;
8+
private readonly Func<TcpClient, Task> _onConnectionEstablished;
9+
private CancellationTokenSource? _cancellationTokenSource;
10+
private CancellationToken _cancellationToken;
11+
private bool _isListening;
12+
13+
public TcpServer(IPEndPoint localEndPoint, Func<TcpClient, Task> onConnectionEstablished)
14+
{
15+
_listener = new TcpListener(localEndPoint);
16+
_onConnectionEstablished = onConnectionEstablished;
17+
_isListening = false;
18+
}
19+
20+
public bool IsListening => _isListening;
21+
22+
public void Start()
23+
{
24+
if (_isListening)
25+
return;
26+
27+
_cancellationTokenSource = new CancellationTokenSource();
28+
_cancellationToken = _cancellationTokenSource.Token;
29+
_listener.Start();
30+
_ = MainLoop();
31+
_isListening = true;
32+
}
33+
34+
public void Stop()
35+
{
36+
_cancellationTokenSource!.Cancel();
37+
_listener.Stop();
38+
_isListening = false;
39+
}
40+
41+
private async Task MainLoop()
42+
{
43+
while(!_cancellationToken.IsCancellationRequested)
44+
{
45+
var tcpClient = await _listener.AcceptTcpClientAsync(_cancellationToken);
46+
_ = _onConnectionEstablished(tcpClient);
47+
}
48+
}
49+
50+
public TaskAwaiter GetAwaiter()
51+
{
52+
TaskCompletionSource tcs = new TaskCompletionSource();
53+
_cancellationToken.Register(() => tcs.TrySetResult());
54+
return tcs.Task.GetAwaiter();
55+
}
56+
57+
public void Dispose()
58+
{
59+
Stop();
60+
}
61+
}

Socksy.Core/Socks5Server.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Socksy.Core.Network;
2+
using System.Buffers;
3+
using System.Runtime.CompilerServices;
4+
5+
namespace Socksy.Core;
6+
public class Socks5Server : IDisposable
7+
{
8+
private readonly TcpServer _server;
9+
10+
public Socks5Server(IPEndPoint localEndPoint)
11+
{
12+
_server = new TcpServer(localEndPoint, OnConnectionEstablished);
13+
}
14+
15+
public void Start()
16+
{
17+
_server.Start();
18+
}
19+
20+
public void Stop()
21+
{
22+
_server.Stop();
23+
}
24+
25+
public bool IsListening => _server.IsListening;
26+
27+
private async Task OnConnectionEstablished(TcpClient tcpClient)
28+
{
29+
await Task.CompletedTask;
30+
}
31+
32+
public TaskAwaiter GetAwaiter()
33+
{
34+
return _server.GetAwaiter();
35+
}
36+
37+
38+
public void Dispose()
39+
{
40+
_server.Dispose();
41+
}
42+
}

Socksy/Program.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,17 @@
11
// See https://aka.ms/new-console-template for more information
2+
using Socksy.Core;
3+
using System.Net;
4+
25
Console.WriteLine("Hello, World!");
6+
Socks5Server server = new Socks5Server(IPEndPoint.Parse("127.0.0.1:3050"));
7+
server.Start();
8+
9+
Console.CancelKeyPress += (s, e) =>
10+
{
11+
server.Stop();
12+
};
13+
14+
await server;
15+
16+
Console.WriteLine("OK");
17+
Console.ReadLine();

Socksy/Socksy.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@
1111
<InternalsVisibleTo Include="$(AssemblyName).Test" />
1212
</ItemGroup>
1313

14+
<ItemGroup>
15+
<ProjectReference Include="..\Socksy.Core\Socksy.Core.csproj" />
16+
</ItemGroup>
17+
1418
</Project>

0 commit comments

Comments
 (0)