Browse Source

Merge branch 'master' into master

tags/4.2.0.0
Student Main GitHub 5 years ago
parent
commit
f87d4dacf9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 293 additions and 43 deletions
  1. +47
    -0
      shadowsocks-csharp/Controller/Service/NamedPipeServer.cs
  2. +19
    -0
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  3. +107
    -0
      shadowsocks-csharp/Controller/System/ProtocolHandler.cs
  4. +5
    -1
      shadowsocks-csharp/Data/i18n.csv
  5. +99
    -40
      shadowsocks-csharp/Program.cs
  6. +14
    -2
      shadowsocks-csharp/View/MenuViewController.cs
  7. +2
    -0
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 47
- 0
shadowsocks-csharp/Controller/Service/NamedPipeServer.cs View File

@@ -0,0 +1,47 @@
using System;
using System.IO;
using System.IO.Pipes;
using System.Net;
using System.Text;

namespace Shadowsocks.Controller
{
class RequestAddUrlEventArgs : EventArgs
{
public readonly string Url;

public RequestAddUrlEventArgs(string url)
{
this.Url = url;
}
}

internal class NamedPipeServer
{
public event EventHandler<RequestAddUrlEventArgs> AddUrlRequested;
public async void Run(string path)
{
byte[] buf = new byte[4096];
while (true)
{
using (NamedPipeServerStream stream = new NamedPipeServerStream(path))
{
stream.WaitForConnection();
await stream.ReadAsync(buf, 0, 4);
int opcode = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buf, 0));
if (opcode == 1)
{
await stream.ReadAsync(buf, 0, 4);
int strlen = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buf, 0));

await stream.ReadAsync(buf, 0, strlen);
string url = Encoding.UTF8.GetString(buf, 0, strlen);

AddUrlRequested?.Invoke(this, new RequestAddUrlEventArgs(url));
}
stream.Close();
}
}
}
}
}

+ 19
- 0
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -218,6 +218,25 @@ namespace Shadowsocks.Controller
StatisticsStrategyConfiguration.Save(configuration);
}
public bool AskAddServerBySSURL(string ssURL)
{
var dr = MessageBox.Show(I18N.GetString("Import from URL: {0} ?", ssURL), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo);
if (dr == DialogResult.Yes)
{
if (AddServerBySSURL(ssURL))
{
MessageBox.Show(I18N.GetString("Successfully imported from {0}", ssURL));
return true;
}
else
{
MessageBox.Show(I18N.GetString("Failed to import. Please check if the link is valid."));
}
}
return false;
}


public bool AddServerBySSURL(string ssURL)
{
try


+ 107
- 0
shadowsocks-csharp/Controller/System/ProtocolHandler.cs View File

@@ -0,0 +1,107 @@
using Microsoft.Win32;
using NLog;
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Shadowsocks.Controller
{
static class ProtocolHandler
{
const string ssURLRegKey = @"SOFTWARE\Classes\ss";

private static Logger logger = LogManager.GetCurrentClassLogger();

// Don't use Application.ExecutablePath
// see https://stackoverflow.com/questions/12945805/odd-c-sharp-path-issue
private static readonly string ExecutablePath = Assembly.GetEntryAssembly().Location;

// TODO: Elevate when necessary
public static bool Set(bool enabled)
{
RegistryKey ssURLAssociation = null;

try
{
ssURLAssociation = Registry.CurrentUser.CreateSubKey(ssURLRegKey, RegistryKeyPermissionCheck.ReadWriteSubTree);
if (ssURLAssociation == null)
{
logger.Error(@"Failed to create HKCU\SOFTWARE\Classes\ss to register ss:// association.");
return false;
}
if (enabled)
{
ssURLAssociation.SetValue("", "URL:Shadowsocks");
ssURLAssociation.SetValue("URL Protocol", "");
var shellOpen = ssURLAssociation.CreateSubKey("shell").CreateSubKey("open").CreateSubKey("command");
shellOpen.SetValue("", $"{ExecutablePath} --open-url %1");
logger.Info(@"Successfully added ss:// association.");
}
else
{
Registry.CurrentUser.DeleteSubKeyTree(ssURLRegKey);
logger.Info(@"Successfully removed ss:// association.");
}
return true;
}
catch (Exception e)
{
logger.LogUsefulException(e);
return false;
}
finally
{
if (ssURLAssociation != null)
{
try
{
ssURLAssociation.Close();
ssURLAssociation.Dispose();
}
catch (Exception e)
{ logger.LogUsefulException(e); }
}
}
}

public static bool Check()
{
RegistryKey ssURLAssociation = null;
try
{
ssURLAssociation = Registry.CurrentUser.OpenSubKey(ssURLRegKey, true);
if (ssURLAssociation == null)
{
//logger.Info(@"ss:// links not associated.");
return false;
}

var shellOpen = ssURLAssociation.OpenSubKey("shell").OpenSubKey("open").OpenSubKey("command");
return (string)shellOpen.GetValue("") == $"{ExecutablePath} --open-url %1";
}
catch (Exception e)
{
logger.LogUsefulException(e);
return false;
}
finally
{
if (ssURLAssociation != null)
{
try
{
ssURLAssociation.Close();
ssURLAssociation.Dispose();
}
catch (Exception e)
{ logger.LogUsefulException(e); }
}
}
}

}
}

+ 5
- 1
shadowsocks-csharp/Data/i18n.csv View File

@@ -1,4 +1,4 @@
en,ru-RU,zh-CN,zh-TW,ja,ko,fr
en,ru-RU,zh-CN,zh-TW,ja,ko,fr
#Restart program to apply translation,,,,,,
#This is comment line,,,,,,
#Always keep language name at head of file,,,,,,
@@ -18,6 +18,7 @@ Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs
Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs…
Statistics Config...,Настройки статистики…,统计配置...,統計設定檔...,統計情報の設定...,통계 설정,Configuration des statistiques…
Start on Boot,Автозагрузка,开机启动,開機啟動,システムと同時に起動,시스템 시작 시에 시작하기,Démarrage automatique
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,,,
Forward Proxy...,Прямой прокси…,正向代理设置...,正向 Proxy 設定...,フォワードプロキシの設定...,포워드 프록시,Forward-proxy…
Allow other Devices to connect,Общий доступ к подключению,允许其他设备连入,允許其他裝置連入,他のデバイスからの接続を許可する,다른 기기에서 연결 허용,Autoriser d'autres appareils à se connecter
Local PAC,Локальный PAC,使用本地 PAC,使用本機 PAC,ローカル PAC,로컬 PAC,PAC local
@@ -184,6 +185,9 @@ Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно
"If you want to start multiple Shadowsocks, make a copy in another directory.","Если вы хотите запустить несколько копий Shadowsocks одновременно, создайте отдельную папку на каждую копию.",如果想同时启动多个,可以另外复制一份到别的目录。,如果想同時啟動多個,可以另外複製一份至別的目錄。,複数起動したい場合は、プログラムファイルを別のフォルダーにコピーしてから、もう一度実行して下さい。,"만약 여러 개의 Shadowsocks를 사용하고 싶으시다면, 파일을 다른 곳에 복사해주세요.","Si vous souhaitez démarrer plusieurs Shadowsocks, faites une copie dans un autre répertoire."
Failed to decode QRCode,Не удалось распознать QRCode,无法解析二维码,QR 碼解碼失敗,QR コードの読み取りに失敗しました。,QR코드를 해석하는데에 실패했습니다.,Impossible de décoder le QRCode
Failed to update registry,Не удалось обновить запись в реестре,无法修改注册表,無法修改登錄檔,レジストリの更新に失敗しました。,레지스트리를 업데이트하는데에 실패했습니다.,Impossible de mettre à jour de la base de registre
Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,,,
Successfully imported from {0},Успешно импортировано из {0},导入成功:{0},導入成功:{0},,,
Failed to import. Please check if the link is valid.,,导入失败,请检查链接是否有效。,導入失敗,請檢查鏈接是否有效。,,,
System Proxy On: ,Системный прокси:,系统代理已启用:,系統 Proxy 已啟用:,システム プロキシが有効:,시스템 프록시 활성화됨: ,Proxy système activé:
Running: Port {0},Запущен на порту {0},正在运行:端口 {0},正在執行:連接埠號碼 {0},実行中:ポート {0},실행 중: 포트 {0}번,En cours d'exécution: port {0}
"Unexpected error, shadowsocks will exit. Please report to","Непредвиденная ошибка, пожалуйста сообщите на",非预期错误,Shadowsocks将退出。请提交此错误到,非預期錯誤,Shadowsocks 將結束。請報告此錯誤至,予想外のエラーが発生したため、Shadowsocks を終了します。詳しくは下記までお問い合わせ下さい:,알 수 없는 오류로 Shadowsocks가 종료될 것입니다. 오류를 제보해주세요:,Shadowsocks va quitter en présence d/érreur inattendue. Veuillez signaler à


+ 99
- 40
shadowsocks-csharp/Program.cs View File

@@ -1,21 +1,26 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;
using NLog;
using Microsoft.Win32;
using Shadowsocks.Controller;
using Shadowsocks.Controller.Hotkeys;
using Shadowsocks.Util;
using Shadowsocks.View;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Shadowsocks
{
static class Program
internal static class Program
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public static ShadowsocksController MainController { get; private set; }
public static MenuViewController MenuController { get; private set; }
public static string[] Args { get; private set; }
@@ -24,15 +29,15 @@ namespace Shadowsocks
/// </summary>
/// </summary>
[STAThread]
static void Main(string[] args)
private static void Main(string[] args)
{
Directory.SetCurrentDirectory(Application.StartupPath);
// todo: initialize the NLog configuartion
Model.NLogConfig.TouchAndApplyNLogConfig();
// .NET Framework 4.7.2 on Win7 compatibility
System.Net.ServicePointManager.SecurityProtocol |=
System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
ServicePointManager.SecurityProtocol |=
SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
// store args for further use
Args = args;
@@ -50,27 +55,58 @@ namespace Shadowsocks
if (DialogResult.OK == MessageBox.Show(I18N.GetString("Unsupported .NET Framework, please update to {0} or later.", "4.7.2"),
"Shadowsocks Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error))
{
//Process.Start("https://www.microsoft.com/download/details.aspx?id=53344"); // 4.6.2
Process.Start("https://dotnet.microsoft.com/download/dotnet-framework/net472");
}
return;
}
string pipename = $"Shadowsocks\\{Application.StartupPath.GetHashCode()}";
Utils.ReleaseMemory(true);
using (Mutex mutex = new Mutex(false, $"Global\\Shadowsocks_{Application.StartupPath.GetHashCode()}"))
string addedUrl = null;

using (NamedPipeClientStream pipe = new NamedPipeClientStream(pipename))
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// handle UI exceptions
Application.ThreadException += Application_ThreadException;
// handle non-UI exceptions
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.ApplicationExit += Application_ApplicationExit;
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
AutoStartup.RegisterForRestart(true);
if (!mutex.WaitOne(0, false))
bool pipeExist = false;
try
{
pipe.Connect(10);
pipeExist = true;
}
catch (TimeoutException)
{
pipeExist = false;
}

// TODO: switch to better argv parser when it's getting complicate
List<string> alist = Args.ToList();
// check --open-url param
int urlidx = alist.IndexOf("--open-url") + 1;
if (urlidx > 0)
{
if (Args.Length <= urlidx)
{
return;
}

// --open-url exist, and no other instance, add it later
if (!pipeExist)
{
addedUrl = Args[urlidx];
}
// has other instance, send url via pipe then exit
else
{
byte[] b = Encoding.UTF8.GetBytes(Args[urlidx]);
byte[] opAddUrl = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(1));
byte[] blen = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(b.Length));
pipe.Write(opAddUrl, 0, 4); // opcode addurl
pipe.Write(blen, 0, 4);
pipe.Write(b, 0, b.Length);
pipe.Close();
return;
}
}
// has another instance, and no need to communicate with it return
else if (pipeExist)
{
Process[] oldProcesses = Process.GetProcessesByName("Shadowsocks");
if (oldProcesses.Length > 0)
@@ -83,21 +119,44 @@ namespace Shadowsocks
I18N.GetString("Shadowsocks is already running."));
return;
}
Directory.SetCurrentDirectory(Application.StartupPath);
}

Utils.ReleaseMemory(true);

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// handle UI exceptions
Application.ThreadException += Application_ThreadException;
// handle non-UI exceptions
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.ApplicationExit += Application_ApplicationExit;
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
AutoStartup.RegisterForRestart(true);

Directory.SetCurrentDirectory(Application.StartupPath);

#if DEBUG
// truncate privoxy log file while debugging
string privoxyLogFilename = Utils.GetTempPath("privoxy.log");
if (File.Exists(privoxyLogFilename))
using (new FileStream(privoxyLogFilename, FileMode.Truncate)) { }
// truncate privoxy log file while debugging
string privoxyLogFilename = Utils.GetTempPath("privoxy.log");
if (File.Exists(privoxyLogFilename))
using (new FileStream(privoxyLogFilename, FileMode.Truncate)) { }
#endif
MainController = new ShadowsocksController();
MenuController = new MenuViewController(MainController);
MainController = new ShadowsocksController();
MenuController = new MenuViewController(MainController);
HotKeys.Init(MainController);
MainController.Start();
Application.Run();
}
HotKeys.Init(MainController);
MainController.Start();

NamedPipeServer namedPipeServer = new NamedPipeServer();
Task.Run(() => namedPipeServer.Run(pipename));
namedPipeServer.AddUrlRequested += (_1, e) => MainController.AskAddServerBySSURL(e.Url);
if (!addedUrl.IsNullOrEmpty())
{
MainController.AskAddServerBySSURL(addedUrl);
}

Application.Run();
}
private static int exited = 0;
@@ -135,7 +194,7 @@ namespace Shadowsocks
logger.Info("os wake up");
if (MainController != null)
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
Task.Factory.StartNew(() =>
{
Thread.Sleep(10 * 1000);
try


+ 14
- 2
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -36,6 +36,7 @@ namespace Shadowsocks.View
private ContextMenu contextMenu1;
private MenuItem disableItem;
private MenuItem AutoStartupItem;
private MenuItem ProtocolHandlerItem;
private MenuItem ShareOverLANItem;
private MenuItem SeperatorItem;
private MenuItem ConfigItem;
@@ -315,6 +316,7 @@ namespace Shadowsocks.View
this.proxyItem = CreateMenuItem("Forward Proxy...", new EventHandler(this.proxyItem_Click)),
new MenuItem("-"),
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.ProtocolHandlerItem = CreateMenuItem("Associate ss:// Links", new EventHandler(this.ProtocolHandlerItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow other Devices to connect", new EventHandler(this.ShareOverLANItem_Click)),
new MenuItem("-"),
this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
@@ -442,6 +444,7 @@ namespace Shadowsocks.View
VerboseLoggingToggleItem.Checked = config.isVerboseLogging;
ShowPluginOutputToggleItem.Checked = config.showPluginOutput;
AutoStartupItem.Checked = AutoStartup.Check();
ProtocolHandlerItem.Checked = ProtocolHandler.Check();
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked;
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac;
@@ -821,8 +824,7 @@ namespace Shadowsocks.View
private void ImportURLItem_Click(object sender, EventArgs e)
{
var success = controller.AddServerBySSURL(Clipboard.GetText(TextDataFormat.Text));
if (success)
if (controller.AskAddServerBySSURL(Clipboard.GetText(TextDataFormat.Text)))
{
ShowConfigForm();
}
@@ -845,6 +847,16 @@ namespace Shadowsocks.View
{
MessageBox.Show(I18N.GetString("Failed to update registry"));
}
LoadCurrentConfiguration();
}
private void ProtocolHandlerItem_Click(object sender, EventArgs e)
{
ProtocolHandlerItem.Checked = !ProtocolHandlerItem.Checked;
if (!ProtocolHandler.Set(ProtocolHandlerItem.Checked))
{
MessageBox.Show(I18N.GetString("Failed to update registry"));
}
LoadCurrentConfiguration();
}
private void LocalPACItem_Click(object sender, EventArgs e)


+ 2
- 0
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -113,6 +113,8 @@
<Compile Include="Controller\HotkeyReg.cs" />
<Compile Include="Controller\LoggerExtension.cs" />
<Compile Include="Controller\Service\PACDaemon.cs" />
<Compile Include="Controller\Service\NamedPipeServer.cs" />
<Compile Include="Controller\System\ProtocolHandler.cs" />
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" />
<Compile Include="Encryption\AEAD\AEADEncryptor.cs" />
<Compile Include="Encryption\AEAD\AEADMbedTLSEncryptor.cs" />


Loading…
Cancel
Save