Browse Source

Merge 08c6b18269 into 967c4e8c8b

pull/222/merge
TsungKang 10 years ago
parent
commit
190e9c63c4
28 changed files with 4142 additions and 171 deletions
  1. +15
    -29
      README.md
  2. +9
    -8
      shadowsocks-csharp.sln
  3. +113
    -0
      shadowsocks-csharp/3rd/ProxySocket/AuthMethod.cs
  4. +58
    -0
      shadowsocks-csharp/3rd/ProxySocket/AuthNone.cs
  5. +156
    -0
      shadowsocks-csharp/3rd/ProxySocket/AuthUserPass.cs
  6. +96
    -0
      shadowsocks-csharp/3rd/ProxySocket/IAsyncProxyResult.cs
  7. +82
    -0
      shadowsocks-csharp/3rd/ProxySocket/ProxyException.cs
  8. +385
    -0
      shadowsocks-csharp/3rd/ProxySocket/ProxySocket.cs
  9. +234
    -0
      shadowsocks-csharp/3rd/ProxySocket/Socks4Handler.cs
  10. +418
    -0
      shadowsocks-csharp/3rd/ProxySocket/Socks5Handler.cs
  11. +204
    -0
      shadowsocks-csharp/3rd/ProxySocket/SocksHandler.cs
  12. +283
    -0
      shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebRequest.cs
  13. +109
    -0
      shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebResponse.cs
  14. +312
    -0
      shadowsocks-csharp/3rd/ProxySocket/SocksWebClient.cs
  15. +754
    -0
      shadowsocks-csharp/3rd/QQWry.cs
  16. +3
    -3
      shadowsocks-csharp/Controller/AutoStartup.cs
  17. +3
    -0
      shadowsocks-csharp/Controller/FileManager.cs
  18. +30
    -1
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  19. +23
    -1
      shadowsocks-csharp/Data/cn.txt
  20. +12
    -15
      shadowsocks-csharp/Properties/Resources.Designer.cs
  21. +11
    -0
      shadowsocks-csharp/Util/Util.cs
  22. +3
    -2
      shadowsocks-csharp/View/ConfigForm.cs
  23. +29
    -1
      shadowsocks-csharp/View/MenuViewController.cs
  24. +237
    -0
      shadowsocks-csharp/View/PingForm.Designer.cs
  25. +325
    -0
      shadowsocks-csharp/View/PingForm.cs
  26. +147
    -0
      shadowsocks-csharp/View/PingForm.resx
  27. +17
    -4
      shadowsocks-csharp/app.config
  28. +74
    -107
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 15
- 29
README.md View File

@@ -1,48 +1,34 @@
Shadowsocks for Windows
=======================

[![Build Status]][Appveyor]
#### Features compare with official version

#### Features
1. Add a speed test form
2. Get server location
3. Download speed test

1. System proxy configuration
2. Fast profile switching
3. PAC mode and global mode
4. GFWList and user rules
5. Supports HTTP proxy
#### 和官方版本相比有什么不同

#### Download
Download a [latest release].
1. 新增了一个测速的功能
2. 可以获取获取服务器IP归属地,如果不想联网获取,请下载[纯真IP库]并把qqwry.dat文件放置在当前目录
3. 下载速度测试(通过下载谷歌浏览器的安装器来测试,大概900多K)

For >= Windows 8 or with .Net 4.0, download Shadowsocks-win-dotnet4.0-x.x.x.zip.
#### Download

For <= Windows 7 or with .Net 2.0, download Shadowsocks-win-x.x.x.zip.
Download [latest release].

#### Usage

1. Find Shadowsocks icon in the notification tray
2. You can add multiple servers in servers menu
3. Select Enable System Proxy menu to enable system proxy. Please disable other
proxy addons in your browser, or set them to use system proxy
4. You can also configure your browser proxy manually if you don't want to enable
system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this
port in Server -> Edit Servers
5. You can change PAC rules by editing the PAC file. When you save the PAC file
with any editor, Shadowsocks will notify browsers about the change automatically
6. You can also update the PAC file from GFWList. Note your modifications to the PAC
file will be lost. However you can put your rules in the user rule file for GFWList.
Don't forget to update from GFWList again after you've edited the user rule
See [official]

### Develop

Visual Studio Express 2012 is recommended.
Visual Studio 2015 is recommended.

#### License

GPLv3


[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master
[latest release]: https://sourceforge.net/projects/shadowsocksgui/files/dist/
[latest release]: https://github.com/TkYu/shadowsocks-csharp-withping/releases
[official]: https://github.com/shadowsocks/shadowsocks-csharp
[纯真IP库]: http://update.cz88.net/soft/setup.zip

+ 9
- 8
shadowsocks-csharp.sln View File

@@ -1,11 +1,13 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2012 for Windows Desktop
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}"
# Visual Studio 14
VisualStudioVersion = 14.0.22823.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{F58374A5-9AFB-430A-AF20-C509D3DCED3F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{45913187-0685-4903-B250-DCEF0479CD86}"
ProjectSection(ProjectDependencies) = postProject
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} = {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}
{F58374A5-9AFB-430A-AF20-C509D3DCED3F} = {F58374A5-9AFB-430A-AF20-C509D3DCED3F}
EndProjectSection
EndProject
Global
@@ -14,11 +16,10 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.ActiveCfg = Debug|x86
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Build.0 = Debug|x86
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Deploy.0 = Debug|x86
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.ActiveCfg = Release|x86
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.Build.0 = Release|x86
{F58374A5-9AFB-430A-AF20-C509D3DCED3F}.Debug|x86.ActiveCfg = Debug|Any CPU
{F58374A5-9AFB-430A-AF20-C509D3DCED3F}.Debug|x86.Build.0 = Debug|Any CPU
{F58374A5-9AFB-430A-AF20-C509D3DCED3F}.Release|x86.ActiveCfg = Release|Any CPU
{F58374A5-9AFB-430A-AF20-C509D3DCED3F}.Release|x86.Build.0 = Release|Any CPU
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.ActiveCfg = Debug|x86
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.Build.0 = Debug|x86
{45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.ActiveCfg = Release|x86


+ 113
- 0
shadowsocks-csharp/3rd/ProxySocket/AuthMethod.cs View File

@@ -0,0 +1,113 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Net;
using System.Net.Sockets;

namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// Implements a SOCKS authentication scheme.
/// </summary>
/// <remarks>This is an abstract class; it must be inherited.</remarks>
internal abstract class AuthMethod {
/// <summary>
/// Initializes an AuthMethod instance.
/// </summary>
/// <param name="server">The socket connection with the proxy server.</param>
public AuthMethod(Socket server) {
Server = server;
}
/// <summary>
/// Authenticates the user.
/// </summary>
/// <exception cref="ProxyException">Authentication with the proxy server failed.</exception>
/// <exception cref="ProtocolViolationException">The proxy server uses an invalid protocol.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
public abstract void Authenticate();
/// <summary>
/// Authenticates the user asynchronously.
/// </summary>
/// <param name="callback">The method to call when the authentication is complete.</param>
/// <exception cref="ProxyException">Authentication with the proxy server failed.</exception>
/// <exception cref="ProtocolViolationException">The proxy server uses an invalid protocol.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
public abstract void BeginAuthenticate(HandShakeComplete callback);
/// <summary>
/// Gets or sets the socket connection with the proxy server.
/// </summary>
/// <value>The socket connection with the proxy server.</value>
protected Socket Server {
get {
return m_Server;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Server = value;
}
}
/// <summary>
/// Gets or sets a byt array that can be used to store data.
/// </summary>
/// <value>A byte array to store data.</value>
protected byte[] Buffer {
get {
return m_Buffer;
}
set {
m_Buffer = value;
}
}
/// <summary>
/// Gets or sets the number of bytes that have been received from the remote proxy server.
/// </summary>
/// <value>An integer that holds the number of bytes that have been received from the remote proxy server.</value>
protected int Received {
get {
return m_Received;
}
set {
m_Received = value;
}
}
// private variables
/// <summary>Holds the value of the Buffer property.</summary>
private byte[] m_Buffer;
/// <summary>Holds the value of the Server property.</summary>
private Socket m_Server;
/// <summary>Holds the address of the method to call when the proxy has authenticated the client.</summary>
protected HandShakeComplete CallBack;
/// <summary>Holds the value of the Received property.</summary>
private int m_Received;
}
}

+ 58
- 0
shadowsocks-csharp/3rd/ProxySocket/AuthNone.cs View File

@@ -0,0 +1,58 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System.Net.Sockets;

namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// This class implements the 'No Authentication' scheme.
/// </summary>
internal sealed class AuthNone : AuthMethod {
/// <summary>
/// Initializes an AuthNone instance.
/// </summary>
/// <param name="server">The socket connection with the proxy server.</param>
public AuthNone(Socket server) : base(server) {}
/// <summary>
/// Authenticates the user.
/// </summary>
public override void Authenticate() {
return; // Do Nothing
}
/// <summary>
/// Authenticates the user asynchronously.
/// </summary>
/// <param name="callback">The method to call when the authentication is complete.</param>
/// <remarks>This method immediately calls the callback method.</remarks>
public override void BeginAuthenticate(HandShakeComplete callback) {
callback(null);
}
}
}

+ 156
- 0
shadowsocks-csharp/3rd/ProxySocket/AuthUserPass.cs View File

@@ -0,0 +1,156 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Net.Sockets;
using System.Text;

namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// This class implements the 'username/password authentication' scheme.
/// </summary>
internal sealed class AuthUserPass : AuthMethod {
/// <summary>
/// Initializes a new AuthUserPass instance.
/// </summary>
/// <param name="server">The socket connection with the proxy server.</param>
/// <param name="user">The username to use.</param>
/// <param name="pass">The password to use.</param>
/// <exception cref="ArgumentNullException"><c>user</c> -or- <c>pass</c> is null.</exception>
public AuthUserPass(Socket server, string user, string pass) : base(server) {
Username = user;
Password = pass;
}
/// <summary>
/// Creates an array of bytes that has to be sent if the user wants to authenticate with the username/password authentication scheme.
/// </summary>
/// <returns>An array of bytes that has to be sent if the user wants to authenticate with the username/password authentication scheme.</returns>
private byte[] GetAuthenticationBytes() {
byte[] buffer = new byte[3 + Username.Length + Password.Length];
buffer[0] = 1;
buffer[1] = (byte)Username.Length;
Array.Copy(Encoding.ASCII.GetBytes(Username), 0, buffer, 2, Username.Length);
buffer[Username.Length + 2] = (byte)Password.Length;
Array.Copy(Encoding.ASCII.GetBytes(Password), 0, buffer, Username.Length + 3, Password.Length);
return buffer;
}
/// <summary>
/// Starts the authentication process.
/// </summary>
public override void Authenticate() {
Server.Send(GetAuthenticationBytes());
byte[] buffer = new byte[2];
int received = 0;
while (received != 2) {
received += Server.Receive(buffer, received, 2 - received, SocketFlags.None);
}
if (buffer[1] != 0) {
Server.Close();
throw new ProxyException("Username/password combination rejected.");
}
return;
}
/// <summary>
/// Starts the asynchronous authentication process.
/// </summary>
/// <param name="callback">The method to call when the authentication is complete.</param>
public override void BeginAuthenticate(HandShakeComplete callback) {
CallBack = callback;
Server.BeginSend(GetAuthenticationBytes(), 0, 3 + Username.Length + Password.Length, SocketFlags.None, new AsyncCallback(this.OnSent), Server);
return;
}
/// <summary>
/// Called when the authentication bytes have been sent.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnSent(IAsyncResult ar) {
try {
Server.EndSend(ar);
Buffer = new byte[2];
Server.BeginReceive(Buffer, 0, 2, SocketFlags.None, new AsyncCallback(this.OnReceive), Server);
} catch (Exception e) {
CallBack(e);
}
}
/// <summary>
/// Called when the socket received an authentication reply.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnReceive(IAsyncResult ar) {
try {
Received += Server.EndReceive(ar);
if (Received == Buffer.Length)
if (Buffer[1] == 0)
CallBack(null);
else
throw new ProxyException("Username/password combination not accepted.");
else
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnReceive), Server);
} catch (Exception e) {
CallBack(e);
}
}
/// <summary>
/// Gets or sets the username to use when authenticating with the proxy server.
/// </summary>
/// <value>The username to use when authenticating with the proxy server.</value>
/// <exception cref="ArgumentNullException">The specified value is null.</exception>
private string Username {
get {
return m_Username;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Username = value;
}
}
/// <summary>
/// Gets or sets the password to use when authenticating with the proxy server.
/// </summary>
/// <value>The password to use when authenticating with the proxy server.</value>
/// <exception cref="ArgumentNullException">The specified value is null.</exception>
private string Password {
get {
return m_Password;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Password = value;
}
}
// private variables
/// <summary>Holds the value of the Username property.</summary>
private string m_Username;
/// <summary>Holds the value of the Password property.</summary>
private string m_Password;
}
}

+ 96
- 0
shadowsocks-csharp/3rd/ProxySocket/IAsyncProxyResult.cs View File

@@ -0,0 +1,96 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Threading;

namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// A class that implements the IAsyncResult interface. Objects from this class are returned by the BeginConnect method of the ProxySocket class.
/// </summary>
internal class IAsyncProxyResult : IAsyncResult {
/// <summary>Initializes the internal variables of this object</summary>
/// <param name="stateObject">An object that contains state information for this request.</param>
internal void Init(object stateObject) {
m_StateObject = stateObject;
m_Completed = false;
if (m_WaitHandle != null)
m_WaitHandle.Reset();
}
/// <summary>Initializes the internal variables of this object</summary>
internal void Reset() {
m_StateObject = null;
m_Completed = true;
if (m_WaitHandle != null)
m_WaitHandle.Set();
}
/// <summary>Gets a value that indicates whether the server has completed processing the call. It is illegal for the server to use any client supplied resources outside of the agreed upon sharing semantics after it sets the IsCompleted property to "true". Thus, it is safe for the client to destroy the resources after IsCompleted property returns "true".</summary>
/// <value>A boolean that indicates whether the server has completed processing the call.</value>
public bool IsCompleted {
get {
return m_Completed;
}
}
/// <summary>Gets a value that indicates whether the BeginXXXX call has been completed synchronously. If this is detected in the AsyncCallback delegate, it is probable that the thread that called BeginInvoke is the current thread.</summary>
/// <value>Returns false.</value>
public bool CompletedSynchronously {
get {
return false;
}
}
/// <summary>Gets an object that was passed as the state parameter of the BeginXXXX method call.</summary>
/// <value>The object that was passed as the state parameter of the BeginXXXX method call.</value>
public object AsyncState {
get {
return m_StateObject;
}
}
/// <summary>
/// The AsyncWaitHandle property returns the WaitHandle that can use to perform a WaitHandle.WaitOne or WaitAny or WaitAll. The object which implements IAsyncResult need not derive from the System.WaitHandle classes directly. The WaitHandle wraps its underlying synchronization primitive and should be signaled after the call is completed. This enables the client to wait for the call to complete instead polling. The Runtime supplies a number of waitable objects that mirror Win32 synchronization primitives e.g. ManualResetEvent, AutoResetEvent and Mutex.
/// WaitHandle supplies methods that support waiting for such synchronization objects to become signaled with "any" or "all" semantics i.e. WaitHandle.WaitOne, WaitAny and WaitAll. Such methods are context aware to avoid deadlocks. The AsyncWaitHandle can be allocated eagerly or on demand. It is the choice of the IAsyncResult implementer.
///</summary>
/// <value>The WaitHandle associated with this asynchronous result.</value>
public WaitHandle AsyncWaitHandle {
get {
if (m_WaitHandle == null)
m_WaitHandle = new ManualResetEvent(false);
return m_WaitHandle;
}
}
// private variables
/// <summary>Used internally to represent the state of the asynchronous request</summary>
internal bool m_Completed = true;
/// <summary>Holds the value of the StateObject property.</summary>
private object m_StateObject;
/// <summary>Holds the value of the WaitHandle property.</summary>
private ManualResetEvent m_WaitHandle;
}
}

+ 82
- 0
shadowsocks-csharp/3rd/ProxySocket/ProxyException.cs View File

@@ -0,0 +1,82 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;

namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// The exception that is thrown when a proxy error occurs.
/// </summary>
public class ProxyException : Exception {
/// <summary>
/// Initializes a new instance of the ProxyException class.
/// </summary>
public ProxyException() : this("An error occured while talking to the proxy server.") {}
/// <summary>
/// Initializes a new instance of the ProxyException class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public ProxyException(string message) : base(message) {}
/// <summary>
/// Initializes a new instance of the ProxyException class.
/// </summary>
/// <param name="socks5Error">The error number returned by a SOCKS5 server.</param>
public ProxyException(int socks5Error) : this(ProxyException.Socks5ToString(socks5Error)) {}
/// <summary>
/// Converts a SOCKS5 error number to a human readable string.
/// </summary>
/// <param name="socks5Error">The error number returned by a SOCKS5 server.</param>
/// <returns>A string representation of the specified SOCKS5 error number.</returns>
public static string Socks5ToString(int socks5Error) {
switch(socks5Error) {
case 0:
return "Connection succeeded.";
case 1:
return "General SOCKS server failure.";
case 2:
return "Connection not allowed by ruleset.";
case 3:
return "Network unreachable.";
case 4:
return "Host unreachable.";
case 5:
return "Connection refused.";
case 6:
return "TTL expired.";
case 7:
return "Command not supported.";
case 8:
return "Address type not supported.";
default:
return "Unspecified SOCKS error.";
}
}
}
}

+ 385
- 0
shadowsocks-csharp/3rd/ProxySocket/ProxySocket.cs View File

@@ -0,0 +1,385 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Net;
using System.Net.Sockets;

// Implements a number of classes to allow Sockets to connect trough a firewall.
namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// Specifies the type of proxy servers that an instance of the ProxySocket class can use.
/// </summary>
public enum ProxyTypes {
/// <summary>No proxy server; the ProxySocket object behaves exactly like an ordinary Socket object.</summary>
None,
/// <summary>A SOCKS4[A] proxy server.</summary>
Socks4,
/// <summary>A SOCKS5 proxy server.</summary>
Socks5
}
/// <summary>
/// Implements a Socket class that can connect trough a SOCKS proxy server.
/// </summary>
/// <remarks>This class implements SOCKS4[A] and SOCKS5.<br>It does not, however, implement the BIND commands, so you cannot .</br></remarks>
public class ProxySocket : Socket {
/// <summary>
/// Initializes a new instance of the ProxySocket class.
/// </summary>
/// <param name="addressFamily">One of the AddressFamily values.</param>
/// <param name="socketType">One of the SocketType values.</param>
/// <param name="protocolType">One of the ProtocolType values.</param>
/// <exception cref="SocketException">The combination of addressFamily, socketType, and protocolType results in an invalid socket.</exception>
public ProxySocket (AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) : this(addressFamily, socketType, protocolType, "") {}
/// <summary>
/// Initializes a new instance of the ProxySocket class.
/// </summary>
/// <param name="addressFamily">One of the AddressFamily values.</param>
/// <param name="socketType">One of the SocketType values.</param>
/// <param name="protocolType">One of the ProtocolType values.</param>
/// <param name="proxyUsername">The username to use when authenticating with the proxy server.</param>
/// <exception cref="SocketException">The combination of addressFamily, socketType, and protocolType results in an invalid socket.</exception>
/// <exception cref="ArgumentNullException"><c>proxyUsername</c> is null.</exception>
public ProxySocket (AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, string proxyUsername) : this(addressFamily, socketType, protocolType, proxyUsername, "") {}
/// <summary>
/// Initializes a new instance of the ProxySocket class.
/// </summary>
/// <param name="addressFamily">One of the AddressFamily values.</param>
/// <param name="socketType">One of the SocketType values.</param>
/// <param name="protocolType">One of the ProtocolType values.</param>
/// <param name="proxyUsername">The username to use when authenticating with the proxy server.</param>
/// <param name="proxyPassword">The password to use when authenticating with the proxy server.</param>
/// <exception cref="SocketException">The combination of addressFamily, socketType, and protocolType results in an invalid socket.</exception>
/// <exception cref="ArgumentNullException"><c>proxyUsername</c> -or- <c>proxyPassword</c> is null.</exception>
public ProxySocket (AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, string proxyUsername, string proxyPassword) : base(addressFamily, socketType, protocolType) {
ProxyUser = proxyUsername;
ProxyPass = proxyPassword;
ToThrow = new InvalidOperationException();
}
/// <summary>
/// Establishes a connection to a remote device.
/// </summary>
/// <param name="remoteEP">An EndPoint that represents the remote device.</param>
/// <exception cref="ArgumentNullException">The remoteEP parameter is a null reference (Nothing in Visual Basic).</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
/// <exception cref="ProxyException">An error occured while talking to the proxy server.</exception>
public new void Connect(EndPoint remoteEP) {
if (remoteEP == null)
throw new ArgumentNullException("<remoteEP> cannot be null.");
if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null)
base.Connect(remoteEP);
else {
base.Connect(ProxyEndPoint);
if (ProxyType == ProxyTypes.Socks4)
(new Socks4Handler(this, ProxyUser)).Negotiate((IPEndPoint)remoteEP);
else if (ProxyType == ProxyTypes.Socks5)
(new Socks5Handler(this, ProxyUser, ProxyPass)).Negotiate((IPEndPoint)remoteEP);
}
}
/// <summary>
/// Establishes a connection to a remote device.
/// </summary>
/// <param name="host">The remote host to connect to.</param>
/// <param name="port">The remote port to connect to.</param>
/// <exception cref="ArgumentNullException">The host parameter is a null reference (Nothing in Visual Basic).</exception>
/// <exception cref="ArgumentException">The port parameter is invalid.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
/// <exception cref="ProxyException">An error occured while talking to the proxy server.</exception>
/// <remarks>If you use this method with a SOCKS4 server, it will let the server resolve the hostname. Not all SOCKS4 servers support this 'remote DNS' though.</remarks>
public void Connect(string host, int port) {
if (host == null)
throw new ArgumentNullException("<host> cannot be null.");
if (port <= 0 || port > 65535)
throw new ArgumentException("Invalid port.");
if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null)
base.Connect(new IPEndPoint(Dns.Resolve(host).AddressList[0], port));
else {
base.Connect(ProxyEndPoint);
if (ProxyType == ProxyTypes.Socks4)
(new Socks4Handler(this, ProxyUser)).Negotiate(host, port);
else if (ProxyType == ProxyTypes.Socks5)
(new Socks5Handler(this, ProxyUser, ProxyPass)).Negotiate(host, port);
}
}
/// <summary>
/// Begins an asynchronous request for a connection to a network device.
/// </summary>
/// <param name="remoteEP">An EndPoint that represents the remote device.</param>
/// <param name="callback">The AsyncCallback delegate.</param>
/// <param name="state">An object that contains state information for this request.</param>
/// <returns>An IAsyncResult that references the asynchronous connection.</returns>
/// <exception cref="ArgumentNullException">The remoteEP parameter is a null reference (Nothing in Visual Basic).</exception>
/// <exception cref="SocketException">An operating system error occurs while creating the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
public new IAsyncResult BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state) {
if (remoteEP == null || callback == null)
throw new ArgumentNullException();
if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) {
return base.BeginConnect(remoteEP, callback, state);
} else {
CallBack = callback;
if (ProxyType == ProxyTypes.Socks4) {
AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate((IPEndPoint)remoteEP, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint);
return AsyncResult;
} else if(ProxyType == ProxyTypes.Socks5) {
AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate((IPEndPoint)remoteEP, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint);
return AsyncResult;
}
return null;
}
}
/// <summary>
/// Begins an asynchronous request for a connection to a network device.
/// </summary>
/// <param name="host">The host to connect to.</param>
/// <param name="port">The port on the remote host to connect to.</param>
/// <param name="callback">The AsyncCallback delegate.</param>
/// <param name="state">An object that contains state information for this request.</param>
/// <returns>An IAsyncResult that references the asynchronous connection.</returns>
/// <exception cref="ArgumentNullException">The host parameter is a null reference (Nothing in Visual Basic).</exception>
/// <exception cref="ArgumentException">The port parameter is invalid.</exception>
/// <exception cref="SocketException">An operating system error occurs while creating the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
public IAsyncResult BeginConnect(string host, int port, AsyncCallback callback, object state) {
if (host == null || callback == null)
throw new ArgumentNullException();
if (port <= 0 || port > 65535)
throw new ArgumentException();
CallBack = callback;
if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) {
RemotePort = port;
AsyncResult = BeginDns(host, new HandShakeComplete(this.OnHandShakeComplete));
return AsyncResult;
} else {
if (ProxyType == ProxyTypes.Socks4) {
AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate(host, port, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint);
return AsyncResult;
} else if(ProxyType == ProxyTypes.Socks5) {
AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate(host, port, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint);
return AsyncResult;
}
return null;
}
}
/// <summary>
/// Ends a pending asynchronous connection request.
/// </summary>
/// <param name="asyncResult">Stores state information for this asynchronous operation as well as any user-defined data.</param>
/// <exception cref="ArgumentNullException">The asyncResult parameter is a null reference (Nothing in Visual Basic).</exception>
/// <exception cref="ArgumentException">The asyncResult parameter was not returned by a call to the BeginConnect method.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
/// <exception cref="InvalidOperationException">EndConnect was previously called for the asynchronous connection.</exception>
/// <exception cref="ProxyException">The proxy server refused the connection.</exception>
public new void EndConnect(IAsyncResult asyncResult) {
if (asyncResult == null)
throw new ArgumentNullException();
if (!asyncResult.IsCompleted)
throw new ArgumentException();
if (ToThrow != null)
throw ToThrow;
return;
}
/// <summary>
/// Begins an asynchronous request to resolve a DNS host name or IP address in dotted-quad notation to an IPAddress instance.
/// </summary>
/// <param name="host">The host to resolve.</param>
/// <param name="callback">The method to call when the hostname has been resolved.</param>
/// <returns>An IAsyncResult instance that references the asynchronous request.</returns>
/// <exception cref="SocketException">There was an error while trying to resolve the host.</exception>
internal IAsyncProxyResult BeginDns(string host, HandShakeComplete callback) {
try {
Dns.BeginResolve(host, new AsyncCallback(this.OnResolved), this);
return new IAsyncProxyResult();
} catch {
throw new SocketException();
}
}
/// <summary>
/// Called when the specified hostname has been resolved.
/// </summary>
/// <param name="asyncResult">The result of the asynchronous operation.</param>
private void OnResolved(IAsyncResult asyncResult) {
try {
IPHostEntry dns = Dns.EndResolve(asyncResult);
base.BeginConnect(new IPEndPoint(dns.AddressList[0], RemotePort), new AsyncCallback(this.OnConnect), State);
} catch (Exception e) {
OnHandShakeComplete(e);
}
}
/// <summary>
/// Called when the Socket is connected to the remote host.
/// </summary>
/// <param name="asyncResult">The result of the asynchronous operation.</param>
private void OnConnect(IAsyncResult asyncResult) {
try {
base.EndConnect(asyncResult);
OnHandShakeComplete(null);
} catch (Exception e) {
OnHandShakeComplete(e);
}
}
/// <summary>
/// Called when the Socket has finished talking to the proxy server and is ready to relay data.
/// </summary>
/// <param name="error">The error to throw when the EndConnect method is called.</param>
private void OnHandShakeComplete(Exception error) {
if (error != null)
this.Close();
ToThrow = error;
AsyncResult.Reset();
if (CallBack != null)
CallBack(AsyncResult);
}
/// <summary>
/// Gets or sets the EndPoint of the proxy server.
/// </summary>
/// <value>An IPEndPoint object that holds the IP address and the port of the proxy server.</value>
public IPEndPoint ProxyEndPoint {
get {
return m_ProxyEndPoint;
}
set {
m_ProxyEndPoint = value;
}
}
/// <summary>
/// Gets or sets the type of proxy server to use.
/// </summary>
/// <value>One of the ProxyTypes values.</value>
public ProxyTypes ProxyType {
get {
return m_ProxyType;
}
set {
m_ProxyType = value;
}
}
/// <summary>
/// Gets or sets a user-defined object.
/// </summary>
/// <value>The user-defined object.</value>
private object State {
get {
return m_State;
}
set {
m_State = value;
}
}
/// <summary>
/// Gets or sets the username to use when authenticating with the proxy.
/// </summary>
/// <value>A string that holds the username that's used when authenticating with the proxy.</value>
/// <exception cref="ArgumentNullException">The specified value is null.</exception>
public string ProxyUser {
get {
return m_ProxyUser;
}
set {
if (value == null)
throw new ArgumentNullException();
m_ProxyUser = value;
}
}
/// <summary>
/// Gets or sets the password to use when authenticating with the proxy.
/// </summary>
/// <value>A string that holds the password that's used when authenticating with the proxy.</value>
/// <exception cref="ArgumentNullException">The specified value is null.</exception>
public string ProxyPass {
get {
return m_ProxyPass;
}
set {
if (value == null)
throw new ArgumentNullException();
m_ProxyPass = value;
}
}
/// <summary>
/// Gets or sets the asynchronous result object.
/// </summary>
/// <value>An instance of the IAsyncProxyResult class.</value>
private IAsyncProxyResult AsyncResult {
get {
return m_AsyncResult;
}
set {
m_AsyncResult = value;
}
}
/// <summary>
/// Gets or sets the exception to throw when the EndConnect method is called.
/// </summary>
/// <value>An instance of the Exception class (or subclasses of Exception).</value>
private Exception ToThrow {
get {
return m_ToThrow;
}
set {
m_ToThrow = value;
}
}
/// <summary>
/// Gets or sets the remote port the user wants to connect to.
/// </summary>
/// <value>An integer that specifies the port the user wants to connect to.</value>
private int RemotePort {
get {
return m_RemotePort;
}
set {
m_RemotePort = value;
}
}
// private variables
/// <summary>Holds the value of the State property.</summary>
private object m_State;
/// <summary>Holds the value of the ProxyEndPoint property.</summary>
private IPEndPoint m_ProxyEndPoint = null;
/// <summary>Holds the value of the ProxyType property.</summary>
private ProxyTypes m_ProxyType = ProxyTypes.None;
/// <summary>Holds the value of the ProxyUser property.</summary>
private string m_ProxyUser = null;
/// <summary>Holds the value of the ProxyPass property.</summary>
private string m_ProxyPass = null;
/// <summary>Holds a pointer to the method that should be called when the Socket is connected to the remote device.</summary>
private AsyncCallback CallBack = null;
/// <summary>Holds the value of the AsyncResult property.</summary>
private IAsyncProxyResult m_AsyncResult;
/// <summary>Holds the value of the ToThrow property.</summary>
private Exception m_ToThrow = null;
/// <summary>Holds the value of the RemotePort property.</summary>
private int m_RemotePort;
}
}

+ 234
- 0
shadowsocks-csharp/3rd/ProxySocket/Socks4Handler.cs View File

@@ -0,0 +1,234 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// Implements the SOCKS4[A] protocol.
/// </summary>
internal sealed class Socks4Handler : SocksHandler {
/// <summary>
/// Initilizes a new instance of the SocksHandler class.
/// </summary>
/// <param name="server">The socket connection with the proxy server.</param>
/// <param name="user">The username to use when authenticating with the server.</param>
/// <exception cref="ArgumentNullException"><c>server</c> -or- <c>user</c> is null.</exception>
public Socks4Handler(Socket server, string user) : base(server, user) {}
/// <summary>
/// Creates an array of bytes that has to be sent when the user wants to connect to a specific host/port combination.
/// </summary>
/// <param name="host">The host to connect to.</param>
/// <param name="port">The port to connect to.</param>
/// <returns>An array of bytes that has to be sent when the user wants to connect to a specific host/port combination.</returns>
/// <remarks>Resolving the host name will be done at server side. Do note that some SOCKS4 servers do not implement this functionality.</remarks>
/// <exception cref="ArgumentNullException"><c>host</c> is null.</exception>
/// <exception cref="ArgumentException"><c>port</c> is invalid.</exception>
private byte[] GetHostPortBytes(string host, int port) {
if (host == null)
throw new ArgumentNullException();
if (port <= 0 || port > 65535)
throw new ArgumentException();
byte [] connect = new byte[10 + Username.Length + host.Length];
connect[0] = 4;
connect[1] = 1;
Array.Copy(PortToBytes(port), 0, connect, 2, 2);
connect[4] = connect[5] = connect[6] = 0;
connect[7] = 1;
Array.Copy(Encoding.ASCII.GetBytes(Username), 0, connect, 8, Username.Length);
connect[8 + Username.Length] = 0;
Array.Copy(Encoding.ASCII.GetBytes(host), 0, connect, 9 + Username.Length, host.Length);
connect[9 + Username.Length + host.Length] = 0;
return connect;
}
/// <summary>
/// Creates an array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint.
/// </summary>
/// <param name="remoteEP">The IPEndPoint to connect to.</param>
/// <returns>An array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint.</returns>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is null.</exception>
private byte[] GetEndPointBytes(IPEndPoint remoteEP) {
if (remoteEP == null)
throw new ArgumentNullException();
byte [] connect = new byte[9 + Username.Length];
connect[0] = 4;
connect[1] = 1;
Array.Copy(PortToBytes(remoteEP.Port), 0, connect, 2, 2);
Array.Copy(AddressToBytes(remoteEP.Address.Address), 0, connect, 4, 4);
Array.Copy(Encoding.ASCII.GetBytes(Username), 0, connect, 8, Username.Length);
connect[8 + Username.Length] = 0;
return connect;
}
/// <summary>
/// Starts negotiating with the SOCKS server.
/// </summary>
/// <param name="host">The host to connect to.</param>
/// <param name="port">The port to connect to.</param>
/// <exception cref="ArgumentNullException"><c>host</c> is null.</exception>
/// <exception cref="ArgumentException"><c>port</c> is invalid.</exception>
/// <exception cref="ProxyException">The proxy rejected the request.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
public override void Negotiate(string host, int port) {
Negotiate(GetHostPortBytes(host, port));
}
/// <summary>
/// Starts negotiating with the SOCKS server.
/// </summary>
/// <param name="remoteEP">The IPEndPoint to connect to.</param>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is null.</exception>
/// <exception cref="ProxyException">The proxy rejected the request.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
public override void Negotiate(IPEndPoint remoteEP) {
Negotiate(GetEndPointBytes(remoteEP));
}
/// <summary>
/// Starts negotiating with the SOCKS server.
/// </summary>
/// <param name="connect">The bytes to send when trying to authenticate.</param>
/// <exception cref="ArgumentNullException"><c>connect</c> is null.</exception>
/// <exception cref="ArgumentException"><c>connect</c> is too small.</exception>
/// <exception cref="ProxyException">The proxy rejected the request.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
private void Negotiate(byte [] connect) {
if (connect == null)
throw new ArgumentNullException();
if (connect.Length < 2)
throw new ArgumentException();
Server.Send(connect);
byte [] buffer = ReadBytes(8);
if (buffer[1] != 90) {
Server.Close();
throw new ProxyException("Negotiation failed.");
}
}
/// <summary>
/// Starts negotiating asynchronously with a SOCKS proxy server.
/// </summary>
/// <param name="host">The remote server to connect to.</param>
/// <param name="port">The remote port to connect to.</param>
/// <param name="callback">The method to call when the connection has been established.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, IPEndPoint proxyEndPoint) {
ProtocolComplete = callback;
Buffer = GetHostPortBytes(host, port);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server);
AsyncResult = new IAsyncProxyResult();
return AsyncResult;
}
/// <summary>
/// Starts negotiating asynchronously with a SOCKS proxy server.
/// </summary>
/// <param name="remoteEP">An IPEndPoint that represents the remote device.</param>
/// <param name="callback">The method to call when the connection has been established.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, IPEndPoint proxyEndPoint) {
ProtocolComplete = callback;
Buffer = GetEndPointBytes(remoteEP);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server);
AsyncResult = new IAsyncProxyResult();
return AsyncResult;
}
/// <summary>
/// Called when the Socket is connected to the remote proxy server.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnConnect(IAsyncResult ar) {
try {
Server.EndConnect(ar);
} catch (Exception e) {
ProtocolComplete(e);
return;
}
try {
Server.BeginSend(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnSent), Server);
} catch (Exception e) {
ProtocolComplete(e);
}
}
/// <summary>
/// Called when the Socket has sent the handshake data.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnSent(IAsyncResult ar) {
try {
if (Server.EndSend(ar) < Buffer.Length) {
ProtocolComplete(new SocketException());
return;
}
} catch (Exception e) {
ProtocolComplete(e);
return;
}
try {
Buffer = new byte[8];
Received = 0;
Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceive), Server);
} catch (Exception e) {
ProtocolComplete(e);
}
}
/// <summary>
/// Called when the Socket has received a reply from the remote proxy server.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnReceive(IAsyncResult ar) {
try {
int received = Server.EndReceive(ar);
if (received <= 0) {
ProtocolComplete(new SocketException());
return;
}
Received += received;
if (Received == 8) {
if (Buffer[1] == 90)
ProtocolComplete(null);
else {
Server.Close();
ProtocolComplete(new ProxyException("Negotiation failed."));
}
} else {
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnReceive), Server);
}
} catch (Exception e) {
ProtocolComplete(e);
}
}
}
}



+ 418
- 0
shadowsocks-csharp/3rd/ProxySocket/Socks5Handler.cs View File

@@ -0,0 +1,418 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// Implements the SOCKS5 protocol.
/// </summary>
internal sealed class Socks5Handler : SocksHandler {
/// <summary>
/// Initiliazes a new Socks5Handler instance.
/// </summary>
/// <param name="server">The socket connection with the proxy server.</param>
/// <exception cref="ArgumentNullException"><c>server</c> is null.</exception>
public Socks5Handler(Socket server) : this(server, "") {}
/// <summary>
/// Initiliazes a new Socks5Handler instance.
/// </summary>
/// <param name="server">The socket connection with the proxy server.</param>
/// <param name="user">The username to use.</param>
/// <exception cref="ArgumentNullException"><c>server</c> -or- <c>user</c> is null.</exception>
public Socks5Handler(Socket server, string user) : this(server, user, "") {}
/// <summary>
/// Initiliazes a new Socks5Handler instance.
/// </summary>
/// <param name="server">The socket connection with the proxy server.</param>
/// <param name="user">The username to use.</param>
/// <param name="pass">The password to use.</param>
/// <exception cref="ArgumentNullException"><c>server</c> -or- <c>user</c> -or- <c>pass</c> is null.</exception>
public Socks5Handler(Socket server, string user, string pass) : base(server, user) {
Password = pass;
}
/// <summary>
/// Starts the synchronous authentication process.
/// </summary>
/// <exception cref="ProxyException">Authentication with the proxy server failed.</exception>
/// <exception cref="ProtocolViolationException">The proxy server uses an invalid protocol.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
private void Authenticate() {
Server.Send(new byte [] {5, 2, 0, 2});
byte[] buffer = ReadBytes(2);
if (buffer[1] == 255)
throw new ProxyException("No authentication method accepted.");
AuthMethod authenticate;
switch (buffer[1]) {
case 0:
authenticate = new AuthNone(Server);
break;
case 2:
authenticate = new AuthUserPass(Server, Username, Password);
break;
default:
throw new ProtocolViolationException();
}
authenticate.Authenticate();
}
/// <summary>
/// Creates an array of bytes that has to be sent when the user wants to connect to a specific host/port combination.
/// </summary>
/// <param name="host">The host to connect to.</param>
/// <param name="port">The port to connect to.</param>
/// <returns>An array of bytes that has to be sent when the user wants to connect to a specific host/port combination.</returns>
/// <exception cref="ArgumentNullException"><c>host</c> is null.</exception>
/// <exception cref="ArgumentException"><c>port</c> or <c>host</c> is invalid.</exception>
private byte[] GetHostPortBytes(string host, int port) {
if (host == null)
throw new ArgumentNullException();
if (port <= 0 || port > 65535 || host.Length > 255)
throw new ArgumentException();
byte [] connect = new byte[7 + host.Length];
connect[0] = 5;
connect[1] = 1;
connect[2] = 0; //reserved
connect[3] = 3;
connect[4] = (byte)host.Length;
Array.Copy(Encoding.ASCII.GetBytes(host), 0, connect, 5, host.Length);
Array.Copy(PortToBytes(port), 0, connect, host.Length + 5, 2);
return connect;
}
/// <summary>
/// Creates an array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint.
/// </summary>
/// <param name="remoteEP">The IPEndPoint to connect to.</param>
/// <returns>An array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint.</returns>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is null.</exception>
private byte[] GetEndPointBytes(IPEndPoint remoteEP) {
if (remoteEP == null)
throw new ArgumentNullException();
byte [] connect = new byte[10];
connect[0] = 5;
connect[1] = 1;
connect[2] = 0; //reserved
connect[3] = 1;
Array.Copy(AddressToBytes(remoteEP.Address.Address), 0, connect, 4, 4);
Array.Copy(PortToBytes(remoteEP.Port), 0, connect, 8, 2);
return connect;
}
/// <summary>
/// Starts negotiating with the SOCKS server.
/// </summary>
/// <param name="host">The host to connect to.</param>
/// <param name="port">The port to connect to.</param>
/// <exception cref="ArgumentNullException"><c>host</c> is null.</exception>
/// <exception cref="ArgumentException"><c>port</c> is invalid.</exception>
/// <exception cref="ProxyException">The proxy rejected the request.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
/// <exception cref="ProtocolViolationException">The proxy server uses an invalid protocol.</exception>
public override void Negotiate(string host, int port) {
Negotiate(GetHostPortBytes(host, port));
}
/// <summary>
/// Starts negotiating with the SOCKS server.
/// </summary>
/// <param name="remoteEP">The IPEndPoint to connect to.</param>
/// <exception cref="ArgumentNullException"><c>remoteEP</c> is null.</exception>
/// <exception cref="ProxyException">The proxy rejected the request.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
/// <exception cref="ProtocolViolationException">The proxy server uses an invalid protocol.</exception>
public override void Negotiate(IPEndPoint remoteEP) {
Negotiate(GetEndPointBytes(remoteEP));
}
/// <summary>
/// Starts negotiating with the SOCKS server.
/// </summary>
/// <param name="connect">The bytes to send when trying to authenticate.</param>
/// <exception cref="ArgumentNullException"><c>connect</c> is null.</exception>
/// <exception cref="ArgumentException"><c>connect</c> is too small.</exception>
/// <exception cref="ProxyException">The proxy rejected the request.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
/// <exception cref="ProtocolViolationException">The proxy server uses an invalid protocol.</exception>
private void Negotiate(byte[] connect) {
Authenticate();
Server.Send(connect);
byte[] buffer = ReadBytes(4);
if (buffer[1] != 0) {
Server.Close();
throw new ProxyException(buffer[1]);
}
switch(buffer[3]) {
case 1:
buffer = ReadBytes(6); //IPv4 address with port
break;
case 3:
buffer = ReadBytes(1);
buffer = ReadBytes(buffer[0] + 2); //domain name with port
break;
case 4:
buffer = ReadBytes(18); //IPv6 address with port
break;
default:
Server.Close();
throw new ProtocolViolationException();
}
}
/// <summary>
/// Starts negotiating asynchronously with the SOCKS server.
/// </summary>
/// <param name="host">The host to connect to.</param>
/// <param name="port">The port to connect to.</param>
/// <param name="callback">The method to call when the negotiation is complete.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, IPEndPoint proxyEndPoint) {
ProtocolComplete = callback;
HandShake = GetHostPortBytes(host, port);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server);
AsyncResult = new IAsyncProxyResult();
return AsyncResult;
}
/// <summary>
/// Starts negotiating asynchronously with the SOCKS server.
/// </summary>
/// <param name="remoteEP">An IPEndPoint that represents the remote device.</param>
/// <param name="callback">The method to call when the negotiation is complete.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, IPEndPoint proxyEndPoint) {
ProtocolComplete = callback;
HandShake = GetEndPointBytes(remoteEP);
Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server);
AsyncResult = new IAsyncProxyResult();
return AsyncResult;
}
/// <summary>
/// Called when the socket is connected to the remote server.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnConnect(IAsyncResult ar) {
try {
Server.EndConnect(ar);
} catch (Exception e) {
ProtocolComplete(e);
return;
}
try {
Server.BeginSend(new byte [] {5, 2, 0, 2}, 0, 4, SocketFlags.None, new AsyncCallback(this.OnAuthSent), Server);
} catch (Exception e) {
ProtocolComplete(e);
}
}
/// <summary>
/// Called when the authentication bytes have been sent.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnAuthSent(IAsyncResult ar) {
try {
Server.EndSend(ar);
} catch (Exception e) {
ProtocolComplete(e);
return;
}
try {
Buffer = new byte[1024];
Received = 0;
Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnAuthReceive), Server);
} catch (Exception e) {
ProtocolComplete(e);
}
}
/// <summary>
/// Called when an authentication reply has been received.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnAuthReceive(IAsyncResult ar) {
try {
Received += Server.EndReceive(ar);
if (Received <= 0)
throw new SocketException();
} catch (Exception e) {
ProtocolComplete(e);
return;
}
try {
if (Received < 2) {
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnAuthReceive), Server);
} else {
AuthMethod authenticate;
switch(Buffer[1]) {
case 0:
authenticate = new AuthNone(Server);
break;
case 2:
authenticate = new AuthUserPass(Server, Username, Password);
break;
default:
ProtocolComplete(new SocketException());
return;
}
authenticate.BeginAuthenticate(new HandShakeComplete(this.OnAuthenticated));
}
} catch (Exception e) {
ProtocolComplete(e);
}
}
/// <summary>
/// Called when the socket has been successfully authenticated with the server.
/// </summary>
/// <param name="e">The exception that has occured while authenticating, or <em>null</em> if no error occured.</param>
private void OnAuthenticated(Exception e) {
if (e != null) {
ProtocolComplete(e);
return;
}
try {
Server.BeginSend(HandShake, 0, HandShake.Length, SocketFlags.None, new AsyncCallback(this.OnSent), Server);
} catch (Exception ex) {
ProtocolComplete(ex);
}
}
/// <summary>
/// Called when the connection request has been sent.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnSent(IAsyncResult ar) {
try {
Server.EndSend(ar);
} catch (Exception e) {
ProtocolComplete(e);
return;
}
try {
Buffer = new byte[5];
Received = 0;
Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceive), Server);
} catch (Exception e) {
ProtocolComplete(e);
}
}
/// <summary>
/// Called when a connection reply has been received.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnReceive(IAsyncResult ar) {
try {
Received += Server.EndReceive(ar);
} catch (Exception e) {
ProtocolComplete(e);
return;
}
try {
if (Received == Buffer.Length)
ProcessReply(Buffer);
else
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnReceive), Server);
} catch (Exception e) {
ProtocolComplete(e);
}
}
/// <summary>
/// Processes the received reply.
/// </summary>
/// <param name="buffer">The received reply</param>
/// <exception cref="ProtocolViolationException">The received reply is invalid.</exception>
private void ProcessReply(byte[] buffer) {
switch(buffer[3]) {
case 1:
Buffer = new byte[5]; //IPv4 address with port - 1 byte
break;
case 3:
Buffer = new byte[buffer[4] + 2]; //domain name with port
break;
case 4:
buffer = new byte[17]; //IPv6 address with port - 1 byte
break;
default:
throw new ProtocolViolationException();
}
Received = 0;
Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReadLast), Server);
}
/// <summary>
/// Called when the last bytes are read from the socket.
/// </summary>
/// <param name="ar">Stores state information for this asynchronous operation as well as any user-defined data.</param>
private void OnReadLast(IAsyncResult ar) {
try {
Received += Server.EndReceive(ar);
} catch (Exception e) {
ProtocolComplete(e);
return;
}
try {
if (Received == Buffer.Length)
ProtocolComplete(null);
else
Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnReadLast), Server);
} catch (Exception e) {
ProtocolComplete(e);
}
}
/// <summary>
/// Gets or sets the password to use when authenticating with the SOCKS5 server.
/// </summary>
/// <value>The password to use when authenticating with the SOCKS5 server.</value>
private string Password {
get {
return m_Password;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Password = value;
}
}
/// <summary>
/// Gets or sets the bytes to use when sending a connect request to the proxy server.
/// </summary>
/// <value>The array of bytes to use when sending a connect request to the proxy server.</value>
private byte[] HandShake {
get {
return m_HandShake;
}
set {
m_HandShake = value;
}
}
// private variables
/// <summary>Holds the value of the Password property.</summary>
private string m_Password;
/// <summary>Holds the value of the HandShake property.</summary>
private byte[] m_HandShake;
}
}

+ 204
- 0
shadowsocks-csharp/3rd/ProxySocket/SocksHandler.cs View File

@@ -0,0 +1,204 @@
/*
Copyright ?2002, The KPD-Team
All rights reserved.
http://www.mentalis.org/

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Neither the name of the KPD-Team, nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Net;
using System.Net.Sockets;

namespace Shadowsocks._3rd.ProxySocket {
/// <summary>
/// References the callback method to be called when the protocol negotiation is completed.
/// </summary>
internal delegate void HandShakeComplete(Exception error);
/// <summary>
/// Implements a specific version of the SOCKS protocol. This is an abstract class; it must be inherited.
/// </summary>
internal abstract class SocksHandler {
/// <summary>
/// Initilizes a new instance of the SocksHandler class.
/// </summary>
/// <param name="server">The socket connection with the proxy server.</param>
/// <param name="user">The username to use when authenticating with the server.</param>
/// <exception cref="ArgumentNullException"><c>server</c> -or- <c>user</c> is null.</exception>
public SocksHandler(Socket server, string user) {
Server = server;
Username = user;
}
/// <summary>
/// Converts a port number to an array of bytes.
/// </summary>
/// <param name="port">The port to convert.</param>
/// <returns>An array of two bytes that represents the specified port.</returns>
protected byte[] PortToBytes(int port) {
byte [] ret = new byte[2];
ret[0] = (byte)(port / 256);
ret[1] = (byte)(port % 256);
return ret;
}
/// <summary>
/// Converts an IP address to an array of bytes.
/// </summary>
/// <param name="address">The IP address to convert.</param>
/// <returns>An array of four bytes that represents the specified IP address.</returns>
protected byte[] AddressToBytes(long address) {
byte [] ret = new byte[4];
ret[0] = (byte)(address % 256);
ret[1] = (byte)((address / 256) % 256);
ret[2] = (byte)((address / 65536) % 256);
ret[3] = (byte)(address / 16777216);
return ret;
}
/// <summary>
/// Reads a specified number of bytes from the Server socket.
/// </summary>
/// <param name="count">The number of bytes to return.</param>
/// <returns>An array of bytes.</returns>
/// <exception cref="ArgumentException">The number of bytes to read is invalid.</exception>
/// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception>
/// <exception cref="ObjectDisposedException">The Socket has been closed.</exception>
protected byte[] ReadBytes(int count) {
if (count <= 0)
throw new ArgumentException();
byte[] buffer = new byte[count];
int received = 0;
while(received != count) {
received += Server.Receive(buffer, received, count - received, SocketFlags.None);
}
return buffer;
}
/// <summary>
/// Gets or sets the socket connection with the proxy server.
/// </summary>
/// <value>A Socket object that represents the connection with the proxy server.</value>
/// <exception cref="ArgumentNullException">The specified value is null.</exception>
protected Socket Server {
get {
return m_Server;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Server = value;
}
}
/// <summary>
/// Gets or sets the username to use when authenticating with the proxy server.
/// </summary>
/// <value>A string that holds the username to use when authenticating with the proxy server.</value>
/// <exception cref="ArgumentNullException">The specified value is null.</exception>
protected string Username {
get {
return m_Username;
}
set {
if (value == null)
throw new ArgumentNullException();
m_Username = value;
}
}
/// <summary>
/// Gets or sets the return value of the BeginConnect call.
/// </summary>
/// <value>An IAsyncProxyResult object that is the return value of the BeginConnect call.</value>
protected IAsyncProxyResult AsyncResult {
get {
return m_AsyncResult;
}
set {
m_AsyncResult = value;
}
}
/// <summary>
/// Gets or sets a byte buffer.
/// </summary>
/// <value>An array of bytes.</value>
protected byte[] Buffer {
get {
return m_Buffer;
}
set {
m_Buffer = value;
}
}
/// <summary>
/// Gets or sets the number of bytes that have been received from the remote proxy server.
/// </summary>
/// <value>An integer that holds the number of bytes that have been received from the remote proxy server.</value>
protected int Received {
get {
return m_Received;
}
set {
m_Received = value;
}
}
// private variables
/// <summary>Holds the value of the Server property.</summary>
private Socket m_Server;
/// <summary>Holds the value of the Username property.</summary>
private string m_Username;
/// <summary>Holds the value of the AsyncResult property.</summary>
private IAsyncProxyResult m_AsyncResult;
/// <summary>Holds the value of the Buffer property.</summary>
private byte[] m_Buffer;
/// <summary>Holds the value of the Received property.</summary>
private int m_Received;
/// <summary>Holds the address of the method to call when the SOCKS protocol has been completed.</summary>
protected HandShakeComplete ProtocolComplete;
/// <summary>
/// Starts negotiating with a SOCKS proxy server.
/// </summary>
/// <param name="host">The remote server to connect to.</param>
/// <param name="port">The remote port to connect to.</param>
public abstract void Negotiate(string host, int port);
/// <summary>
/// Starts negotiating with a SOCKS proxy server.
/// </summary>
/// <param name="remoteEP">The remote endpoint to connect to.</param>
public abstract void Negotiate(IPEndPoint remoteEP);
/// <summary>
/// Starts negotiating asynchronously with a SOCKS proxy server.
/// </summary>
/// <param name="remoteEP">An IPEndPoint that represents the remote device. </param>
/// <param name="callback">The method to call when the connection has been established.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public abstract IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, IPEndPoint proxyEndPoint);
/// <summary>
/// Starts negotiating asynchronously with a SOCKS proxy server.
/// </summary>
/// <param name="host">The remote server to connect to.</param>
/// <param name="port">The remote port to connect to.</param>
/// <param name="callback">The method to call when the connection has been established.</param>
/// <param name="proxyEndPoint">The IPEndPoint of the SOCKS proxy server.</param>
/// <returns>An IAsyncProxyResult that references the asynchronous connection.</returns>
public abstract IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, IPEndPoint proxyEndPoint);
}
}

+ 283
- 0
shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebRequest.cs View File

@@ -0,0 +1,283 @@
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Shadowsocks._3rd.ProxySocket
{
public class SocksHttpWebRequest : WebRequest
{

#region Private
private Encoding _correctEncoding;
#endregion Private

#region Member Variables

private readonly Uri _requestUri;
private WebHeaderCollection _requestHeaders;
private string _method;
private SocksHttpWebResponse _response;
private string _requestMessage;
private byte[] _requestContentBuffer;

// darn MS for making everything internal (yeah, I'm talking about you, System.net.KnownHttpVerb)
static readonly StringCollection validHttpVerbs =
new StringCollection { "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "OPTIONS" };

#endregion

#region Constructor

private SocksHttpWebRequest(Uri requestUri)
{
_requestUri = requestUri;
_correctEncoding = Encoding.Default;
}

#endregion

#region WebRequest Members

public override WebResponse GetResponse()
{
if (Proxy == null)
{
throw new InvalidOperationException("Proxy property cannot be null.");
}
if (String.IsNullOrEmpty(Method))
{
throw new InvalidOperationException("Method has not been set.");
}

if (RequestSubmitted)
{
return _response;
}
_response = InternalGetResponse();
RequestSubmitted = true;
return _response;
}

public override Uri RequestUri
{
get { return _requestUri; }
}

public override IWebProxy Proxy { get; set; }

public override WebHeaderCollection Headers
{
get
{
if (_requestHeaders == null)
{
_requestHeaders = new WebHeaderCollection();
}
return _requestHeaders;
}
set
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}
_requestHeaders = value;
}
}

public bool RequestSubmitted { get; private set; }

public override string Method
{
get
{
return _method ?? "GET";
}
set
{
if (validHttpVerbs.Contains(value))
{
_method = value;
}
else
{
throw new ArgumentOutOfRangeException("value", string.Format("'{0}' is not a known HTTP verb.", value));
}
}
}

public override long ContentLength { get; set; }

public override string ContentType { get; set; }

public override Stream GetRequestStream()
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}

if (_requestContentBuffer == null)
{
_requestContentBuffer = new byte[ContentLength];
}
else if (ContentLength == default(long))
{
_requestContentBuffer = new byte[int.MaxValue];
}
else if (_requestContentBuffer.Length != ContentLength)
{
Array.Resize(ref _requestContentBuffer, (int)ContentLength);
}
return new MemoryStream(_requestContentBuffer);
}

#endregion

#region Methods

public static new WebRequest Create(string requestUri)
{
return new SocksHttpWebRequest(new Uri(requestUri));
}

public static new WebRequest Create(Uri requestUri)
{
return new SocksHttpWebRequest(requestUri);
}

private string BuildHttpRequestMessage()
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}

var message = new StringBuilder();
message.AppendFormat("{0} {1} HTTP/1.0\r\nHost: {2}\r\n", Method, RequestUri.PathAndQuery, RequestUri.Host);

// add the headers
foreach (var key in Headers.Keys)
{
message.AppendFormat("{0}: {1}\r\n", key, Headers[key.ToString()]);
}

if (!string.IsNullOrEmpty(ContentType))
{
message.AppendFormat("Content-Type: {0}\r\n", ContentType);
}
if (ContentLength > 0)
{
message.AppendFormat("Content-Length: {0}\r\n", ContentLength);
}

// add a blank line to indicate the end of the headers
message.Append("\r\n");

// add content
if (_requestContentBuffer != null && _requestContentBuffer.Length > 0)
{
using (var stream = new MemoryStream(_requestContentBuffer, false))
{
using (var reader = new StreamReader(stream))
{
message.Append(reader.ReadToEnd());
}
}
}

return message.ToString();
}

private SocksHttpWebResponse InternalGetResponse()
{
var response = new StringBuilder();
using (var _socksConnection =
new ProxySocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
var proxyUri = Proxy.GetProxy(RequestUri);
var ipAddress = GetProxyIpAddress(proxyUri);
_socksConnection.ProxyEndPoint = new IPEndPoint(ipAddress, proxyUri.Port);
_socksConnection.ProxyType = ProxyTypes.Socks5;


// open connection
_socksConnection.Connect(RequestUri.Host, 80);
// send an HTTP request
_socksConnection.Send(_correctEncoding.GetBytes(RequestMessage));
// read the HTTP reply
var buffer = new byte[1024];

var bytesReceived = _socksConnection.Receive(buffer);
while (bytesReceived > 0)
{
string chunk = _correctEncoding.GetString(buffer, 0, bytesReceived);
string encString = EncodingHelper.GetEncodingFromChunk(chunk);
if (!string.IsNullOrEmpty(encString))
{
try
{
_correctEncoding = Encoding.GetEncoding(encString);
}
catch
{
//TODO: do something here
}
}
response.Append(chunk);
bytesReceived = _socksConnection.Receive(buffer);
}
}
return new SocksHttpWebResponse(response.ToString(),_correctEncoding);
}

private static IPAddress GetProxyIpAddress(Uri proxyUri)
{
IPAddress ipAddress;
if (!IPAddress.TryParse(proxyUri.Host, out ipAddress))
{
try
{
return Dns.GetHostEntry(proxyUri.Host).AddressList[0];
}
catch (Exception e)
{
throw new InvalidOperationException(
string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", proxyUri.Host), e);
}
}
return ipAddress;
}

#endregion

#region Properties

public Encoding CorrectEncoding
{
get
{
return _correctEncoding;
}
}

public string RequestMessage
{
get
{
if (string.IsNullOrEmpty(_requestMessage))
{
_requestMessage = BuildHttpRequestMessage();
}
return _requestMessage;
}
}

#endregion

}
}

+ 109
- 0
shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebResponse.cs View File

@@ -0,0 +1,109 @@
using System;
using System.IO;
using System.Net;
using System.Text;

namespace Shadowsocks._3rd.ProxySocket
{
public class SocksHttpWebResponse : WebResponse
{

#region Member Variables

private WebHeaderCollection _httpResponseHeaders;
private string _responseContent;

#endregion

#region Constructors

public SocksHttpWebResponse(string httpResponseMessage)
{
SetHeadersAndResponseContent(httpResponseMessage);
CorrectEncoding = Encoding.Default;
}

public SocksHttpWebResponse(string httpResponseMessage, Encoding encoding)
{
SetHeadersAndResponseContent(httpResponseMessage);
CorrectEncoding = encoding;
}

#endregion

#region WebResponse Members

public override Stream GetResponseStream()
{
return ResponseContent.Length == 0 ? Stream.Null : new MemoryStream(CorrectEncoding.GetBytes(ResponseContent));
}

public override void Close() { /* the base implementation throws an exception */ }

public override WebHeaderCollection Headers
{
get
{
if (_httpResponseHeaders == null)
{
_httpResponseHeaders = new WebHeaderCollection();
}
return _httpResponseHeaders;
}
}

public override long ContentLength
{
get
{
return ResponseContent.Length;
}
set
{
throw new NotSupportedException();
}
}

#endregion

#region Methods

private void SetHeadersAndResponseContent(string responseMessage)
{
if (string.IsNullOrEmpty(responseMessage))
return;

// the HTTP headers can be found before the first blank line
var indexOfFirstBlankLine = responseMessage.IndexOf("\r\n\r\n");

var headers = responseMessage.Substring(0, indexOfFirstBlankLine);
var headerValues = headers.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
// ignore the first line in the header since it is the HTTP response code
for (int i = 1; i < headerValues.Length; i++)
{
var headerEntry = headerValues[i].Split(new[] { ':' });
Headers.Add(headerEntry[0], headerEntry[1]);
}

ResponseContent = responseMessage.Substring(indexOfFirstBlankLine + 4);
}

#endregion

#region Properties

private string ResponseContent
{
get { return _responseContent ?? string.Empty; }
set { _responseContent = value; }
}

public Encoding CorrectEncoding
{
get;set;
}

#endregion

}
}

+ 312
- 0
shadowsocks-csharp/3rd/ProxySocket/SocksWebClient.cs View File

@@ -0,0 +1,312 @@
using System;
using System.Net;

namespace Shadowsocks._3rd.ProxySocket
{
#region old
/*
public class Calculagraph
{
/// <summary>
/// 时间到事件
/// </summary>
public event TimeoutCaller TimeOver;

/// <summary>
/// 开始时间
/// </summary>
private DateTime _startTime;
private TimeSpan _timeout = new TimeSpan(0, 0, 10);
private bool _hasStarted = false;
object _userdata;

/// <summary>
/// 计时器构造方法
/// </summary>
/// <param name="userdata">计时结束时回调的用户数据</param>
public Calculagraph(object userdata)
{
TimeOver += new TimeoutCaller(OnTimeOver);
_userdata = userdata;
}

/// <summary>
/// 超时退出
/// </summary>
/// <param name="userdata"></param>
public virtual void OnTimeOver(object userdata)
{
Stop();
}

/// <summary>
/// 过期时间(秒)
/// </summary>
public int Timeout
{
get
{
return _timeout.Seconds;
}
set
{
if (value <= 0)
return;
_timeout = new TimeSpan(0, 0, value);
}
}

/// <summary>
/// 是否已经开始计时
/// </summary>
public bool HasStarted
{
get
{
return _hasStarted;
}
}

/// <summary>
/// 开始计时
/// </summary>
public void Start()
{
Reset();
_hasStarted = true;
System.Threading.Thread th = new System.Threading.Thread(WaitCall);
th.IsBackground = true;
th.Start();
}

/// <summary>
/// 重置
/// </summary>
public void Reset()
{
_startTime = DateTime.Now;
}

/// <summary>
/// 停止计时
/// </summary>
public void Stop()
{
_hasStarted = false;
}

/// <summary>
/// 检查是否过期
/// </summary>
/// <returns></returns>
private bool checkTimeout()
{
return (DateTime.Now - _startTime).Seconds >= Timeout;
}

private void WaitCall()
{
try
{
//循环检测是否过期
while (_hasStarted && !checkTimeout())
{
System.Threading.Thread.Sleep(1000);
}
if (TimeOver != null)
TimeOver(_userdata);
}
catch (Exception)
{
Stop();
}
}
}

/// <summary>
/// 过期时回调委托
/// </summary>
/// <param name="userdata"></param>
public delegate void TimeoutCaller(object userdata);

public class CNNWebClient : WebClient
{

private Calculagraph _timer;
private int _timeOut = 5;

/// <summary>
/// 过期时间
/// </summary>
public int Timeout
{
get
{
return _timeOut;
}
set
{
if (value <= 0)
_timeOut = 5;
_timeOut = value;
}
}

/// <summary>
/// 重写GetWebRequest,添加WebRequest对象超时时间
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
request.Timeout = 1000 * Timeout;
request.ReadWriteTimeout = 1000 * Timeout;
return request;
}

/// <summary>
/// 带过期计时的下载
/// </summary>
public void DownloadFileAsyncWithTimeout(Uri address, string fileName, object userToken)
{
if (_timer == null)
{
_timer = new Calculagraph(this);
_timer.Timeout = Timeout;
_timer.TimeOver += new TimeoutCaller(_timer_TimeOver);
this.DownloadProgressChanged += new DownloadProgressChangedEventHandler(CNNWebClient_DownloadProgressChanged);
}

DownloadFileAsync(address, fileName, userToken);
_timer.Start();
}

/// <summary>
/// WebClient下载过程事件,接收到数据时引发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void CNNWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
_timer.Reset();//重置计时器
}

/// <summary>
/// 计时器过期
/// </summary>
/// <param name="userdata"></param>
void _timer_TimeOver(object userdata)
{
this.CancelAsync();//取消下载
}
}
*/
#endregion

public class SocksWebClient : WebClient
{
public IProxyDetails ProxyDetails { get; set; }
public string UserAgent { get; set; }

protected override WebRequest GetWebRequest(Uri address)
{
WebRequest result = null;

if (ProxyDetails != null)
{
if (ProxyDetails.ProxyType == ProxyType.Proxy)
{
result = (HttpWebRequest)WebRequest.Create(address);
result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
else if (ProxyDetails.ProxyType == ProxyType.Socks)
{
result = SocksHttpWebRequest.Create(address);
result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
//TODO: implement user and password

}
else if (ProxyDetails.ProxyType == ProxyType.None)
{
result = (HttpWebRequest)WebRequest.Create(address);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
}
else
{
result = (HttpWebRequest)WebRequest.Create(address);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
return result;
}
}

public interface IProxyDetails
{
ProxyType ProxyType { get; set; }
/// <summary>
/// adress and port
/// </summary>
string FullProxyAddress { get; set; }
string ProxyAddress { get; set; }
int ProxyPort { get; set; }
string ProxyUserName { get; set; }
string ProxyPassword { get; set; }
}
public class ProxyDetails : IProxyDetails
{
public ProxyType ProxyType { get; set; }
/// <summary>
/// adress and port
/// </summary>
public string FullProxyAddress { get; set; }
public string ProxyAddress { get; set; }
public int ProxyPort { get; set; }
public string ProxyUserName { get; set; }
public string ProxyPassword { get; set; }

public ProxyDetails()
{
}
public ProxyDetails(int port)
{
FullProxyAddress = IPAddress.Loopback + ":" + port;
ProxyAddress = IPAddress.Loopback.ToString();
ProxyPort = port;
ProxyType = ProxyType.Socks;
}
}

public enum ProxyType
{
None = 0,
Proxy = 1,
Socks = 2
}

public static class EncodingHelper
{
public static string GetEncodingFromChunk(string chunk)
{
string charset = null;
int charsetStart = chunk.IndexOf("charset=");
int charsetEnd = -1;
if (charsetStart != -1)
{
charsetEnd = chunk.IndexOfAny(new[] { ' ', '\"', ';', '\r', '\n' }, charsetStart);
if (charsetEnd != -1)
{
int start = charsetStart + 8;
charset = chunk.Substring(start, charsetEnd - start + 1);
charset = charset.TrimEnd(new Char[] { '>', '"', '\r', '\n' });
}
}
return charset;
}
}
}

+ 754
- 0
shadowsocks-csharp/3rd/QQWry.cs View File

@@ -0,0 +1,754 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;

namespace Shadowsocks._3rd
{
///<summary>
/// QQWry 的摘要说明。
///</summary>
public class QQWry
{
#region Properties
///<summary>
///第一种模式
///</summary>
private const byte REDIRECT_MODE_1 = 0x01;

///<summary>
///第二种模式
///</summary>
private const byte REDIRECT_MODE_2 = 0x02;

///<summary>
///每条记录长度
///</summary>
private const int IP_RECORD_LENGTH = 7;

///<summary>
///文件对象
///</summary>
private FileStream ipFile;

private const string unCountry = "未知国家";
private const string unArea = "未知地区";

///<summary>
///索引开始位置
///</summary>
private long ipBegin;

///<summary>
///索引结束位置
///</summary>
private long ipEnd;

///<summary>
/// IP对象
///</summary>
private IPLocation loc;

///<summary>
///存储文本内容
///</summary>
private byte[] buf;

///<summary>
///存储3字节
///</summary>
private byte[] b3;

///<summary>
///存储4字节IP地址
///</summary>
private byte[] b4;
#endregion

#region 构造函数

///<summary>
///构造函数
///</summary>
///<param name="ipfile">IP数据库文件绝对路径</param>
public QQWry(string ipfile)
{

buf = new byte[100];
b3 = new byte[3];
b4 = new byte[4];
try
{
ipFile = new FileStream(ipfile, FileMode.Open);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
ipBegin = readLong4(0);
ipEnd = readLong4(4);
loc = new IPLocation();
}
#endregion

#region 根据IP地址搜索

///<summary>
///搜索IP地址搜索
///</summary>
///<param name="ip"></param>
///<returns></returns>
public IPLocation SearchIPLocation(string ip)
{
//将字符IP转换为字节
string[] ipSp = ip.Split('.');
if (ipSp.Length != 4)
{
throw new ArgumentOutOfRangeException("不是合法的IP地址!");
}
byte[] IP = new byte[4];
for (int i = 0; i < IP.Length; i++)
{
IP[i] = (byte) (Int32.Parse(ipSp[i]) & 0xFF);
}

IPLocation local = null;
long offset = locateIP(IP);

if (offset != -1)
{
local = getIPLocation(offset);
}

if (local == null)
{
local = new IPLocation();
local.area = unArea;
local.country = unCountry;
}
return local;
}
#endregion

#region 取得具体信息
///<summary>
///取得具体信息
///</summary>
///<param name="offset"></param>
///<returns></returns>
private IPLocation getIPLocation(long offset)
{
ipFile.Position = offset + 4;
//读取第一个字节判断是否是标志字节
byte one = (byte) ipFile.ReadByte();
if (one == REDIRECT_MODE_1)
{
//第一种模式
//读取国家偏移
long countryOffset = readLong3();
//转至偏移处
ipFile.Position = countryOffset;
//再次检查标志字节
byte b = (byte) ipFile.ReadByte();
if (b == REDIRECT_MODE_2)
{
loc.country = readString(readLong3());
ipFile.Position = countryOffset + 4;
}
else
loc.country = readString(countryOffset);

//读取地区标志
loc.area = readArea(ipFile.Position);

}
else if (one == REDIRECT_MODE_2)
{
//第二种模式
loc.country = readString(readLong3());
loc.area = readArea(offset + 8);
}
else
{
//普通模式
loc.country = readString(--ipFile.Position);
loc.area = readString(ipFile.Position);
}
return loc;
}
#endregion

#region 取得地区信息
///<summary>
///读取地区名称
///</summary>
///<param name="offset"></param>
///<returns></returns>
private string readArea(long offset)
{
ipFile.Position = offset;
byte one = (byte) ipFile.ReadByte();
if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2)
{
long areaOffset = readLong3(offset + 1);
if (areaOffset == 0)
return unArea;
else
{
return readString(areaOffset);
}
}
else
{
return readString(offset);
}
}

#endregion

#region 读取字符串

///<summary>
///读取字符串
///</summary>
///<param name="offset"></param>
///<returns></returns>

private string readString(long offset)
{
ipFile.Position = offset;
int i = 0;
for (i = 0, buf[i] = (byte) ipFile.ReadByte(); buf[i] != (byte) (0); buf[++i] = (byte) ipFile.ReadByte()) ;

if (i > 0)
return Encoding.Default.GetString(buf, 0, i);
else
return "";
}
#endregion

#region 查找IP地址所在的绝对偏移量

/**/

///<summary>
///查找IP地址所在的绝对偏移量
///</summary>
///<param name="ip"></param>
///<returns></returns>

private long locateIP(byte[] ip)
{
long m = 0;
int r;

//比较第一个IP项
readIP(ipBegin, b4);
r = compareIP(ip, b4);
if (r == 0)
return ipBegin;
else if (r < 0)
return -1;
//开始二分搜索
for (long i = ipBegin, j = ipEnd; i < j;)
{
m = this.getMiddleOffset(i, j);
readIP(m, b4);
r = compareIP(ip, b4);
if (r > 0)
i = m;
else if (r < 0)
{
if (m == j)
{
j -= IP_RECORD_LENGTH;
m = j;
}
else
{
j = m;
}
}
else
return readLong3(m + 4);
}
m = readLong3(m + 4);
readIP(m, b4);
r = compareIP(ip, b4);
if (r <= 0)
return m;
else
return -1;
}

#endregion

#region 读出4字节的IP地址

/**/

///<summary>
///从当前位置读取四字节,此四字节是IP地址
///</summary>
///<param name="offset"></param>
///<param name="ip"></param>

private void readIP(long offset, byte[] ip)
{
ipFile.Position = offset;
ipFile.Read(ip, 0, ip.Length);
byte tmp = ip[0];
ip[0] = ip[3];
ip[3] = tmp;
tmp = ip[1];
ip[1] = ip[2];
ip[2] = tmp;
}

#endregion

#region 比较IP地址是否相同

/**/

///<summary>
///比较IP地址是否相同
///</summary>
///<param name="ip"></param>
///<param name="beginIP"></param>
///<returns>0:相等,1:ip大于beginIP,-1:小于</returns>

private int compareIP(byte[] ip, byte[] beginIP)
{
for (int i = 0; i < 4; i++)
{
int r = compareByte(ip[i], beginIP[i]);
if (r != 0)
return r;
}
return 0;
}

#endregion

#region 比较两个字节是否相等

/**/

///<summary>
///比较两个字节是否相等
///</summary>
///<param name="bsrc"></param>
///<param name="bdst"></param>
///<returns></returns>

private int compareByte(byte bsrc, byte bdst)
{
if ((bsrc & 0xFF) > (bdst & 0xFF))
return 1;
else if ((bsrc ^ bdst) == 0)
return 0;
else
return -1;
}

#endregion

#region 根据当前位置读取4字节

/**/

///<summary>
///从当前位置读取4字节,转换为长整型
///</summary>
///<param name="offset"></param>
///<returns></returns>

private long readLong4(long offset)
{
long ret = 0;
ipFile.Position = offset;
ret |= (ipFile.ReadByte() & 0xFF);
ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
ret |= ((ipFile.ReadByte() << 24) & 0xFF000000);
return ret;
}

#endregion

#region 根据当前位置,读取3字节

/**/

///<summary>
///根据当前位置,读取3字节
///</summary>
///<param name="offset"></param>
///<returns></returns>

private long readLong3(long offset)
{
long ret = 0;
ipFile.Position = offset;
ret |= (ipFile.ReadByte() & 0xFF);
ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
return ret;
}

#endregion

#region 从当前位置读取3字节

/**/

///<summary>
///从当前位置读取3字节
///</summary>
///<returns></returns>

private long readLong3()
{
long ret = 0;
ret |= (ipFile.ReadByte() & 0xFF);
ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
return ret;
}

#endregion

#region 取得begin和end之间的偏移量

/**/

///<summary>
///取得begin和end中间的偏移
///</summary>
///<param name="begin"></param>
///<param name="end"></param>
///<returns></returns>
private long getMiddleOffset(long begin, long end)
{
long records = (end - begin)/IP_RECORD_LENGTH;
records >>= 1;
if (records == 0)
records = 1;
return begin + records*IP_RECORD_LENGTH;
}

#endregion
}

public class IPLocation
{
public String country;
public String area;

public IPLocation()
{
country = area = "";
}

public IPLocation getCopy()
{
IPLocation ret = new IPLocation();
ret.country = country;
ret.area = area;
return ret;
}
}

#region Old
/*
/// <summary>
/// 存储地区的结构
/// </summary>
public struct stLocation
{
/// <summary>
/// 未使用
/// </summary>
public string Ip;

/// <summary>
/// 国家名
/// </summary>
public string Contry;

/// <summary>
/// 城市名
/// </summary>
public string City;
}


/// <summary>
/// 纯真IP数据库查询辅助类
/// </summary>
public static class QqwryHelper
{
#region 成员变量

private const byte REDIRECT_MODE_1 = 0x01;//名称存储模式一
private const byte REDIRECT_MODE_2 = 0x02;//名称存储模式二
private const int IP_RECORD_LENGTH = 7; //每条索引的长度

private static long beginIndex = 0;//索引开始
private static long endIndex = 0;//索引结束

private static stLocation loc = new stLocation() { City = "未知城市", Contry = "未知国家" };

private static Stream fs;

#endregion

#region 私有成员函数

/// <summary>
/// 在索引区查找指定IP对应的记录区地址
/// </summary>
/// <param name="_ip">字节型IP</param>
/// <returns></returns>
private static long SearchIpIndex(byte[] _ip)
{
long index = 0;

byte[] nextIp = new byte[4];

ReadIp(beginIndex, ref nextIp);

int flag = CompareIp(_ip, nextIp);
if (flag == 0) return beginIndex;
else if (flag < 0) return -1;

for (long i = beginIndex, j = endIndex; i < j; )
{
index = GetMiddleOffset(i, j);

ReadIp(index, ref nextIp);
flag = CompareIp(_ip, nextIp);

if (flag == 0) return ReadLong(index + 4, 3);
else if (flag > 0) i = index;
else if (flag < 0)
{
if (index == j)
{
j -= IP_RECORD_LENGTH;
index = j;
}
else
{
j = index;
}
}
}

index = ReadLong(index + 4, 3);
ReadIp(index, ref nextIp);

flag = CompareIp(_ip, nextIp);
if (flag <= 0) return index;
else return -1;
}

/// <summary>
/// 获取两个索引的中间位置
/// </summary>
/// <param name="begin">索引1</param>
/// <param name="end">索引2</param>
/// <returns></returns>
private static long GetMiddleOffset(long begin, long end)
{
long records = (end - begin) / IP_RECORD_LENGTH;
records >>= 1;
if (records == 0) records = 1;
return begin + records * IP_RECORD_LENGTH;
}

/// <summary>
/// 读取记录区的地区名称
/// </summary>
/// <param name="offset">位置</param>
/// <returns></returns>
private static string ReadString(long offset)
{
fs.Position = offset;

byte b = (byte)fs.ReadByte();
if (b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2)
{
long areaOffset = ReadLong(offset + 1, 3);
if (areaOffset == 0)
return "未知地区";

else fs.Position = areaOffset;
}
else
{
fs.Position = offset;
}

List<byte> buf = new List<byte>();

int i = 0;
for (i = 0, buf.Add((byte)fs.ReadByte()); buf[i] != (byte)(0); ++i, buf.Add((byte)fs.ReadByte())) ;

if (i > 0) return Encoding.Default.GetString(buf.ToArray(), 0, i);
else return "";
}

/// <summary>
/// 从自定位置读取指定长度的字节,并转换为big-endian字节序(数据源文件为little-endian字节序)
/// </summary>
/// <param name="offset">开始读取位置</param>
/// <param name="length">读取长度</param>
/// <returns></returns>
private static long ReadLong(long offset, int length)
{
long ret = 0;
fs.Position = offset;
for (int i = 0; i < length; i++)
{
ret |= ((fs.ReadByte() << (i * 8)) & (0xFF * ((int)Math.Pow(16, i * 2))));
}

return ret;
}

/// <summary>
/// 从指定位置处读取一个IP
/// </summary>
/// <param name="offset">指定的位置</param>
/// <param name="_buffIp">保存IP的缓存区</param>
private static void ReadIp(long offset, ref byte[] _buffIp)
{
fs.Position = offset;
fs.Read(_buffIp, 0, _buffIp.Length);

for (int i = 0; i < _buffIp.Length / 2; i++)
{
byte temp = _buffIp[i];
_buffIp[i] = _buffIp[_buffIp.Length - i - 1];
_buffIp[_buffIp.Length - i - 1] = temp;
}
}

/// <summary>
/// 比较两个IP是否相等,1:IP1大于IP2,-1:IP1小于IP2,0:IP1=IP2
/// </summary>
/// <param name="_buffIp1">IP1</param>
/// <param name="_buffIp2">IP2</param>
/// <returns></returns>
private static int CompareIp(byte[] _buffIp1, byte[] _buffIp2)
{
if (_buffIp1.Length > 4 || _buffIp2.Length > 4) throw new Exception("指定的IP无效。");

for (int i = 0; i < 4; i++)
{
if ((_buffIp1[i] & 0xFF) > (_buffIp2[i] & 0xFF)) return 1;
else if ((_buffIp1[i] & 0xFF) < (_buffIp2[i] & 0xFF)) return -1;
}

return 0;
}

/// <summary>
/// 从指定的地址获取区域名称
/// </summary>
/// <param name="offset"></param>
private static void GetAreaName(long offset)
{
fs.Position = offset + 4;
long flag = fs.ReadByte();
long contryIndex = 0;
if (flag == REDIRECT_MODE_1)
{
contryIndex = ReadLong(fs.Position, 3);
fs.Position = contryIndex;

flag = fs.ReadByte();

if (flag == REDIRECT_MODE_2) //是否仍然为重定向
{
loc.Contry = ReadString(ReadLong(fs.Position, 3));
fs.Position = contryIndex + 4;
}
else
{
loc.Contry = ReadString(contryIndex);
}
loc.City = ReadString(fs.Position);
}
else if (flag == REDIRECT_MODE_2)
{
contryIndex = ReadLong(fs.Position, 3);
loc.Contry = ReadString(contryIndex);
loc.City = ReadString(contryIndex + 3);
}
else
{
loc.Contry = ReadString(offset + 4);
loc.City = ReadString(fs.Position);
}
}

#endregion

#region 公有成员函数

/// <summary>
/// 加载数据库文件到缓存
/// </summary>
/// <param name="path">数据库文件地址</param>
/// <returns></returns>
public static void Init(string path)
{
if (fs != null) return;
var bt = File.ReadAllBytes(path);
fs = new MemoryStream(bt);
}

/// <summary>
/// 根据IP获取区域名
/// </summary>
/// <param name="ip">指定的IP</param>
/// <returns></returns>
public static stLocation GetLocation(string ip)
{
IPAddress ipAddress = null;
if (!IPAddress.TryParse(ip, out ipAddress)) throw new Exception("无效的IP地址。");

byte[] buff_local_ip = ipAddress.GetAddressBytes();

beginIndex = ReadLong(0, 4);
endIndex = ReadLong(4, 4);

long offset = SearchIpIndex(buff_local_ip);
if (offset != -1)
{
GetAreaName(offset);
}

loc.Contry = loc.Contry.Trim();
loc.City = loc.City.Trim().Replace("CZ88.NET", "");

return loc;
}

/// <summary>
/// 释放资源
/// </summary>
public static void Dispose()
{
fs.Dispose();
}

#endregion
} */
#endregion
}

+ 3
- 3
shadowsocks-csharp/Controller/AutoStartup.cs View File

@@ -1,5 +1,5 @@
using System;
using System.Windows.Forms;
using System;
using System.Windows.Forms;
using Microsoft.Win32;
namespace Shadowsocks.Controller
@@ -52,4 +52,4 @@ namespace Shadowsocks.Controller
}
}
}
}
}

+ 3
- 0
shadowsocks-csharp/Controller/FileManager.cs View File

@@ -29,6 +29,9 @@ namespace Shadowsocks.Controller
public static void UncompressFile(string fileName, byte[] content)
{
//some people use RamDisk, their Temp dir maybe disappeared, so we need check it first
if (!Directory.Exists(Path.GetDirectoryName(fileName)))Directory.CreateDirectory(Path.GetDirectoryName(fileName));
FileStream destinationFile = File.Create(fileName);
// Because the uncompressed size of the file is unknown,


+ 30
- 1
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -35,7 +35,7 @@ namespace Shadowsocks.Controller
public event EventHandler EnableStatusChanged;
public event EventHandler EnableGlobalChanged;
public event EventHandler ShareOverLANStatusChanged;
// when user clicked Edit PAC, and PAC file has already created
public event EventHandler<PathEventArgs> PACFileReadyToOpen;
public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen;
@@ -137,6 +137,35 @@ namespace Shadowsocks.Controller
SaveConfig(_config);
}
public void SelectServerIndexTemp(int index)
{
_config.index = index;
if (polipoRunner == null)
polipoRunner = new PolipoRunner();
if (_listener != null)
_listener.Stop();
try
{
Local local = new Local(_config);
List<Listener.Service> services = new List<Listener.Service>();
services.Add(local);
_listener = new Listener(services);
_listener.Start(_config);
}
catch (Exception e)
{
var se = e as SocketException;
if (se?.SocketErrorCode == SocketError.AccessDenied)
e = new Exception(I18N.GetString("Port already in use"), e);
Logging.LogUsefulException(e);
ReportError(e);
}
Util.Utils.ReleaseMemory();
}
public void Stop()
{
if (stopped)


+ 23
- 1
shadowsocks-csharp/Data/cn.txt View File

@@ -71,4 +71,26 @@ No QRCode found. Try to zoom in or move it to the center of the screen.=未发
Failed to decode QRCode=无法解析二维码
Failed to update registry=无法修改注册表
System Proxy On: =系统代理已启用:
Running: Port {0}=正在运行:端口 {0}
Running: Port {0}=正在运行:端口 {0}


# Pingform
UsableTest...=可用性测试...
UsableTest=服务器可用性测试
SvcAddr=地址
Location=物理地址
Max=最大
Min=最小
Average=平均
IP=IP地址
Speed=下载速度
TestSpeed=测速
Pinging=正在获取
PingFail=获取失败
FailTime=失败次数
CurrentStatus:=当前状态:
Ready=准备就绪
DoSomething=我们正在处理一些事情
Wrong=遇到一些问题
Unknow=未知
Seems your server is down, r u sure apply this server?=看起来你的服务器不可用,确定要使用这个服务器吗?

+ 12
- 15
shadowsocks-csharp/Properties/Resources.Designer.cs View File

@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34209
// Runtime Version:4.0.30319.18408
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -71,7 +71,12 @@ namespace Shadowsocks.Properties {
}
/// <summary>
/// Looks up a localized string similar to Shadowsocks=Shadowsocks
/// Looks up a localized string similar to # translation for Simplified Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=启用系统代理
///Mode=系统代理模式
///PAC=PAC 模式
@@ -80,24 +85,16 @@ namespace Shadowsocks.Properties {
///Edit Servers...=编辑服务器...
///Start on Boot=开机启动
///Allow Clients from LAN=允许来自局域网的连接
///Edit PAC File...=编辑 PAC 文件...
///Local PAC=使用本地 PAC
///Online PAC=使用在线 PAC
///Edit Local PAC File...=编辑本地 PAC 文件...
///Update Local PAC from GFWList=从 GFWList 更新本地 PAC
///Edit User Rule for GFWList...=编辑 GFWList 的用户规则...
///Show QRCode...=显示二维码...
///Scan QRCode from Screen...=扫描屏幕上的二维码...
///Show Logs...=显示日志...
///About...=关于...
///Quit=退出
///Edit Servers=编辑服务器
///&amp;Add=添加(&amp;A)
///&amp;Delete=删除(&amp;D)
///Server=服务器
///Server IP=服务器 IP
///Server Port=服务器端口
///Password=密码
///Encryption=加密
///Proxy Port=代理端口
///Remarks=备注
///OK= [rest of string was truncated]&quot;;.
///Quit=退出 [rest of string was truncated]&quot;;.
/// </summary>
internal static string cn {
get {


+ 11
- 0
shadowsocks-csharp/Util/Util.cs View File

@@ -1,15 +1,26 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Shadowsocks._3rd.ProxySocket;
namespace Shadowsocks.Util
{
public class Utils
{
public static Shadowsocks._3rd.QQWry qqwry;
public static Font GetFont()
{
var fs = FontFamily.Families;
var ret = fs.FirstOrDefault(f => f.Name == "Microsoft YaHei UI Light") ?? fs.FirstOrDefault(f => f.Name == "Microsoft YaHei UI");
return ret == null ? SystemFonts.MessageBoxFont : new Font(ret,9f,FontStyle.Regular);
}
public static void ReleaseMemory()
{
// release any unused pages


+ 3
- 2
shadowsocks-csharp/View/ConfigForm.cs View File

@@ -22,11 +22,12 @@ namespace Shadowsocks.View
public ConfigForm(ShadowsocksController controller)
{
this.Font = System.Drawing.SystemFonts.MessageBoxFont;
//this.Font = System.Drawing.SystemFonts.MessageBoxFont;
Font = Util.Utils.GetFont();
InitializeComponent();
// a dirty hack
this.ServersListBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ServersListBox.Dock = DockStyle.Fill;
this.PerformLayout();
UpdateTexts();


+ 29
- 1
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -42,6 +42,9 @@ namespace Shadowsocks.View
private MenuItem editGFWUserRuleItem;
private MenuItem editOnlinePACItem;
private ConfigForm configForm;
private PingForm pingForm;
private string _urlToOpen;
public MenuViewController(ShadowsocksController controller)
@@ -132,12 +135,12 @@ namespace Shadowsocks.View
+ "\n" + config.GetCurrentServer().FriendlyName();
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length));
}
private MenuItem CreateMenuItem(string text, EventHandler click)
{
return new MenuItem(I18N.GetString(text), click);
}
private MenuItem CreateMenuGroup(string text, MenuItem[] items)
{
return new MenuItem(I18N.GetString(text), items);
@@ -170,6 +173,8 @@ 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("UsableTest...", new EventHandler(this.PingForm_Click)),
new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
new MenuItem("-"),
@@ -280,6 +285,29 @@ namespace Shadowsocks.View
}
}
private void ShowPingForm()
{
if (pingForm != null)
{
pingForm.Activate();
}
else
{
pingForm = new PingForm(controller);
pingForm.Show();
pingForm.FormClosed += pingForm_FormClosed;
}
}
private void PingForm_Click(object sender, EventArgs e)
{
ShowPingForm();
}
void pingForm_FormClosed(object sender, FormClosedEventArgs e)
{
pingForm = null;
Util.Utils.ReleaseMemory();
}
private void ShowConfigForm()
{
if (configForm != null)


+ 237
- 0
shadowsocks-csharp/View/PingForm.Designer.cs View File

@@ -0,0 +1,237 @@
using Microsoft.VisualBasic;

namespace Shadowsocks.View
{
sealed partial class PingForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dgvMain = new System.Windows.Forms.DataGridView();
this.SvcAddr = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.IP = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Remarks = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Location = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Max = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Min = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Average = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.FailTime = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Speed = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.TestSpeed = new System.Windows.Forms.DataGridViewLinkColumn();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.tssStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.tssStatus = new System.Windows.Forms.ToolStripStatusLabel();
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
this.pBar = new System.Windows.Forms.ToolStripProgressBar();
((System.ComponentModel.ISupportInitialize)(this.dgvMain)).BeginInit();
this.statusStrip1.SuspendLayout();
this.SuspendLayout();
//
// dgvMain
//
this.dgvMain.AllowUserToAddRows = false;
this.dgvMain.AllowUserToDeleteRows = false;
this.dgvMain.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgvMain.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.SvcAddr,
this.IP,
this.Remarks,
this.Location,
this.Max,
this.Min,
this.Average,
this.FailTime,
this.Speed,
this.TestSpeed});
this.dgvMain.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgvMain.Location = new System.Drawing.Point(0, 0);
this.dgvMain.Name = "dgvMain";
this.dgvMain.ReadOnly = true;
this.dgvMain.RowTemplate.Height = 23;
this.dgvMain.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dgvMain.Size = new System.Drawing.Size(878, 507);
this.dgvMain.TabIndex = 0;
this.dgvMain.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvMain_CellClick);
this.dgvMain.CellMouseDoubleClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dgvMain_CellMouseDoubleClick);
//
// SvcAddr
//
this.SvcAddr.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
this.SvcAddr.Frozen = true;
this.SvcAddr.HeaderText = "Address";
this.SvcAddr.Name = "SvcAddr";
this.SvcAddr.ReadOnly = true;
this.SvcAddr.Width = 72;
//
// IP
//
this.IP.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
this.IP.HeaderText = "IP";
this.IP.Name = "IP";
this.IP.ReadOnly = true;
this.IP.Width = 42;
//
// Remarks
//
this.Remarks.HeaderText = "Remark";
this.Remarks.Name = "Remarks";
this.Remarks.ReadOnly = true;
this.Remarks.Width = 80;
//
// Location
//
this.Location.HeaderText = "Location";
this.Location.MinimumWidth = 70;
this.Location.Name = "Location";
this.Location.ReadOnly = true;
this.Location.Width = 150;
//
// Max
//
this.Max.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
this.Max.HeaderText = "Max";
this.Max.Name = "Max";
this.Max.ReadOnly = true;
this.Max.Width = 48;
//
// Min
//
this.Min.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
this.Min.HeaderText = "Min";
this.Min.Name = "Min";
this.Min.ReadOnly = true;
this.Min.Width = 48;
//
// Average
//
this.Average.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
this.Average.HeaderText = "Average";
this.Average.Name = "Average";
this.Average.ReadOnly = true;
this.Average.Width = 72;
//
// FailTime
//
this.FailTime.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
this.FailTime.HeaderText = "FailTime";
this.FailTime.Name = "FailTime";
this.FailTime.ReadOnly = true;
this.FailTime.Width = 78;
//
// Speed
//
this.Speed.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
this.Speed.HeaderText = "Speed";
this.Speed.Name = "Speed";
this.Speed.ReadOnly = true;
this.Speed.Width = 60;
//
// TestSpeed
//
this.TestSpeed.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
this.TestSpeed.HeaderText = "TestSpeed";
this.TestSpeed.Name = "TestSpeed";
this.TestSpeed.ReadOnly = true;
this.TestSpeed.Width = 65;
//
// statusStrip1
//
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.tssStatusLabel,
this.tssStatus,
this.toolStripStatusLabel1,
this.pBar});
this.statusStrip1.Location = new System.Drawing.Point(0, 485);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(878, 22);
this.statusStrip1.TabIndex = 1;
this.statusStrip1.Text = "statusStrip1";
//
// tssStatusLabel
//
this.tssStatusLabel.Name = "tssStatusLabel";
this.tssStatusLabel.Size = new System.Drawing.Size(89, 17);
this.tssStatusLabel.Text = "CurrentStatus:";
//
// tssStatus
//
this.tssStatus.Name = "tssStatus";
this.tssStatus.Size = new System.Drawing.Size(55, 17);
this.tssStatus.Text = "Unknow";
//
// toolStripStatusLabel1
//
this.toolStripStatusLabel1.Name = "toolStripStatusLabel1";
this.toolStripStatusLabel1.Size = new System.Drawing.Size(719, 17);
this.toolStripStatusLabel1.Spring = true;
//
// pBar
//
this.pBar.Name = "pBar";
this.pBar.Size = new System.Drawing.Size(100, 16);
this.pBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.pBar.Visible = false;
//
// PingForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.AutoSize = true;
this.ClientSize = new System.Drawing.Size(878, 507);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.dgvMain);
this.Name = "PingForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "PingForm";
((System.ComponentModel.ISupportInitialize)(this.dgvMain)).EndInit();
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private System.Windows.Forms.DataGridView dgvMain;
private System.Windows.Forms.DataGridViewTextBoxColumn Local;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel tssStatusLabel;
private System.Windows.Forms.ToolStripStatusLabel tssStatus;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1;
private System.Windows.Forms.ToolStripProgressBar pBar;
private System.Windows.Forms.DataGridViewTextBoxColumn SvcAddr;
private System.Windows.Forms.DataGridViewTextBoxColumn IP;
private System.Windows.Forms.DataGridViewTextBoxColumn Remarks;
private System.Windows.Forms.DataGridViewTextBoxColumn Location;
private System.Windows.Forms.DataGridViewTextBoxColumn Max;
private System.Windows.Forms.DataGridViewTextBoxColumn Min;
private System.Windows.Forms.DataGridViewTextBoxColumn Average;
private System.Windows.Forms.DataGridViewTextBoxColumn FailTime;
private System.Windows.Forms.DataGridViewTextBoxColumn Speed;
private System.Windows.Forms.DataGridViewLinkColumn TestSpeed;
}
}

+ 325
- 0
shadowsocks-csharp/View/PingForm.cs View File

@@ -0,0 +1,325 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks._3rd;
using Shadowsocks._3rd.ProxySocket;

namespace Shadowsocks.View
{
public sealed partial class PingForm : Form
{
private readonly ShadowsocksController controller;

#region Delegate
private delegate void selectMinMax();

private void SelectMinMax()
{
if (dgvMain.InvokeRequired)
{
var invoke = new selectMinMax(SelectMinMax);
Invoke(invoke);
}
else
{
var q = dgvMain.Rows.Cast<DataGridViewRow>().Where(row => (int)row.Cells["Average"].Value != 9999).ToArray();
int max = q.Max(x => Convert.ToInt32(x.Cells["Average"].Value));
int min = q.Min(x => Convert.ToInt32(x.Cells["Average"].Value));
foreach (DataGridViewRow row in dgvMain.Rows)
{
if ((int)row.Cells["Average"].Value == min) row.DefaultCellStyle.ForeColor = Color.Green;
if ((int)row.Cells["Average"].Value == max) row.DefaultCellStyle.ForeColor = Color.Red;
}
}
}

private delegate void changeStatus(string val);
private void ChangeStatus(string val)
{
if (statusStrip1.InvokeRequired)
{
var invoke = new changeStatus(ChangeStatus);
Invoke(invoke, val);
}
else
{
if (val == I18N.GetString("Ready"))
{
pBar.Visible = false;
dgvMain.Enabled = true;
tssStatus.Text = I18N.GetString("Ready");
tssStatus.ForeColor = Color.Green;
}
else if (val == I18N.GetString("Wrong"))
{
pBar.Visible = false;
dgvMain.Enabled = true;
tssStatus.Text = I18N.GetString("Wrong");
tssStatus.ForeColor = Color.Red;
}
else
{
pBar.Visible = true;
dgvMain.Enabled = false;
tssStatus.Text = val;
tssStatus.ForeColor = Color.Blue;
}
}
}
private delegate void modifyRow(int rowID, string ip,string loc, int max, int min, int avg, int failtime);
private void ModifyRow(int rowID, string ip,string loc, int max, int min, int avg, int failtime)
{
if (dgvMain.InvokeRequired)
{
var del = new modifyRow(ModifyRow);
Invoke(del, new object[] { rowID, ip,loc, max, min, avg, failtime });
}
else
{
dgvMain.Rows[rowID].Cells[1].Value = string.IsNullOrEmpty(ip) ? I18N.GetString("PingFail") : ip;
dgvMain.Rows[rowID].Cells[4].Value = max;
dgvMain.Rows[rowID].Cells[5].Value = min;
dgvMain.Rows[rowID].Cells[6].Value = avg;
dgvMain.Rows[rowID].Cells[7].Value = failtime;
if (!string.IsNullOrEmpty(loc)) dgvMain.Rows[rowID].Cells[3].Value = loc;
}
}

private delegate void modifyRowSpeed(int rowID,string ip, string loc,string speed);

private void ModifyRowSpeed(int rowID, string ip, string loc, string speed)
{
if (dgvMain.InvokeRequired)
{
var del = new modifyRowSpeed(ModifyRowSpeed);
Invoke(del, new object[] {rowID, ip, loc, speed});
}
else
{
dgvMain.Rows[rowID].Cells[1].Value = string.IsNullOrEmpty(ip) ? I18N.GetString("PingFail") : ip;
if (!string.IsNullOrEmpty(loc)) dgvMain.Rows[rowID].Cells[3].Value = loc;
if (!string.IsNullOrEmpty(speed)) dgvMain.Rows[rowID].Cells[8].Value = speed;
}
}

#endregion

#region Method
private void Ping(object r)
{
var row = r as DataGridViewRow;
if (row == null) return;
var addr = "";
var result = new List<long>();
var failTime = 0;

using (var ping = new Ping())
{
for (var times = 0; times < 4; times++)
{
try
{
var reply = ping.Send((string)row.Cells[0].Value, 2000);
if (reply == null) { failTime++; continue;}
addr = reply.Address.ToString();
if (reply.Status == IPStatus.Success)
{
result.Add(reply.RoundtripTime);
}
else
{
failTime++;
}
}
catch
{
failTime++;
}
}
}
var location = "";
if (Util.Utils.qqwry != null & addr != "")
{
var v = Util.Utils.qqwry.SearchIPLocation(addr);
location = (v.country + v.area.Replace("CZ88.NET", ""));
}
if(result.Count == 0)
ModifyRow(row.Index, addr,location, 9999, 9999, 9999, 10);
else
ModifyRow(row.Index, addr,location, (int)result.Max(), (int)result.Min(), (int)result.Average(), failTime);
}

private void Go(object rc)
{
var rows = rc as DataGridViewRowCollection;
if (rows != null)
{
foreach (DataGridViewRow row in rows)
Ping(row);
SelectMinMax();
ChangeStatus(I18N.GetString("Ready"));
}
else
{
ChangeStatus(I18N.GetString("Wrong"));
}
}

private void Test(object index)
{
try
{
var rowIndex = (int)index;
string ip, location;
float speed;
GetIPAddress(rowIndex, out ip, out location, out speed);
ModifyRowSpeed(rowIndex, ip, location, speed.ToString("N0") + "KB/s");
ChangeStatus(I18N.GetString("Ready"));
}
catch
{
ChangeStatus(I18N.GetString("Wrong"));
}
}

private void GetIPAddress(int svc, out string addr, out string stat, out float speed)
{
var currentIndex = controller.GetConfiguration().index;
controller.SelectServerIndexTemp(svc);
var webClient = new SocksWebClient { ProxyDetails = new ProxyDetails(controller.GetConfiguration().localPort)};

try
{
//get location
var regx1 = new Regex(@"\d+\.\d+\.\d+\.\d+");
var regx2 = new Regex(@"来自:(.*?)\<");
var response = webClient.DownloadString(@"http://1111.ip138.com/ic.asp");

var mc1 = regx1.Match(response);
addr = mc1.Success ? mc1.Value : I18N.GetString("Unknow");

var mc2 = regx2.Match(response);
stat = mc2.Success ? mc2.Groups[1].Value : I18N.GetString("…(⊙_⊙;)…");
}
catch
{
addr = I18N.GetString("Unknow");
stat = I18N.GetString("…(⊙_⊙;)…");
}

try
{
//speed test
var sw = System.Diagnostics.Stopwatch.StartNew();
var dl = webClient.DownloadData("https://dl.google.com/tag/s/appguid%3D%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26iid%3D%7B7B1E2CBF-95F1-5FDD-C836-E5930E3E51CD%7D%26lang%3Den%26browser%3D4%26usagestats%3D0%26appname%3DGoogle%2520Chrome%26needsadmin%3Dprefers%26installdataindex%3Ddefaultbrowser/update2/installers/ChromeSetup.exe");//http://dl.google.com/googletalk/googletalk-setup.exe
sw.Stop();
var len = dl.Length / 1024f;
var sec = sw.Elapsed.Milliseconds / 1000f;
speed = len / sec;
}
catch
{
speed = 0;
}
webClient.Dispose();
controller.SelectServerIndexTemp(currentIndex);
}
#endregion
public PingForm(ShadowsocksController sc)
{
InitializeComponent();

var qqwryPath = Environment.CurrentDirectory + "\\qqwry.dat";
if (Util.Utils.qqwry == null && File.Exists(qqwryPath)) Util.Utils.qqwry = new QQWry(qqwryPath);

controller = sc;
Font = Util.Utils.GetFont();

PerformLayout();

UpdateTexts();

Icon = Icon.FromHandle(Resources.ssw128.GetHicon());

LoadConfiguration(controller.GetConfiguration());

if (dgvMain.Rows.Count <= 5)
{
foreach (var row in dgvMain.Rows)
{
var t = new Thread(Ping) { IsBackground = true };
t.Start(row);
}
}
else
{
ChangeStatus(I18N.GetString("DoSomething"));
var t = new Thread(Go) { IsBackground = true };
t.Start(dgvMain.Rows);
}

}

private void UpdateTexts()
{
foreach (DataGridViewColumn col in dgvMain.Columns)
col.HeaderText = I18N.GetString(col.Name);
Text = I18N.GetString("UsableTest");
tssStatusLabel.Text = I18N.GetString("CurrentStatus:");
tssStatus.Text = I18N.GetString("Ready");
}


private void LoadConfiguration(Configuration configuration)
{
dgvMain.Rows.Clear();
foreach (Server server in configuration.configs)
{
int index = dgvMain.Rows.Add();
dgvMain.Rows[index].Cells[0].Value = server.server;
dgvMain.Rows[index].Cells[1].Value = I18N.GetString("Pinging");
dgvMain.Rows[index].Cells[2].Value = server.remarks;
dgvMain.Rows[index].Cells[9].Value = I18N.GetString("TestSpeed");
}
var row = dgvMain.Rows[configuration.index];
dgvMain.ClearSelection();
dgvMain.CurrentCell = row.Cells[0];
row.Selected = true;
}


private void dgvMain_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (dgvMain.Columns[e.ColumnIndex].Name != "TestSpeed") return;
ChangeStatus(I18N.GetString("DoSomething"));
var t = new Thread(Test) {IsBackground = true};
t.Start(e.RowIndex);
//10sec time out because WebClient dont have Timeout property
var tm = new System.Timers.Timer(10000);
tm.Elapsed += (ss, ee) =>{ tm.Stop(); t.Abort(); };
tm.Start();
}

private void dgvMain_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex < 0) return;
if ((int) dgvMain.Rows[e.RowIndex].Cells[4].Value == 9999)
{ if (MessageBox.Show(I18N.GetString("Seems your server is down, r u sure apply this server?"), "", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) return; }
controller.SelectServerIndex(e.RowIndex);
Close();
}
}
}

+ 147
- 0
shadowsocks-csharp/View/PingForm.resx View File

@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="SvcAddr.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="IP.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Max.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Min.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Average.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="FailTime.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Speed.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="TestSpeed.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

+ 17
- 4
shadowsocks-csharp/app.config View File

@@ -1,6 +1,19 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
</startup></configuration>
<supportedRuntime version="v4.0" />
<supportedRuntime version="v2.0.50727" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

+ 74
- 107
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -1,76 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}</ProjectGuid>
<ProjectGuid>{F58374A5-9AFB-430A-AF20-C509D3DCED3F}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Shadowsocks</RootNamespace>
<AssemblyName>Shadowsocks</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<StartupObject>
</StartupObject>
<ApplicationIcon>shadowsocks.ico</ApplicationIcon>
<IsWebBootstrapper>false</IsWebBootstrapper>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>1</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
<DebugSymbols>true</DebugSymbols>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>shadowsocks.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.XML" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="3rd\ProxySocket\AuthMethod.cs" />
<Compile Include="3rd\ProxySocket\AuthNone.cs" />
<Compile Include="3rd\ProxySocket\AuthUserPass.cs" />
<Compile Include="3rd\ProxySocket\IAsyncProxyResult.cs" />
<Compile Include="3rd\ProxySocket\ProxyException.cs" />
<Compile Include="3rd\ProxySocket\ProxySocket.cs" />
<Compile Include="3rd\ProxySocket\Socks4Handler.cs" />
<Compile Include="3rd\ProxySocket\Socks5Handler.cs" />
<Compile Include="3rd\ProxySocket\SocksHandler.cs" />
<Compile Include="3rd\ProxySocket\SocksHttpWebRequest.cs" />
<Compile Include="3rd\ProxySocket\SocksHttpWebResponse.cs" />
<Compile Include="3rd\ProxySocket\SocksWebClient.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="3rd\QQWry.cs" />
<Compile Include="3rd\SimpleJson.cs" />
<Compile Include="3rd\zxing\BarcodeFormat.cs" />
<Compile Include="3rd\zxing\BaseLuminanceSource.cs" />
<Compile Include="3rd\zxing\Binarizer.cs" />
@@ -117,7 +111,6 @@
<Compile Include="3rd\zxing\qrcode\encoder\MaskUtil.cs" />
<Compile Include="3rd\zxing\qrcode\encoder\MatrixUtil.cs" />
<Compile Include="3rd\zxing\qrcode\encoder\QRCode.cs" />
<Compile Include="3rd\SimpleJson.cs" />
<Compile Include="3rd\zxing\qrcode\QRCodeReader.cs" />
<Compile Include="3rd\zxing\Result.cs" />
<Compile Include="3rd\zxing\ResultMetadataType.cs" />
@@ -126,29 +119,30 @@
<Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Controller\AutoStartup.cs" />
<Compile Include="Controller\FileManager.cs" />
<Compile Include="Controller\GFWListUpdater.cs" />
<Compile Include="Controller\GfwListUpdater.cs" />
<Compile Include="Controller\I18N.cs" />
<Compile Include="Controller\Listener.cs" />
<Compile Include="Controller\Local.cs" />
<Compile Include="Controller\Logging.cs" />
<Compile Include="Controller\PACServer.cs" />
<Compile Include="Controller\PolipoRunner.cs" />
<Compile Include="Controller\PortForwarder.cs" />
<Compile Include="Controller\ShadowsocksController.cs" />
<Compile Include="Controller\SystemProxy.cs" />
<Compile Include="Controller\UpdateChecker.cs" />
<Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" />
<Compile Include="Encryption\IEncryptor.cs" />
<Compile Include="Encryption\IVEncryptor.cs" />
<Compile Include="Encryption\PolarSSL.cs" />
<Compile Include="Encryption\PolarSSLEncryptor.cs" />
<Compile Include="Encryption\Sodium.cs" />
<Compile Include="Encryption\SodiumEncryptor.cs" />
<Compile Include="Encryption\TableEncryptor.cs" />
<Compile Include="Encryption\IEncryptor.cs" />
<Compile Include="Controller\PACServer.cs" />
<Compile Include="Model\Server.cs" />
<Compile Include="Model\Configuration.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Model\Server.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Util\Util.cs" />
<Compile Include="View\ConfigForm.cs">
<SubType>Form</SubType>
@@ -156,13 +150,13 @@
<Compile Include="View\ConfigForm.Designer.cs">
<DependentUpon>ConfigForm.cs</DependentUpon>
</Compile>
<Compile Include="Controller\Local.cs" />
<Compile Include="Controller\PolipoRunner.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Controller\ShadowsocksController.cs" />
<Compile Include="Controller\SystemProxy.cs" />
<Compile Include="View\MenuViewController.cs" />
<Compile Include="View\PingForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="View\PingForm.Designer.cs">
<DependentUpon>PingForm.cs</DependentUpon>
</Compile>
<Compile Include="View\QRCodeForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -172,68 +166,41 @@
<Compile Include="View\QRCodeSplashForm.cs">
<SubType>Form</SubType>
</Compile>
<EmbeddedResource Include="View\ConfigForm.resx">
<DependentUpon>ConfigForm.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<EmbeddedResource Include="View\ConfigForm.resx">
<DependentUpon>ConfigForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\PingForm.resx">
<DependentUpon>PingForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\QRCodeForm.resx">
<DependentUpon>QRCodeForm.cs</DependentUpon>
</EmbeddedResource>
<None Include="app.config" />
<None Include="app.manifest">
<SubType>Designer</SubType>
</None>
<None Include="app.manifest" />
<None Include="Data\abp.js.gz" />
<None Include="Data\libsscrypto.dll.gz" />
<None Include="Data\polipo.exe.gz" />
<None Include="Data\proxy.pac.txt.gz" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\ss20.png" />
<None Include="Resources\ss16.png" />
<None Include="Resources\ss24.png" />
<None Include="Resources\ssw128.png" />
<Content Include="Data\cn.txt" />
<Content Include="Data\polipo_config.txt" />
<Content Include="Data\user-rule.txt" />
<Content Include="Resources\ss16.png" />
<Content Include="Resources\ss20.png" />
<Content Include="Resources\ss24.png" />
<Content Include="Resources\ssw128.png" />
<Content Include="shadowsocks.ico" />
<None Include="Data\polipo_config.txt" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
<Visible>False</Visible>
<ProductName>.NET Framework 2.0 %28x86%29</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
<Visible>False</Visible>
<ProductName>.NET Framework 3.0 %28x86%29</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.


Loading…
Cancel
Save