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