Improve sysproxy exception handlingtags/4.1.3
@@ -6,6 +6,7 @@ using System.Net; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.Text; | using System.Text; | ||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using Shadowsocks.Util.SystemProxy; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -145,6 +146,24 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
Info(e); | Info(e); | ||||
} | } | ||||
} | |||||
else if (e is ProxyException) | |||||
{ | |||||
var ex = (ProxyException)e; | |||||
switch (ex.Type) | |||||
{ | |||||
case ProxyExceptionType.FailToRun: | |||||
case ProxyExceptionType.QueryReturnMalformed: | |||||
case ProxyExceptionType.SysproxyExitError: | |||||
Error($"sysproxy - {ex.Type.ToString()}:{ex.Message}"); | |||||
break; | |||||
case ProxyExceptionType.QueryReturnEmpty: | |||||
case ProxyExceptionType.Unspecific: | |||||
Error($"sysproxy - {ex.Type.ToString()}"); | |||||
break; | |||||
} | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -1,4 +1,5 @@ | |||||
using System; | using System; | ||||
using System.Windows.Forms; | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Util.SystemProxy; | using Shadowsocks.Util.SystemProxy; | ||||
@@ -11,7 +12,7 @@ namespace Shadowsocks.Controller | |||||
return value.ToString("yyyyMMddHHmmssfff"); | return value.ToString("yyyyMMddHHmmssfff"); | ||||
} | } | ||||
public static void Update(Configuration config, bool forceDisable, PACServer pacSrv) | |||||
public static void Update(Configuration config, bool forceDisable, PACServer pacSrv, bool noRetry = false) | |||||
{ | { | ||||
bool global = config.global; | bool global = config.global; | ||||
bool enabled = config.enabled; | bool enabled = config.enabled; | ||||
@@ -51,6 +52,19 @@ namespace Shadowsocks.Controller | |||||
catch (ProxyException ex) | catch (ProxyException ex) | ||||
{ | { | ||||
Logging.LogUsefulException(ex); | Logging.LogUsefulException(ex); | ||||
if (ex.Type != ProxyExceptionType.Unspecific && !noRetry) | |||||
{ | |||||
var ret = MessageBox.Show(I18N.GetString("Error occured when process proxy setting, do you want reset current setting and retry?"), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo, MessageBoxIcon.Warning); | |||||
if (ret == DialogResult.Yes) | |||||
{ | |||||
Sysproxy.ResetIEProxy(); | |||||
Update(config, forceDisable, pacSrv, true); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Unrecoverable proxy setting error occured, see log for detail"), I18N.GetString("Shadowsocks"), MessageBoxButtons.OK, MessageBoxIcon.Error); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -147,4 +147,4 @@ Proxy request failed=プロキシ要求が失敗しました。 | |||||
Proxy handshake failed=プロキシ ハンドシェイクに失敗しました。 | Proxy handshake failed=プロキシ ハンドシェイクに失敗しました。 | ||||
Register hotkey failed=ホットキーの登錄に失敗しました。 | Register hotkey failed=ホットキーの登錄に失敗しました。 | ||||
Cannot parse hotkey: {0}=ホットキーを解析できません: {0} | Cannot parse hotkey: {0}=ホットキーを解析できません: {0} | ||||
Timeout is invalid, it should not exceed {0}=タイムアウト値が無効です。{0} 以下の値を指定して下さい。 | |||||
Timeout is invalid, it should not exceed {0}=タイムアウト値が無効です。{0} 以下の値を指定して下さい。 |
@@ -148,3 +148,6 @@ Proxy handshake failed=代理握手失败 | |||||
Register hotkey failed=注册快捷键失败 | Register hotkey failed=注册快捷键失败 | ||||
Cannot parse hotkey: {0}=解析快捷键失败: {0} | Cannot parse hotkey: {0}=解析快捷键失败: {0} | ||||
Timeout is invalid, it should not exceed {0}=超时无效,不应超过 {0} | Timeout is invalid, it should not exceed {0}=超时无效,不应超过 {0} | ||||
Error occured when process proxy setting, do you want reset current setting and retry?=处理代理设置时发生错误,是否重置当前代理设置并重试? | |||||
Unrecoverable proxy setting error occured, see log for detail=发生不可恢复的代理设置错误,查看日志以取得详情 |
@@ -147,4 +147,4 @@ Proxy request failed=Proxy 要求失敗 | |||||
Proxy handshake failed=Proxy 交握失敗 | Proxy handshake failed=Proxy 交握失敗 | ||||
Register hotkey failed=註冊快速鍵失敗 | Register hotkey failed=註冊快速鍵失敗 | ||||
Cannot parse hotkey: {0}=剖析快速鍵失敗: {0} | Cannot parse hotkey: {0}=剖析快速鍵失敗: {0} | ||||
Timeout is invalid, it should not exceed {0}=逾時無效,不應超過 {0} | |||||
Timeout is invalid, it should not exceed {0}=逾時無效,不應超過 {0} |
@@ -7,8 +7,20 @@ using System.Threading.Tasks; | |||||
namespace Shadowsocks.Util.SystemProxy | namespace Shadowsocks.Util.SystemProxy | ||||
{ | { | ||||
enum ProxyExceptionType | |||||
{ | |||||
Unspecific, | |||||
FailToRun, | |||||
QueryReturnEmpty, | |||||
SysproxyExitError, | |||||
QueryReturnMalformed | |||||
} | |||||
class ProxyException : Exception | class ProxyException : Exception | ||||
{ | { | ||||
// provide more specific information about exception | |||||
public ProxyExceptionType Type { get; } | |||||
public ProxyException() | public ProxyException() | ||||
{ | { | ||||
} | } | ||||
@@ -24,5 +36,24 @@ namespace Shadowsocks.Util.SystemProxy | |||||
protected ProxyException(SerializationInfo info, StreamingContext context) : base(info, context) | protected ProxyException(SerializationInfo info, StreamingContext context) : base(info, context) | ||||
{ | { | ||||
} | } | ||||
public ProxyException(ProxyExceptionType type) | |||||
{ | |||||
this.Type = type; | |||||
} | |||||
public ProxyException(ProxyExceptionType type, string message) : base(message) | |||||
{ | |||||
this.Type = type; | |||||
} | |||||
public ProxyException(ProxyExceptionType type, string message, Exception innerException) : base(message, innerException) | |||||
{ | |||||
this.Type = type; | |||||
} | |||||
protected ProxyException(ProxyExceptionType type, SerializationInfo info, StreamingContext context) : base(info, context) | |||||
{ | |||||
this.Type = type; | |||||
} | |||||
} | } | ||||
} | } |
@@ -1,12 +1,12 @@ | |||||
using System; | |||||
using Newtonsoft.Json; | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using System; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.IO; | using System.IO; | ||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Model; | |||||
using Newtonsoft.Json; | |||||
namespace Shadowsocks.Util.SystemProxy | namespace Shadowsocks.Util.SystemProxy | ||||
{ | { | ||||
@@ -82,6 +82,26 @@ namespace Shadowsocks.Util.SystemProxy | |||||
ExecSysproxy(arguments); | 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 e) | |||||
{ | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
private static void ExecSysproxy(string arguments) | private static void ExecSysproxy(string arguments) | ||||
{ | { | ||||
// using event to avoid hanging when redirect standard output/error | // using event to avoid hanging when redirect standard output/error | ||||
@@ -132,27 +152,35 @@ namespace Shadowsocks.Util.SystemProxy | |||||
error.AppendLine(e.Data); | error.AppendLine(e.Data); | ||||
} | } | ||||
}; | }; | ||||
try | |||||
{ | |||||
process.Start(); | |||||
process.Start(); | |||||
process.BeginErrorReadLine(); | |||||
process.BeginOutputReadLine(); | |||||
process.WaitForExit(); | |||||
process.BeginErrorReadLine(); | |||||
process.BeginOutputReadLine(); | |||||
process.WaitForExit(); | |||||
} | |||||
catch (System.ComponentModel.Win32Exception e) | |||||
{ | |||||
// log the arguements | |||||
throw new ProxyException(ProxyExceptionType.FailToRun, process.StartInfo.Arguments, e); | |||||
} | |||||
var stderr = error.ToString(); | var stderr = error.ToString(); | ||||
var stdout = output.ToString(); | var stdout = output.ToString(); | ||||
var exitCode = process.ExitCode; | var exitCode = process.ExitCode; | ||||
if (exitCode != (int)RET_ERRORS.RET_NO_ERROR) | if (exitCode != (int)RET_ERRORS.RET_NO_ERROR) | ||||
{ | { | ||||
throw new ProxyException(stderr); | |||||
throw new ProxyException(ProxyExceptionType.SysproxyExitError, stderr); | |||||
} | } | ||||
if (arguments == "query") { | |||||
if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty()) { | |||||
if (arguments == "query") | |||||
{ | |||||
if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty()) | |||||
{ | |||||
// we cannot get user settings | // we cannot get user settings | ||||
throw new ProxyException("failed to query wininet settings"); | |||||
throw new ProxyException(ProxyExceptionType.QueryReturnEmpty); | |||||
} | } | ||||
_queryStr = stdout; | _queryStr = stdout; | ||||
} | } | ||||
@@ -183,9 +211,13 @@ namespace Shadowsocks.Util.SystemProxy | |||||
{ | { | ||||
string configContent = File.ReadAllText(Utils.GetTempPath(_userWininetConfigFile)); | string configContent = File.ReadAllText(Utils.GetTempPath(_userWininetConfigFile)); | ||||
_userSettings = JsonConvert.DeserializeObject<SysproxyConfig>(configContent); | _userSettings = JsonConvert.DeserializeObject<SysproxyConfig>(configContent); | ||||
} catch(Exception) { | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
// Suppress all exceptions. finally block will initialize new user config settings. | // Suppress all exceptions. finally block will initialize new user config settings. | ||||
} finally { | |||||
} | |||||
finally | |||||
{ | |||||
if (_userSettings == null) _userSettings = new SysproxyConfig(); | if (_userSettings == null) _userSettings = new SysproxyConfig(); | ||||
} | } | ||||
} | } | ||||
@@ -193,6 +225,21 @@ namespace Shadowsocks.Util.SystemProxy | |||||
private static void ParseQueryStr(string str) | private static void ParseQueryStr(string str) | ||||
{ | { | ||||
string[] userSettingsArr = str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); | 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]; | _userSettings.Flags = userSettingsArr[0]; | ||||
// handle output from WinINET | // handle output from WinINET | ||||