|
|
@@ -1,291 +0,0 @@ |
|
|
|
using Newtonsoft.Json;
|
|
|
|
using NLog;
|
|
|
|
using Shadowsocks.Controller;
|
|
|
|
using Shadowsocks.Model;
|
|
|
|
using Shadowsocks.Properties;
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Text;
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
namespace Shadowsocks.Util.SystemProxy
|
|
|
|
{
|
|
|
|
public static class Sysproxy
|
|
|
|
{
|
|
|
|
private static Logger logger = LogManager.GetCurrentClassLogger();
|
|
|
|
|
|
|
|
private const string _userWininetConfigFile = "user-wininet.json";
|
|
|
|
|
|
|
|
private readonly static string[] _lanIP = {
|
|
|
|
"<local>",
|
|
|
|
"localhost",
|
|
|
|
"127.*",
|
|
|
|
"10.*",
|
|
|
|
"172.16.*",
|
|
|
|
"172.17.*",
|
|
|
|
"172.18.*",
|
|
|
|
"172.19.*",
|
|
|
|
"172.20.*",
|
|
|
|
"172.21.*",
|
|
|
|
"172.22.*",
|
|
|
|
"172.23.*",
|
|
|
|
"172.24.*",
|
|
|
|
"172.25.*",
|
|
|
|
"172.26.*",
|
|
|
|
"172.27.*",
|
|
|
|
"172.28.*",
|
|
|
|
"172.29.*",
|
|
|
|
"172.30.*",
|
|
|
|
"172.31.*",
|
|
|
|
"192.168.*"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
private static string _queryStr;
|
|
|
|
|
|
|
|
// In general, this won't change
|
|
|
|
// format:
|
|
|
|
// <flags><CR-LF>
|
|
|
|
// <proxy-server><CR-LF>
|
|
|
|
// <bypass-list><CR-LF>
|
|
|
|
// <pac-url>
|
|
|
|
private static SysproxyConfig _userSettings = null;
|
|
|
|
|
|
|
|
enum RET_ERRORS : int
|
|
|
|
{
|
|
|
|
RET_NO_ERROR = 0,
|
|
|
|
INVALID_FORMAT = 1,
|
|
|
|
NO_PERMISSION = 2,
|
|
|
|
SYSCALL_FAILED = 3,
|
|
|
|
NO_MEMORY = 4,
|
|
|
|
INVAILD_OPTION_COUNT = 5,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Sysproxy()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"),
|
|
|
|
Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe);
|
|
|
|
}
|
|
|
|
catch (IOException e)
|
|
|
|
{
|
|
|
|
logger.LogUsefulException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL)
|
|
|
|
{
|
|
|
|
Read();
|
|
|
|
|
|
|
|
if (!_userSettings.UserSettingsRecorded)
|
|
|
|
{
|
|
|
|
// record user settings
|
|
|
|
ExecSysproxy("query");
|
|
|
|
ParseQueryStr(_queryStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
string arguments;
|
|
|
|
if (enable)
|
|
|
|
{
|
|
|
|
string customBypassString = _userSettings.BypassList ?? "";
|
|
|
|
List<string> customBypassList = new List<string>(customBypassString.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
|
|
|
|
customBypassList.AddRange(_lanIP);
|
|
|
|
string[] realBypassList = customBypassList.Distinct().ToArray();
|
|
|
|
string realBypassString = string.Join(";", realBypassList);
|
|
|
|
|
|
|
|
arguments = global
|
|
|
|
? $"global {proxyServer} {realBypassString}"
|
|
|
|
: $"pac {pacURL}";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// restore user settings
|
|
|
|
var flags = _userSettings.Flags;
|
|
|
|
var proxy_server = _userSettings.ProxyServer ?? "-";
|
|
|
|
var bypass_list = _userSettings.BypassList ?? "-";
|
|
|
|
var pac_url = _userSettings.PacUrl ?? "-";
|
|
|
|
arguments = $"set {flags} {proxy_server} {bypass_list} {pac_url}";
|
|
|
|
|
|
|
|
// have to get new settings
|
|
|
|
_userSettings.UserSettingsRecorded = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Save();
|
|
|
|
ExecSysproxy(arguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set system proxy to 1 (null) (null) (null)
|
|
|
|
public static bool ResetIEProxy()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// clear user-wininet.json
|
|
|
|
_userSettings = new SysproxyConfig();
|
|
|
|
Save();
|
|
|
|
// clear system setting
|
|
|
|
ExecSysproxy("set 1 - - -");
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void ExecSysproxy(string arguments)
|
|
|
|
{
|
|
|
|
// using event to avoid hanging when redirect standard output/error
|
|
|
|
// ref: https://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why
|
|
|
|
// and http://blog.csdn.net/zhangweixing0/article/details/7356841
|
|
|
|
using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
|
|
|
|
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
|
|
|
|
{
|
|
|
|
using (var process = new Process())
|
|
|
|
{
|
|
|
|
// Configure the process using the StartInfo properties.
|
|
|
|
process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe");
|
|
|
|
process.StartInfo.Arguments = arguments;
|
|
|
|
process.StartInfo.WorkingDirectory = Utils.GetTempPath();
|
|
|
|
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
|
|
|
process.StartInfo.UseShellExecute = false;
|
|
|
|
process.StartInfo.RedirectStandardError = true;
|
|
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
|
|
|
|
|
|
// Need to provide encoding info, or output/error strings we got will be wrong.
|
|
|
|
process.StartInfo.StandardOutputEncoding = Encoding.Unicode;
|
|
|
|
process.StartInfo.StandardErrorEncoding = Encoding.Unicode;
|
|
|
|
|
|
|
|
process.StartInfo.CreateNoWindow = true;
|
|
|
|
|
|
|
|
StringBuilder output = new StringBuilder();
|
|
|
|
StringBuilder error = new StringBuilder();
|
|
|
|
|
|
|
|
process.OutputDataReceived += (sender, e) =>
|
|
|
|
{
|
|
|
|
if (e.Data == null)
|
|
|
|
{
|
|
|
|
outputWaitHandle.Set();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
output.AppendLine(e.Data);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
process.ErrorDataReceived += (sender, e) =>
|
|
|
|
{
|
|
|
|
if (e.Data == null)
|
|
|
|
{
|
|
|
|
errorWaitHandle.Set();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error.AppendLine(e.Data);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
try
|
|
|
|
{
|
|
|
|
process.Start();
|
|
|
|
|
|
|
|
process.BeginErrorReadLine();
|
|
|
|
process.BeginOutputReadLine();
|
|
|
|
|
|
|
|
process.WaitForExit();
|
|
|
|
}
|
|
|
|
catch (System.ComponentModel.Win32Exception e)
|
|
|
|
{
|
|
|
|
// log the arguments
|
|
|
|
throw new ProxyException(ProxyExceptionType.FailToRun, process.StartInfo.Arguments, e);
|
|
|
|
}
|
|
|
|
var stderr = error.ToString();
|
|
|
|
var stdout = output.ToString();
|
|
|
|
|
|
|
|
var exitCode = process.ExitCode;
|
|
|
|
if (exitCode != (int)RET_ERRORS.RET_NO_ERROR)
|
|
|
|
{
|
|
|
|
throw new ProxyException(ProxyExceptionType.SysproxyExitError, stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arguments == "query")
|
|
|
|
{
|
|
|
|
if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty())
|
|
|
|
{
|
|
|
|
// we cannot get user settings
|
|
|
|
throw new ProxyException(ProxyExceptionType.QueryReturnEmpty);
|
|
|
|
}
|
|
|
|
_queryStr = stdout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void Save()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using (StreamWriter sw = new StreamWriter(File.Open(Utils.GetTempPath(_userWininetConfigFile), FileMode.Create)))
|
|
|
|
{
|
|
|
|
string jsonString = JsonConvert.SerializeObject(_userSettings, Formatting.Indented);
|
|
|
|
sw.Write(jsonString);
|
|
|
|
sw.Flush();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (IOException e)
|
|
|
|
{
|
|
|
|
logger.LogUsefulException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void Read()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
string configContent = File.ReadAllText(Utils.GetTempPath(_userWininetConfigFile));
|
|
|
|
_userSettings = JsonConvert.DeserializeObject<SysproxyConfig>(configContent);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
// Suppress all exceptions. finally block will initialize new user config settings.
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
if (_userSettings == null) _userSettings = new SysproxyConfig();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void ParseQueryStr(string str)
|
|
|
|
{
|
|
|
|
string[] userSettingsArr = str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
|
|
// sometimes sysproxy output in utf16le instead of ascii
|
|
|
|
// manually translate it
|
|
|
|
if (userSettingsArr.Length != 4)
|
|
|
|
{
|
|
|
|
byte[] strByte = Encoding.ASCII.GetBytes(str);
|
|
|
|
str = Encoding.Unicode.GetString(strByte);
|
|
|
|
userSettingsArr = str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
// still fail, throw exception with string hexdump
|
|
|
|
if (userSettingsArr.Length != 4)
|
|
|
|
{
|
|
|
|
throw new ProxyException(ProxyExceptionType.QueryReturnMalformed, BitConverter.ToString(strByte));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_userSettings.Flags = userSettingsArr[0];
|
|
|
|
|
|
|
|
// handle output from WinINET
|
|
|
|
if (userSettingsArr[1] == "(null)") _userSettings.ProxyServer = null;
|
|
|
|
else _userSettings.ProxyServer = userSettingsArr[1];
|
|
|
|
if (userSettingsArr[2] == "(null)") _userSettings.BypassList = null;
|
|
|
|
else _userSettings.BypassList = userSettingsArr[2];
|
|
|
|
if (userSettingsArr[3] == "(null)") _userSettings.PacUrl = null;
|
|
|
|
else _userSettings.PacUrl = userSettingsArr[3];
|
|
|
|
|
|
|
|
_userSettings.UserSettingsRecorded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|