@@ -5,16 +5,17 @@ using System.Net.Sockets; | |||
using System.Net; | |||
using Shadowsocks.Encryption; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Controller.Strategy; | |||
namespace Shadowsocks.Controller | |||
{ | |||
class TCPRelay : Listener.Service | |||
{ | |||
private Configuration _config; | |||
public TCPRelay(Configuration config) | |||
private ShadowsocksController _controller; | |||
public TCPRelay(ShadowsocksController controller) | |||
{ | |||
this._config = config; | |||
this._controller = controller; | |||
} | |||
public bool Handle(byte[] firstPacket, int length, Socket socket, object state) | |||
@@ -30,7 +31,7 @@ namespace Shadowsocks.Controller | |||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | |||
Handler handler = new Handler(); | |||
handler.connection = socket; | |||
Server server = _config.GetCurrentServer(); | |||
Server server = _controller.GetCurrentStrategy().GetAServer(IStrategyCallerType.TCP, (IPEndPoint)socket.RemoteEndPoint); | |||
handler.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | |||
handler.server = server; | |||
@@ -6,16 +6,17 @@ using Shadowsocks.Model; | |||
using System.Net.Sockets; | |||
using System.Net; | |||
using System.Runtime.CompilerServices; | |||
using Shadowsocks.Controller.Strategy; | |||
namespace Shadowsocks.Controller | |||
{ | |||
class UDPRelay : Listener.Service | |||
{ | |||
private Configuration _config; | |||
private ShadowsocksController _controller; | |||
private LRUCache<IPEndPoint, UDPHandler> _cache; | |||
public UDPRelay(Configuration config) | |||
public UDPRelay(ShadowsocksController controller) | |||
{ | |||
this._config = config; | |||
this._controller = controller; | |||
this._cache = new LRUCache<IPEndPoint, UDPHandler>(512); // todo: choose a smart number | |||
} | |||
@@ -34,7 +35,7 @@ namespace Shadowsocks.Controller | |||
UDPHandler handler = _cache.get(remoteEndPoint); | |||
if (handler == null) | |||
{ | |||
handler = new UDPHandler(socket, _config.GetCurrentServer(), remoteEndPoint); | |||
handler = new UDPHandler(socket, _controller.GetCurrentStrategy().GetAServer(IStrategyCallerType.UDP, remoteEndPoint), remoteEndPoint); | |||
_cache.add(remoteEndPoint, handler); | |||
} | |||
handler.Send(firstPacket, length); |
@@ -5,6 +5,7 @@ using System.Collections.Generic; | |||
using System.Text; | |||
using System.Threading; | |||
using System.Net.Sockets; | |||
using Shadowsocks.Controller.Strategy; | |||
namespace Shadowsocks.Controller | |||
{ | |||
@@ -20,6 +21,7 @@ namespace Shadowsocks.Controller | |||
private Listener _listener; | |||
private PACServer _pacServer; | |||
private Configuration _config; | |||
private StrategyManager _strategyManager; | |||
private PolipoRunner polipoRunner; | |||
private GFWListUpdater gfwListUpdater; | |||
private bool stopped = false; | |||
@@ -49,6 +51,7 @@ namespace Shadowsocks.Controller | |||
public ShadowsocksController() | |||
{ | |||
_config = Configuration.Load(); | |||
_strategyManager = new StrategyManager(this); | |||
} | |||
public void Start() | |||
@@ -70,11 +73,34 @@ namespace Shadowsocks.Controller | |||
} | |||
// always return copy | |||
public Configuration GetConfiguration() | |||
public Configuration GetConfigurationCopy() | |||
{ | |||
return Configuration.Load(); | |||
} | |||
// always return current instance | |||
public Configuration GetCurrentConfiguration() | |||
{ | |||
return _config; | |||
} | |||
public IList<IStrategy> GetStrategies() | |||
{ | |||
return _strategyManager.GetStrategies(); | |||
} | |||
public IStrategy GetCurrentStrategy() | |||
{ | |||
foreach (var strategy in _strategyManager.GetStrategies()) | |||
{ | |||
if (strategy.ID == this._config.strategy) | |||
{ | |||
return strategy; | |||
} | |||
} | |||
return null; | |||
} | |||
public void SaveServers(List<Server> servers, int localPort) | |||
{ | |||
_config.configs = servers; | |||
@@ -134,6 +160,14 @@ namespace Shadowsocks.Controller | |||
public void SelectServerIndex(int index) | |||
{ | |||
_config.index = index; | |||
_config.strategy = null; | |||
SaveConfig(_config); | |||
} | |||
public void SelectStrategy(string strategyID) | |||
{ | |||
_config.index = -1; | |||
_config.strategy = strategyID; | |||
SaveConfig(_config); | |||
} | |||
@@ -250,8 +284,8 @@ namespace Shadowsocks.Controller | |||
{ | |||
polipoRunner.Start(_config); | |||
TCPRelay tcpRelay = new TCPRelay(_config); | |||
UDPRelay udpRelay = new UDPRelay(_config); | |||
TCPRelay tcpRelay = new TCPRelay(this); | |||
UDPRelay udpRelay = new UDPRelay(this); | |||
List<Listener.Service> services = new List<Listener.Service>(); | |||
services.Add(tcpRelay); | |||
services.Add(udpRelay); | |||
@@ -0,0 +1,72 @@ | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Model; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
using System.Text; | |||
namespace Shadowsocks.Controller.Strategy | |||
{ | |||
class BalancingStrategy : IStrategy | |||
{ | |||
ShadowsocksController _controller; | |||
Random _random; | |||
public BalancingStrategy(ShadowsocksController controller) | |||
{ | |||
_controller = controller; | |||
_random = new Random(); | |||
} | |||
public string Name | |||
{ | |||
get | |||
{ | |||
return "Load Balance"; | |||
} | |||
} | |||
public string ID | |||
{ | |||
get | |||
{ | |||
return "com.shadowsocks.strategy.balancing"; | |||
} | |||
} | |||
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) | |||
{ | |||
var configs = _controller.GetCurrentConfiguration().configs; | |||
int index; | |||
if (type == IStrategyCallerType.TCP) | |||
{ | |||
index = _random.Next(); | |||
} | |||
else | |||
{ | |||
index = localIPEndPoint.GetHashCode(); | |||
} | |||
return configs[index % configs.Count]; | |||
} | |||
public void UpdateLatency(Model.Server server) | |||
{ | |||
// do nothing | |||
} | |||
public void UpdateLastRead(Model.Server server) | |||
{ | |||
// do nothing | |||
} | |||
public void UpdateLastWrite(Model.Server server) | |||
{ | |||
// do nothing | |||
} | |||
public void SetFailure(Model.Server server) | |||
{ | |||
// do nothing | |||
} | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
using Shadowsocks.Model; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
using System.Text; | |||
namespace Shadowsocks.Controller.Strategy | |||
{ | |||
public enum IStrategyCallerType | |||
{ | |||
TCP, | |||
UDP | |||
} | |||
public interface IStrategy | |||
{ | |||
string Name { get; } | |||
string ID { get; } | |||
Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint); | |||
void UpdateLatency(Server server); | |||
void UpdateLastRead(Server server); | |||
void UpdateLastWrite(Server server); | |||
void SetFailure(Server server); | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
using Shadowsocks.Controller; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace Shadowsocks.Controller.Strategy | |||
{ | |||
class StrategyManager | |||
{ | |||
List<IStrategy> _strategies; | |||
public StrategyManager(ShadowsocksController controller) | |||
{ | |||
_strategies = new List<IStrategy>(); | |||
_strategies.Add(new BalancingStrategy(controller)); | |||
} | |||
public IList<IStrategy> GetStrategies() | |||
{ | |||
return _strategies; | |||
} | |||
} | |||
} |
@@ -23,6 +23,7 @@ Show Logs...=显示日志... | |||
About...=关于... | |||
Quit=退出 | |||
Edit Servers=编辑服务器 | |||
Load Balance=负载均衡 | |||
# Config Form | |||
@@ -11,6 +11,9 @@ namespace Shadowsocks.Model | |||
public class Configuration | |||
{ | |||
public List<Server> configs; | |||
// when strategy is set, index is ignored | |||
public string strategy; | |||
public int index; | |||
public bool global; | |||
public bool enabled; | |||
@@ -79,9 +82,9 @@ namespace Shadowsocks.Model | |||
{ | |||
config.index = config.configs.Count - 1; | |||
} | |||
if (config.index < 0) | |||
if (config.index < -1) | |||
{ | |||
config.index = 0; | |||
config.index = -1; | |||
} | |||
config.isDefault = false; | |||
try | |||
@@ -133,7 +133,7 @@ namespace Shadowsocks.View | |||
private void LoadCurrentConfiguration() | |||
{ | |||
_modifiedConfiguration = controller.GetConfiguration(); | |||
_modifiedConfiguration = controller.GetConfigurationCopy(); | |||
LoadConfiguration(_modifiedConfiguration); | |||
_oldSelectedIndex = _modifiedConfiguration.index; | |||
ServersListBox.SelectedIndex = _modifiedConfiguration.index; | |||
@@ -71,9 +71,9 @@ namespace Shadowsocks.View | |||
LoadCurrentConfiguration(); | |||
updateChecker.CheckUpdate(controller.GetConfiguration()); | |||
updateChecker.CheckUpdate(controller.GetConfigurationCopy()); | |||
if (controller.GetConfiguration().isDefault) | |||
if (controller.GetConfigurationCopy().isDefault) | |||
{ | |||
_isFirstRun = true; | |||
ShowConfigForm(); | |||
@@ -106,7 +106,7 @@ namespace Shadowsocks.View | |||
{ | |||
icon = Resources.ss24; | |||
} | |||
Configuration config = controller.GetConfiguration(); | |||
Configuration config = controller.GetConfigurationCopy(); | |||
bool enabled = config.enabled; | |||
bool global = config.global; | |||
if (!enabled) | |||
@@ -124,12 +124,21 @@ namespace Shadowsocks.View | |||
} | |||
_notifyIcon.Icon = Icon.FromHandle(icon.GetHicon()); | |||
string serverInfo = null; | |||
if (config.strategy != null) | |||
{ | |||
serverInfo = I18N.GetString(controller.GetCurrentStrategy().Name); | |||
} | |||
else | |||
{ | |||
serverInfo = config.GetCurrentServer().FriendlyName(); | |||
} | |||
// we want to show more details but notify icon title is limited to 63 characters | |||
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" + config.GetCurrentServer().FriendlyName(); | |||
+ "\n" + serverInfo; | |||
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length)); | |||
} | |||
@@ -185,18 +194,18 @@ namespace Shadowsocks.View | |||
private void controller_EnableStatusChanged(object sender, EventArgs e) | |||
{ | |||
enableItem.Checked = controller.GetConfiguration().enabled; | |||
enableItem.Checked = controller.GetConfigurationCopy().enabled; | |||
modeItem.Enabled = enableItem.Checked; | |||
} | |||
void controller_ShareOverLANStatusChanged(object sender, EventArgs e) | |||
{ | |||
ShareOverLANItem.Checked = controller.GetConfiguration().shareOverLan; | |||
ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; | |||
} | |||
void controller_EnableGlobalChanged(object sender, EventArgs e) | |||
{ | |||
globalModeItem.Checked = controller.GetConfiguration().global; | |||
globalModeItem.Checked = controller.GetConfigurationCopy().global; | |||
PACModeItem.Checked = !globalModeItem.Checked; | |||
} | |||
@@ -243,7 +252,7 @@ namespace Shadowsocks.View | |||
private void LoadCurrentConfiguration() | |||
{ | |||
Configuration config = controller.GetConfiguration(); | |||
Configuration config = controller.GetConfigurationCopy(); | |||
UpdateServersMenu(); | |||
enableItem.Checked = config.enabled; | |||
modeItem.Enabled = config.enabled; | |||
@@ -263,20 +272,32 @@ namespace Shadowsocks.View | |||
{ | |||
items.RemoveAt(0); | |||
} | |||
Configuration configuration = controller.GetConfiguration(); | |||
for (int i = 0; i < configuration.configs.Count; i++) | |||
int i = 0; | |||
foreach (var strategy in controller.GetStrategies()) | |||
{ | |||
MenuItem item = new MenuItem(I18N.GetString(strategy.Name)); | |||
item.Tag = strategy.ID; | |||
item.Click += AStrategyItem_Click; | |||
items.Add(i, item); | |||
i++; | |||
} | |||
Configuration configuration = controller.GetConfigurationCopy(); | |||
foreach (var server in configuration.configs) | |||
{ | |||
Server server = configuration.configs[i]; | |||
MenuItem item = new MenuItem(server.FriendlyName()); | |||
item.Tag = i; | |||
item.Click += AServerItem_Click; | |||
items.Add(i, item); | |||
i++; | |||
} | |||
if (configuration.index >= 0 && configuration.index < configuration.configs.Count) | |||
foreach (MenuItem item in items) | |||
{ | |||
items[configuration.index].Checked = true; | |||
if (item.Tag != null && (item.Tag.ToString() == configuration.index.ToString() || item.Tag.ToString() == configuration.strategy)) | |||
{ | |||
item.Checked = true; | |||
} | |||
} | |||
} | |||
@@ -380,6 +401,12 @@ namespace Shadowsocks.View | |||
controller.SelectServerIndex((int)item.Tag); | |||
} | |||
private void AStrategyItem_Click(object sender, EventArgs e) | |||
{ | |||
MenuItem item = (MenuItem)sender; | |||
controller.SelectStrategy((string)item.Tag); | |||
} | |||
private void ShowLogItem_Click(object sender, EventArgs e) | |||
{ | |||
string argument = Logging.LogFile; | |||
@@ -511,11 +538,11 @@ namespace Shadowsocks.View | |||
{ | |||
if (!onlinePACItem.Checked) | |||
{ | |||
if (String.IsNullOrEmpty(controller.GetConfiguration().pacUrl)) | |||
if (String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl)) | |||
{ | |||
UpdateOnlinePACURLItem_Click(sender, e); | |||
} | |||
if (!String.IsNullOrEmpty(controller.GetConfiguration().pacUrl)) | |||
if (!String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl)) | |||
{ | |||
localPACItem.Checked = false; | |||
onlinePACItem.Checked = true; | |||
@@ -527,7 +554,7 @@ namespace Shadowsocks.View | |||
private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) | |||
{ | |||
string origPacUrl = controller.GetConfiguration().pacUrl; | |||
string origPacUrl = controller.GetConfigurationCopy().pacUrl; | |||
string pacUrl = Microsoft.VisualBasic.Interaction.InputBox( | |||
I18N.GetString("Please input PAC Url"), | |||
I18N.GetString("Edit Online PAC URL"), | |||
@@ -124,15 +124,15 @@ | |||
<Compile Include="3rd\zxing\ResultPoint.cs" /> | |||
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> | |||
<Compile Include="3rd\zxing\WriterException.cs" /> | |||
<Compile Include="Controller\AutoStartup.cs" /> | |||
<Compile Include="Controller\System\AutoStartup.cs" /> | |||
<Compile Include="Controller\FileManager.cs" /> | |||
<Compile Include="Controller\GFWListUpdater.cs" /> | |||
<Compile Include="Controller\Service\GFWListUpdater.cs" /> | |||
<Compile Include="Controller\I18N.cs" /> | |||
<Compile Include="Controller\Listener.cs" /> | |||
<Compile Include="Controller\Service\Listener.cs" /> | |||
<Compile Include="Controller\Logging.cs" /> | |||
<Compile Include="Controller\PortForwarder.cs" /> | |||
<Compile Include="Controller\UDPRelay.cs" /> | |||
<Compile Include="Controller\UpdateChecker.cs" /> | |||
<Compile Include="Controller\Service\PortForwarder.cs" /> | |||
<Compile Include="Controller\Service\UDPRelay.cs" /> | |||
<Compile Include="Controller\Service\UpdateChecker.cs" /> | |||
<Compile Include="Encryption\EncryptorBase.cs" /> | |||
<Compile Include="Encryption\EncryptorFactory.cs" /> | |||
<Compile Include="Encryption\IVEncryptor.cs" /> | |||
@@ -142,7 +142,7 @@ | |||
<Compile Include="Encryption\SodiumEncryptor.cs" /> | |||
<Compile Include="Encryption\TableEncryptor.cs" /> | |||
<Compile Include="Encryption\IEncryptor.cs" /> | |||
<Compile Include="Controller\PACServer.cs" /> | |||
<Compile Include="Controller\Service\PACServer.cs" /> | |||
<Compile Include="Model\Server.cs" /> | |||
<Compile Include="Model\Configuration.cs" /> | |||
<Compile Include="Properties\Resources.Designer.cs"> | |||
@@ -150,6 +150,9 @@ | |||
<DesignTime>True</DesignTime> | |||
<DependentUpon>Resources.resx</DependentUpon> | |||
</Compile> | |||
<Compile Include="Controller\Strategy\BalancingStrategy.cs" /> | |||
<Compile Include="Controller\Strategy\StrategyManager.cs" /> | |||
<Compile Include="Controller\Strategy\IStrategy.cs" /> | |||
<Compile Include="Util\Util.cs" /> | |||
<Compile Include="View\ConfigForm.cs"> | |||
<SubType>Form</SubType> | |||
@@ -157,12 +160,12 @@ | |||
<Compile Include="View\ConfigForm.Designer.cs"> | |||
<DependentUpon>ConfigForm.cs</DependentUpon> | |||
</Compile> | |||
<Compile Include="Controller\TCPRelay.cs" /> | |||
<Compile Include="Controller\PolipoRunner.cs" /> | |||
<Compile Include="Controller\Service\TCPRelay.cs" /> | |||
<Compile Include="Controller\Service\PolipoRunner.cs" /> | |||
<Compile Include="Program.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
<Compile Include="Controller\ShadowsocksController.cs" /> | |||
<Compile Include="Controller\SystemProxy.cs" /> | |||
<Compile Include="Controller\System\SystemProxy.cs" /> | |||
<Compile Include="View\MenuViewController.cs" /> | |||
<Compile Include="View\QRCodeForm.cs"> | |||
<SubType>Form</SubType> | |||