1) Tor.exe was updated. 2) There were found to socket handling issues in the Proxy class which was causing connectiion to be dropped. Also found were issues with the retrieval of the client parameters. In short it's been overhauled and found to be working much better than ever.
471 lines
18 KiB
C#
471 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;
|
|
|
|
|
|
// 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
|
|
{
|
|
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)
|
|
{
|
|
Console.WriteLine("TorWebClient is running {0} bits.",Utility.Is64Bit()?64:32);
|
|
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.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...");
|
|
}
|
|
}
|
|
}
|