diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f604126 --- /dev/null +++ b/.gitignore @@ -0,0 +1,547 @@ + +# Created by https://www.gitignore.io/api/csharp,windows,dotnetcore,visualstudio +# Edit at https://www.gitignore.io/?templates=csharp,windows,dotnetcore,visualstudio + +### Csharp ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +### DotnetCore ### +# .NET Core build folders +/bin +/obj + +# Common node modules locations +/node_modules +/wwwroot/node_modules + + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### VisualStudio ### + +# User-specific files + +# User-specific files (MonoDevelop/Xamarin Studio) + +# Mono auto generated files + +# Build results + +# Visual Studio 2015/2017 cache/options directory +# Uncomment if you have tasks that create the project's static files in wwwroot + +# Visual Studio 2017 auto generated files + +# MSTest test Results + +# NUnit + +# Build Results of an ATL Project + +# Benchmark Results + +# .NET Core + +# StyleCop + +# Files built by Visual Studio + +# Chutzpah Test files + +# Visual C++ cache files + +# Visual Studio profiler + +# Visual Studio Trace Files + +# TFS 2012 Local Workspace + +# Guidance Automation Toolkit + +# ReSharper is a .NET coding add-in + +# JustCode is a .NET coding add-in + +# TeamCity is a build add-in + +# DotCover is a Code Coverage Tool + +# AxoCover is a Code Coverage Tool + +# Visual Studio code coverage results + +# NCrunch + +# MightyMoose + +# Web workbench (sass) + +# Installshield output folder + +# DocProject is a documentation generator add-in + +# Click-Once directory + +# Publish Web Output +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted + +# NuGet Packages +# NuGet Symbol Packages +# The packages folder can be ignored because of Package Restore +# except build/, which is used as an MSBuild target. +# Uncomment if necessary however generally it will be regenerated when needed +# NuGet v3's project.json files produces more ignorable files + +# Microsoft Azure Build Output + +# Microsoft Azure Emulator + +# Windows Store app package directories and files + +# Visual Studio cache files +# files ending in .cache can be ignored +# but keep track of directories ending in .cache + +# Others + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) + +# RIA/Silverlight projects + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) + +# SQL Server files + +# Business Intelligence projects + +# Microsoft Fakes + +# GhostDoc plugin setting file + +# Node.js Tools for Visual Studio + +# Visual Studio 6 build log + +# Visual Studio 6 workspace options file + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) + +# Visual Studio LightSwitch build output + +# Paket dependency manager + +# FAKE - F# Make + +# CodeRush personal settings + +# Python Tools for Visual Studio (PTVS) + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio + +# Telerik's JustMock configuration file + +# BizTalk build output + +# OpenCover UI analysis results + +# Azure Stream Analytics local run output + +# MSBuild Binary and Structured Log + +# NVidia Nsight GPU debugger configuration file + +# MFractors (Xamarin productivity tool) working folder + +# Local History for Visual Studio + +# BeatPulse healthcheck temp database + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 + +# End of https://www.gitignore.io/api/csharp,windows,dotnetcore,visualstudio \ No newline at end of file diff --git a/README.md b/README.md index 0affa43..bbe0165 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,95 @@ -# Trou -Tor + Privoxy for the best anonymous HTTP proxy implementation on C# +# +--- + +> Join **ToWolf** server to get latest news about **Trou** - https://discord.gg/m7CZ6md + +> Tor + Privoxy for the best anonymous HTTP proxy implementation on C# + +Trou is a complete Tor (and Tor Controller) and Privoxy implementation on C# +You can use every services separately or combined to have a local anonymous proxy ! + +**You could use Trou with**: +- HttpClients +- WebClients +- WebBrowsers +- (and everything that support HTTP or Socks5 proxy..) + +## Compatibility + +**Trou is made using .NET Core 3.1 and it's working currently working on:** +- Windows 8 and higher (you could go up to windows 7, just check the project wiki) + +/!\ **Linux and Mac OS compatibility is planned** /!\ + +# :rocket: Quick example +--- + +Here's a quick example on how to use Trou. +This example is very minimalist, and it doesn't even care about errors/warnings/exceptions.. + +You can also get this [example project](https://github.com/NaolShow/Trou/blob/master/TrouExample) + +```cs + +// - Instantiate Trou proxy + +TrouProxy proxy = new TrouProxy(new TorProxySettings() { + TorBundlePath = @"C:\AnyFolder\TorProxy" +}, new TorControllerSettings() { + +}, new PrivoxyProxySettings() { + PrivoxyBundlePath = @"C:\AnyFolder\PrivoxyProxy" +}); + +// - Start + +proxy.Start(); + +// - Check IP + +// Create client connected to Tor using Trou +WebClient client = new WebClient() { + Proxy = new WebProxy("127.0.0.1:8118") +}; + +// Write TOR IP address +Console.WriteLine(client.DownloadString("http://api.ipify.org")); + +// - Stop +Console.ReadLine(); +client.Dispose(); +proxy.Dispose(); + +``` + +# :books: Documentation +--- + +**The complete Trou documentation [can be found here](https://github.com/NaolShow/Trou/wiki)** + +# :question: Help +--- + +If you need help, or if you want to contact me in general, just make a github issue ticket ! +You can also contact me on my discord server or in private messages: [NaolShow#7243](#) + +# :wrench: Installation +--- + +You have two ways to install Trou, the first one is by far the most simplest one: +``` +// With the package manager (Nuget) +PM> Install-Package Trou + +// With .NET CLI +dotnet add package Trou +``` +You can also go in your project top bar menu in visual studio > Manage Nuget packages > Search for "Trou" > Install + +The second way is to go in the [release](https://github.com/NaolShow/Trou/releases) tab in the github project, +and download the last .dll, and then just reference it in your project! + +# :newspaper: Licence +--- + +Distributed under the GNU General Public Licence v3.0. See LICENSE for more information. \ No newline at end of file diff --git a/Trou.sln b/Trou.sln new file mode 100644 index 0000000..1a949bd --- /dev/null +++ b/Trou.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.87 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trou", "Trou\Trou.csproj", "{818BC883-C5C2-4638-844F-48A68CEECE8D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrouExample", "TrouExample\TrouExample.csproj", "{D5124A10-86EE-4904-B266-88F3A4AAD32A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {818BC883-C5C2-4638-844F-48A68CEECE8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {818BC883-C5C2-4638-844F-48A68CEECE8D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {818BC883-C5C2-4638-844F-48A68CEECE8D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {818BC883-C5C2-4638-844F-48A68CEECE8D}.Release|Any CPU.Build.0 = Release|Any CPU + {D5124A10-86EE-4904-B266-88F3A4AAD32A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5124A10-86EE-4904-B266-88F3A4AAD32A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5124A10-86EE-4904-B266-88F3A4AAD32A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5124A10-86EE-4904-B266-88F3A4AAD32A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6C833C61-2A95-4808-A4FD-C44E78365717} + EndGlobalSection +EndGlobal diff --git a/Trou/.editorconfig b/Trou/.editorconfig new file mode 100644 index 0000000..8f0432e --- /dev/null +++ b/Trou/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = none diff --git a/Trou/Services/PrivoxyProxy.cs b/Trou/Services/PrivoxyProxy.cs new file mode 100644 index 0000000..492da58 --- /dev/null +++ b/Trou/Services/PrivoxyProxy.cs @@ -0,0 +1,137 @@ +using System; +using System.IO; +using System.Linq; +using Trou.ServicesSettings; +using Trou.Tools; + +namespace Trou.Services { + + /// + /// Allows to start a which will listen to an IP and a Port for HTTP requests + /// + public class PrivoxyProxy : IDisposable { + + #region Variables + + #region PrivoxyProxy + + /// + /// hidden process + /// + public HiddenProcess Process { get; private set; } + /// + /// process executable path + /// + public string PrivoxyExe { get; private set; } + + /// + /// settings + /// + public PrivoxyProxySettings Settings { get; set; } + + #endregion + + #endregion + + #region Constructors + + /// + /// Initializes a new intance of the service + /// + public PrivoxyProxy(PrivoxyProxySettings settings) { + + // Save + Settings = settings; + + } + + #endregion + + /// + /// Refreshes every Arguments and Configuration keys from the before starting the process + /// + private void RefreshArguments() { + + #region Paths + + // Privoxy executable path + PrivoxyExe = Settings.PrivoxyFile ?? Path.Combine(Settings.PrivoxyBundlePath, "privoxy.exe"); + + #endregion + + #region Network + + // Listening address & port + Settings.Configuration["listen-address"] = string.Format("{0}:{1}", Settings.ListeningAddress, Settings.ListeningPort); + + #region Forward + + // Forward to SOCKS 5 proxy + Settings.Configuration["forward-socks5t"] = string.Format("/ {0}:{1} .", Settings.ForwardAddress, Settings.ForwardPort); + + #endregion + + #endregion + + #region Arguments + + // Set process as not a daemon + Settings.Arguments["--no-daemon"] = string.Empty; + + // Set configuration file in arguments + Settings.Arguments["configfile"] = Path.Combine(Path.GetDirectoryName(PrivoxyExe), "trou_privoxy_config"); + + #endregion + + } + + #region Start & Stop + + /// + /// Starts the service + /// + public void Start() { + + // Privoxy process is already started + if (Process != null) { + + // Stop Privoxy process + Dispose(); + + } + + // Refresh arguments and get them as a single string + RefreshArguments(); + string arguments = string.Join(" ", Settings.Arguments.ToList().Select(a => string.Format("{0} {1}", a.Key, a.Value))); + + // Write the configuration file + File.WriteAllText(Path.Combine(Path.GetDirectoryName(PrivoxyExe), "trou_privoxy_config"), string.Join(Environment.NewLine, Settings.Configuration.ToList().Select(a => string.Format("{0} {1}", a.Key, a.Value)))); + + // Create hidden process + Process = new HiddenProcess(Path.GetDirectoryName(PrivoxyExe), PrivoxyExe, arguments, HiddenProcess.PriorityClass.NORMAL_PRIORITY_CLASS, true); + + // Start hidden process + if (!Process.Start()) { + + // Throw exception + throw new Exception("start_failed: Privoxy process start failed"); + + } + + } + + /// + /// Dispose the service + /// + public void Dispose() { + + // Dispose process + Process?.Dispose(); + + } + + #endregion + + } + +} diff --git a/Trou/Services/TorController.cs b/Trou/Services/TorController.cs new file mode 100644 index 0000000..28f622f --- /dev/null +++ b/Trou/Services/TorController.cs @@ -0,0 +1,202 @@ +using System; +using System.IO; +using System.Net.Sockets; +using System.Text; +using Trou.ServicesSettings; + +namespace Trou.Services { + + /// + /// Allows to start a which can interact with a + /// + public class TorController : IDisposable { + + #region Variables + + /// + /// settings + /// + public TorControllerSettings Settings { get; set; } + + #region TCPClient + + /// + /// tcp client + /// + private TcpClient ControlClient; + + /// + /// writer + /// + private StreamWriter ControlWriter; + /// + /// reader + /// + private StreamReader ControlReader; + + #endregion + + #endregion + + #region Constructors + + /// + /// Initializes a new intance of the service + /// + public TorController(TorControllerSettings settings) { + + // Save + Settings = settings; + + } + + #endregion + + /// + /// Refresh the circuit of (changes every nodes so get new IP Address)
+ /// Returns true if the refresh was successful + ///
+ public bool RefreshCircuit() { + return Send("SIGNAL NEWNYM"); + } + + /// + /// Authenticates to
+ /// Returns true if the authentication was successful + ///
+ public bool Authenticate() { + return Send(string.Format("AUTHENTICATE \"{0}\"", Settings.ControlPassword)); + } + + #region Quit, Shutdown, Dormant/Active + + /// + /// Quit/Log-Off from
+ /// Returns true if the log-off was successful + ///
+ public bool Quit() { + return Send("QUIT"); + } + + /// + /// Stops completely the
+ /// Returns true if the shutdown was successful + ///
+ public bool Shutdown() { + return Send("SIGNAL SHUTDOWN"); + } + + /// + /// Sets to a "sleep mode"
+ /// Returns true if the sleep mode set was successful + ///
+ public bool Dormant() { + return Send("SIGNAL DORMANT"); + } + + /// + /// Removes the "sleep mode" (Dormant) from + /// Returns true if the sleep mode remove was successful + /// + public bool Active() { + return Send("SIGNAL ACTIVE"); + } + + #endregion + + #region TcpClient + + /// + /// Sends a text message to
+ /// Returns true if the sending was successful + ///
+ public bool Send(string text) { + + try { + + // Send text to tor controller + ControlWriter.WriteLine(text); + ControlWriter.Flush(); + + // Return response state (valid or not) + return (ControlReader.ReadLine()).Contains("250"); + + } catch { return false; } + + } + + #endregion + + #region Start & Stop + + /// + /// Starts the service + /// + public void Start() { + + // Control client is already started + if (ControlClient != null) { + + // Stop TorController + Dispose(); + + } + + // Create ControlClient + ControlClient = new TcpClient(); + + // Connect the client to Tor and check if the connection was successful + ControlClient.Connect(Settings.TorAddress, Settings.TorPort); + if (ControlClient.Connected) { + + // Save network stream + NetworkStream stream = ControlClient.GetStream(); + + // Create stream writer & reader + ControlWriter = new StreamWriter(stream, Encoding.ASCII, 4096, true); + ControlReader = new StreamReader(stream, Encoding.ASCII, false, 4096, true); + + // If the authentification to Tor didn't work (due to wrong password) + if (!Authenticate()) { + + // Throw exception + throw new Exception("authentification_failed: Control client authentification failed"); + + } + + } else { + + // Throw exception + throw new Exception("connection_failed: Control client connection to Tor service failed"); + + } + + + } + + /// + /// Dispose the service + /// + public void Dispose() { + + // If ControlClient is connected + if (ControlClient != null && ControlClient.Connected) { + + // Quit Tor connection + Quit(); + + } + + // Dispose client & streams + ControlWriter?.Dispose(); + ControlReader?.Dispose(); + + ControlClient?.Dispose(); + + } + + #endregion + + } + +} diff --git a/Trou/Services/TorProxy.cs b/Trou/Services/TorProxy.cs new file mode 100644 index 0000000..0301589 --- /dev/null +++ b/Trou/Services/TorProxy.cs @@ -0,0 +1,350 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using Trou.ServicesSettings; +using Trou.Tools; + +namespace Trou.Services { + + /// + /// Allows to start a which will listen to an IP and a Port for SOCKS 5 requests + /// + public class TorProxy : IDisposable { + + #region Variables + + #region TorProxy + + /// + /// hidden process + /// + public HiddenProcess Process { get; private set; } + /// + /// process executable path + /// + public string TorExe { get; private set; } + + /// + /// settings + /// + public TorProxySettings Settings { get; set; } + + #endregion + + #region Events + + /// + /// Triggered when process output informations + /// + public event EventHandler OnOutput; + /// + /// Triggered when process output warns or errors + /// + public event EventHandler OnErrorOutput; + + #endregion + + #endregion + + #region Constructors + + /// + /// Initializes a new intance of the service + /// + public TorProxy(TorProxySettings settings) { + + // Save + Settings = settings; + + } + + #endregion + + /// + /// Refreshes every Arguments from the before starting the process + /// + private void RefreshArguments() { + + #region Paths + + // Tor executable path + TorExe = Settings.TorFile ?? Path.Combine(Settings.TorBundlePath, "Tor", "tor.exe"); + // Tor cache path + SetPathArgument("CacheDirectory", Settings.CachePath); + + #region GeoIPFiles + + // Geo IP File (IPV4) + SetPathArgument("GeoIPFile", Settings.GeoIPFile ?? Path.Combine(Settings.TorBundlePath, "Data", "Tor", "geoip")); + // Geo IPv6 File (IPV6) + SetPathArgument("GeoIPv6File", Settings.GeoIPv6File ?? Path.Combine(Settings.TorBundlePath, "Data", "Tor", "geoip6")); + + #endregion + + #endregion + + #region Network + + // Listening address & port + SetArgument("SocksPort", string.Format("{0}:{1}", Settings.ListeningAddress, Settings.ListeningPort)); + + #region Controller + + // Listening address & port + SetArgument("ControlPort", string.Format("{0}:{1}", Settings.ListeningControlAddress, Settings.ListeningControlPort), Settings.UseController); + + // Set the control password, use the hashed one if it has been set, or hash the non hashed one + SetArgument("HashedControlPassword", Settings.HashedControlPassword ?? HashPassword(Settings.ControlPassword), Settings.ControlPassword != null || Settings.HashedControlPassword != null); + + #endregion + + #region Nodes filtering + + // If Tor should respect the nodes filtering + SetArgument("StrictNodes", (Settings.StrictNodes) ? "1" : "0"); + + #region Whitelists + + SetListArgument("ExitNodes", Settings.ExitNodes); + SetListArgument("MiddleNodes", Settings.MiddleNodes); + SetListArgument("EntryNodes", Settings.EntryNodes); + + #endregion + #region Blacklists + + SetListArgument("ExcludeNodes", Settings.ExcludeNodes); + SetListArgument("ExcludeExitNodes", Settings.ExcludeExitNodes); + + #endregion + + #endregion + #region Traffic + + // No traffic to .onion websites + SetToggleArgument("NoOnionTraffic", Settings.NoOnionTraffic); + // Accept traffic only to .onion websites + SetToggleArgument("OnionTrafficOnly", Settings.OnionTrafficOnly); + + #endregion + + #endregion + + } + + /// + /// Triggered when we receive process output that needs to be forwarded to events + /// + private void ForwardOutput(string message) { + + // If message is a normal output + if (message.Contains("[notice]")) { + + // Trigger event + OnOutput?.Invoke(this, message); + + } else { + + // Trigger event + OnErrorOutput?.Invoke(this, message); + + } + + } + + #region Start & Stop + + /// + /// Starts the service + /// + public void Start() { + + // Tor process is already started + if (Process != null) { + + // Stop Tor process + Dispose(); + + } + + // Refresh arguments and get them as a single string + RefreshArguments(); + string arguments = string.Join(" ", Settings.Arguments.ToList().Select(a => string.Format("{0} {1}", a.Key, a.Value))); + + // Create hidden process + Process = new HiddenProcess(Path.GetDirectoryName(TorExe), TorExe, arguments, HiddenProcess.PriorityClass.NORMAL_PRIORITY_CLASS, true); + + // Set output event (forward to ForwardOuput function) + Process.OnOutput += (sender, message) => ForwardOutput(message); + + // Start hidden process + if (Process.Start()) { + + // Process already stopped (this is due to a critical error, like port already used..) + if (Process.Process != null && Process.Process.HasExited) { + + // Throw exception + throw new Exception("start_critical_error: Tor process start failed due to a critical error, check if the port isn't already used"); + + } + + } else { + + // Throw exception + throw new Exception("start_failed: Tor process start failed, try increasing the privileges"); + + } + + } + + /// + /// Dispose the service + /// + public void Dispose() { + + // Dispose process + Process?.Dispose(); + + } + + #endregion + + #region Arguments utility + + /// + /// Sets an argument that can be active or not
+ /// (If the argument isn't active it will be removed) + ///
+ public void SetArgument(string name, string value, bool active = true) { + + // We accept nullable arguments or the value isn't empty + if (active) { + + // Set argument + Settings.Arguments[name] = value; + + } else { + + // Remove argument + Settings.Arguments.Remove(name); + + } + + } + + /// + /// Sets a toggle argument and remove it if the value is false + /// + public void SetToggleArgument(string name, bool value) { + + // We have to set the argument + if (value) { + + // Set argument + Settings.Arguments[name] = string.Empty; + + } else { + + // Clear entry with key + Settings.Arguments.Remove(name); + + } + + } + + /// + /// Sets an argument to a list and remove it if the list is empty + /// + public void SetListArgument(string name, List value) { + + // The list isn't empty + if (value.Count > 0) { + + // Set argument + Settings.Arguments[name] = string.Join(",", value.Select(a => "{" + a + "}")); + + } else { + + // Remove argument + Settings.Arguments.Remove(name); + + } + + + + } + + /// + /// Sets a path argument, throw an exception if the path isn't valid + /// + public void SetPathArgument(string name, string path) { + + // If path isn't null + if (path != null) { + + // Path exists + if (File.Exists(path) || Directory.Exists(path)) { + + // Set argument + Settings.Arguments[name] = path; + + } else { + + // Throw exception + throw new Exception(string.Format("invalid_path: Path '{0}' assigned to '{1}' isn't valid", path, name)); + + } + + } + + } + + #endregion + + /// + /// Hash a password using the process + /// + public string HashPassword(string password) { + + // Password isn't empty + if (!string.IsNullOrEmpty(password)) { + + // Create process runner (the process will stop itself) + HiddenProcess runner = new HiddenProcess(null, TorExe, string.Format("--hash-password \"{0}\"", password), HiddenProcess.PriorityClass.NORMAL_PRIORITY_CLASS, false); + + // Process on output event (get password hash) + string passwordHash = null; + runner.OnOutput += (sender, message) => { + + // If it's the password hash + if (message.StartsWith("16:")) { + + // Save password hash + passwordHash = message; + + } + + }; + + // Start process + runner.Start(); + + // Wait to get the password hash or wait for the end of the timeout + Stopwatch timeout = Stopwatch.StartNew(); + while (timeout.ElapsedMilliseconds < 5000 && passwordHash == null) { Thread.Sleep(500); } + + // Stop process + runner.Dispose(); + + return passwordHash; + + } + return null; + + } + + } + +} diff --git a/Trou/ServicesSettings/PrivoxyProxySettings.cs b/Trou/ServicesSettings/PrivoxyProxySettings.cs new file mode 100644 index 0000000..175249e --- /dev/null +++ b/Trou/ServicesSettings/PrivoxyProxySettings.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using Trou.Services; + +namespace Trou.ServicesSettings { + + /// + /// Allows to configure a + /// + public class PrivoxyProxySettings { + + #region Paths + + /// + /// Path to the extracted Privoxy Bundle folder (folder that contains the privoxy.exe file). This is the fastest way of configuring paths.
+ /// For a more advanced paths configuration, you can set every paths manually (if those paths are set, they will not be recovered from path):
+ /// + ///
+ public string PrivoxyBundlePath { get; set; } = null; + + /// + /// Path to the privoxy.exe file
+ /// If this value isn't set, will try to recover it using
+ /// (Default: null) + ///
+ public string PrivoxyFile { get; set; } = null; + + #endregion + + #region Process Arguments + + /// + /// Arguments that are used when starting the process
+ /// Useful when you want to add arguments that are not natively supported by Trou
+ /// -> List of the supported Privoxy arguments here + ///
+ public readonly Dictionary Arguments = new Dictionary(); + + #endregion + #region Configuration + + /// + /// Configuration pairs that are used when starting the process
+ /// Useful when you want to add configuration pairs that are not natively supported by Trou
+ /// -> List of the supported Tor arguments here + ///
+ public readonly Dictionary Configuration = new Dictionary(); + + #region Network + + /// + /// Address that the is listening at (HTTP)
+ /// -> Documentation here
+ /// (Default: 127.0.0.1) + ///
+ public string ListeningAddress { get; set; } = "127.0.0.1"; + + /// + /// Port that the is listening at (HTTP)
+ /// -> Documentation here
+ /// (Default: 8118) + ///
+ public int ListeningPort { get; set; } = 8118; + + #region Forward + + /// + /// Address that the will forward requests to (SOCKS 5)
+ /// -> Documentation here
+ /// (Default: 127.0.0.1) + ///
+ public string ForwardAddress { get; set; } = "127.0.0.1"; + + /// + /// Port that the will forward requests to (SOCKS 5)
+ /// -> Documentation here
+ /// (Default: 9050) + ///
+ public int ForwardPort { get; set; } = 9050; + + #endregion + + #endregion + + #endregion + + } + +} diff --git a/Trou/ServicesSettings/TorControllerSettings.cs b/Trou/ServicesSettings/TorControllerSettings.cs new file mode 100644 index 0000000..c2cd575 --- /dev/null +++ b/Trou/ServicesSettings/TorControllerSettings.cs @@ -0,0 +1,34 @@ +using Trou.Services; + +namespace Trou.ServicesSettings { + + /// + /// Allows to configure a + /// + public class TorControllerSettings { + + #region Network + + /// + /// Address that the will connect to
+ /// (Default: 127.0.0.1) + ///
+ public string TorAddress { get; set; } = "127.0.0.1"; + + /// + /// Port that the will connect to
+ /// (Default: 9051) + ///
+ public int TorPort { get; set; } = 9051; + + /// + /// Password that the will use to authenticate to the
+ /// (Default: null) + ///
+ public string ControlPassword { get; set; } = null; + + #endregion + + } + +} diff --git a/Trou/ServicesSettings/TorProxySettings.cs b/Trou/ServicesSettings/TorProxySettings.cs new file mode 100644 index 0000000..71b60fa --- /dev/null +++ b/Trou/ServicesSettings/TorProxySettings.cs @@ -0,0 +1,201 @@ +using System.Collections.Generic; +using Trou.Services; + +namespace Trou.ServicesSettings { + + /// + /// Allows to configure a + /// + public class TorProxySettings { + + #region Paths + + /// + /// Path to the extracted Tor Bundle folder (folder that contains the Tor and Data folders). This is the fastest way of configuring paths
+ /// For a more advanced paths configuration, you can set every paths manually (if those paths are set, they will not be recovered from path):
+ /// , , , + ///
+ public string TorBundlePath { get; set; } = null; + + /// + /// Path to the tor.exe file
+ /// If this value isn't set, will try to recover it using
+ /// (Default: null) + ///
+ public string TorFile { get; set; } = null; + + /// + /// Path where the cache will be stored
+ /// If this value isn't set, will use your ApplicationData folder
+ /// -> Documentation here
+ /// (Default: null) + ///
+ public string CachePath { get; set; } = null; + + #region GeoIPFiles + + /// + /// Path to the geoip file (this file is used when using nodes filtering) + /// If this value isn't set, will try to recover it using
+ /// -> Documentation here
+ /// (Default: null) + ///
+ public string GeoIPFile { get; set; } = null; + + /// + /// Path to the geoip6 file (this file is used when using nodes filtering) + /// If this value isn't set, will try to recover it using
+ /// -> Documentation here
+ /// (Default: null) + ///
+ public string GeoIPv6File { get; set; } = null; + + #endregion + + #endregion + + #region Process Arguments + + /// + /// Arguments that are used when starting the process
+ /// Useful when you want to add arguments that are not natively supported by Trou
+ /// -> List of the supported Tor arguments here + ///
+ public readonly Dictionary Arguments = new Dictionary(); + + #region Network + + /// + /// Address that the is listening at (SOCKS 5)
+ /// -> Documentation here
+ /// (Default: 127.0.0.1) + ///
+ public string ListeningAddress { get; set; } = "127.0.0.1"; + + /// + /// Port that the is listening at (SOCKS 5)
+ /// -> Documentation here
+ /// (Default: 9050) + ///
+ public int ListeningPort { get; set; } = 9050; + + #region Controller + + /// + /// Determines if should listen to
+ /// (Default: true) + ///
+ public bool UseController { get; set; } = true; + + /// + /// Address that the is listening at (for the )
+ /// -> Documentation here
+ /// (Default: 127.0.0.1) + ///
+ public string ListeningControlAddress { get; set; } = "127.0.0.1"; + + /// + /// Port that the is listening at (for the )
+ /// -> Documentation here
+ /// (Default: 9051) + ///
+ public int ListeningControlPort { get; set; } = 9051; + + #region Password + + /// + /// Password that is required for the when he wants to control the
+ /// If the is set, will ignore
+ /// -> Documentation here
+ /// (Default: null) + ///
+ public string ControlPassword { get; set; } = null; + + /// + /// Password that is required for the when he wants to control the
+ /// If the is set, will ignore
+ /// -> Documentation here
+ /// (Default: null) + ///
+ public string HashedControlPassword { get; set; } = null; + + #endregion + + #endregion + + #region Nodes filtering + + /// + /// Determines if should strictly respect the nodes filtering
+ /// (Default: true) + ///
+ public bool StrictNodes { get; set; } = false; + + #region Whitelists + + /// + /// List of accepted exit nodes
+ /// -> Documentation here
+ /// (Default: empty) + ///
+ public readonly List ExitNodes = new List(); + + /// + /// List of accepted middle nodes
+ /// -> Documentation here
+ /// (Default: empty) + ///
+ public readonly List MiddleNodes = new List(); + + /// + /// List of accepted entry nodes
+ /// -> Documentation here
+ /// (Default: empty) + ///
+ public readonly List EntryNodes = new List(); + + #endregion + #region Blacklists + + /// + /// List of non-accepted nodes
+ /// -> Documentation here
+ /// (Default: empty) + ///
+ public readonly List ExcludeNodes = new List(); + + /// + /// List of non-accepted exit nodes
+ /// -> Documentation here
+ /// (Default: empty) + ///
+ public readonly List ExcludeExitNodes = new List(); + + #endregion + + #endregion + #region Traffic + + /// + /// Determines if the shouldn't accept connections to .onion websites
+ /// -> Documentation here
+ /// (Default: false) + ///
+ public bool NoOnionTraffic { get; set; } = false; + + /// + /// Determines if the should only accept connections to .onion websites
+ /// -> Documentation here
+ /// (Default: false) + ///
+ public bool OnionTrafficOnly { get; set; } = false; + + #endregion + + #endregion + + #endregion + + } + +} \ No newline at end of file diff --git a/Trou/Tools/ChildProcessTracker.cs b/Trou/Tools/ChildProcessTracker.cs new file mode 100644 index 0000000..ba4d127 --- /dev/null +++ b/Trou/Tools/ChildProcessTracker.cs @@ -0,0 +1,135 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Trou.Tools { + + /// + /// Allows processes to be automatically killed if this parent process unexpectedly quits. + /// This feature requires Windows 8 or greater. On Windows 7, nothing is done. + /// References: + /// http://stackoverflow.com/a/4657392/386091 + /// http://stackoverflow.com/a/9164742/386091 + public static class ChildProcessTracker { + + /// + /// Add the process to be tracked. If our current process is killed, the child processes + /// that we are tracking will be automatically killed, too. If the child process terminates + /// first, that's fine, too. + /// + public static void AddProcess(Process process) { + if (s_jobHandle != IntPtr.Zero) { + bool success = AssignProcessToJobObject(s_jobHandle, process.Handle); + if (!success) + throw new Win32Exception(); + } + } + + static ChildProcessTracker() { + // This feature requires Windows 8 or later. To support Windows 7 requires + // registry settings to be added if you are using Visual Studio plus an + // app.manifest change. + // http://stackoverflow.com/a/4232259/386091 + // http://stackoverflow.com/a/9507862/386091 + if (Environment.OSVersion.Version < new Version(6, 2)) + return; + + // The job name is optional (and can be null) but it helps with diagnostics. + // If it's not null, it has to be unique. Use SysInternals' Handle command-line + // utility: handle -a ChildProcessTracker + string jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id; + s_jobHandle = CreateJobObject(IntPtr.Zero, jobName); + + var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION { + + // This is the key flag. When our process is killed, Windows will automatically + // close the job handle, and when that happens, we want the child processes to + // be killed, too. + LimitFlags = JOBOBJECTLIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE + + }; + + var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION { + BasicLimitInformation = info + }; + + int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); + IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); + try { + Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); + + if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation, + extendedInfoPtr, (uint)length)) { + throw new Win32Exception(); + } + } finally { + Marshal.FreeHGlobal(extendedInfoPtr); + } + } + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name); + + [DllImport("kernel32.dll")] + static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, + IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); + + // Windows will automatically close any open job handles when our process terminates. + // This can be verified by using SysInternals' Handle utility. When the job handle + // is closed, the child processes will be killed. + private static readonly IntPtr s_jobHandle; + } + + public enum JobObjectInfoType { + AssociateCompletionPortInformation = 7, + BasicLimitInformation = 2, + BasicUIRestrictions = 4, + EndOfJobTimeInformation = 6, + ExtendedLimitInformation = 9, + SecurityLimitInformation = 5, + GroupInformation = 11 + } + + [StructLayout(LayoutKind.Sequential)] + public struct JOBOBJECT_BASIC_LIMIT_INFORMATION { + public Int64 PerProcessUserTimeLimit; + public Int64 PerJobUserTimeLimit; + public JOBOBJECTLIMIT LimitFlags; + public UIntPtr MinimumWorkingSetSize; + public UIntPtr MaximumWorkingSetSize; + public UInt32 ActiveProcessLimit; + public Int64 Affinity; + public UInt32 PriorityClass; + public UInt32 SchedulingClass; + } + + [Flags] + public enum JOBOBJECTLIMIT : uint { + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000 + } + + [StructLayout(LayoutKind.Sequential)] + public struct IO_COUNTERS { + public UInt64 ReadOperationCount; + public UInt64 WriteOperationCount; + public UInt64 OtherOperationCount; + public UInt64 ReadTransferCount; + public UInt64 WriteTransferCount; + public UInt64 OtherTransferCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { + public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; + public IO_COUNTERS IoInfo; + public UIntPtr ProcessMemoryLimit; + public UIntPtr JobMemoryLimit; + public UIntPtr PeakProcessMemoryUsed; + public UIntPtr PeakJobMemoryUsed; + } + +} diff --git a/Trou/Tools/HiddenProcess.cs b/Trou/Tools/HiddenProcess.cs new file mode 100644 index 0000000..a1ed93f --- /dev/null +++ b/Trou/Tools/HiddenProcess.cs @@ -0,0 +1,353 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Trou.Tools { + + /// + /// Allows to start processes, they are placed in a virtual desktop in order to be hidden + /// + public class HiddenProcess : IDisposable { + + #region Variables + + #region Path + + /// + /// working directory + /// + public string WorkingDirectory; + + /// + /// executable file path + /// + public string FileName; + + #endregion + + #region Process + + /// + /// process instance + /// + public Process Process; + + /// + /// Determines if the should close when his parent process close + /// + public readonly bool EndAsParent; + + /// + /// arguments + /// + public string Arguments; + + /// + /// priority + /// + public PriorityClass Priority; + + /// + /// read buffer size + /// + public uint ReadBufferSize = 4096; + + #endregion + + #region Events + + /// + /// Triggered when the process has completely started + /// + public event EventHandler OnStart; + + /// + /// Triggered when the process output something + /// + public event EventHandler OnOutput; + + #endregion + + #endregion + + /// + /// Initializes a new intance of the class + /// + public HiddenProcess(string workingDirectory, string fileName, string arguments, PriorityClass priority, bool endAsParent) { + + // Save + WorkingDirectory = workingDirectory; + FileName = fileName; + Arguments = arguments; + EndAsParent = endAsParent; + + Priority = priority; + + } + + /// + /// Starts the process, returns true if the process is started successfully + /// + public bool Start() { + + #region Create desktop + + // Create process virtual desktop + CreateDesktop("HiddenDesktop", IntPtr.Zero, IntPtr.Zero, 0, (uint)ACCESS_MASK.GENERIC_ALL, IntPtr.Zero); + + #endregion + + #region Create process + + // Process informations (this is set after the process is started) + PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION(); + // Process startup informations + STARTUPINFO sInfo = new STARTUPINFO { + + dwFlags = 0x100, // STARTF_USESTDHANDLES + lpDesktop = "HiddenDesktop" + + }; + + #region Pipe + + // Pipes security attributes + SECURITY_ATTRIBUTES pipeSecurityAttributes = new SECURITY_ATTRIBUTES() { + nLength = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES)), + lpSecurityDescriptor = IntPtr.Zero, + bInheritHandle = 1 + }; + + // Pipe handles + IntPtr hRead = IntPtr.Zero; + IntPtr hWrite = IntPtr.Zero; + + // We want to read the process output + if (OnOutput != null) { + + // The pipe has been created successfully + if (CreatePipe(out hRead, out hWrite, ref pipeSecurityAttributes, 0)) { + + // Set process error output, and start reading + sInfo.hStdOutput = hWrite; + Task.Run(() => ReadOutput(hRead)); + + } else { return false; } + + } + + #endregion + + // Create the process + if (!CreateProcess(null, string.Format("{0} {1}", FileName, Arguments), IntPtr.Zero, IntPtr.Zero, true, (uint)Priority, IntPtr.Zero, WorkingDirectory, ref sInfo, out pInfo)) { + return false; + } + + // Get process and save it + Process = Process.GetProcessById(pInfo.dwProcessId); + + // Trigger event + OnStart?.Invoke(this, Process); + + // Close process handles + CloseHandle(pInfo.hProcess); + CloseHandle(pInfo.hThread); + + // Close write pipe + CloseHandle(hWrite); + + #endregion + + #region Process check + + // Check if the process isn't already closed + // (Can occur when it's for example a console app) + if (Process != null && !Process.HasExited) { + + // If the process should end when the parent process end + if (EndAsParent) { + + // Track the process to end when the parent end + ChildProcessTracker.AddProcess(Process); + + } + + } + + #endregion + + return true; + + } + + /// + /// Reads the process output from an handle + /// + private void ReadOutput(IntPtr readHandle) { + + // Buffer & read length + byte[] buffer = new byte[ReadBufferSize]; + uint dwRead = 0; + + // Read as long as the process is available (not closed..) + while (ReadFile(readHandle, buffer, ReadBufferSize, ref dwRead, IntPtr.Zero)) { + + // For every buffer lines + foreach (string line in Encoding.UTF8.GetString(buffer, 0, Convert.ToInt32(dwRead)).Split(Environment.NewLine)) { + + // If the line isn't empty + if (!string.IsNullOrEmpty(line)) { + + // Trigger event + OnOutput?.Invoke(this, line.Trim()); + + } + + } + + // Reset buffer + buffer = new byte[ReadBufferSize]; + + } + + // Close read handle + CloseHandle(readHandle); + + } + + /// + /// Kill and dispose the process + /// + public void Dispose() { + + // Dispose process + Process?.Kill(); + Process?.Dispose(); + + } + + #region Enums + + #region Process priorities + + /// + /// Process priority enumeration + /// + public enum PriorityClass { + + ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000, + BELOW_NORMAL_PRIORITY_CLASS = 0x00004000, + HIGH_PRIORITY_CLASS = 0x00000080, + IDLE_PRIORITY_CLASS = 0x00000040, + NORMAL_PRIORITY_CLASS = 0x00000020, + REALTIME_PRIORITY_CLASS = 0x00000100 + + } + + + #endregion + #region Security Attributes + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_ATTRIBUTES { + public int nLength; + public IntPtr lpSecurityDescriptor; + public int bInheritHandle; + } + + #endregion + + #endregion + #region Dll-Imports + + #region Desktops + + [DllImport("user32.dll")] + public static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags, uint dwDesiredAccess, IntPtr lpsa); + + [Flags] + internal enum ACCESS_MASK : uint { + DESKTOP_NONE = 0, + DESKTOP_READOBJECTS = 0x0001, + DESKTOP_CREATEWINDOW = 0x0002, + DESKTOP_CREATEMENU = 0x0004, + DESKTOP_HOOKCONTROL = 0x0008, + DESKTOP_JOURNALRECORD = 0x0010, + DESKTOP_JOURNALPLAYBACK = 0x0020, + DESKTOP_ENUMERATE = 0x0040, + DESKTOP_WRITEOBJECTS = 0x0080, + DESKTOP_SWITCHDESKTOP = 0x0100, + + GENERIC_ALL = (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | + DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | + DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP), + } + + #endregion + + #region Pipes + + [DllImport("kernel32.dll")] + static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool ReadFile(IntPtr handle, byte[] buffer, uint toRead, ref uint read, IntPtr lpOverLapped); + + #endregion + #region Create process + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool CreateProcess( + string lpApplicationName, + string lpCommandLine, + IntPtr lpProcessAttributes, + IntPtr lpThreadAttributes, + bool bInheritHandles, + uint dwCreationFlags, + IntPtr lpEnvironment, + string lpCurrentDirectory, + [In] ref STARTUPINFO lpStartupInfo, + out PROCESS_INFORMATION lpProcessInformation); + + [StructLayout(LayoutKind.Sequential)] + internal struct PROCESS_INFORMATION { + public IntPtr hProcess; + public IntPtr hThread; + public int dwProcessId; + public int dwThreadId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct STARTUPINFO { + public Int32 cb; + public string lpReserved; + public string lpDesktop; + public string lpTitle; + public Int32 dwX; + public Int32 dwY; + public Int32 dwXSize; + public Int32 dwYSize; + public Int32 dwXCountChars; + public Int32 dwYCountChars; + public Int32 dwFillAttribute; + public Int32 dwFlags; + public Int16 wShowWindow; + public Int16 cbReserved2; + public IntPtr lpReserved2; + public IntPtr hStdInput; + public IntPtr hStdOutput; + public IntPtr hStdError; + } + + #endregion + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool CloseHandle(IntPtr handle); + + #endregion + + } + +} diff --git a/Trou/Trou.csproj b/Trou/Trou.csproj new file mode 100644 index 0000000..5d4709c --- /dev/null +++ b/Trou/Trou.csproj @@ -0,0 +1,35 @@ + + + + netcoreapp3.1 + NaolShow + ToWolf + https://github.com/NaolShow/Trou + README.md + © 2020 NaolShow, ToWolf + https://github.com/NaolShow/Trou/blob/master/logo.png?raw=true + logo.png + https://github.com/NaolShow/Trou + 1.0.0 + 1.0.0 + Tor,Privoxy,HTTP,Socks,Socks5 + Tor + Privoxy for the best anonymous HTTP proxy implementation on C# + true + true + false + + + + C:\Users\Loan\Dropbox\Developpement\CSharp\--Network\Trou\Trou\Trou.xml + + + + + + + True + + + + + diff --git a/Trou/Trou.xml b/Trou/Trou.xml new file mode 100644 index 0000000..0e696ed --- /dev/null +++ b/Trou/Trou.xml @@ -0,0 +1,582 @@ + + + + Trou + + + + + Allows to configure a + + + + + Path to the extracted Privoxy Bundle folder (folder that contains the privoxy.exe file). This is the fastest way of configuring paths.
+ For a more advanced paths configuration, you can set every paths manually (if those paths are set, they will not be recovered from path):
+ +
+
+ + + Path to the privoxy.exe file
+ If this value isn't set, will try to recover it using
+ (Default: null) +
+
+ + + Arguments that are used when starting the process
+ Useful when you want to add arguments that are not natively supported by Trou
+ -> List of the supported Privoxy arguments here +
+
+ + + Configuration pairs that are used when starting the process
+ Useful when you want to add configuration pairs that are not natively supported by Trou
+ -> List of the supported Tor arguments here +
+
+ + + Address that the is listening at (HTTP)
+ -> Documentation here
+ (Default: 127.0.0.1) +
+
+ + + Port that the is listening at (HTTP)
+ -> Documentation here
+ (Default: 8118) +
+
+ + + Address that the will forward requests to (SOCKS 5)
+ -> Documentation here
+ (Default: 127.0.0.1) +
+
+ + + Port that the will forward requests to (SOCKS 5)
+ -> Documentation here
+ (Default: 9050) +
+
+ + + Allows to configure a + + + + + Address that the will connect to
+ (Default: 127.0.0.1) +
+
+ + + Port that the will connect to
+ (Default: 9051) +
+
+ + + Password that the will use to authenticate to the
+ (Default: null) +
+
+ + + Allows to configure a + + + + + Path to the extracted Tor Bundle folder (folder that contains the Tor and Data folders). This is the fastest way of configuring paths
+ For a more advanced paths configuration, you can set every paths manually (if those paths are set, they will not be recovered from path):
+ , , , +
+
+ + + Path to the tor.exe file
+ If this value isn't set, will try to recover it using
+ (Default: null) +
+
+ + + Path where the cache will be stored
+ If this value isn't set, will use your ApplicationData folder
+ -> Documentation here
+ (Default: null) +
+
+ + + Path to the geoip file (this file is used when using nodes filtering) + If this value isn't set, will try to recover it using
+ -> Documentation here
+ (Default: null) +
+
+ + + Path to the geoip6 file (this file is used when using nodes filtering) + If this value isn't set, will try to recover it using
+ -> Documentation here
+ (Default: null) +
+
+ + + Arguments that are used when starting the process
+ Useful when you want to add arguments that are not natively supported by Trou
+ -> List of the supported Tor arguments here +
+
+ + + Address that the is listening at (SOCKS 5)
+ -> Documentation here
+ (Default: 127.0.0.1) +
+
+ + + Port that the is listening at (SOCKS 5)
+ -> Documentation here
+ (Default: 9050) +
+
+ + + Determines if should listen to
+ (Default: true) +
+
+ + + Address that the is listening at (for the )
+ -> Documentation here
+ (Default: 127.0.0.1) +
+
+ + + Port that the is listening at (for the )
+ -> Documentation here
+ (Default: 9051) +
+
+ + + Password that is required for the when he wants to control the
+ If the is set, will ignore
+ -> Documentation here
+ (Default: null) +
+
+ + + Password that is required for the when he wants to control the
+ If the is set, will ignore
+ -> Documentation here
+ (Default: null) +
+
+ + + Determines if should strictly respect the nodes filtering
+ (Default: true) +
+
+ + + List of accepted exit nodes
+ -> Documentation here
+ (Default: empty) +
+
+ + + List of accepted middle nodes
+ -> Documentation here
+ (Default: empty) +
+
+ + + List of accepted entry nodes
+ -> Documentation here
+ (Default: empty) +
+
+ + + List of non-accepted nodes
+ -> Documentation here
+ (Default: empty) +
+
+ + + List of non-accepted exit nodes
+ -> Documentation here
+ (Default: empty) +
+
+ + + Determines if the shouldn't accept connections to .onion websites
+ -> Documentation here
+ (Default: false) +
+
+ + + Determines if the should only accept connections to .onion websites
+ -> Documentation here
+ (Default: false) +
+
+ + + Allows to start a which will listen to an IP and a Port for HTTP requests + + + + + hidden process + + + + + process executable path + + + + + settings + + + + + Initializes a new intance of the service + + + + + Refreshes every Arguments and Configuration keys from the before starting the process + + + + + Starts the service + + + + + Dispose the service + + + + + Allows to start a which can interact with a + + + + + settings + + + + + tcp client + + + + + writer + + + + + reader + + + + + Initializes a new intance of the service + + + + + Refresh the circuit of (changes every nodes so get new IP Address)
+ Returns true if the refresh was successful +
+
+ + + Authenticates to
+ Returns true if the authentication was successful +
+
+ + + Quit/Log-Off from
+ Returns true if the log-off was successful +
+
+ + + Stops completely the
+ Returns true if the shutdown was successful +
+
+ + + Sets to a "sleep mode"
+ Returns true if the sleep mode set was successful +
+
+ + + Removes the "sleep mode" (Dormant) from + Returns true if the sleep mode remove was successful + + + + + Sends a text message to
+ Returns true if the sending was successful +
+
+ + + Starts the service + + + + + Dispose the service + + + + + Allows to start a which will listen to an IP and a Port for SOCKS 5 requests + + + + + hidden process + + + + + process executable path + + + + + settings + + + + + Triggered when process output informations + + + + + Triggered when process output warns or errors + + + + + Initializes a new intance of the service + + + + + Refreshes every Arguments from the before starting the process + + + + + Triggered when we receive process output that needs to be forwarded to events + + + + + Starts the service + + + + + Dispose the service + + + + + Sets an argument that can be active or not
+ (If the argument isn't active it will be removed) +
+
+ + + Sets a toggle argument and remove it if the value is false + + + + + Sets an argument to a list and remove it if the list is empty + + + + + Sets a path argument, throw an exception if the path isn't valid + + + + + Hash a password using the process + + + + + Allows processes to be automatically killed if this parent process unexpectedly quits. + This feature requires Windows 8 or greater. On Windows 7, nothing is done. + References: + http://stackoverflow.com/a/4657392/386091 + http://stackoverflow.com/a/9164742/386091 + + + + Add the process to be tracked. If our current process is killed, the child processes + that we are tracking will be automatically killed, too. If the child process terminates + first, that's fine, too. + + + + + Allows to start processes, they are placed in a virtual desktop in order to be hidden + + + + + working directory + + + + + executable file path + + + + + process instance + + + + + Determines if the should close when his parent process close + + + + + arguments + + + + + priority + + + + + read buffer size + + + + + Triggered when the process has completely started + + + + + Triggered when the process output something + + + + + Initializes a new intance of the class + + + + + Starts the process, returns true if the process is started successfully + + + + + Reads the process output from an handle + + + + + Kill and dispose the process + + + + + Process priority enumeration + + + + + Trou Proxy - .NET Core library made by NaolShow + -> https://github.com/NaolShow/Trou + + If you like the project, please star it :) (It's helping me a lot :D) + + + + Allows to start , and simultaneously + + + + + instance + + + + + instance + + + + + instance + + + + + Initializes a new intance of the service + + + + + Starts the service + + + + + Dispose the service + + +
+
diff --git a/Trou/TrouProxy.cs b/Trou/TrouProxy.cs new file mode 100644 index 0000000..65aa455 --- /dev/null +++ b/Trou/TrouProxy.cs @@ -0,0 +1,78 @@ +using System; +using Trou.Services; +using Trou.ServicesSettings; + +namespace Trou { + + /** + * + * Trou Proxy - .NET Core library made by NaolShow + * -> https://github.com/NaolShow/Trou + * + * If you like the project, please star it :) (It's helping me a lot :D) + * + **/ + + /// + /// Allows to start , and simultaneously + /// + public class TrouProxy : IDisposable { + + /// + /// instance + /// + public TorProxy Tor; + + /// + /// instance + /// + public TorController Controller; + + /// + /// instance + /// + public PrivoxyProxy Privoxy; + + #region Constructors + + /// + /// Initializes a new intance of the service + /// + public TrouProxy(TorProxySettings torSettings, TorControllerSettings controllerSettings, PrivoxyProxySettings privoxySettings) { + + // -- Instance + + Tor = new TorProxy(torSettings); + Controller = new TorController(controllerSettings); + + Privoxy = new PrivoxyProxy(privoxySettings); + + } + + #endregion + + #region On/Off/Terminate + + /// + /// Starts the service + /// + public void Start() { + Tor.Start(); + Controller.Start(); + Privoxy.Start(); + } + + /// + /// Dispose the service + /// + public void Dispose() { + Tor.Dispose(); + Controller.Dispose(); + Privoxy.Dispose(); + } + + #endregion + + } + +} diff --git a/TrouExample/Program.cs b/TrouExample/Program.cs new file mode 100644 index 0000000..a47d095 --- /dev/null +++ b/TrouExample/Program.cs @@ -0,0 +1,51 @@ +using System; +using System.Net; +using Trou; +using Trou.ServicesSettings; + +namespace TrouExample { + + /// + /// Program class + /// + public class Program { + + /// + /// Entry point + /// + private static void Main() { + + // - Instantiate Trou proxy + + TrouProxy proxy = new TrouProxy(new TorProxySettings() { + TorBundlePath = @"C:\Users\Loan\Dropbox\Developpement\CSharp\--Network\Trou\TrouExample\bin\Debug\netcoreapp3.0\Proxies\TorProxy" + }, new TorControllerSettings() { + + }, new PrivoxyProxySettings() { + PrivoxyBundlePath = @"C:\Users\Loan\Dropbox\Developpement\CSharp\--Network\Trou\TrouExample\bin\Debug\netcoreapp3.0\Proxies\PrivoxyProxy" + }); + + // - Start + + proxy.Start(); + + // - Check ip + + // Create client connected to Tor using Trou + WebClient client = new WebClient() { + Proxy = new WebProxy("127.0.0.1:8118") + }; + + // Write TOR Ip address + Console.WriteLine(client.DownloadString("http://api.ipify.org")); + + // - Stop + Console.ReadLine(); + client.Dispose(); + proxy.Dispose(); + + } + + } + +} diff --git a/TrouExample/TrouExample.csproj b/TrouExample/TrouExample.csproj new file mode 100644 index 0000000..cb3a743 --- /dev/null +++ b/TrouExample/TrouExample.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + + diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..cb55a32 Binary files /dev/null and b/logo.png differ