Skip to content

Commit

Permalink
Fix: PacketCapture Include Filter;
Browse files Browse the repository at this point in the history
Fix Linux Install;
Remove TCP Nagling
  • Loading branch information
trudyhood committed Jan 21, 2022
1 parent 7535078 commit 5384a9e
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 143 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Upcoming
### Client
* Fix: Error when setting PacketCapture include filter

### Server
* Fix: Always show TcpHost is already Started!
* Fix: Linux install on some distribution

# v2.4.292
### Client
Expand Down
2 changes: 0 additions & 2 deletions VpnHood.Client/TcpProxyHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ private async Task ProcessClient(TcpClient orgTcpClient, CancellationToken cance
try
{
// config tcpOrgClient
// todo: tcpOrgClient.NoDelay = true;
Client.SocketFactory.SetKeepAlive(orgTcpClient.Client, true);

// get original remote from NAT
Expand Down Expand Up @@ -230,7 +229,6 @@ await Client.AddPassthruTcpStream(
tcpProxyClientStream.TcpClient.ReceiveBufferSize = orgTcpClient.ReceiveBufferSize;
tcpProxyClientStream.TcpClient.SendBufferSize = orgTcpClient.SendBufferSize;
tcpProxyClientStream.TcpClient.SendTimeout = orgTcpClient.SendTimeout;
// todo: tcpProxyClientStream.TcpClient.NoDelay = true;
Client.SocketFactory.SetKeepAlive(tcpProxyClientStream.TcpClient.Client, true);

// read the response
Expand Down
11 changes: 6 additions & 5 deletions VpnHood.Client/VpnHoodClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,12 @@ internal async Task AddPassthruTcpStream(TcpClientStream orgTcpClientStream, IPE
{
// config Tcp
SocketFactory.SetKeepAlive(orgTcpClientStream.TcpClient.Client, true);
//todo: orgTcpClientStream.TcpClient.NoDelay = true;

var tcpClient = SocketFactory.CreateTcpClient(hostEndPoint.AddressFamily);
tcpClient.ReceiveBufferSize = orgTcpClientStream.TcpClient.ReceiveBufferSize;
tcpClient.SendBufferSize = orgTcpClientStream.TcpClient.SendBufferSize;
tcpClient.SendTimeout = orgTcpClientStream.TcpClient.SendTimeout;
SocketFactory.SetKeepAlive(tcpClient.Client, true);
//todo: tcpClient.NoDelay = true;


// connect to host
_packetCapture.ProtectSocket(tcpClient.Client);
Expand Down Expand Up @@ -248,7 +245,12 @@ private void ConfigPacketFilter(IPEndPoint hostEndPoint)
var includeNetworks = new List<IpNetwork>();
if (PacketCaptureIncludeIpRanges?.Length > 0)
{
includeNetworks.AddRange(IpNetwork.FromIpRange(PacketCaptureIncludeIpRanges));
// remove hostEndPoint from include
var exclude = IpRange.Invert(PacketCaptureIncludeIpRanges).ToList();
exclude.Add(new IpRange(hostEndPoint.Address));
var include = IpRange.Invert(exclude.ToArray());

includeNetworks.AddRange(IpNetwork.FromIpRange(include));
}
else
{
Expand Down Expand Up @@ -594,7 +596,6 @@ internal async Task<TcpClientStream> GetSslConnectionToServer(EventId eventId, C
if (HostEndPoint == null)
throw new InvalidOperationException($"{nameof(HostEndPoint)} is not initialized!");
var tcpClient = SocketFactory.CreateTcpClient(HostEndPoint.AddressFamily);
//todo: tcpClient.Client.NoDelay = true;

try
{
Expand Down
4 changes: 2 additions & 2 deletions VpnHood.Server.App.Net/Install/install-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ After=network.target
[Service]
Type=simple
ExecStart=sh -c \"dotnet '$destinationPath/launcher/run.dll' -launcher:noLaunchAfterUpdate && sleep 10s\"
ExecStop=sh -c \"dotnet '$destinationPath/launcher/run.dll' stop\"
ExecStart=/bin/sh -c \"dotnet '$destinationPath/launcher/run.dll' -launcher:noLaunchAfterUpdate && sleep 10s\"
ExecStop=/bin/sh -c \"dotnet '$destinationPath/launcher/run.dll' stop\"
TimeoutStartSec=0
Restart=always
RestartSec=2
Expand Down
1 change: 0 additions & 1 deletion VpnHood.Server/TcpHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,6 @@ private async Task ProcessTcpProxyChannel(TcpClientStream tcpClientStream, Cance
$"Connecting to the requested endpoint. RequestedEP: {VhLogger.Format(request.DestinationEndPoint)}");

tcpClient2 = _socketFactory.CreateTcpClient(request.DestinationEndPoint.AddressFamily);
//todo: tcpClient2.NoDelay = true;
if (TcpTimeout != TimeSpan.Zero && TcpTimeout != Timeout.InfiniteTimeSpan)
_socketFactory.SetKeepAlive(tcpClient2.Client, true, TcpTimeout, TimeSpan.FromSeconds(10), 6);

Expand Down
267 changes: 135 additions & 132 deletions VpnHood.Tunneling/TcpProxyChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,164 +6,167 @@
using System.Threading.Tasks;
using VpnHood.Common.Logging;

namespace VpnHood.Tunneling
namespace VpnHood.Tunneling;

public class TcpProxyChannel : IChannel
{
public class TcpProxyChannel : IChannel
private readonly int _orgStreamReadBufferSize;
private readonly TcpClientStream _orgTcpClientStream;
private readonly int _tunnelStreamReadBufferSize;
private readonly TcpClientStream _tunnelTcpClientStream;
private readonly TimeSpan _tcpTimeout;
private readonly int BufferSize_Max = 0x14000 * 2;
private bool _disposed;

public TcpProxyChannel(TcpClientStream orgTcpClientStream, TcpClientStream tunnelTcpClientStream,
TimeSpan tcpTimeout, int orgStreamReadBufferSize = 0, int tunnelStreamReadBufferSize = 0)
{
private readonly object _lockCleanup = new();
private readonly int _orgStreamReadBufferSize;
private readonly TcpClientStream _orgTcpClientStream;
private readonly int _tunnelStreamReadBufferSize;
private readonly TcpClientStream _tunnelTcpClientStream;
private readonly TimeSpan _tcpTimeout;
private readonly int BufferSize_Max = 0x14000 * 2;
private bool _disposed;

public TcpProxyChannel(TcpClientStream orgTcpClientStream, TcpClientStream tunnelTcpClientStream,
TimeSpan tcpTimeout, int orgStreamReadBufferSize = 0, int tunnelStreamReadBufferSize = 0)
{
_tcpTimeout = tcpTimeout;
_tcpTimeout = tcpTimeout;

_orgTcpClientStream = orgTcpClientStream ?? throw new ArgumentNullException(nameof(orgTcpClientStream));
_tunnelTcpClientStream = tunnelTcpClientStream ?? throw new ArgumentNullException(nameof(tunnelTcpClientStream));
if (orgStreamReadBufferSize == 0) orgStreamReadBufferSize = TunnelUtil.StreamBufferSize;
if (tunnelStreamReadBufferSize == 0) tunnelStreamReadBufferSize = TunnelUtil.StreamBufferSize;
_orgTcpClientStream = orgTcpClientStream ?? throw new ArgumentNullException(nameof(orgTcpClientStream));
_tunnelTcpClientStream = tunnelTcpClientStream ?? throw new ArgumentNullException(nameof(tunnelTcpClientStream));

if (orgStreamReadBufferSize == 0) orgStreamReadBufferSize = TunnelUtil.StreamBufferSize;
if (tunnelStreamReadBufferSize == 0) tunnelStreamReadBufferSize = TunnelUtil.StreamBufferSize;

_orgStreamReadBufferSize = orgStreamReadBufferSize > 0 && orgStreamReadBufferSize <= BufferSize_Max
? orgStreamReadBufferSize
: throw new ArgumentOutOfRangeException($"Value must greater than 0 and less than {BufferSize_Max}", orgStreamReadBufferSize, nameof(orgStreamReadBufferSize));
_orgStreamReadBufferSize = orgStreamReadBufferSize > 0 && orgStreamReadBufferSize <= BufferSize_Max
? orgStreamReadBufferSize
: throw new ArgumentOutOfRangeException($"Value must greater than 0 and less than {BufferSize_Max}", orgStreamReadBufferSize, nameof(orgStreamReadBufferSize));

_tunnelStreamReadBufferSize = tunnelStreamReadBufferSize > 0 && tunnelStreamReadBufferSize <= BufferSize_Max
? tunnelStreamReadBufferSize
: throw new ArgumentOutOfRangeException($"Value must greater than 0 and less than {BufferSize_Max}", tunnelStreamReadBufferSize, nameof(tunnelStreamReadBufferSize));
}
_tunnelStreamReadBufferSize = tunnelStreamReadBufferSize > 0 && tunnelStreamReadBufferSize <= BufferSize_Max
? tunnelStreamReadBufferSize
: throw new ArgumentOutOfRangeException($"Value must greater than 0 and less than {BufferSize_Max}", tunnelStreamReadBufferSize, nameof(tunnelStreamReadBufferSize));

public event EventHandler<ChannelEventArgs>? OnFinished;
public bool Connected { get; private set; }
public long SentByteCount { get; private set; }
public long ReceivedByteCount { get; private set; }
public DateTime LastActivityTime { get; private set; } = DateTime.Now;
// We don't know about client or server delay, so lets pessimistic
orgTcpClientStream.TcpClient.NoDelay = true;
tunnelTcpClientStream.TcpClient.NoDelay = true;
}

public event EventHandler<ChannelEventArgs>? OnFinished;
public bool Connected { get; private set; }
public long SentByteCount { get; private set; }
public long ReceivedByteCount { get; private set; }
public DateTime LastActivityTime { get; private set; } = DateTime.Now;

public async Task Start()
public async Task Start()
{
Connected = true;
try
{
Connected = true;
try
{
var task1 = CopyToAsync(_tunnelTcpClientStream.Stream, _orgTcpClientStream.Stream, false, _tunnelStreamReadBufferSize, CancellationToken.None); // read
var task2 = CopyToAsync(_orgTcpClientStream.Stream, _tunnelTcpClientStream.Stream, true, _orgStreamReadBufferSize, CancellationToken.None); //write
await Task.WhenAll(task1, task2);
}
finally
{
Dispose();
OnFinished?.Invoke(this, new ChannelEventArgs(this));
}
var task1 = CopyToAsync(_tunnelTcpClientStream.Stream, _orgTcpClientStream.Stream, false, _tunnelStreamReadBufferSize, CancellationToken.None); // read
var task2 = CopyToAsync(_orgTcpClientStream.Stream, _tunnelTcpClientStream.Stream, true, _orgStreamReadBufferSize, CancellationToken.None); //write
await Task.WhenAll(task1, task2);
}

private static bool IsConnectionValid(Socket socket)
finally
{
try
{
return !socket.Poll(0, SelectMode.SelectError);
}
catch
{
return false;
}
Dispose();
OnFinished?.Invoke(this, new ChannelEventArgs(this));
}
}

public void CheckConnection()
private static bool IsConnectionValid(Socket socket)
{
try
{
if (_disposed)
return;
return !socket.Poll(0, SelectMode.SelectError);
}
catch
{
return false;
}
}

public void CheckConnection()
{
if (_disposed)
return;

if (LastActivityTime > DateTime.Now - _tcpTimeout)
return;
if (LastActivityTime > DateTime.Now - _tcpTimeout)
return;

if (!IsConnectionValid(_orgTcpClientStream.TcpClient.Client) ||
!IsConnectionValid(_tunnelTcpClientStream.TcpClient.Client))
{
VhLogger.Instance.LogInformation(GeneralEventId.StreamChannel,
$"Disposing an {VhLogger.FormatTypeName(this)} due to its error state. Timeout: {_tcpTimeout.TotalMinutes} minutes.");
Dispose();
}
if (!IsConnectionValid(_orgTcpClientStream.TcpClient.Client) ||
!IsConnectionValid(_tunnelTcpClientStream.TcpClient.Client))
{
VhLogger.Instance.LogInformation(GeneralEventId.StreamChannel,
$"Disposing an {VhLogger.FormatTypeName(this)} due to its error state. Timeout: {_tcpTimeout.TotalMinutes} minutes.");
Dispose();
}
}

public void Dispose()
{
if (_disposed) return;
_disposed = true;
public void Dispose()
{
if (_disposed) return;
_disposed = true;

Connected = false;
_orgTcpClientStream.Dispose();
_tunnelTcpClientStream.Dispose();
}

Connected = false;
_orgTcpClientStream.Dispose();
_tunnelTcpClientStream.Dispose();
private async Task CopyToAsync(Stream source, Stream destination, bool isSendingOut, int bufferSize,
CancellationToken cancellationToken)
{
try
{
await CopyToInternalAsync(source, destination, isSendingOut, bufferSize, cancellationToken);
}
catch (Exception ex)
{
// Dispose if any side throw an exception
var message = isSendingOut ? "to" : "from";
VhLogger.Instance.LogInformation(GeneralEventId.Tcp, $"TcpProxyChannel: Error in copying {message} tunnel. Message: {ex.Message}");
Dispose();
}
}

private async Task CopyToAsync(Stream source, Stream destination, bool isSendingOut, int bufferSize,
CancellationToken cancellationToken)
private async Task CopyToInternalAsync(Stream source, Stream destination, bool isSendingOut, int bufferSize,
CancellationToken cancellationToken)
{
var doubleBuffer = false; //i am not sure it could help!

// Microsoft Stream Source Code:
// We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
// improvement in Copy performance.
// 0x14000 recommended by microsoft for copying buffers
if (bufferSize > BufferSize_Max)
throw new ArgumentException($"Buffer is too big, maximum supported size is {BufferSize_Max}",
nameof(bufferSize));

// <<----------------- the MOST memory consuming in the APP! >> ----------------------
var readBuffer = new byte[bufferSize];
var writeBuffer = doubleBuffer ? new byte[readBuffer.Length] : null;
Task? writeTask = null;
while (!cancellationToken.IsCancellationRequested)
{
try
// read from source
var bytesRead = await source.ReadAsync(readBuffer, 0, readBuffer.Length, cancellationToken);
if (writeTask != null)
await writeTask;

// check end of the stream
if (bytesRead == 0)
break;

// write to destination
if (writeBuffer != null)
{
await CopyToInternalAsync(source, destination, isSendingOut, bufferSize, cancellationToken);
Array.Copy(readBuffer, writeBuffer, bytesRead);
writeTask = destination.WriteAsync(writeBuffer, 0, bytesRead, cancellationToken);
}
catch (Exception ex)
else
{
// Dispose if any side throw an exception
var message = isSendingOut ? "to" : "from";
VhLogger.Instance.LogInformation(GeneralEventId.Tcp, $"TcpProxyChannel: Error in copying {message} tunnel. Message: {ex.Message}");
Dispose();
await destination.WriteAsync(readBuffer, 0, bytesRead, cancellationToken);
}
}

private async Task CopyToInternalAsync(Stream source, Stream destination, bool isSendingOut, int bufferSize,
CancellationToken cancellationToken)
{
var doubleBuffer = false; //i am not sure it could help!

// Microsoft Stream Source Code:
// We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
// improvement in Copy performance.
// 0x14000 recommended by microsoft for copying buffers
if (bufferSize > BufferSize_Max)
throw new ArgumentException($"Buffer is too big, maximum supported size is {BufferSize_Max}",
nameof(bufferSize));

// <<----------------- the MOST memory consuming in the APP! >> ----------------------
var readBuffer = new byte[bufferSize];
var writeBuffer = doubleBuffer ? new byte[readBuffer.Length] : null;
Task? writeTask = null;
while (!cancellationToken.IsCancellationRequested)
{
// read from source
var bytesRead = await source.ReadAsync(readBuffer, 0, readBuffer.Length, cancellationToken);
if (writeTask != null)
await writeTask;

// check end of the stream
if (bytesRead == 0)
break;

// write to destination
if (writeBuffer != null)
{
Array.Copy(readBuffer, writeBuffer, bytesRead);
writeTask = destination.WriteAsync(writeBuffer, 0, bytesRead, cancellationToken);
}
else
{
await destination.WriteAsync(readBuffer, 0, bytesRead, cancellationToken);
}

// calculate transferred bytes
if (!isSendingOut)
ReceivedByteCount += bytesRead;
else
SentByteCount += bytesRead;

// set LastActivityTime as some data delegated
LastActivityTime = DateTime.Now;
}
// calculate transferred bytes
if (!isSendingOut)
ReceivedByteCount += bytesRead;
else
SentByteCount += bytesRead;

// set LastActivityTime as some data delegated
LastActivityTime = DateTime.Now;
}
}
}
2 changes: 1 addition & 1 deletion VpnHood.ZTest/Tests/ClientServerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ public void Reset_tcp_connection_immediately_after_vpn_connected()
using var server = TestHelper.CreateServer();
var token = TestHelper.CreateAccessToken(server);

using TcpClient tcpClient = new(TestHelper.TEST_HttpsUri1.Host, 443) { NoDelay = true };
using TcpClient tcpClient = new(TestHelper.TEST_HttpsUri1.Host, 443);
using var stream = tcpClient.GetStream();

// create client
Expand Down

0 comments on commit 5384a9e

Please sign in to comment.