From 9dfc8ea609a471219ae125a19edeb2443f395403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Jim=C3=A9nez=20Delgado?= Date: Mon, 17 Jun 2019 16:08:34 +0200 Subject: [PATCH 1/6] UPnP support --- TorrentCore.Cli/Program.cs | 6 +++ TorrentCore.Cli/TorrentCore.Cli.csproj | 4 ++ TorrentCore/TorrentClientBuilder.cs | 10 +++++ TorrentCore/TorrentClientSettings.cs | 6 +++ TorrentCore/TorrentCore.csproj | 1 + TorrentCore/Transport/IUPnPClient.cs | 16 +++++++ .../Tcp/LocalTcpConnectionOptions.cs | 5 +++ .../Transport/Tcp/TcpTransportProtocol.cs | 4 ++ .../Transport/Tcp/TcpTransportUPnPClient.cs | 42 +++++++++++++++++++ 9 files changed, 94 insertions(+) create mode 100644 TorrentCore/Transport/IUPnPClient.cs create mode 100644 TorrentCore/Transport/Tcp/TcpTransportUPnPClient.cs diff --git a/TorrentCore.Cli/Program.cs b/TorrentCore.Cli/Program.cs index 37712a8..c2c2221 100644 --- a/TorrentCore.Cli/Program.cs +++ b/TorrentCore.Cli/Program.cs @@ -33,11 +33,13 @@ public static void Main(string[] args) string input = null; string output = null; bool verbose = false; + bool upnp = false; ArgumentSyntax.Parse(args, syntax => { syntax.DefineOption("p|port", ref port, "Port to listen for incoming connections on."); syntax.DefineOption("o|output", ref output, "Path to save downloaded files to."); syntax.DefineOption("v|verbose", ref verbose, "Show detailed logging information."); + syntax.DefineOption("u|upnp", ref upnp, "Use UPnP for port forwarding."); var uiPortArgument = syntax.DefineOption("ui", ref uiPort, false, "Run a web UI, optionally specifying the port to listen on (default: 5001)."); runWebUi = uiPortArgument.IsSpecified; @@ -53,6 +55,10 @@ public static void Main(string[] args) // Listen for incoming connections on the specified port builder.UsePort(port); + // If upnp is selected use it. + if (upnp) + builder.AddUPnP(); + // Add extension protocol builder.ConfigureServices(services => { diff --git a/TorrentCore.Cli/TorrentCore.Cli.csproj b/TorrentCore.Cli/TorrentCore.Cli.csproj index 2f3d79d..3f3394d 100644 --- a/TorrentCore.Cli/TorrentCore.Cli.csproj +++ b/TorrentCore.Cli/TorrentCore.Cli.csproj @@ -12,6 +12,10 @@ bin\Debug\$(AssemblyName).xml + + AnyCPU + + diff --git a/TorrentCore/TorrentClientBuilder.cs b/TorrentCore/TorrentClientBuilder.cs index d76d693..17d9c80 100644 --- a/TorrentCore/TorrentClientBuilder.cs +++ b/TorrentCore/TorrentClientBuilder.cs @@ -88,6 +88,16 @@ public TorrentClientBuilder UsePort(int port) return this; } + /// + /// Add UPnP port forwarding feature. + /// + /// TorrentClientBuilder. + public TorrentClientBuilder AddUPnP() + { + _services.Configure(options => options.UseUPnP = true); + return this; + } + /// /// Sets up a default Torrent Client using the TCP transport protocol and the BitTorrent application protocol. /// diff --git a/TorrentCore/TorrentClientSettings.cs b/TorrentCore/TorrentClientSettings.cs index 616d4b7..3c50fae 100644 --- a/TorrentCore/TorrentClientSettings.cs +++ b/TorrentCore/TorrentClientSettings.cs @@ -21,6 +21,7 @@ public TorrentClientSettings() PeerId = PeerId.CreateNew(); ListenPort = 6881; AdapterAddress = IPAddress.Any; + UseUPnP = false; } /// @@ -28,6 +29,11 @@ public TorrentClientSettings() /// public PeerId PeerId { get; set; } + /// + /// Gets or sets a value indicating whether to use UPnP for port forwarding. + /// + public bool UseUPnP { get; set; } + /// /// Gets or sets the port to listen for incoming connections on. /// diff --git a/TorrentCore/TorrentCore.csproj b/TorrentCore/TorrentCore.csproj index 3c0f200..3001353 100644 --- a/TorrentCore/TorrentCore.csproj +++ b/TorrentCore/TorrentCore.csproj @@ -25,6 +25,7 @@ + diff --git a/TorrentCore/Transport/IUPnPClient.cs b/TorrentCore/Transport/IUPnPClient.cs new file mode 100644 index 0000000..0e090e2 --- /dev/null +++ b/TorrentCore/Transport/IUPnPClient.cs @@ -0,0 +1,16 @@ +// This file is part of TorrentCore. +// https://torrentcore.org +// Copyright (c) Samuel Fisher. +// +// Licensed under the GNU Lesser General Public License, version 3. See the +// LICENSE file in the project root for full license information. + +using System.Threading.Tasks; + +namespace TorrentCore.Transport +{ + public interface IUPnPClient + { + Task TryAddPortMappingAsync(int port); + } +} diff --git a/TorrentCore/Transport/Tcp/LocalTcpConnectionOptions.cs b/TorrentCore/Transport/Tcp/LocalTcpConnectionOptions.cs index ff95fd5..b33ca21 100644 --- a/TorrentCore/Transport/Tcp/LocalTcpConnectionOptions.cs +++ b/TorrentCore/Transport/Tcp/LocalTcpConnectionOptions.cs @@ -28,5 +28,10 @@ public class LocalTcpConnectionOptions /// Gets or sets the address of the local adapter used for connections. /// public IPAddress BindAddress { get; set; } = IPAddress.Any; + + /// + /// Gets or sets a value indicating whether to use UPnP for port forwarding. + /// + public bool UseUPnP { get; set; } } } diff --git a/TorrentCore/Transport/Tcp/TcpTransportProtocol.cs b/TorrentCore/Transport/Tcp/TcpTransportProtocol.cs index 0b9ca20..38d2d73 100644 --- a/TorrentCore/Transport/Tcp/TcpTransportProtocol.cs +++ b/TorrentCore/Transport/Tcp/TcpTransportProtocol.cs @@ -27,6 +27,7 @@ class TcpTransportProtocol : ITcpTransportProtocol { private readonly ILogger _logger; private readonly ConcurrentBag _streams; + private readonly IUPnPClient _uPnPClient; private TcpListener _listener; @@ -40,6 +41,7 @@ public TcpTransportProtocol(ILogger logger, IOptions(); Port = options.Value.Port; + _uPnPClient = new TcpTransportUPnPClient(options.Value.UseUPnP); LocalBindAddress = options.Value.BindAddress ?? IPAddress.Any; RateLimiter = new RateLimiter(); } @@ -108,6 +110,8 @@ public void Start() // If port=0 was supplied, set the actual port we are listening on. Port = ((IPEndPoint)_listener.LocalEndpoint).Port; ListenForIncomingConnections(); + + _uPnPClient.TryAddPortMappingAsync(Port); } /// diff --git a/TorrentCore/Transport/Tcp/TcpTransportUPnPClient.cs b/TorrentCore/Transport/Tcp/TcpTransportUPnPClient.cs new file mode 100644 index 0000000..ac90e7c --- /dev/null +++ b/TorrentCore/Transport/Tcp/TcpTransportUPnPClient.cs @@ -0,0 +1,42 @@ +// This file is part of TorrentCore. +// https://torrentcore.org +// Copyright (c) Samuel Fisher. +// +// Licensed under the GNU Lesser General Public License, version 3. See the +// LICENSE file in the project root for full license information. + +using System.Threading; +using System.Threading.Tasks; +using Open.Nat; + +namespace TorrentCore.Transport.Tcp +{ + class TcpTransportUPnPClient : IUPnPClient + { + private bool _useUPnP; + + public TcpTransportUPnPClient(bool useUPnP) + { + _useUPnP = useUPnP; + } + + public async Task TryAddPortMappingAsync(int port) + { + if (!_useUPnP) + return; + + try + { + var discoverer = new NatDiscoverer(); + var cts = new CancellationTokenSource(10000); + + var device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts); + await device.CreatePortMapAsync(new Mapping(Protocol.Tcp, port, port, "TorrentCore")); + } + catch + { + // Do nothing + } + } + } +} From 30817e1092d5b11f5293cfdc4a667e7cb0a65b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Jim=C3=A9nez=20Delgado?= Date: Mon, 4 Apr 2022 13:14:55 +0200 Subject: [PATCH 2/6] Remove nuget.config and replace wrong console package --- NuGet.config | 5 -- TorrentCore.Cli/Program.cs | 93 ++++++++++++++++---------- TorrentCore.Cli/TorrentCore.Cli.csproj | 6 +- TorrentCore.sln | 5 +- 4 files changed, 64 insertions(+), 45 deletions(-) delete mode 100644 NuGet.config diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index 65d701d..0000000 --- a/NuGet.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/TorrentCore.Cli/Program.cs b/TorrentCore.Cli/Program.cs index b210dc3..2ea3e59 100644 --- a/TorrentCore.Cli/Program.cs +++ b/TorrentCore.Cli/Program.cs @@ -6,50 +6,62 @@ // LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; using System.CommandLine; -using System.Diagnostics; +using System.CommandLine.Builder; +using System.CommandLine.Hosting; +using System.CommandLine.NamingConventionBinder; +using System.CommandLine.Parsing; using System.IO; -using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Serilog; using TorrentCore.Extensions.ExtensionProtocol; using TorrentCore.Extensions.PeerExchange; using TorrentCore.Extensions.SendMetadata; using TorrentCore.Modularity; -using TorrentCore.Web; namespace TorrentCore.Cli { public class Program { - public static void Main(string[] args) + public static async Task Main(string[] args) { - int port = 5000; - int uiPort = 5001; - bool runWebUi = false; - string input = null; - string output = null; - bool verbose = false; - ArgumentSyntax.Parse(args, syntax => - { - syntax.DefineOption("p|port", ref port, "Port to listen for incoming connections on."); - syntax.DefineOption("o|output", ref output, "Path to save downloaded files to."); - syntax.DefineOption("v|verbose", ref verbose, "Show detailed logging information."); - var uiPortArgument = syntax.DefineOption("ui", ref uiPort, false, "Run a web UI, optionally specifying the port to listen on (default: 5001)."); - runWebUi = uiPortArgument.IsSpecified; + return await BuildCommandLine() + .UseHost(host => host.UseSerilog()) + .UseDefaults() + .Build() + .InvokeAsync(args); + } - syntax.DefineParameter("input", ref input, "Path of torrent file to download."); - }); + private static CommandLineBuilder BuildCommandLine() + { + var root = new RootCommand() + { + new Option(new[] { "-p", "--port" }, "Port to listen for incoming connections on.") + { + IsRequired = true, + }, + new Option(new[] { "-o", "--output" }, "Path to save downloaded files to.") + { + IsRequired = true, + }, + new Option(new[] { "v", "verbose" }, "Show detailed logging information."), + new Argument("--input", "Path of torrent file to download.") + { + Arity = ArgumentArity.ExactlyOne, + }, + }; + root.Handler = CommandHandler.Create(RunAsync); + return new CommandLineBuilder(root); + } + private static async Task RunAsync(Options options) + { var builder = TorrentClientBuilder.CreateDefaultBuilder(); // Configure logging Log.Logger = new LoggerConfiguration() - .MinimumLevel.Is(verbose ? Serilog.Events.LogEventLevel.Debug : Serilog.Events.LogEventLevel.Information) + .MinimumLevel.Is(options.Verbose ? Serilog.Events.LogEventLevel.Debug : Serilog.Events.LogEventLevel.Information) .Enrich.FromLogContext() .WriteTo.Console() .CreateLogger(); @@ -57,7 +69,7 @@ public static void Main(string[] args) builder.ConfigureServices(services => services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true))); // Listen for incoming connections on the specified port - builder.UsePort(port); + builder.UsePort(options.Port); // Add extension protocol builder.ConfigureServices(services => @@ -72,15 +84,9 @@ public static void Main(string[] args) }); }); - var client = builder.Build(); + using var client = builder.Build(); - ////if (runWebUi) - ////{ - //// var uri = client.EnableWebUI(uiPort); - //// Console.WriteLine($"Web UI started at {uri}"); - ////} - - var download = client.Add(input, output); + var download = client.Add(options.Input.FullName, options.Output.FullName); download.Start(); Console.WriteLine("Downloading..."); @@ -90,14 +96,33 @@ public static void Main(string[] args) timer.Elapsed += (o, e) => LogStatus(download); timer.Start(); - download.WaitForDownloadCompletionAsync().Wait(); + try + { + await download.WaitForDownloadCompletionAsync(); + } + catch (TaskCanceledException) + { + return -1; + } } - Console.ReadKey(); + + return 0; } private static void LogStatus(TorrentDownload download) { Console.WriteLine($"{download.State} ({download.Progress:P})"); } + + private class Options + { + public int Port { get; set; } + + public DirectoryInfo Output { get; set; } + + public bool Verbose { get; set; } + + public FileInfo Input { get; set; } + } } } diff --git a/TorrentCore.Cli/TorrentCore.Cli.csproj b/TorrentCore.Cli/TorrentCore.Cli.csproj index 915ee78..5eeee8d 100644 --- a/TorrentCore.Cli/TorrentCore.Cli.csproj +++ b/TorrentCore.Cli/TorrentCore.Cli.csproj @@ -22,9 +22,9 @@ - - - + + + diff --git a/TorrentCore.sln b/TorrentCore.sln index b06187e..3611dd0 100644 --- a/TorrentCore.sln +++ b/TorrentCore.sln @@ -1,14 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5BD8F3F0-5E3F-40CB-9534-CA78C50E88EF}" ProjectSection(SolutionItems) = preProject .travis.yml = .travis.yml Directory.build.props = Directory.build.props LICENSE = LICENSE - NuGet.config = NuGet.config README.md = README.md stylecop.json = stylecop.json StyleCopRules.ruleset = StyleCopRules.ruleset From 5ade5c567d0ad60204b87c86441eb6c358e59c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Jim=C3=A9nez=20Delgado?= Date: Mon, 4 Apr 2022 13:16:26 +0200 Subject: [PATCH 3/6] fix wrong console argument specification --- TorrentCore.Cli/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TorrentCore.Cli/Program.cs b/TorrentCore.Cli/Program.cs index 2ea3e59..a59ebd2 100644 --- a/TorrentCore.Cli/Program.cs +++ b/TorrentCore.Cli/Program.cs @@ -45,7 +45,7 @@ private static CommandLineBuilder BuildCommandLine() { IsRequired = true, }, - new Option(new[] { "v", "verbose" }, "Show detailed logging information."), + new Option(new[] { "-v", "--verbose" }, "Show detailed logging information."), new Argument("--input", "Path of torrent file to download.") { Arity = ArgumentArity.ExactlyOne, From aa50d36c21371a336e93ed1beb8fabc823d6c0a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Jim=C3=A9nez=20Delgado?= Date: Mon, 4 Apr 2022 13:20:39 +0200 Subject: [PATCH 4/6] Remove wrong pasted code --- TorrentCore.Cli/Program.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/TorrentCore.Cli/Program.cs b/TorrentCore.Cli/Program.cs index a59ebd2..94d9fef 100644 --- a/TorrentCore.Cli/Program.cs +++ b/TorrentCore.Cli/Program.cs @@ -96,14 +96,7 @@ private static async Task RunAsync(Options options) timer.Elapsed += (o, e) => LogStatus(download); timer.Start(); - try - { - await download.WaitForDownloadCompletionAsync(); - } - catch (TaskCanceledException) - { - return -1; - } + await download.WaitForDownloadCompletionAsync(); } return 0; From a365d035c9a276cc8bca51f7bd360f7cb3dc0d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Jim=C3=A9nez=20Delgado?= Date: Sat, 9 Apr 2022 01:22:53 +0200 Subject: [PATCH 5/6] add missing package --- TorrentCore/TorrentCore.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/TorrentCore/TorrentCore.csproj b/TorrentCore/TorrentCore.csproj index eb97155..02360d8 100644 --- a/TorrentCore/TorrentCore.csproj +++ b/TorrentCore/TorrentCore.csproj @@ -31,6 +31,7 @@ + From 58d821534bbe38d793d800e7f1caa39271d1caef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Jim=C3=A9nez=20Delgado?= Date: Sat, 9 Apr 2022 01:35:31 +0200 Subject: [PATCH 6/6] fix cli --- TorrentCore.Cli/Program.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TorrentCore.Cli/Program.cs b/TorrentCore.Cli/Program.cs index e797c72..10ebebb 100644 --- a/TorrentCore.Cli/Program.cs +++ b/TorrentCore.Cli/Program.cs @@ -45,6 +45,7 @@ private static CommandLineBuilder BuildCommandLine() { IsRequired = true, }, + new Option(new[] { "-u", "--upnp" }, "Use UPnP for port forwarding."), new Option(new[] { "-v", "--verbose" }, "Show detailed logging information."), new Argument("--input", "Path of torrent file to download.") { @@ -72,7 +73,7 @@ private static async Task RunAsync(Options options) builder.UsePort(options.Port); // If upnp is selected use it. - if (upnp) + if (options.UPnP) builder.AddUPnP(); // Add extension protocol @@ -117,6 +118,8 @@ private class Options public DirectoryInfo Output { get; set; } + public bool UPnP { get; set; } + public bool Verbose { get; set; } public FileInfo Input { get; set; }