Skip to content

Commit

Permalink
Merge pull request #1298 from rwasef1830/sip003
Browse files Browse the repository at this point in the history
SIP002 and SIP003 support.
  • Loading branch information
celeron533 committed Aug 21, 2017
2 parents ed31a3c + caa96ad commit 71120e5
Show file tree
Hide file tree
Showing 8 changed files with 546 additions and 80 deletions.
134 changes: 134 additions & 0 deletions shadowsocks-csharp/Controller/Service/Sip003Plugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using Shadowsocks.Model;

namespace Shadowsocks.Controller.Service
{
// https://github.com/shadowsocks/shadowsocks-org/wiki/Plugin
public sealed class Sip003Plugin : IDisposable
{
public IPEndPoint LocalEndPoint { get; private set; }
public int ProcessId => _started ? _pluginProcess.Id : 0;

private readonly object _startProcessLock = new object();
private readonly Process _pluginProcess;
private bool _started;
private bool _disposed;

public static Sip003Plugin CreateIfConfigured(Server server)
{
if (server == null)
{
throw new ArgumentNullException(nameof(server));
}

if (string.IsNullOrWhiteSpace(server.plugin))
{
return null;
}

return new Sip003Plugin(server.plugin, server.plugin_opts, server.server, server.server_port);
}

private Sip003Plugin(string plugin, string pluginOpts, string serverAddress, int serverPort)
{
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
if (string.IsNullOrWhiteSpace(serverAddress))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(serverAddress));
}
if ((ushort)serverPort != serverPort)
{
throw new ArgumentOutOfRangeException("serverPort");
}

var appPath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath);

_pluginProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = plugin,
UseShellExecute = false,
CreateNoWindow = true,
ErrorDialog = false,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = appPath ?? Environment.CurrentDirectory,
Environment =
{
["SS_REMOTE_HOST"] = serverAddress,
["SS_REMOTE_PORT"] = serverPort.ToString(),
["SS_PLUGIN_OPTIONS"] = pluginOpts
}
}
};
}

public bool StartIfNeeded()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}

lock (_startProcessLock)
{
if (_started && !_pluginProcess.HasExited)
{
return false;
}

var localPort = GetNextFreeTcpPort();
LocalEndPoint = new IPEndPoint(IPAddress.Loopback, localPort);

_pluginProcess.StartInfo.Environment["SS_LOCAL_HOST"] = LocalEndPoint.Address.ToString();
_pluginProcess.StartInfo.Environment["SS_LOCAL_PORT"] = LocalEndPoint.Port.ToString();
_pluginProcess.Start();
_started = true;
}

return true;
}

static int GetNextFreeTcpPort()
{
var l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}

public void Dispose()
{
if (_disposed)
{
return;
}

try
{
if (!_pluginProcess.HasExited)
{
_pluginProcess.Kill();
_pluginProcess.WaitForExit();
}
}
catch (Exception) { }
finally
{
try
{
_pluginProcess.Dispose();
}
catch (Exception) { }

_disposed = true;
}
}
}
}
12 changes: 10 additions & 2 deletions shadowsocks-csharp/Controller/Service/TCPRelay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,15 @@ private void StartConnect()
// Setting up proxy
IProxy remote;
EndPoint proxyEP = null;
if (_config.proxy.useProxy)
EndPoint serverEP = SocketUtil.GetEndPoint(_server.server, _server.server_port);
EndPoint pluginEP = _controller.GetPluginLocalEndPointIfConfigured(_server);

if (pluginEP != null)
{
serverEP = pluginEP;
remote = new DirectConnect();
}
else if (_config.proxy.useProxy)
{
switch (_config.proxy.proxyType)
{
Expand Down Expand Up @@ -607,7 +615,7 @@ private void StartConnect()
proxyTimer.Enabled = true;

proxyTimer.Session = session;
proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(_server.server, _server.server_port);
proxyTimer.DestEndPoint = serverEP;
proxyTimer.Server = _server;

_proxyConnected = false;
Expand Down
71 changes: 67 additions & 4 deletions shadowsocks-csharp/Controller/ShadowsocksController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
Expand All @@ -14,6 +15,8 @@
using Shadowsocks.Properties;
using Shadowsocks.Util;
using System.Linq;
using Shadowsocks.Controller.Service;
using Shadowsocks.Proxy;

namespace Shadowsocks.Controller
{
Expand All @@ -33,6 +36,8 @@ public class ShadowsocksController
private StrategyManager _strategyManager;
private PrivoxyRunner privoxyRunner;
private GFWListUpdater gfwListUpdater;
private readonly ConcurrentDictionary<Server, Sip003Plugin> _pluginsByServer;

public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance;
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }

Expand Down Expand Up @@ -79,6 +84,7 @@ public ShadowsocksController()
_config = Configuration.Load();
StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
_strategyManager = new StrategyManager(this);
_pluginsByServer = new ConcurrentDictionary<Server, Sip003Plugin>();
StartReleasingMemory();
StartTrafficStatistics(61);
}
Expand Down Expand Up @@ -144,6 +150,23 @@ public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, E
return GetCurrentServer();
}

public EndPoint GetPluginLocalEndPointIfConfigured(Server server)
{
var plugin = _pluginsByServer.GetOrAdd(server, Sip003Plugin.CreateIfConfigured);
if (plugin == null)
{
return null;
}

if (plugin.StartIfNeeded())
{
Logging.Info(
$"Started SIP003 plugin for {server.Identifier()} on {plugin.LocalEndPoint} - PID: {plugin.ProcessId}");
}

return plugin.LocalEndPoint;
}

public void SaveServers(List<Server> servers, int localPort)
{
_config.configs = servers;
Expand Down Expand Up @@ -259,6 +282,7 @@ public void Stop()
{
_listener.Stop();
}
StopPlugins();
if (privoxyRunner != null)
{
privoxyRunner.Stop();
Expand All @@ -270,6 +294,15 @@ public void Stop()
Encryption.RNG.Close();
}

private void StopPlugins()
{
foreach (var serverAndPlugin in _pluginsByServer)
{
serverAndPlugin.Value?.Dispose();
}
_pluginsByServer.Clear();
}

public void TouchPACFile()
{
string pacFilename = _pacServer.TouchPACFile();
Expand Down Expand Up @@ -297,13 +330,41 @@ public string GetQRCodeForCurrentServer()
public static string GetQRCode(Server server)
{
string tag = string.Empty;
string parts = $"{server.method}:{server.password}@{server.server}:{server.server_port}";
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
if(!server.remarks.IsNullOrEmpty())
string url = string.Empty;

if (string.IsNullOrWhiteSpace(server.plugin))
{
// For backwards compatiblity, if no plugin, use old url format
string parts = $"{server.method}:{server.password}@{server.server}:{server.server_port}";
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
url = base64;
}
else
{
// SIP002
string parts = $"{server.method}:{server.password}";
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
string websafeBase64 = base64.Replace('+', '-').Replace('/', '_').TrimEnd('=');

string pluginPart = server.plugin;
if (!string.IsNullOrWhiteSpace(server.plugin_opts))
{
pluginPart += ";" + server.plugin_opts;
}

url = string.Format(
"{0}@{1}:{2}/?plugin={3}",
websafeBase64,
HttpUtility.UrlEncode(server.server, Encoding.UTF8),
server.server_port,
HttpUtility.UrlEncode(pluginPart, Encoding.UTF8));
}

if (!server.remarks.IsNullOrEmpty())
{
tag = $"#{HttpUtility.UrlEncode(server.remarks, Encoding.UTF8)}";
}
return $"ss://{base64}{tag}";
return $"ss://{url}{tag}";
}

public void UpdatePACFromGFWList()
Expand Down Expand Up @@ -421,6 +482,8 @@ public void UpdateOutboundCounter(Server server, long n)

protected void Reload()
{
StopPlugins();

Encryption.RNG.Reload();
// some logic in configuration updated the config when saving, we need to read it again
_config = Configuration.Load();
Expand Down
Loading

0 comments on commit 71120e5

Please sign in to comment.