Files
Tor/TorWebClient/Program.cs

482 lines
18 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tor;
using Tor.Config;
using MarketData;
using MarketData.Utils;
using Utility=MarketData.Utils.Utility;
// This project must be built in 32 bit mode. Due to the size of IntPtr being 64 and not 32 as needed.
// Tor Current Version
// https://www.torproject.org/download/tor/
// Tor older versions
// https://archive.torproject.org/tor-package-archive/torbrowser/
// Tor Command Line Options
// https://2019.www.torproject.org/docs/tor-manual.html.en
#pragma warning disable 618, 3021, 169
namespace TorWebClient
{
public class Program
{
private static readonly int TIMER_INTERVAL_MINUTES=2;
static readonly string agentName="Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
[DllImport("user32.dll")]
static extern bool SetProcessDPIAware();
[DllImport("wininet.dll",SetLastError=true,CharSet=CharSet.Auto)]
static extern IntPtr InternetOpen(string lpszAgent,int dwAccessType,string lpszProxyName,string lpszProxyBypass,int dwFlags);
[DllImport("wininet.dll",SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool InternetCloseHandle(IntPtr hInternet);
[DllImport("wininet.dll",CharSet=CharSet.Ansi,SetLastError=true)]
static extern bool InternetSetOption(IntPtr hInternet,INTERNET_OPTION dwOption,IntPtr lpBuffer,int lpdwBufferLength);
[DllImport("wininet.dll",CharSet=CharSet.Ansi,SetLastError=true)]
static extern bool InternetQueryOptionList(IntPtr handle,INTERNET_OPTION optionFlag,ref INTERNET_PER_CONN_OPTION_LIST optionList,ref int size);
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine handler,bool add);
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
struct INTERNET_PER_CONN_OPTION_LIST
{
public int Size;
public IntPtr Connection;
public int OptionCount;
public int OptionError;
public IntPtr pOptions;
}
enum INTERNET_OPTION
{
INTERNET_OPTION_PER_CONNECTION_OPTION=75,
INTERNET_OPTION_SETTINGS_CHANGED=39,
INTERNET_OPTION_REFRESH=37
}
enum INTERNET_PER_CONN_OPTIONENUM
{
INTERNET_PER_CONN_FLAGS=1,
INTERNET_PER_CONN_PROXY_SERVER=2,
INTERNET_PER_CONN_PROXY_BYPASS=3,
INTERNET_PER_CONN_AUTOCONFIG_URL=4,
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS=5,
INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL=6,
INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS=7,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME=8,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL=9,
INTERNET_PER_CONN_FLAGS_UI=10
}
const int INTERNET_OPEN_TYPE_DIRECT=1;
const int INTERNET_OPEN_TYPE_PRECONFIG=0;
enum INTERNET_OPTION_PER_CONN_FLAGS
{
PROXY_TYPE_DIRECT=0x00000001, // direct to net
PROXY_TYPE_PROXY=0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL=0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT=0x00000008 // use autoproxy detection
}
[StructLayout(LayoutKind.Explicit)]
struct INTERNET_PER_CONN_OPTION_OPTIONUNION
{
[FieldOffset(0)]
public int dwValue;
[FieldOffset(0)]
public IntPtr pszValue;
[FieldOffset(0)]
public FILETIME ftValue;
}
[StructLayout(LayoutKind.Sequential)]
struct INTERNET_PER_CONN_OPTION
{
public int dwOption;
public INTERNET_PER_CONN_OPTION_OPTIONUNION value;
}
private static Program instance=null;
private RouterCollection allRouters;
private volatile bool closing;
private CircuitCollection circuits;
private Client client;
private Timer dispatcherTimer;
private Program()
{
closing=false;
dispatcherTimer=new Timer(new TimerCallback(OnDispatcherTimerTick),null,0,(TIMER_INTERVAL_MINUTES*60*1000)); // every minute
}
public ManualResetEvent ResetEvent{get;set;}
public static Program GetInstance()
{
lock(typeof(Program))
{
if(null==instance)instance=new Program();
return instance;
}
}
private void OnDispatcherTimerTick(object sender)
{
lock(this)
{
if(null==client) return;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} DispatcherTimerTick : Total Uploaded: {1}, Total Downloaded: {2}",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now),client.Status.TotalBytesUploaded.ToString(),client.Status.TotalBytesDownloaded.ToString()));
}
}
private void OnClientBandwidthChanged(object sender,BandwidthEventArgs e)
{
//lock(this)
//{
// MDTrace.WriteLine(LogLevel.DEBUG,"OnClientBandwidthChanged");
// if(closing) return;
//}
}
private void OnClientCircuitsChanged(object sender,EventArgs e)
{
//lock(this)
//{
// MDTrace.WriteLine(LogLevel.DEBUG,"OnClientCircuitsChanged");
// circuits=client.Status.Circuits;
// if(closing) return;
//}
}
private void OnClientConnectionsChanged(object sender,EventArgs e)
{
//lock(this)
//{
//MDTrace.WriteLine(LogLevel.DEBUG,"OnClientConnectionsChanged");
//if(closing) return;
//}
}
private void OnClientStreamsChanged(object sender,EventArgs e)
{
//lock(this)
//{
// MDTrace.WriteLine(LogLevel.DEBUG,"OnClientStreamsChanged");
// if(closing) return;
//}
}
private void OnClientShutdown(object sender,EventArgs e)
{
lock(this)
{
MDTrace.WriteLine(LogLevel.DEBUG,"OnClientShutdown");
if(!closing)
{
MDTrace.WriteLine(LogLevel.DEBUG,"OnClientShutdown : The tor client has been terminated without warning");
return;
}
client=null;
}
}
public void Start()
{
lock(this)
{
Process[] previous=Process.GetProcessesByName("tor");
if(previous!=null&&previous.Length>0)
{
MDTrace.WriteLine(LogLevel.DEBUG,"Killing previous tor instances..");
foreach(Process process in previous) process.Kill();
}
MDTrace.WriteLine(LogLevel.DEBUG,"Creating the tor client..");
ClientCreateParams createParams=new ClientCreateParams();
createParams.CaptureTorConsoleOutput=Boolean.Parse(ConfigurationManager.AppSettings["captureTorConsoleOuput"]);
createParams.ConfigurationFile=ConfigurationManager.AppSettings["torConfigurationFile"];
createParams.ControlPassword=ConfigurationManager.AppSettings["torControlPassword"];
createParams.ControlPort=Convert.ToInt32(ConfigurationManager.AppSettings["torControlPort"]);
createParams.DefaultConfigurationFile=ConfigurationManager.AppSettings["torDefaultConfigurationFile"];
createParams.Path=ConfigurationManager.AppSettings["torPath"];
createParams.SetConfig(ConfigurationNames.AvoidDiskWrites,true);
createParams.SetConfig(ConfigurationNames.GeoIPFile,ConfigurationManager.AppSettings["geoipfile"]);
createParams.SetConfig(ConfigurationNames.GeoIPv6File,ConfigurationManager.AppSettings["geoip6file"]);
createParams.SetConfig(ConfigurationNames.ExitNodes,"{US}"); // making the final exit node a U.S. node provides better ligitimacy
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Current Working Directory:{0}",Directory.GetCurrentDirectory()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TORPath:{0}",ConfigurationManager.AppSettings["torPath"]));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TORConfiguration:{0}",ConfigurationManager.AppSettings["torConfigurationFile"]));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TORControlPort:{0}",ConfigurationManager.AppSettings["torControlPort"]));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GEOIPFile:{0}",ConfigurationManager.AppSettings["geoipfile"]));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GEOIPV6File:{0}",ConfigurationManager.AppSettings["geoip6file"]));
CreateTorConfig(createParams.ConfigurationFile);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOR Arguments:{0}",createParams.ToString()));
client=Client.Create(createParams);
try{Thread.Sleep(2500);}catch(Exception){;}
client.Configuration.Save();
if(!client.IsRunning)
{
MDTrace.WriteLine(LogLevel.DEBUG,"The tor client could not be created");
return;
}
if(!client.Proxy.IsRunning)
{
MDTrace.WriteLine(LogLevel.DEBUG,"The proxy is not running");
return;
}
client.Status.BandwidthChanged+=OnClientBandwidthChanged;
client.Status.CircuitsChanged+=OnClientCircuitsChanged;
client.Status.ORConnectionsChanged+=OnClientConnectionsChanged;
client.Status.StreamsChanged+=OnClientStreamsChanged;
client.Shutdown+=new EventHandler(OnClientShutdown);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Setting Proxy Configuration: 127.0.0.1:{0}",client.Proxy.Port));
if(!Program.SetConnectionProxy(string.Format("127.0.0.1:{0}",client.Proxy.Port)))
{
MDTrace.WriteLine(LogLevel.DEBUG,"The application could not set the default connection proxy. The browser control is not using the tor service as a proxy!");
}
allRouters=client.Status.GetAllRouters();
if(null!=allRouters)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Got {0} routers",allRouters.Count));
}
//DisplayRouters(allRouters);
//if(null==allRouters)
//{
// MDTrace.WriteLine(LogLevel.DEBUG,"Could not download routers");
//}
}
}
public bool CreateTorConfig(String pathTorConfig)
{
try
{
String currentWorkingDirectory = Directory.GetCurrentDirectory();
pathTorConfig=currentWorkingDirectory+@"\Tor\Tor\"+pathTorConfig;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Creating {0} ",pathTorConfig));
String pathBackupTorConfig=pathTorConfig+".bak";
if(File.Exists(pathTorConfig))
{
if(File.Exists(pathBackupTorConfig))File.Delete(pathBackupTorConfig);
File.Copy(pathTorConfig, pathBackupTorConfig);
File.Delete(pathTorConfig);
}
FileStream outStream = new FileStream(pathTorConfig,FileMode.CreateNew, FileAccess.Write);
StreamWriter streamWriter = new StreamWriter(outStream);
streamWriter.WriteLine("# Created by TorWebClient");
streamWriter.WriteLine("# If non-zero, try to write to disk less frequently than we would otherwise.");
streamWriter.WriteLine("AvoidDiskWrites 1");
streamWriter.Flush();
streamWriter.Close();
streamWriter.Dispose();
outStream.Close();
outStream.Dispose();
return true;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CreateTorConfig: Exception {0}",exception.ToString()));
return false;
}
}
public void Stop()
{
lock(this)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Disposing client.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
client.Dispose();
}
}
private String[] GetRouterNicknames(List<Router> routers)
{
lock(this)
{
List<String> nicknames=new List<String>();
foreach(Router router in routers)
{
nicknames.Add(router.Nickname);
}
return nicknames.ToArray<String>();
}
}
public void DisplayRouters(RouterCollection routers)
{
lock(this)
{
if(null==routers)return;
MDTrace.WriteLine(LogLevel.DEBUG,"Nickname,Identity,IP Address,Digest,Published,Bandwidth,Country,Flags");
foreach(Router router in routers)
{
string countryCode = client.Status.GetCountryCode(router);
StringBuilder sb=new StringBuilder();
sb.Append(router.Nickname).Append(",");
sb.Append(router.Identity).Append(",");
sb.Append(router.IPAddress).Append(",");
sb.Append(router.Digest).Append(",");
sb.Append((router.Publication == DateTime.MinValue ? "Unknown" : Convert.ToString(router.Publication))).Append(",");
sb.Append(router.Bandwidth).Append(",");
sb.Append(countryCode).Append(",");
sb.Append(router.Flags);
MDTrace.WriteLine(LogLevel.DEBUG,sb.ToString());
}
}
}
public void DisplayRouters(List<Router> routers)
{
lock(this)
{
MDTrace.WriteLine(LogLevel.DEBUG,"Nickname,Identity,IP Address,Digest,Published,Bandwidth,Country,Flags");
foreach(Router router in routers)
{
string countryCode=client.Status.GetCountryCode(router);
StringBuilder sb=new StringBuilder();
sb.Append(router.Nickname).Append(",");
sb.Append(router.Identity).Append(",");
sb.Append(router.IPAddress).Append(",");
sb.Append(router.Digest).Append(",");
sb.Append((router.Publication==DateTime.MinValue?"Unknown":Convert.ToString(router.Publication))).Append(",");
sb.Append(router.Bandwidth).Append(",");
sb.Append(countryCode).Append(",");
sb.Append(router.Flags);
MDTrace.WriteLine(LogLevel.DEBUG,sb.ToString());
}
}
}
public static bool SetConnectionProxy(string proxyServer)
{
IntPtr hInternet=InternetOpen(agentName,INTERNET_OPEN_TYPE_DIRECT,null,null,0);
INTERNET_PER_CONN_OPTION[] options=new INTERNET_PER_CONN_OPTION[2];
options[0]=new INTERNET_PER_CONN_OPTION();
options[0].dwOption=(int)INTERNET_PER_CONN_OPTIONENUM.INTERNET_PER_CONN_FLAGS;
options[0].value.dwValue=(int)INTERNET_OPTION_PER_CONN_FLAGS.PROXY_TYPE_PROXY;
options[1]=new INTERNET_PER_CONN_OPTION();
options[1].dwOption=(int)INTERNET_PER_CONN_OPTIONENUM.INTERNET_PER_CONN_PROXY_SERVER;
options[1].value.pszValue=Marshal.StringToHGlobalAnsi(proxyServer);
IntPtr buffer=Marshal.AllocCoTaskMem(Marshal.SizeOf(options[0])+Marshal.SizeOf(options[1]));
IntPtr current=buffer;
for(int i=0;i<options.Length;i++)
{
Marshal.StructureToPtr(options[i],current,false);
current=(IntPtr)((int)current+Marshal.SizeOf(options[i]));
}
INTERNET_PER_CONN_OPTION_LIST optionList=new INTERNET_PER_CONN_OPTION_LIST();
optionList.pOptions=buffer;
optionList.Size=Marshal.SizeOf(optionList);
optionList.Connection=IntPtr.Zero;
optionList.OptionCount=options.Length;
optionList.OptionError=0;
int size=Marshal.SizeOf(optionList);
IntPtr intPtrStruct=Marshal.AllocCoTaskMem(size);
Marshal.StructureToPtr(optionList,intPtrStruct,true);
bool bReturn=InternetSetOption(hInternet,INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,intPtrStruct,size);
Marshal.FreeCoTaskMem(buffer);
Marshal.FreeCoTaskMem(intPtrStruct);
InternetCloseHandle(hInternet);
return bReturn;
}
public delegate bool HandlerRoutine(CtrlTypes handler);
public enum CtrlTypes { CTRL_C_EVENT=0, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT=5, CTRL_SHUTDOWN_EVENT }
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
switch(ctrlType)
{
case CtrlTypes.CTRL_C_EVENT:
Program.GetInstance().Stop();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_C_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
Program.GetInstance().ResetEvent.Set();
break;
case CtrlTypes.CTRL_BREAK_EVENT:
Program.GetInstance().Stop();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_BREAK_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
Program.GetInstance().ResetEvent.Set();
break;
case CtrlTypes.CTRL_CLOSE_EVENT:
Program.GetInstance().Stop();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_CLOSE_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
Program.GetInstance().ResetEvent.Set();
break;
case CtrlTypes.CTRL_LOGOFF_EVENT:
Program.GetInstance().Stop();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_LOGOFF_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
Program.GetInstance().ResetEvent.Set();
break;
case CtrlTypes.CTRL_SHUTDOWN_EVENT:
Program.GetInstance().Stop();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} Received CTRL_SHUTDOWN_EVENT.",Utility.DateTimeToStringMMSDDSYYYYHHMMSS(DateTime.Now)));
Program.GetInstance().ResetEvent.Set();
break;
}
return true;
}
static void Main(string[] args)
{
try
{
try{Program.SetConsoleCtrlHandler(new HandlerRoutine(Program.ConsoleCtrlCheck),true);}
catch(Exception exception){Console.WriteLine(exception.ToString());}
if(!Utility.IsAdministrator())
{
Console.WriteLine("TorProxy needs to be under administrator account. Press any key to exit.");
Console.ReadKey();
return;
}
MDTrace.LogLevel=LogLevel.DEBUG;
String strLogFile="torwebclient.log";
Utility.CopyFile(strLogFile,Utility.DateTimeToStringYYYYMMDDMMSSTT(DateTime.Now)+strLogFile);
Utility.DeleteFile(strLogFile);
Trace.Listeners.Add(new TextWriterTraceListener(strLogFile));
ManualResetEvent[] resetEvents=new ManualResetEvent[1];
for(int index=0;index<resetEvents.Length;index++) resetEvents[index]=new ManualResetEvent(false);
resetEvents[0].Reset();
ThreadPool.QueueUserWorkItem(delegate
{
Program.GetInstance().Start();
Program.GetInstance().ResetEvent=resetEvents[0];
MDTrace.WriteLine(LogLevel.DEBUG,"Running...");
});
MDTrace.WriteLine(LogLevel.DEBUG,"Waiting for terminal event...");
WaitHandle.WaitAll(resetEvents);
MDTrace.WriteLine(LogLevel.DEBUG,"Terminated...");
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception: {0}",exception.ToString()));
throw;
}
}
}
}