Skip to content

Commit

Permalink
Merge pull request #592 from alanmcgovern/compact-peer-ipv4-ipv6
Browse files Browse the repository at this point in the history
[core] Support ipv4 and ipv6 compact addresses
  • Loading branch information
alanmcgovern authored Jan 5, 2023
2 parents fb05f30 + 4fe7fe3 commit 8a40c20
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 111 deletions.
2 changes: 1 addition & 1 deletion src/MonoTorrent.Client/MonoTorrent.Client.Modes/Mode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ protected virtual async void HandlePeerExchangeMessage (PeerId id, PeerExchangeM
if ((Manager.Peers.Available + Manager.OpenConnections) >= Manager.Settings.MaximumConnections)
return;

var newPeers = PeerDecoder.Decode (BEncodedString.FromMemory (message.Added));
var newPeers = PeerInfo.FromCompact (message.Added.Span, id.Connection.AddressBytes.Length == 16 ? System.Net.Sockets.AddressFamily.InterNetworkV6 : System.Net.Sockets.AddressFamily.InterNetwork);
for (int i = 0; i < newPeers.Count && i < message.AddedDotF.Length; i++)
newPeers[i] = new PeerInfo (newPeers[i].ConnectionUri, newPeers[i].PeerId, (message.AddedDotF.Span[i] & 0x2) == 0x2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,13 @@ internal void OnTick ()
var added = memory.Slice (0, len * 6);
var addedDotF = memory.Slice (added.Length, len);

int stride = 6;

for (int i = 0; i < len; i++) {
var peer = addedPeers.Dequeue ();
peer.Peer.CompactPeer (added.Span.Slice (i * 6, 6));
if (!peer.Peer.TryWriteCompactPeer (added.Span.Slice (i * stride, stride), out int written) || written != stride)
throw new NotSupportedException ();

if (EncryptionTypes.SupportsRC4 (peer.Peer.AllowedEncryption)) {
addedDotF.Span[i] = 0x01;
} else {
Expand All @@ -107,16 +111,17 @@ internal void OnTick ()

// The remainder of our buffer can be filled with dropped peers.
// We do some math to slice the remainder of the original memory
// buffer to an even multiple of 6. Then we calculate how many
// buffer to an even multiple of 'stride'. Then we calculate how many
// peers we actually want to put in it, and then we slice it one
// more time if we don't have enough dropped peers.
var dropped = memory.Slice (added.Length + addedDotF.Length);
dropped = dropped.Slice (0, (dropped.Length / 6) * 6);
len = Math.Min (dropped.Length / 6, droppedPeers.Count);
dropped = dropped.Slice (0, len * 6);
dropped = dropped.Slice (0, (dropped.Length / stride) * stride);
len = Math.Min (dropped.Length / stride, droppedPeers.Count);
dropped = dropped.Slice (0, len * stride);

for (int i = 0; i < len; i++)
droppedPeers.Dequeue ().Peer.CompactPeer (dropped.Span.Slice (i * 6, 6));
if (!droppedPeers.Dequeue ().Peer.TryWriteCompactPeer (dropped.Span.Slice (i * stride, stride), out int written) || written != stride)
throw new NotSupportedException ();

(var message, var releaser) = PeerMessage.Rent<PeerExchangeMessage> ();
message.Initialize (new ExtensionSupports (new[] { PeerExchangeMessage.Support }), added, addedDotF, dropped, memoryReleaser);
Expand Down
4 changes: 2 additions & 2 deletions src/MonoTorrent.Client/MonoTorrent.Client/Peers/Peer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ public override string ToString ()
internal byte[] CompactPeer ()
=> Info.CompactPeer ();

internal void CompactPeer (Span<byte> buffer)
=> Info.CompactPeer (buffer);
internal bool TryWriteCompactPeer (Span<byte> buffer, out int written)
=> Info.TryWriteCompactPeer (buffer, out written);

internal static BEncodedList Encode (IEnumerable<Peer> peers)
{
Expand Down
14 changes: 6 additions & 8 deletions src/MonoTorrent.Client/MonoTorrent.Client/Peers/PeerDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,23 @@

using System;
using System.Collections.Generic;
using System.Net.Sockets;

using MonoTorrent.BEncoding;

namespace MonoTorrent
{
static class PeerDecoder
{
public static IList<PeerInfo> Decode (BEncodedList peers)
public static IList<PeerInfo> Decode (BEncodedList peers, AddressFamily addressFamily)
{
var list = new List<PeerInfo> (peers.Count);
foreach (BEncodedValue value in peers) {
try {
if (value is BEncodedDictionary)
list.Add (DecodeFromDict ((BEncodedDictionary) value));
else if (value is BEncodedString)
foreach (PeerInfo p in Decode ((BEncodedString) value))
if (value is BEncodedDictionary dict)
list.Add (DecodeFromDict (dict));
else if (value is BEncodedString str)
foreach (var p in PeerInfo.FromCompact (str.Span, addressFamily))
list.Add (p);
} catch {
// If something is invalid and throws an exception, ignore it
Expand Down Expand Up @@ -73,8 +74,5 @@ static PeerInfo DecodeFromDict (BEncodedDictionary dict)
var connectionUri = new Uri ($"ipv4://{dict[IPKey]}:{dict[PortKey]}");
return new PeerInfo (connectionUri, peerId);
}

public static IList<PeerInfo> Decode (BEncodedString peers)
=> PeerInfo.FromCompact (peers.Span);
}
}
2 changes: 1 addition & 1 deletion src/MonoTorrent.Dht/MonoTorrent.Dht.Tasks/GetPeersTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public async Task<IEnumerable<Node>> ExecuteAsync ()
// The response had some actual peers
if (response.Values != null) {
// We have actual peers!
var peers = response.Values.OfType<BEncodedString> ().SelectMany (t => PeerInfo.FromCompact (t.Span)).ToArray ();
var peers = response.Values.OfType<BEncodedString> ().SelectMany (t => PeerInfo.FromCompact (t.Span, Engine.AddressFamily)).ToArray ();
Engine.RaisePeersFound (InfoHash, peers);
foreach (var peer in peers)
FoundPeers.Add (peer);
Expand Down
4 changes: 4 additions & 0 deletions src/MonoTorrent.Dht/MonoTorrent.Dht/DhtEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Threading.Tasks;

Expand Down Expand Up @@ -74,6 +75,9 @@ public class DhtEngine : IDisposable, IDhtEngine

internal static MainLoop MainLoop { get; } = new MainLoop ("DhtLoop");

// IPV6 - create an IPV4 and an IPV6 dht engine
public AddressFamily AddressFamily { get; private set; } = AddressFamily.InterNetwork;

public TimeSpan AnnounceInterval => DefaultAnnounceInternal;

public bool Disposed { get; private set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,34 @@

using System;
using System.Collections.Generic;
using System.Net.Sockets;

namespace MonoTorrent.Messages.UdpTracker
{
public class AnnounceResponseMessage : UdpTrackerMessage
{
public override int ByteLength => (4 * 5 + Peers.Count * 6);
public AddressFamily AddressFamily { get; private set; }
public override int ByteLength => (4 * 5 + Peers.Count * Stride);
public TimeSpan Interval { get; private set; }
public int Leechers { get; private set; }
public List<PeerInfo> Peers { get; private set; }
public int Seeders { get; private set; }
int Stride => AddressFamily switch {
AddressFamily.InterNetwork => 6,
AddressFamily.InterNetworkV6 => 18,
_ => throw new NotSupportedException ()
};

public AnnounceResponseMessage ()
: this (0, TimeSpan.Zero, 0, 0, new List<PeerInfo> ())
public AnnounceResponseMessage (AddressFamily addressFamily)
: this (addressFamily, 0, TimeSpan.Zero, 0, 0, new List<PeerInfo> ())
{

}

public AnnounceResponseMessage (int transactionId, TimeSpan interval, int leechers, int seeders, List<PeerInfo> peers)
public AnnounceResponseMessage (AddressFamily addressFamily, int transactionId, TimeSpan interval, int leechers, int seeders, List<PeerInfo> peers)
: base (1, transactionId)
{
AddressFamily = addressFamily;
Interval = interval;
Leechers = leechers;
Peers = peers;
Expand All @@ -64,7 +72,7 @@ public override void Decode (ReadOnlySpan<byte> buffer)
Leechers = ReadInt (ref buffer);
Seeders = ReadInt (ref buffer);

IList<PeerInfo> peers = PeerInfo.FromCompact (buffer);
IList<PeerInfo> peers = PeerInfo.FromCompact (buffer, AddressFamily);
Peers.AddRange (peers);
}

Expand All @@ -79,7 +87,8 @@ public override int Encode (Span<byte> buffer)
Write (ref buffer, Seeders);

for (int i = 0; i < Peers.Count; i++)
Peers[i].CompactPeer (buffer.Slice (i * 6));
if (!Peers[i].TryWriteCompactPeer (buffer.Slice (i * Stride, Stride), out int dataWritten) || dataWritten != Stride)
throw new InvalidOperationException ();

return written - buffer.Length;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@


using System;
using System.Net.Sockets;

namespace MonoTorrent.Messages.UdpTracker
{
Expand All @@ -42,7 +43,7 @@ protected UdpTrackerMessage (int action, int transactionId)
TransactionId = transactionId;
}

public static UdpTrackerMessage DecodeMessage (ReadOnlySpan<byte> buffer, MessageType type)
public static UdpTrackerMessage DecodeMessage (ReadOnlySpan<byte> buffer, MessageType type, AddressFamily addressFamily)
{
UdpTrackerMessage m;
var actionBuffer = type == MessageType.Request ? buffer.Slice (8) : buffer;
Expand All @@ -58,7 +59,7 @@ public static UdpTrackerMessage DecodeMessage (ReadOnlySpan<byte> buffer, Messag
if (type == MessageType.Request)
m = new AnnounceMessage ();
else
m = new AnnounceResponseMessage ();
m = new AnnounceResponseMessage (addressFamily);
break;
case 2:
if (type == MessageType.Request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -52,6 +53,8 @@ public class HttpTrackerConnection : ITrackerConnection

static readonly Random random = new Random ();

public AddressFamily AddressFamily { get; }

public bool CanScrape { get; }

public Uri? ScrapeUri { get; }
Expand All @@ -69,7 +72,14 @@ public class HttpTrackerConnection : ITrackerConnection
HttpClient Client { get; }

public HttpTrackerConnection (Uri announceUri, HttpClient client)
: this(announceUri, client, AddressFamily.InterNetwork)
{

}

public HttpTrackerConnection (Uri announceUri, HttpClient client, AddressFamily addressFamily)
{
AddressFamily = addressFamily;
Uri = announceUri;
Client = client;

Expand Down Expand Up @@ -282,9 +292,9 @@ AnnounceResponse HandleAnnounce (InfoHash infoHash, BEncodedDictionary dict)

case ("peers"):
if (keypair.Value is BEncodedList bencodedList) // Non-compact response
peers.AddRange (PeerDecoder.Decode (bencodedList));
peers.AddRange (PeerDecoder.Decode (bencodedList, AddressFamily));
else if (keypair.Value is BEncodedString bencodedStr) // Compact response
peers.AddRange (PeerDecoder.Decode (bencodedStr));
peers.AddRange (PeerInfo.FromCompact (bencodedStr.Span, AddressFamily));
break;

case ("failure reason"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ async Task<long> ConnectAsync ()
{
while (!token.IsCancellationRequested) {
UdpReceiveResult received = await client.ReceiveAsync ();
var rsp = UdpTrackerMessage.DecodeMessage (received.Buffer.AsSpan (0, received.Buffer.Length), MessageType.Response);
var rsp = UdpTrackerMessage.DecodeMessage (received.Buffer.AsSpan (0, received.Buffer.Length), MessageType.Response, received.RemoteEndPoint.AddressFamily);

if (transactionId == rsp.TransactionId) {
if (rsp is ErrorMessage error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async void ReceiveAsync (UdpClient client, CancellationToken token)
if (data.Length < 16)
return; //bad request

var request = UdpTrackerMessage.DecodeMessage (data.AsSpan (0, data.Length), MessageType.Request);
var request = UdpTrackerMessage.DecodeMessage (data.AsSpan (0, data.Length), MessageType.Request, result.RemoteEndPoint.AddressFamily);

if (sendTask != null) {
try {
Expand Down Expand Up @@ -180,16 +180,16 @@ protected virtual async Task ReceiveAnnounce (UdpClient client, AnnounceMessage

case ("peers"):
if (keypair.Value is BEncodedList) // Non-compact response
peers.AddRange (PeerDecoder.Decode ((BEncodedList) keypair.Value));
else if (keypair.Value is BEncodedString) // Compact response
peers.AddRange (PeerDecoder.Decode ((BEncodedString) keypair.Value));
peers.AddRange (PeerDecoder.Decode ((BEncodedList) keypair.Value, remotePeer.AddressFamily));
else if (keypair.Value is BEncodedString str) // Compact response
peers.AddRange (PeerInfo.FromCompact (str.Span, remotePeer.AddressFamily));
break;

default:
break;
}
}
m = new AnnounceResponseMessage (announceMessage.TransactionId, interval, leechers, seeders, peers);
m = new AnnounceResponseMessage (remotePeer.AddressFamily, announceMessage.TransactionId, interval, leechers, seeders, peers);
}
byte[] data = m.Encode ();
await client.SendAsync (data, data.Length, remotePeer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

using System;
using System.Net;
using System.Net.Sockets;

using ReusableTasks;

Expand Down
1 change: 1 addition & 0 deletions src/MonoTorrent/MonoTorrent.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<UseMemoryExtensions>true</UseMemoryExtensions>
<TargetFrameworks>net6.0;net5.0;netcoreapp3.0;netstandard2.1;netstandard2.0</TargetFrameworks>
</PropertyGroup>

Expand Down
Loading

0 comments on commit 8a40c20

Please sign in to comment.