Files
shadowsocks-windows/shadowsocks-csharp/Controller/OnlineConfigController.cs
T
2015-12-09 20:00:16 +08:00

240 lines
8.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.IO.Compression;
using Shadowsocks.Controller.Service;
using Shadowsocks.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Converters;
namespace Shadowsocks.Controller
{
class OnlineConfigController
{
private string _cookie = null;
public string Url = "";
public string Type = "unknown";
public string LocalPublickey = "";
public string LocalPrivateKey = "";
public string RemotePubkey = "";
private KeyExchangeResponse KeyExchangeRes = null;
//public RSAHandler _rsa;
public OnlineConfigController(string Url)
{
this.Url = Url;
string[] Prefix = Url.Split(new char[3] { ':', '/', '/' });
switch (Prefix[0].ToLower())
{
case "http":
Type = "http";
break;
case "https":
Type = "https";
break;
default:
this.Url = "";
this.Type = "unknown";
break;
}
RSAHandler.GenerateKeyPair();
this.LocalPrivateKey = System.Environment.CurrentDirectory + "\\keys\\local.private.pem";
this.LocalPublickey = System.Environment.CurrentDirectory + "\\keys\\local.public.pem";
}
public Server[] GetOnlineConfig()
{
try
{
KeyExchangeResponse KeyExchangeResponse = this.KeyExchange(this.Url);
GetConfigResponse ConfigResponse = this.GetConfig();
return ConfigResponse.Config;
}
catch
{
throw;
}
}
private KeyExchangeResponse KeyExchange(string Url)
{
if(this.Url=="" || this.Type == "unknown")
{
throw new ArgumentException("Invalid Url");
}
StreamReader LocalPublicKeyStream = new StreamReader(this.LocalPublickey);
string LocalPublicKey = LocalPublicKeyStream.ReadToEnd();
LocalPublicKeyStream.Dispose();
HttpWebResponse res = CreatePostHttpResponse(this.Url, LocalPublicKey, _cookie);
if (res.StatusCode != HttpStatusCode.OK)
{
throw new ArgumentException("Invalid Status Code");
}
//string[] x = res.Headers.AllKeys;
string ResponseContent = "";
using (StreamReader sr = new StreamReader(res.GetResponseStream()))
{
ResponseContent = sr.ReadToEnd();
}
this._cookie = res.Headers.Get("Set-Cookie");
try
{
ResponseContent = RSAHandler.DecryptRSA(ResponseContent, this.LocalPrivateKey);
}
catch
{
throw;
}
StringReader reader = new StringReader(ResponseContent);
JsonSerializer serializer = new JsonSerializer();
KeyExchangeResponse response = (KeyExchangeResponse)serializer.Deserialize(new JsonTextReader(reader), typeof(KeyExchangeResponse));
this.KeyExchangeRes = response;
if(File.Exists(System.Environment.CurrentDirectory + "\\keys\\" + response.FingerPrint + ".pem"))
{
StreamReader stream = new StreamReader(System.Environment.CurrentDirectory + "\\keys\\" + response.FingerPrint + ".pem");
string LocalVersion = stream.ReadToEnd();
if (LocalVersion != response.ServerPublicKey)
{
throw new ArgumentException("Server Public Key Mismatch");
}
}
else
{
FileStream LocalVersion = File.Create(System.Environment.CurrentDirectory + "\\keys\\" + response.FingerPrint + ".pem");
LocalVersion.Write(System.Text.Encoding.Default.GetBytes(response.ServerPublicKey), 0, response.ServerPublicKey.Length);
LocalVersion.Close();
LocalVersion.Dispose();
}
this.RemotePubkey = System.Environment.CurrentDirectory + "\\keys\\" + response.FingerPrint + ".pem";
return response;
}
private GetConfigResponse GetConfig()
{
GetConfigRequest Request = new GetConfigRequest();
Request.decrypted_string = this.KeyExchangeRes.VerifyString;
char[] tempChr = new char[32];
Random rand = new Random();
for(int i = 0; i < 32; i++)
{
tempChr[i] = (char)rand.Next(61, 122);
}
Request.verify_string = new string(tempChr);
string RequestContent = JsonConvert.SerializeObject(Request);
string PostData = RSAHandler.EncryptRSA(RequestContent, this.RemotePubkey);
HttpWebResponse res;
string ResponseContent = "";
try
{
res = CreatePostHttpResponse(this.Url, PostData, this._cookie);
if (res.StatusCode != HttpStatusCode.OK)
{
throw new ArgumentException("Invalid Status Code");
}
using (StreamReader sr = new StreamReader(res.GetResponseStream()))
{
ResponseContent = sr.ReadToEnd();
}
}
catch
{
throw;
}
ResponseContent = RSAHandler.DecryptRSA(ResponseContent, this.LocalPrivateKey);
StringReader reader = new StringReader(ResponseContent);
JsonSerializer serializer = new JsonSerializer();
GetConfigResponse response = (GetConfigResponse)serializer.Deserialize(new JsonTextReader(reader), typeof(GetConfigResponse));
if (response.VerifyString != Request.verify_string)
{
throw new ArgumentException("Server Verify Fail");
}
for(int i = 0; i < response.Config.Length;i++)
{
response.Config[i].provider = response.Provider;
response.Config[i].fingerprint = this.KeyExchangeRes.FingerPrint;
}
return response;
}
public static HttpWebResponse CreatePostHttpResponse(string url, string postData,string cookies)
{
Encoding requestEncoding = Encoding.UTF8;
string UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36";
if (string.IsNullOrEmpty(url))
{
throw new ArgumentNullException("url");
}
if (requestEncoding == null)
{
throw new ArgumentNullException("requestEncoding");
}
HttpWebRequest request = null;
if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
request = WebRequest.Create(url) as HttpWebRequest;
request.ProtocolVersion = HttpVersion.Version10;
}
else
{
request = WebRequest.Create(url) as HttpWebRequest;
}
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.UserAgent = UserAgent;
if (cookies != null)
{
request.Headers.Add(HttpRequestHeader.Cookie, cookies);
}
if (postData!="")
{
byte[] data = requestEncoding.GetBytes(postData);
try
{
using (Stream stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
}catch(Exception e)
{
throw new ArgumentException(e.Message);
}
}
HttpWebResponse res = null;
try
{
res = request.GetResponse() as HttpWebResponse;
}
catch (Exception e)
{
throw new ArgumentException(e.Message);
}
return res;
}
private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
}
}
}