Compare commits

...
20 Commits
Author SHA1 Message Date
Syrone Wong e9ba34373b Update CHANGES and bump version
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2017-01-11 14:27:24 +08:00
Syrone Wong 4c49f1b736 Make the previous portable mode as default
- change dir name to "ss_win_temp" to make things clear.
- throw any exception caught when creating temp folder

To workaround silly cleaning softwares and careless users.

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2017-01-11 14:05:11 +08:00
破娃酱 b266bf42f5 create "help" menu group (#989) 2017-01-08 08:44:49 +08:00
Noisyfox 2e7518d802 Send first chunk with Socks5 header (#984)
* Send socks5 handshake respond before read the target address
So that the next package will (hopefully) contain the first data chunk which means we could get a 'random' first package size.

* Try read available data before send first packet

* Add random size first packet
But commented out ^u^

* Oops forget to delete some test code
2017-01-04 07:59:41 +08:00
破娃酱 18e956f34c not set alpha of the icon (#981) 2017-01-02 15:19:23 +08:00
Syrone Wong 333ad2516f Fix language detection
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 19:35:37 +08:00
Syrone Wong 9ffcf77ab1 Update LICENSE
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 16:25:41 +08:00
Syrone Wong d0d6a3cdfa Happy New Year
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 11:11:45 +08:00
Syrone Wong 8c76ac3401 Update CHANGES and bump version
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 19:24:12 +08:00
Syrone Wong 8f3994c7d8 Set LAN even if the RAS query fails
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 19:12:55 +08:00
Syrone Wong db1eafbfd8 Tweak OTA mask
sync with shadowsocks-libev

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 17:28:21 +08:00
LNDDYL 32225ed655 Update translation for Traditional Chinese (#973)
Credit to @LNDDYL
2016-12-30 17:13:46 +08:00
Syrone Wong 9e529361c4 Misc
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 21:48:30 +08:00
Syrone Wong d21162318e Workaround NotifyIcon's 63 chars limit(#965)
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 21:41:11 +08:00
Syrone Wong f9ed9174cc Fix crash when reading pac user rules
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 12:16:26 +08:00
Syrone Wong 675a5801d8 Fix appveyor link
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:39:20 +08:00
Syrone Wong 4407095d01 Fix auto startup
other minor cleanup

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 1a283aa4ea Drop obsolete tray area refreshing code
- naming private vars
- object initializer

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 97d08e4d40 Tweak privoxy config
only redirect
enable critical logging

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 673d6b3c44 update sysproxy
This will reduce false positives on virus detection.
2016-12-21 10:34:27 +08:00
21 changed files with 331 additions and 316 deletions
+13
View File
@@ -1,3 +1,16 @@
3.4.3 2017-1-11
- Make the previous portable mode as default
- Refine networking by Noisyfox
- Bug fixes and improvements
3.4.2.1 2016-12-30
- Refine Traditional Chinese translation by LNDDYL
- sysproxy: reduce false positives on virus detection
- sysproxy: set LAN proxy settings even if RAS query fails
- privoxy: drop obsolete tray area refreshing code
- Fix auto startup
- Bug fixes and improvements
3.4.2 2016-12-16
- Fix null ref in TCPRelay. (#940)
- Bring Privoxy back. (#948)
+19 -19
View File
@@ -994,31 +994,31 @@ https://tls.mbed.org
License: https://raw.githubusercontent.com/ARMmbed/mbedtls/master/LICENSE
SimpleJson
Newtonsoft.Json
----------
https://github.com/facebook-csharp-sdk/simple-json
https://raw.githubusercontent.com/JamesNK/Newtonsoft.Json/master/LICENSE.md
Copyright (c) 2011, The Outercurve Foundation
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Copyright (c) 2007 James Newton-King
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ZXing
+2 -7
View File
@@ -89,11 +89,6 @@ if you want to deactivate all, please clear all textboxes.
Please visit [Servers] for more information.
#### Portable Mode
If you want to put all temporary files into shadowsocks/temp folder instead of
system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks folder.
#### Develop
[Visual Studio 2015] & [.NET Framework 4.6.2 Developer Pack] are required.
@@ -103,8 +98,8 @@ system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks fo
GPLv3
[Appveyor]: https://ci.appveyor.com/project/Noisyfox/shadowsocks-windows
[Build Status]: https://ci.appveyor.com/api/projects/status/04idbevayrghcrh7/branch/master?svg=true
[Appveyor]: https://ci.appveyor.com/project/wongsyrone/shadowsocks-windows-pljx3
[Build Status]: https://ci.appveyor.com/api/projects/status/otie4km27ws1wak2/branch/master?svg=true
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side
+25 -3
View File
@@ -1,10 +1,11 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace Shadowsocks.Controller
{
public class FileManager
public static class FileManager
{
public static bool ByteArrayToFile(string fileName, byte[] content)
{
@@ -16,8 +17,7 @@ namespace Shadowsocks.Controller
}
catch (Exception ex)
{
Console.WriteLine("Exception caught in process: {0}",
ex.ToString());
Logging.Error(ex);
}
return false;
}
@@ -39,5 +39,27 @@ namespace Shadowsocks.Controller
}
}
}
public static string NonExclusiveReadAllText(string path)
{
return NonExclusiveReadAllText(path, Encoding.Default);
}
public static string NonExclusiveReadAllText(string path, Encoding encoding)
{
try
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var sr = new StreamReader(fs, encoding))
{
return sr.ReadToEnd();
}
}
catch (Exception ex)
{
Logging.Error(ex);
throw ex;
}
}
}
}
+13 -23
View File
@@ -7,11 +7,11 @@ namespace Shadowsocks.Controller
{
using Shadowsocks.Properties;
public class I18N
public static class I18N
{
protected static Dictionary<string, string> Strings;
private static Dictionary<string, string> _strings = new Dictionary<string, string>();
static void Init(string res)
private static void Init(string res)
{
using (var sr = new StringReader(res))
{
@@ -23,38 +23,28 @@ namespace Shadowsocks.Controller
var pos = line.IndexOf('=');
if (pos < 1)
continue;
Strings[line.Substring(0, pos)] = line.Substring(pos + 1);
_strings[line.Substring(0, pos)] = line.Substring(pos + 1);
}
}
}
static I18N()
{
Strings = new Dictionary<string, string>();
string name = CultureInfo.CurrentCulture.Name;
if (name.StartsWith("zh"))
string name = CultureInfo.CurrentCulture.EnglishName;
if (name.StartsWith("Chinese", StringComparison.OrdinalIgnoreCase))
{
if (name == "zh" || name == "zh-CN")
{
Init(Resources.cn);
}
else
{
Init(Resources.zh_tw);
}
// choose Traditional Chinese only if we get explicit indication
Init(name.Contains("Traditional")
? Resources.zh_tw
: Resources.cn);
}
}
public static string GetString(string key)
{
if (Strings.ContainsKey(key))
{
return Strings[key];
}
else
{
return key;
}
return _strings.ContainsKey(key)
? _strings[key]
: key;
}
}
}
@@ -39,7 +39,7 @@ namespace Shadowsocks.Controller
List<string> lines = ParseResult(e.Result);
if (File.Exists(PACServer.USER_RULE_FILE))
{
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
string local = FileManager.NonExclusiveReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
using (var sr = new StringReader(local))
{
foreach (var rule in sr.NonWhiteSpaceLines())
@@ -53,7 +53,7 @@ namespace Shadowsocks.Controller
string abpContent;
if (File.Exists(PACServer.USER_ABP_FILE))
{
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
abpContent = FileManager.NonExclusiveReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
}
else
{
@@ -62,7 +62,7 @@ namespace Shadowsocks.Controller
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
if (File.Exists(PACServer.PAC_FILE))
{
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
string original = FileManager.NonExclusiveReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
if (original == abpContent)
{
UpdateCompleted(this, new ResultEventArgs(false));
@@ -1,13 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Shadowsocks.Model;
@@ -19,9 +15,9 @@ namespace Shadowsocks.Controller
{
class PrivoxyRunner
{
private static int Uid;
private static string UniqueConfigFile;
private static Job PrivoxyJob;
private static int _uid;
private static string _uniqueConfigFile;
private static Job _privoxyJob;
private Process _process;
private int _runningPort;
@@ -29,9 +25,9 @@ namespace Shadowsocks.Controller
{
try
{
Uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance.
UniqueConfigFile = $"privoxy_{Uid}.conf";
PrivoxyJob = new Job();
_uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance.
_uniqueConfigFile = $"privoxy_{_uid}.conf";
_privoxyJob = new Job();
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe);
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll);
@@ -54,29 +50,33 @@ namespace Shadowsocks.Controller
KillProcess(p);
}
string privoxyConfig = Resources.privoxy_conf;
_runningPort = this.GetFreePort();
_runningPort = GetFreePort();
privoxyConfig = privoxyConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_PORT__", _runningPort.ToString());
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(Utils.GetTempPath(UniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig));
FileManager.ByteArrayToFile(Utils.GetTempPath(_uniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig));
_process = new Process();
// Configure the process using the StartInfo properties.
_process.StartInfo.FileName = "ss_privoxy.exe";
_process.StartInfo.Arguments = UniqueConfigFile;
_process.StartInfo.WorkingDirectory = Utils.GetTempPath();
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_process.StartInfo.UseShellExecute = true;
_process.StartInfo.CreateNoWindow = true;
_process = new Process
{
// Configure the process using the StartInfo properties.
StartInfo =
{
FileName = "ss_privoxy.exe",
Arguments = _uniqueConfigFile,
WorkingDirectory = Utils.GetTempPath(),
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
CreateNoWindow = true
}
};
_process.Start();
/*
* Add this process to job obj associated with this ss process, so that
* when ss exit unexpectedly, this process will be forced killed by system.
*/
PrivoxyJob.AddProcess(_process.Handle);
_privoxyJob.AddProcess(_process.Handle);
}
RefreshTrayArea();
}
public void Stop()
@@ -87,7 +87,6 @@ namespace Shadowsocks.Controller
_process.Dispose();
_process = null;
}
RefreshTrayArea();
}
private static void KillProcess(Process p)
@@ -122,21 +121,13 @@ namespace Shadowsocks.Controller
{
try
{
if (Utils.IsPortableMode())
{
/*
* Under PortableMode, we could identify it by the path of ss_privoxy.exe.
*/
var path = process.MainModule.FileName;
/*
* Under PortableMode, we could identify it by the path of ss_privoxy.exe.
*/
var path = process.MainModule.FileName;
return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
}
else
{
var cmd = process.GetCommandLine();
return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
return cmd.Contains(UniqueConfigFile);
}
}
catch (Exception ex)
{
@@ -169,48 +160,5 @@ namespace Shadowsocks.Controller
return defaultPort;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public void RefreshTrayArea()
{
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
if (notificationAreaHandle == IntPtr.Zero)
{
notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area");
IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null);
IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area");
RefreshTrayArea(overflowNotificationAreaHandle);
}
RefreshTrayArea(notificationAreaHandle);
}
private static void RefreshTrayArea(IntPtr windowHandle)
{
const uint wmMousemove = 0x0200;
RECT rect;
GetClientRect(windowHandle, out rect);
for (var x = 0; x < rect.right; x += 5)
for (var y = 0; y < rect.bottom; y += 5)
SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x);
}
}
}
@@ -322,24 +322,15 @@ namespace Shadowsocks.Controller
}
else
{
int atyp = _connetionRecvBuffer[3];
switch (atyp)
if (_command == 1)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1);
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[4];
ReadAddress(len + 2);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
_connection.BeginSend(response, 0, response.Length, SocketFlags.None,
new AsyncCallback(ResponseCallback), null);
}
else if (_command == 3)
{
ReadAddress(HandleUDPAssociate);
}
}
}
@@ -356,12 +347,50 @@ namespace Shadowsocks.Controller
}
}
private void ReadAddress(int bytesRemain)
private void ResponseCallback(IAsyncResult ar)
{
try
{
_connection.EndSend(ar);
ReadAddress(StartConnect);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ReadAddress(Action onSuccess)
{
int atyp = _connetionRecvBuffer[3];
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1, onSuccess);
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[4];
ReadAddress(len + 2, onSuccess);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1, onSuccess);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
}
}
private void ReadAddress(int bytesRemain, Action onSuccess)
{
Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2);
// Read the remain address bytes
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain);
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, new object[] {bytesRemain, onSuccess});
}
private void OnAddressFullyRead(IAsyncResult ar)
@@ -370,7 +399,12 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = _connection.EndReceive(ar);
int bytesRemain = (int) ar.AsyncState;
var states = (object[]) ar.AsyncState;
int bytesRemain = (int)states[0];
var onSuccess = (Action) states[1];
if (bytesRead >= bytesRemain)
{
_firstPacketLength = bytesRead + 2;
@@ -405,16 +439,7 @@ namespace Shadowsocks.Controller
_destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port);
if (_command == 1)
{
byte[] response = {5, 0, 0, 1, 0, 0, 0, 0, 0, 0};
_connection.BeginSend(response, 0, response.Length, SocketFlags.None,
new AsyncCallback(ResponseCallback), null);
}
else if (_command == 3)
{
HandleUDPAssociate();
}
onSuccess.Invoke();
}
else
{
@@ -479,20 +504,6 @@ namespace Shadowsocks.Controller
}
}
private void ResponseCallback(IAsyncResult ar)
{
try
{
_connection.EndSend(ar);
StartConnect();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
// inner class
private class ProxyTimer : Timer
{
@@ -716,6 +727,26 @@ namespace Shadowsocks.Controller
}
}
// private static readonly Random Rnd = new Random();
private void TryReadAvailableData()
{
int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength);
if (available > 0)
{
// Pick a random chunk size, or is it truly necessary? Random packet size is some sort of 'characteristic' itself.
//lock (Rnd)
//{
// available = Rnd.Next(available + 1);
//}
var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available,
SocketFlags.None);
_firstPacketLength += size;
}
}
private void StartPipe(AsyncSession session)
{
if (_closed) return;
@@ -723,6 +754,9 @@ namespace Shadowsocks.Controller
{
_startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
TryReadAvailableData();
Logging.Debug($"_firstPacketLength = {_firstPacketLength}");
SendToServer(_firstPacketLength, session);
}
catch (Exception e)
@@ -24,7 +24,7 @@ namespace Shadowsocks.Controller
public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted;
public const string Version = "3.4.2";
public const string Version = "3.4.3";
private class CheckUpdateTimer : System.Timers.Timer
{
@@ -557,10 +557,10 @@ namespace Shadowsocks.Controller
UpdatePACFromGFWList();
return;
}
List<string> lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath("gfwlist.txt")));
List<string> lines = GFWListUpdater.ParseResult(FileManager.NonExclusiveReadAllText(Utils.GetTempPath("gfwlist.txt")));
if (File.Exists(PACServer.USER_RULE_FILE))
{
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
string local = FileManager.NonExclusiveReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
using (var sr = new StringReader(local))
{
foreach (var rule in sr.NonWhiteSpaceLines())
@@ -574,7 +574,7 @@ namespace Shadowsocks.Controller
string abpContent;
if (File.Exists(PACServer.USER_ABP_FILE))
{
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
abpContent = FileManager.NonExclusiveReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
}
else
{
@@ -583,7 +583,7 @@ namespace Shadowsocks.Controller
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
if (File.Exists(PACServer.PAC_FILE))
{
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
string original = FileManager.NonExclusiveReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
if (original == abpContent)
{
return;
@@ -1,20 +1,24 @@
using System;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.Win32;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
class AutoStartup
static class AutoStartup
{
static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode();
// Don't use Application.ExecutablePath
// see https://stackoverflow.com/questions/12945805/odd-c-sharp-path-issue
private static readonly string ExecutablePath = Assembly.GetEntryAssembly().Location;
private static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode();
public static bool Set(bool enabled)
{
RegistryKey runKey = null;
try
{
string path = Application.ExecutablePath;
runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
if ( runKey == null ) {
Logging.Error( @"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run" );
@@ -22,7 +26,7 @@ namespace Shadowsocks.Controller
}
if (enabled)
{
runKey.SetValue(Key, path);
runKey.SetValue(Key, ExecutablePath);
}
else
{
@@ -53,7 +57,6 @@ namespace Shadowsocks.Controller
RegistryKey runKey = null;
try
{
string path = Application.ExecutablePath;
runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
if (runKey == null) {
Logging.Error(@"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run");
@@ -67,10 +70,10 @@ namespace Shadowsocks.Controller
else if (item.Equals("Shadowsocks", StringComparison.OrdinalIgnoreCase)) // Compatibility with older versions
{
string value = Convert.ToString(runKey.GetValue(item));
if (path.Equals(value, StringComparison.OrdinalIgnoreCase))
if (ExecutablePath.Equals(value, StringComparison.OrdinalIgnoreCase))
{
runKey.DeleteValue(item);
runKey.SetValue(Key, path);
runKey.SetValue(Key, ExecutablePath);
return true;
}
}
+1
View File
@@ -33,6 +33,7 @@ Check for Updates at Startup=启动时检查更新
Check Pre-release Version=检查测试版更新
Edit Hotkeys...=编辑快捷键...
About...=关于...
Help=帮助
Quit=退出
Edit Servers=编辑服务器
Load Balance=负载均衡
+2
View File
@@ -1,4 +1,6 @@
listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__
toggle 0
logfile ss_privoxy.log
show-on-task-bar 0
activity-animation 0
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
Binary file not shown.
Binary file not shown.
+84 -81
View File
@@ -10,105 +10,108 @@ PAC=PAC 模式
Global=全局模式
Servers=伺服器
Edit Servers...=編輯伺服器...
Statistics Config...=統計配置...
Statistics Config...=統計設定檔...
Start on Boot=開機啟動
Forward Proxy...=正向代理設置...
Allow Clients from LAN=允許來自區域網路的連接
Local PAC=使用本地 PAC
Online PAC=使用在線 PAC
Edit Local PAC File...=編輯本地 PAC 文件...
Update Local PAC from GFWList=從 GFWList 更新本地 PAC
Edit User Rule for GFWList...=編輯 GFWList 的用戶規則...
Secure Local PAC=保護本地 PAC
Copy Local PAC URL=複製本地 PAC 網址
Share Server Config...=分享伺服器配置...
Forward Proxy...=正向 Proxy 設定...
Allow Clients from LAN=允許來自區域網路的連線
Local PAC=使用本機 PAC
Online PAC=使用線上 PAC
Edit Local PAC File...=編輯本機 PAC 檔案...
Update Local PAC from GFWList=從 GFWList 更新本機 PAC
Edit User Rule for GFWList...=編輯 GFWList 的使用者規則...
Secure Local PAC=安全本機 PAC
Copy Local PAC URL=複製本機 PAC 網址
Share Server Config...=分享伺服器設定檔...
Scan QRCode from Screen...=掃描螢幕上的 QR 碼...
Import URL from Clipboard...=從剪貼板導入URL...
Import URL from Clipboard...=從剪貼簿匯入 URL...
Availability Statistics=統計可用性
Show Logs...=顯示日誌...
Verbose Logging=詳細記錄日誌
Show Logs...=顯示記錄檔...
Verbose Logging=詳細資訊記錄
Updates...=更新...
Check for Updates...=檢查更新
Check for Updates at Startup=啟動時檢查更新
Check Pre-release Version=檢查測試版更新
Edit Hotkeys...=編輯捷徑鍵...
Check Pre-release Version=檢查發行前版本更新
Edit Hotkeys...=編輯快速鍵...
About...=關於...
Quit=退出
Help=幫助
Quit=結束
Edit Servers=編輯伺服器
Load Balance=負載均衡
High Availability=高可用
Load Balance=負載平衡
High Availability=高可用性
Choose by statistics=根據統計
# Config Form
&Add=新增(&A)
&Delete=移除(&D)
Dupli&cate=複製(&C)
&Add=新增 (&A)
&Delete=移除 (&D)
Dupli&cate=複製 (&C)
Server=伺服器
Server Addr=伺服器位址
Server Port=伺服器連接埠
Password=密碼
Encryption=加密
Proxy Port=代理連接埠
Remarks=備註
Timeout(Sec)=超時(秒)
Proxy Port=Proxy 連接埠
Remarks=註解
Timeout(Sec)=逾時 (秒)
Onetime Authentication=單次驗證
OK=確定
Cancel=取消
New server=未配置的伺服器
Move &Up=上移(&U)
Move D&own=下移(&O)
New server=新伺服器
Move &Up=上移 (&U)
Move D&own=下移 (&O)
# Proxy Form
Edit Proxy=代理設置
Use Proxy=使用代理
Proxy Type=代理類別
Proxy Addr=代理位址
Proxy Port=代理連接埠
Edit Proxy=編輯 Proxy
Use Proxy=使用 Proxy
Proxy Type=Proxy 類型
Proxy Addr=Proxy 位址
Proxy Port=Proxy 連接埠
# Log Form
&File=文件(&F)
&Open Location=在資料總管中打開(&O)
E&xit=退出(&X)
&View=視圖(&V)
&Clean Logs=清空日誌(&C)
Change &Font=設置字體(&F)
&Wrap Text=自動換行(&W)
&Top Most=置頂(&T)
&Show Toolbar=顯示工具欄(&S)
Log Viewer=日誌查看器
Inbound=入站
Outbound=出站
&File=檔案 (&F)
&Open Location=在檔案總管中開啟 (&O)
E&xit=結束 (&X)
&View=檢視
(&V)
&Clean Logs=清除記錄檔 (&C)
Change &Font=變更字型 (&F)
&Wrap Text=自動換行 (&W)
&Top Most=置頂 (&T)
&Show Toolbar=顯示工具列 (&S)
Log Viewer=記錄檔檢視器
Inbound=輸入
Outbound=輸出
# QRCode Form
QRCode and URL=QR 碼与 URL
QRCode and URL=QR 碼與 URL
# PAC Url Form
Edit Online PAC URL=編輯在線 PAC 網址
Edit Online PAC URL...=編輯在線 PAC 網址...
Edit Online PAC URL=編輯線上 PAC 網址
Edit Online PAC URL...=編輯線上 PAC 網址...
Please input PAC Url=請輸入 PAC 網址
# HotkeySettings Form
Switch system proxy=切換系統代理狀態
Switch system proxy mode=切換系統代理模式
Switch share over LAN=切換區域網路共享
Show Logs=顯示日誌
Switch to prev server=切換上個伺服器
Switch to next server=切換下個伺服器
Reg All=註冊全部捷徑鍵
Switch system proxy=切換系統 Proxy 狀態
Switch system proxy mode=切換系統 Proxy 模式
Switch share over LAN=切換區域網路共用
Show Logs=顯示記錄檔
Switch to prev server=切換上一個伺服器
Switch to next server=切換下一個伺服器
Reg All=註冊所有快速鍵
# Messages
Shadowsocks Error: {0}=Shadowsocks 錯誤: {0}
Port already in use=連接埠號碼已被占用
Invalid server address=非法伺服器位址
Illegal port number format=非法連接埠號碼格式
Illegal timeout format=非法超時格式
Port already in use=連接埠號碼已被使用
Invalid server address=無效伺服器位址
Illegal port number format=無效連接埠號碼格式
Illegal timeout format=無效逾時格式
Please add at least one server=請新增至少一個伺服器
Server IP can not be blank=伺服器 IP 不能為空
Password can not be blank=密碼不能為空
@@ -116,27 +119,27 @@ Port out of range=連接埠號碼超出範圍
Port can't be 8123=連接埠號碼不能為 8123
Shadowsocks {0} Update Found=Shadowsocks {0} 更新
No update is available=沒有可用的更新
Click here to update=點擊這裏升級
Shadowsocks is here=Shadowsocks 在這裏
You can turn on/off Shadowsocks in the context menu=可以在右鍵菜單中開關 Shadowsocks
System Proxy Enabled=系統代理已啟用
System Proxy Disabled=系統代理未啟用
Failed to update PAC file =更新 PAC 文件失敗
Click here to update=點按此處升級
Shadowsocks is here=Shadowsocks 在這裡
You can turn on/off Shadowsocks in the context menu=可以在右鍵選項單中開關 Shadowsocks
System Proxy Enabled=系統 Proxy 已啟用
System Proxy Disabled=系統 Proxy 未啟用
Failed to update PAC file =更新 PAC 檔案失敗
PAC updated=更新 PAC 成功
No updates found. Please report to GFWList if you have problems with it.=未發現更新。如有問題請提交給 GFWList。
No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近屏幕中間的位置
Shadowsocks is already running.=Shadowsocks 已經在運行。
Find Shadowsocks icon in your notify tray.=請在工作列裏尋找 Shadowsocks 圖標。
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制一份到別的目錄。
Failed to decode QRCode=無法解析 QR 碼
No updates found. Please report to GFWList if you have problems with it.=未發現更新。如有問題請報告至 GFWList。
No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近熒幕中間的位置
Shadowsocks is already running.=Shadowsocks 已經在執行。
Find Shadowsocks icon in your notify tray.=請在工作列裡尋找 Shadowsocks 圖示。
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外複製一份至別的目錄。
Failed to decode QRCode=QR 碼解碼失敗
Failed to update registry=無法修改登錄檔
System Proxy On: =系統代理已啟用:
Running: Port {0}=正在運行:連接埠號碼 {0}
Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shadowsocks將退出。請提交此錯誤到
Unsupported operating system, use Windows Vista at least.=不支持的作業系統版本,最低需求為Windows Vista。
Unsupported .NET Framework, please update to 4.6.2 or later.=當前 .NET Framework 版本過低,最低需求為4.6.2。
Proxy request failed=代理請求失敗
Proxy handshake failed=代理握手失敗
Register hotkey failed=註冊捷徑鍵失敗
Cannot parse hotkey: {0}=解析捷徑鍵失敗: {0}
Timeout is invalid, it should not exceed {0}=超時無效,不應超過 {0}
System Proxy On: =系統 Proxy 已啟用:
Running: Port {0}=正在執行:連接埠號碼 {0}
Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shadowsocks 將結束。請報告此錯誤至
Unsupported operating system, use Windows Vista at least.=不支援的作業系統版本,最低需求為 Windows Vista。
Unsupported .NET Framework, please update to 4.6.2 or later.=目前 .NET Framework 版本過低,最低需求為4.6.2。
Proxy request failed=Proxy 要求失敗
Proxy handshake failed=Proxy 交握失敗
Register hotkey failed=註冊快速鍵失敗
Cannot parse hotkey: {0}=剖析快速鍵失敗: {0}
Timeout is invalid, it should not exceed {0}=逾時無效,不應超過 {0}
+1 -1
View File
@@ -106,7 +106,7 @@ namespace Shadowsocks.Encryption
#region OneTimeAuth
public const int ONETIMEAUTH_FLAG = 0x10;
public const int ADDRTYPE_MASK = 0xF;
public const int ADDRTYPE_MASK = 0xEF;
public const int ONETIMEAUTH_BYTES = 10;
@@ -11,7 +11,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Shadowsocks")]
[assembly: AssemblyCopyright("clowwindy and community 2016")]
[assembly: AssemblyCopyright("clowwindy & community 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
+14 -31
View File
@@ -25,43 +25,26 @@ namespace Shadowsocks.Util
public static class Utils
{
private static bool? _portableMode;
private static string TempPath = null;
public static bool IsPortableMode()
{
if (!_portableMode.HasValue)
{
_portableMode = File.Exists(Path.Combine(Application.StartupPath, "shadowsocks_portable_mode.txt"));
}
return _portableMode.Value;
}
private static string _tempPath = null;
// return path to store temporary files
public static string GetTempPath()
{
if (TempPath == null)
if (_tempPath == null)
{
if (IsPortableMode())
try
{
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "temp"));
}
catch (Exception e)
{
TempPath = Path.GetTempPath();
Logging.LogUsefulException(e);
}
finally
{
// don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log
TempPath = Path.Combine(Application.StartupPath, "temp");
}
else
TempPath = Path.GetTempPath();
try
{
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "ss_win_temp"));
// don't use "/", it will fail when we call explorer /select xxx/ss_win_temp\xxx.log
_tempPath = Path.Combine(Application.StartupPath, "ss_win_temp");
}
catch (Exception e)
{
Logging.Error(e);
throw;
}
}
return TempPath;
return _tempPath;
}
// return a full path with filename combined which pointed to the temporary directory
+14
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
namespace Shadowsocks.Util
@@ -17,5 +18,18 @@ namespace Shadowsocks.Util
var children = control.Controls.OfType<TControl>().ToList();
return children.SelectMany(GetChildControls<TControl>).Concat(children);
}
// Workaround NotifyIcon's 63 chars limit
// https://stackoverflow.com/questions/579665/how-can-i-show-a-systray-tooltip-longer-than-63-chars
public static void SetNotifyIconText(NotifyIcon ni, string text)
{
if (text.Length >= 128)
throw new ArgumentOutOfRangeException("Text limited to 127 characters");
Type t = typeof(NotifyIcon);
BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance;
t.GetField("text", hidden).SetValue(ni, text);
if ((bool)t.GetField("added", hidden).GetValue(ni))
t.GetMethod("UpdateIcon", hidden).Invoke(ni, new object[] { true });
}
}
}
+19 -12
View File
@@ -180,13 +180,13 @@ namespace Shadowsocks.View
{
serverInfo = config.GetCurrentServer().FriendlyName();
}
// we want to show more details but notify icon title is limited to 63 characters
// show more info by hacking the P/Invoke declaration for NOTIFYICONDATA inside Windows Forms
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
(enabled ?
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ "\n" + serverInfo;
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length));
ViewUtils.SetNotifyIconText(_notifyIcon, text);
}
private Bitmap getTrayIconByState(Bitmap originIcon, bool enabled, bool global)
@@ -197,11 +197,16 @@ namespace Shadowsocks.View
for (int y = 0; y < iconCopy.Height; y++)
{
Color color = originIcon.GetPixel(x, y);
if (color.A != 0 && color.R > 30)
if (color.A != 0)
{
if (!enabled)
{
iconCopy.SetPixel(x, y, Color.FromArgb((byte)(color.A / 1.25), color.R, color.G, color.B));
Color flyBlue = Color.FromArgb(192, 192, 192);
// Multiply with flyBlue
int red = color.R * flyBlue.R / 255;
int green = color.G * flyBlue.G / 255;
int blue = color.B * flyBlue.B / 255;
iconCopy.SetPixel(x, y, Color.FromArgb(color.A, red, green, blue));
}
else if (global)
{
@@ -282,16 +287,18 @@ namespace Shadowsocks.View
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
CreateMenuGroup("Updates...", new MenuItem[] {
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new MenuItem("-"),
this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
this.checkPreReleaseToggleItem = CreateMenuItem("Check Pre-release Version", new EventHandler(this.checkPreReleaseToggleItem_Click)),
CreateMenuGroup("Help", new MenuItem[] {
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
CreateMenuGroup("Updates...", new MenuItem[] {
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new MenuItem("-"),
this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
this.checkPreReleaseToggleItem = CreateMenuItem("Check Pre-release Version", new EventHandler(this.checkPreReleaseToggleItem_Click)),
}),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
}),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
new MenuItem("-"),
CreateMenuItem("Quit", new EventHandler(this.Quit_Click))
});