Skip to content
Merged

wip #20

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Netorrent.Tests/Tracker/TrackerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.Extensions.Logging.Abstractions;
using Netorrent.Extensions;
using Netorrent.Tests.Fakes;
using Netorrent.TorrentFile;
using Netorrent.Tracker;
using Netorrent.Tracker.Http;
using Netorrent.Tracker.Udp;
Expand Down Expand Up @@ -207,6 +208,7 @@ public async Task Should_get_peers_from_tracker_client(CancellationToken cancell
await using var trackerClient = new TrackerClient(
httpTrackerHandler,
udptrackerManager,
UsedAdressProtocol.Dual,
1,
new(3),
new(),
Expand Down
17 changes: 13 additions & 4 deletions Netorrent/Extensions/TcpListenerExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
using System.Net;
using System.Net.Sockets;
using Netorrent.TorrentFile;

namespace Netorrent.Extensions;

internal static class TcpListenerExtensions
{
extension(TcpListener)
{
public static TcpListener GetFreeTcpListener()
public static TcpListener GetFreeTcpListener(
UsedAdressProtocol usedAdressProtocol,
int port = 0
)
{
var listener = new TcpListener(IPAddress.IPv6Any, 0);
listener.Server.DualMode = true;
listener.Start();
var ipAddress = usedAdressProtocol.ToIpAddress();
var listener = new TcpListener(ipAddress, port);

if (usedAdressProtocol == UsedAdressProtocol.Dual)
{
listener.Server.DualMode = true;
}

return listener;
}
}
Expand Down
18 changes: 14 additions & 4 deletions Netorrent/Extensions/UdpClientExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
using System.Net;
using System.Net.Sockets;
using Netorrent.TorrentFile;

namespace Netorrent.Extensions;

internal static class UdpClientExtensions
{
extension(UdpClient)
{
public static UdpClient GetFreeUdpClient()
public static UdpClient GetFreeUdpClient(
UsedAdressProtocol usedAdressProtocol,
int port = 0
)
{
var udpClient = new UdpClient(AddressFamily.InterNetworkV6);
udpClient.Client.DualMode = true;
udpClient.Client.Bind(new IPEndPoint(IPAddress.IPv6Any, 0));
var ipAdress = usedAdressProtocol.ToIpAddress();
var udpClient = new UdpClient(ipAdress.AddressFamily);

if (usedAdressProtocol == UsedAdressProtocol.Dual)
{
udpClient.Client.DualMode = true;
}

udpClient.Client.Bind(new IPEndPoint(ipAdress, port));
return udpClient;
}
}
Expand Down
33 changes: 33 additions & 0 deletions Netorrent/Extensions/UsedAdressProtocolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Net;
using System.Net.Sockets;
using Netorrent.TorrentFile;

namespace Netorrent.Extensions;

internal static class UsedAdressProtocolExtensions
{
extension(UsedAdressProtocol usedAdressProtocol)
{
public IPAddress ToIpAddress() =>
usedAdressProtocol switch
{
UsedAdressProtocol.Ipv4 => IPAddress.Any,
UsedAdressProtocol.Ipv6 => IPAddress.IPv6Any,
UsedAdressProtocol.Dual => IPAddress.IPv6Any,
_ => throw new ArgumentOutOfRangeException(nameof(usedAdressProtocol)),
};

public AddressFamily[] ToAddressFamily() =>
usedAdressProtocol switch
{
UsedAdressProtocol.Ipv4 => [AddressFamily.InterNetwork],
UsedAdressProtocol.Ipv6 => [AddressFamily.InterNetworkV6],
UsedAdressProtocol.Dual =>
[
AddressFamily.InterNetwork,
AddressFamily.InterNetworkV6,
],
_ => throw new ArgumentOutOfRangeException(nameof(usedAdressProtocol)),
};
}
}
12 changes: 6 additions & 6 deletions Netorrent/P2P/Tcp/TcpPeersListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

namespace Netorrent.P2P.Tcp;

internal class TcpPeersListener(PeerId peerId, ILogger logger) : IAsyncDisposable
internal class TcpPeersListener(PeerId peerId, TcpListener tcpListener, ILogger logger)
: IAsyncDisposable
{
private readonly TcpListener _tcpListener = TcpListener.GetFreeTcpListener();
private readonly ConcurrentDictionary<
ReadOnlyMemory<byte>,
PeersClient
> _peersClientByInfoHash = new(ReadOnlyMemoryEqualityComparer<byte>.Instance);

public IPEndPoint EndPoint => (IPEndPoint)_tcpListener.LocalEndpoint;
public IPEndPoint EndPoint => (IPEndPoint)tcpListener.LocalEndpoint;

private CancellationTokenSource? _cancellationTokenSource;
private Task? _runTask;
Expand All @@ -30,12 +30,12 @@ public void Start()

private async Task StartAsync()
{
_tcpListener.Start();
tcpListener.Start();
_cancellationTokenSource = new();

while (!_cancellationTokenSource.Token.IsCancellationRequested)
{
var tcpClient = await _tcpListener
var tcpClient = await tcpListener
.AcceptTcpClientAsync(_cancellationTokenSource.Token)
.ConfigureAwait(false);

Expand Down Expand Up @@ -112,7 +112,7 @@ public async ValueTask DisposeAsync()
await _runTask.ConfigureAwait(false);
}
catch { }
_tcpListener.Dispose();
tcpListener.Dispose();
}
}
}
2 changes: 2 additions & 0 deletions Netorrent/TorrentFile/Torrent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ internal Torrent(
string outputDirectory,
ILogger logger,
TcpPeersListener peersListener,
UsedAdressProtocol usedAdressProtocol,
IPAddress? forcedIp = null,
bool bitfieldInitialized = false,
Func<IPAddress, IPAddress>? peerIpProxy = null
Expand Down Expand Up @@ -98,6 +99,7 @@ [.. metaInfo.Info.Pieces.AsValueEnumerable().Chunk(20)]
_trackerClient = new TrackerClient(
new HttpTrackerHandler(httpClient),
trackerTransaction,
usedAdressProtocol,
peersListener.EndPoint.Port,
transferStatistics,
peerId,
Expand Down
18 changes: 15 additions & 3 deletions Netorrent/TorrentFile/TorrentClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,20 @@ public sealed class TorrentClient : IAsyncDisposable

public TorrentClient(Func<TorrentClientOptions, TorrentClientOptions>? action = null)
{
var options = new TorrentClientOptions(new(), NullLogger.Instance, null);
var options = new TorrentClientOptions(
new(),
NullLogger.Instance,
UsedAdressProtocol.Dual,
null
);
_options = action?.Invoke(options) ?? options;
_peersListener = new(_peerId, _options.Logger);
_peersListener = new(
_peerId,
TcpListener.GetFreeTcpListener(_options.UsedAdressProtocol),
_options.Logger
);
_trackerTransactionManager = new(
new UdpClientWrapper(UdpClient.GetFreeUdpClient()),
new UdpClientWrapper(UdpClient.GetFreeUdpClient(_options.UsedAdressProtocol)),
_options.Logger,
15.Seconds,
1.Seconds,
Expand Down Expand Up @@ -70,6 +79,7 @@ public async ValueTask<Torrent> LoadTorrentAsync(
Path.GetFullPath(outputDirectory),
_options.Logger,
_peersListener,
_options.UsedAdressProtocol,
peerIpProxy: _options.PeerIpProxy
);
_torrents.Add(torrent);
Expand All @@ -92,6 +102,7 @@ public Torrent LoadTorrent(MetaInfo metaInfo, string outputDirectory)
Path.GetFullPath(outputDirectory),
_options.Logger,
_peersListener,
_options.UsedAdressProtocol,
_options.ForcedIp,
peerIpProxy: _options.PeerIpProxy
);
Expand Down Expand Up @@ -138,6 +149,7 @@ await CreateMetaInfoFromPathAsync(
Path.GetFullPath(Path.GetDirectoryName(path) ?? ""),
_options.Logger,
_peersListener,
_options.UsedAdressProtocol,
_options.ForcedIp,
true,
peerIpProxy: _options.PeerIpProxy
Expand Down
26 changes: 25 additions & 1 deletion Netorrent/TorrentFile/TorrentClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,37 @@

namespace Netorrent.TorrentFile;

public enum UsedAdressProtocol
{
/// <summary>
/// Create sockets with Dual Mode for both Ipv4 and Ipv6
/// </summary>
/// <remarks>This is the default option</remarks>
Dual,

/// <summary>
/// Create sockets with Ipv4 only
/// </summary>
Ipv4,

/// <summary>
/// Create sockets with Ipv6 only
/// </summary>
Ipv6,
}

/// <summary>
/// Options for torrent client
/// </summary>
/// <param name="HttpClient">Http client used in tracker requests</param>
/// <param name="Logger">Logger used to debug</param>
/// <param name="ForcedIp">Forced ip to use in tracker requests</param>
public record TorrentClientOptions(HttpClient HttpClient, ILogger Logger, IPAddress? ForcedIp)
public record TorrentClientOptions(
HttpClient HttpClient,
ILogger Logger,
UsedAdressProtocol UsedAdressProtocol,
IPAddress? ForcedIp
)
{
/// <summary>
/// Only used for testing
Expand Down
24 changes: 19 additions & 5 deletions Netorrent/Tracker/TrackerClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
using Netorrent.Extensions;
using Netorrent.P2P.Messages;
using Netorrent.Statistics;
using Netorrent.TorrentFile;
using Netorrent.Tracker.Http;
using Netorrent.Tracker.Udp;
using ZLinq;

namespace Netorrent.Tracker;

internal class TrackerClient(
IHttpTrackerHandler httpTrackerHandler,
IUdpTrackerTransactionManager trackerTransactionManager,
UsedAdressProtocol usedAdressProtocol,
int port,
TransferStatistics transferStatistics,
PeerId peerId,
Expand Down Expand Up @@ -91,10 +94,17 @@ private async Task<UdpTracker[]> CreateUdpTrackers(Uri uri, CancellationToken ca
List<UdpTracker> udpTrackers = [];
var ips = await Dns.GetHostAdressesOrEmptyAsync(uri.Host, cancellationToken)
.ConfigureAwait(false);
var ipv4 = ips.FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork);
var ipv6 = ips.FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetworkV6);

if (ipv4 != default && uri.Port > 0)
var ipv4 = ips.AsValueEnumerable()
.FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork);
var ipv6 = ips.AsValueEnumerable()
.FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetworkV6);
var supportedAdressFamilies = usedAdressProtocol.ToAddressFamily();

if (
supportedAdressFamilies.Contains(AddressFamily.InterNetwork)
&& ipv4 != default
&& uri.Port > 0
)
{
var ipEndpoint = new IPEndPoint(ipv4, uri.Port);
var trackerv4 = new UdpTracker(
Expand All @@ -112,7 +122,11 @@ private async Task<UdpTracker[]> CreateUdpTrackers(Uri uri, CancellationToken ca
udpTrackers.Add(trackerv4);
}

if (ipv6 != default && uri.Port > 0)
if (
supportedAdressFamilies.Contains(AddressFamily.InterNetworkV6)
&& ipv6 != default
&& uri.Port > 0
)
{
var ipEndpoint = new IPEndPoint(ipv6, uri.Port);
var trackerv6 = new UdpTracker(
Expand Down