1
0
mirror of https://github.com/Ylianst/MeshCentralRouter synced 2026-02-22 20:33:26 +00:00

Merge pull request #30 from jbfuzier/proxy

Proxy related changes
This commit is contained in:
Ylian Saint-Hilaire
2022-05-18 18:19:18 -07:00
committed by GitHub
10 changed files with 1283 additions and 212 deletions

10
MainForm.Designer.cs generated
View File

@@ -35,6 +35,7 @@
this.mainTabControl = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.panel1 = new System.Windows.Forms.Panel();
this.proxySettings = new System.Windows.Forms.Button();
this.installButton = new System.Windows.Forms.Button();
this.stateLabel = new System.Windows.Forms.Label();
this.label28 = new System.Windows.Forms.Label();
@@ -202,6 +203,7 @@
//
// panel1
//
this.panel1.Controls.Add(this.proxySettings);
this.panel1.Controls.Add(this.installButton);
this.panel1.Controls.Add(this.stateLabel);
this.panel1.Controls.Add(this.label28);
@@ -219,6 +221,13 @@
resources.ApplyResources(this.panel1, "panel1");
this.panel1.Name = "panel1";
//
// proxySettings
//
resources.ApplyResources(this.proxySettings, "proxySettings");
this.proxySettings.Name = "proxySettings";
this.proxySettings.UseVisualStyleBackColor = true;
this.proxySettings.Click += new System.EventHandler(this.button1_Click);
//
// installButton
//
resources.ApplyResources(this.installButton, "installButton");
@@ -1137,6 +1146,7 @@
private System.Windows.Forms.ToolStripMenuItem askConsentToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem privacyBarToolStripMenuItem;
private System.Windows.Forms.PictureBox pictureBox2;
private System.Windows.Forms.Button proxySettings;
private System.Windows.Forms.ToolStripMenuItem customAppsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem customAppsToolStripMenuItem1;
}

View File

@@ -326,6 +326,7 @@ namespace MeshCentralRouter
if (File.Exists(Path.Combine(selfExe.Directory.FullName, @"customization\logo.png"))) { try { pictureBox2.Image = pictureBox6.Image = (Bitmap)Image.FromFile(Path.Combine(selfExe.Directory.FullName, @"customization\logo.png")); showLicense = false; } catch (Exception) { } }
if (File.Exists(Path.Combine(selfExe.Directory.FullName, @"customization\bottombanner.png"))) { try { pictureBox3.Image = pictureBox4.Image = pictureBox5.Image = pictureBox7.Image = (Bitmap)Image.FromFile(Path.Combine(selfExe.Directory.FullName, @"customization\bottombanner.png")); showLicense = false; } catch (Exception) { } }
licenseLinkLabel.Visible = showLicense;
proxySettings.Visible = true;
try
{
if (File.Exists(Path.Combine(selfExe.Directory.FullName, @"customization\customize.txt")))
@@ -2166,6 +2167,12 @@ namespace MeshCentralRouter
cancelAutoClose();
}
private void button1_Click(object sender, EventArgs e)
{
ProxySettings form = new ProxySettings();
if (form.ShowDialog(this) == DialogResult.OK) { }
}
private void customAppsToolStripMenuItem_Click(object sender, EventArgs e)
{
CustomAppsForm f = new CustomAppsForm(Settings.GetApplications());

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,10 @@
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation />
<IsWebBootstrapper>false</IsWebBootstrapper>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
@@ -30,12 +34,8 @@
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -124,6 +124,11 @@
<Compile Include="FileDialogMsgForm.Designer.cs">
<DependentUpon>FileDialogMsgForm.cs</DependentUpon>
</Compile>
<Compile Include="ProxySettings.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ProxySettings.Designer.cs">
<DependentUpon>ProxySettings.cs</DependentUpon>
<Compile Include="ListViewExtended.cs">
<SubType>Component</SubType>
</Compile>
@@ -310,6 +315,9 @@
<EmbeddedResource Include="FileDialogMsgForm.resx">
<DependentUpon>FileDialogMsgForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="ProxySettings.resx">
<DependentUpon>ProxySettings.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="KVMViewerExtra.resx">
<DependentUpon>KVMViewerExtra.cs</DependentUpon>
</EmbeddedResource>

View File

@@ -97,31 +97,6 @@ namespace MeshCentralRouter
try { return Registry.GetValue(@"HKEY_CURRENT_USER\SOFTWARE\OpenSource\MeshRouter", name, "").ToString(); } catch (Exception) { return ""; }
}
public static string GetProxyForUrlUsingPac(string DestinationUrl, string PacUri)
{
IntPtr WinHttpSession = Win32Api.WinHttpOpen("User", Win32Api.WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, IntPtr.Zero, IntPtr.Zero, 0);
Win32Api.WINHTTP_AUTOPROXY_OPTIONS ProxyOptions = new Win32Api.WINHTTP_AUTOPROXY_OPTIONS();
Win32Api.WINHTTP_PROXY_INFO ProxyInfo = new Win32Api.WINHTTP_PROXY_INFO();
ProxyOptions.dwFlags = Win32Api.WINHTTP_AUTOPROXY_CONFIG_URL;
ProxyOptions.dwAutoDetectFlags = (Win32Api.WINHTTP_AUTO_DETECT_TYPE_DHCP | Win32Api.WINHTTP_AUTO_DETECT_TYPE_DNS_A);
ProxyOptions.lpszAutoConfigUrl = PacUri;
// Get Proxy
bool IsSuccess = Win32Api.WinHttpGetProxyForUrl(WinHttpSession, DestinationUrl, ref ProxyOptions, ref ProxyInfo);
Win32Api.WinHttpCloseHandle(WinHttpSession);
if (IsSuccess)
{
return ProxyInfo.lpszProxy;
}
else
{
Console.WriteLine("Error: {0}", Win32Api.GetLastError());
return null;
}
}
// Parse the URL query parameters and returns a collection
public static NameValueCollection GetQueryStringParameters()
@@ -759,8 +734,424 @@ namespace MeshCentralRouter
public string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
private MeshCentralServer parent = null;
private TcpClient wsclient = null;
private SslStream wsstream = null;
private NetworkStream wsrawstream = null;
private int state = 0;
private Uri url = null;
private byte[] readBuffer = new Byte[500];
private int readBufferLen = 0;
private int accopcodes = 0;
private bool accmask = false;
private int acclen = 0;
private bool proxyInUse = false;
private Uri proxyUri = null;
private string user = null;
private string pass = null;
private string token = null;
public bool xdebug = false;
public bool xtlsdump = false;
public bool xignoreCert = false;
public void Dispose() {
try { wsstream.Close(); } catch (Exception) { }
try { wsstream.Dispose(); } catch (Exception) { }
wsstream = null;
wsclient = null;
state = -1;
parent.changeState(0);
parent.wshash = null;
}
public void Debug(string msg) { if (xdebug) { try { File.AppendAllText("debug.log", "Debug-" + msg + "\r\n"); } catch (Exception) { } } }
public void TlsDump(string direction, byte[] data, int offset, int len) { if (xtlsdump) { try { File.AppendAllText("debug.log", direction + ": " + BitConverter.ToString(data, offset, len).Replace("-", string.Empty) + "\r\n"); } catch (Exception) { } } }
public bool Start(MeshCentralServer parent, Uri url, string user, string pass, string token, string fingerprint)
{
if (state != 0) return false;
parent.changeState(1);
state = 1;
this.parent = parent;
this.url = url;
this.user = user;
this.pass = pass;
this.token = token;
this.proxyUri = null;
this.proxyUri = Win32Api.GetProxy(url);
if (proxyUri != null)
{
// Proxy in use
proxyInUse = true;
wsclient = new TcpClient();
// This may log proxy password to debug log
Debug("Connecting with proxy in use: " + proxyUri.ToString());
wsclient.BeginConnect(proxyUri.Host, proxyUri.Port, new AsyncCallback(OnConnectSink), this);
}
else
{
// No proxy in use
proxyInUse = false;
wsclient = new TcpClient();
Debug("Connecting without proxy");
wsclient.BeginConnect(url.Host, url.Port, new AsyncCallback(OnConnectSink), this);
}
return true;
}
private void OnConnectSink(IAsyncResult ar)
{
if (wsclient == null) return;
// Accept the connection
try
{
wsclient.EndConnect(ar);
} catch (Exception ex) {
Debug("Websocket TCP failed to connect: " + ex.ToString());
Dispose();
return;
}
if (proxyInUse == true)
{
// Send proxy connection request
wsrawstream = wsclient.GetStream();
string userCreds = proxyUri.UserInfo;
string basicAuth = "";
if (userCreds.Length > 0)
{
// Base64 encode for basic authentication
userCreds = System.Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(userCreds));
basicAuth = "\r\nProxy-Authorization: Basic " + userCreds;
}
byte[] proxyRequestBuf = UTF8Encoding.UTF8.GetBytes("CONNECT " + url.Host + ":" + url.Port + " HTTP/1.1\r\nHost: " + url.Host + ":" + url.Port + basicAuth + "\r\n\r\n");
TlsDump("OutRaw", proxyRequestBuf, 0, proxyRequestBuf.Length);
try { wsrawstream.Write(proxyRequestBuf, 0, proxyRequestBuf.Length); } catch (Exception ex) { Debug(ex.ToString()); }
wsrawstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnProxyResponseSink), this);
}
else
{
// Start TLS connection
Debug("Websocket TCP connected, doing TLS...");
wsstream = new SslStream(wsclient.GetStream(), false, VerifyServerCertificate, null);
wsstream.BeginAuthenticateAsClient(url.Host, null, System.Security.Authentication.SslProtocols.Tls12, false, new AsyncCallback(OnTlsSetupSink), this);
}
}
private void OnProxyResponseSink(IAsyncResult ar)
{
if (wsrawstream == null) return;
int len = 0;
try { len = wsrawstream.EndRead(ar); } catch (Exception) { }
if (len == 0)
{
// Disconnect
Debug("Websocket proxy disconnected, length = 0.");
Dispose();
return;
}
TlsDump("InRaw", readBuffer, 0, readBufferLen);
readBufferLen += len;
string proxyResponse = UTF8Encoding.UTF8.GetString(readBuffer, 0, readBufferLen);
if (proxyResponse.IndexOf("\r\n\r\n") >= 0)
{
// We get a full proxy response, we should get something like "HTTP/1.1 200 Connection established\r\n\r\n"
if (proxyResponse.StartsWith("HTTP/1.1 200 "))
{
// All good, start TLS setup.
readBufferLen = 0;
Debug("Websocket TCP connected, doing TLS...");
wsstream = new SslStream(wsrawstream, false, VerifyServerCertificate, null);
wsstream.BeginAuthenticateAsClient(url.Host, null, System.Security.Authentication.SslProtocols.Tls12, false, new AsyncCallback(OnTlsSetupSink), this);
}
else
{
// Invalid response
Debug("Proxy connection failed: " + proxyResponse);
Dispose();
}
} else {
if (readBufferLen == readBuffer.Length)
{
// Buffer overflow
Debug("Proxy connection failed");
Dispose();
}
else
{
// Read more proxy data
wsrawstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnProxyResponseSink), this);
}
}
}
public string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
public string Base64Decode(string base64EncodedData)
{
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
private void OnTlsSetupSink(IAsyncResult ar)
{
if (wsstream == null) return;
// Accept the connection
try
{
wsstream.EndAuthenticateAsClient(ar);
}
catch (Exception ex)
{
// Disconnect
if (ex.InnerException != null) {
MessageBox.Show(ex.Message + ", Inner: " + ex.InnerException.ToString(), "MeshCentral Router");
} else {
MessageBox.Show(ex.Message, "MeshCentral Router");
}
Debug("Websocket TLS failed: " + ex.ToString());
Dispose();
return;
}
// Fetch remote certificate
parent.wshash = wsstream.RemoteCertificate.GetCertHashString();
// Setup extra headers if needed
string extraHeaders = "";
if (user != null && pass != null && token != null) { extraHeaders = "x-meshauth: " + Base64Encode(user) + "," + Base64Encode(pass) + "," + Base64Encode(token) + "\r\n"; }
else if (user != null && pass != null) { extraHeaders = "x-meshauth: " + Base64Encode(user) + "," + Base64Encode(pass) + "\r\n"; }
// Send the HTTP headers
Debug("Websocket TLS setup, sending HTTP header...");
string header = "GET " + url.PathAndQuery + " HTTP/1.1\r\nHost: " + url.Host + "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n" + extraHeaders + "\r\n";
try { wsstream.Write(UTF8Encoding.UTF8.GetBytes(header)); } catch (Exception ex) { Debug(ex.ToString()); }
// Start receiving data
wsstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnTlsDataSink), this);
}
private void OnTlsDataSink(IAsyncResult ar)
{
if (wsstream == null) return;
int len = 0;
try { len = wsstream.EndRead(ar); } catch (Exception) { }
if (len == 0)
{
// Disconnect
Debug("Websocket disconnected, length = 0.");
Dispose();
return;
}
//parent.Debug("#" + counter + ": Websocket got new data: " + len);
readBufferLen += len;
TlsDump("In", readBuffer, 0, len);
// Consume all of the data
int consumed = 0;
int ptr = 0;
do
{
consumed = ProcessBuffer(readBuffer, ptr, readBufferLen - ptr);
if (consumed < 0) { Dispose(); return; } // Error, close the connection
ptr += consumed;
} while ((consumed > 0) && ((readBufferLen - consumed) > 0));
// Move the data forward
if ((ptr > 0) && (readBufferLen - ptr) > 0) {
//Console.Write("MOVE FORWARD\r\n");
Array.Copy(readBuffer, ptr, readBuffer, 0, (readBufferLen - ptr));
}
readBufferLen = (readBufferLen - ptr);
// If the buffer is too small, double the size here.
if (readBuffer.Length - readBufferLen == 0)
{
Debug("Increasing the read buffer size from " + readBuffer.Length + " to " + (readBuffer.Length * 2) + ".");
byte[] readBuffer2 = new byte[readBuffer.Length * 2];
Array.Copy(readBuffer, 0, readBuffer2, 0, readBuffer.Length);
readBuffer = readBuffer2;
}
// Receive more data
try { wsstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnTlsDataSink), this); } catch (Exception) { }
}
private int ProcessBuffer(byte[] buffer, int offset, int len)
{
string ss = UTF8Encoding.UTF8.GetString(buffer, offset, len);
if (state == 1)
{
// Look for the end of the http header
string header = UTF8Encoding.UTF8.GetString(buffer, offset, len);
int i = header.IndexOf("\r\n\r\n");
if (i == -1) return 0;
Dictionary<string, string> parsedHeader = ParseHttpHeader(header.Substring(0, i));
if ((parsedHeader == null) || (parsedHeader["_Path"] != "101")) { Debug("Websocket bad header."); return -1; } // Bad header, close the connection
Debug("Websocket got setup upgrade header.");
state = 2;
return len; // TODO: Technically we need to return the header length before UTF8 convert.
} else if (state == 2) {
// Parse a websocket fragment header
if (len < 2) return 0;
int headsize = 2;
accopcodes = buffer[offset];
accmask = ((buffer[offset + 1] & 0x80) != 0);
acclen = (buffer[offset + 1] & 0x7F);
if ((accopcodes & 0x0F) == 8)
{
// Close the websocket
Debug("Websocket got closed fragment.");
return -1;
}
if (acclen == 126)
{
if (len < 4) return 0;
headsize = 4;
acclen = (buffer[offset + 2] << 8) + (buffer[offset + 3]);
}
else if (acclen == 127)
{
if (len < 10) return 0;
headsize = 10;
acclen = (buffer[offset + 6] << 24) + (buffer[offset + 7] << 16) + (buffer[offset + 8] << 8) + (buffer[offset + 9]);
Debug("Websocket receive large fragment: " + acclen);
}
if (accmask == true)
{
// TODO: Do unmasking here.
headsize += 4;
}
//parent.Debug("#" + counter + ": Websocket frag header - FIN: " + ((accopcodes & 0x80) != 0) + ", OP: " + (accopcodes & 0x0F) + ", LEN: " + acclen + ", MASK: " + accmask);
state = 3;
return headsize;
}
else if (state == 3)
{
// Parse a websocket fragment data
if (len < acclen) return 0;
//Console.Write("WSREAD: " + acclen + "\r\n");
ProcessWsBuffer(buffer, offset, acclen, accopcodes);
state = 2;
return acclen;
}
return 0;
}
private void ProcessWsBuffer(byte[] data, int offset, int len, int op)
{
Debug("Websocket got data.");
//try { parent.processServerData(UTF8Encoding.UTF8.GetString(data, offset, len)); } catch (Exception ex) { }
parent.processServerData(UTF8Encoding.UTF8.GetString(data, offset, len));
}
private Dictionary<string, string> ParseHttpHeader(string header)
{
string[] lines = header.Replace("\r\n", "\r").Split('\r');
if (lines.Length < 2) { return null; }
string[] directive = lines[0].Split(' ');
Dictionary<string, string> values = new Dictionary<string, string>();
values["_Action"] = directive[0];
values["_Path"] = directive[1];
values["_Protocol"] = directive[2];
for (int i = 1; i < lines.Length; i++)
{
var j = lines[i].IndexOf(":");
values[lines[i].Substring(0, j).ToLower()] = lines[i].Substring(j + 1).Trim();
}
return values;
}
// Return a modified base64 SHA384 hash string of the certificate public key
public static string GetMeshKeyHash(X509Certificate cert)
{
return ByteArrayToHexString(new SHA384Managed().ComputeHash(cert.GetPublicKey()));
}
// Return a modified base64 SHA384 hash string of the certificate
public static string GetMeshCertHash(X509Certificate cert)
{
return ByteArrayToHexString(new SHA384Managed().ComputeHash(cert.GetRawCertData()));
}
public static string ByteArrayToHexString(byte[] Bytes)
{
StringBuilder Result = new StringBuilder(Bytes.Length * 2);
string HexAlphabet = "0123456789ABCDEF";
foreach (byte B in Bytes) { Result.Append(HexAlphabet[(int)(B >> 4)]); Result.Append(HexAlphabet[(int)(B & 0xF)]); }
return Result.ToString();
}
private bool VerifyServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
parent.certHash = GetMeshKeyHash(certificate);
Debug("Verify cert: " + parent.certHash);
if (xignoreCert) return true;
if (chain.Build(new X509Certificate2(certificate)) == true) return true;
// Check that the remote certificate is the expected one
if ((parent.okCertHash != null) && (parent.okCertHash == certificate.GetCertHashString())) return true;
// Check that the remote certificate is the expected one
if ((parent.okCertHash2 != null) && ((parent.okCertHash2 == GetMeshKeyHash(certificate)) || (parent.okCertHash2 == GetMeshCertHash(certificate)))) { return true; }
parent.certHash = null;
parent.disconnectMsg = "cert";
parent.disconnectCert = new X509Certificate2(certificate);
return false;
}
public void WriteStringWebSocket(string data)
{
// Convert the string into a buffer with 4 byte of header space.
int len = UTF8Encoding.UTF8.GetByteCount(data);
byte[] buf = new byte[4 + len];
UTF8Encoding.UTF8.GetBytes(data, 0, data.Length, buf, 4);
len = buf.Length - 4;
// Check that everything is ok
if ((state < 2) || (len < 1) || (len > 65535)) { Dispose(); return; }
//Console.Write("Length: " + len + "\r\n");
//System.Threading.Thread.Sleep(0);
if (len < 126)
{
// Small fragment
buf[2] = 130; // Fragment op code (129 = text, 130 = binary)
buf[3] = (byte)(len & 0x7F);
//try { wsstream.BeginWrite(buf, 2, len + 2, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { Dispose(); return; }
TlsDump("Out", buf, 2, len + 2);
try { wsstream.Write(buf, 2, len + 2); } catch (Exception ex) { Debug(ex.ToString()); }
}
else
{
// Large fragment
buf[0] = 130; // Fragment op code (129 = text, 130 = binary)
buf[1] = 126;
buf[2] = (byte)((len >> 8) & 0xFF);
buf[3] = (byte)(len & 0xFF);
//try { wsstream.BeginWrite(buf, 0, len + 4, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { Dispose(); return; }
TlsDump("Out", buf, 0, len + 4);
try { wsstream.Write(buf, 0, len + 4); } catch (Exception ex) { Debug(ex.ToString()); }
}
}
}
public string Base64Decode(string base64EncodedData)

194
ProxySettings.Designer.cs generated Normal file
View File

@@ -0,0 +1,194 @@
namespace MeshCentralRouter
{
partial class ProxySettings
{
/// <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.SaveProxyConfig = new System.Windows.Forms.Button();
this.manualHttpProxyHost = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.manualHttpProxyPort = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.manualHttpProxyUsername = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.manualHttpProxyPassword = new System.Windows.Forms.TextBox();
this.label5 = new System.Windows.Forms.Label();
this.useManualProxySettings = new System.Windows.Forms.CheckBox();
this.cancel = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// SaveProxyConfig
//
this.SaveProxyConfig.Location = new System.Drawing.Point(124, 404);
this.SaveProxyConfig.Name = "SaveProxyConfig";
this.SaveProxyConfig.Size = new System.Drawing.Size(75, 23);
this.SaveProxyConfig.TabIndex = 1;
this.SaveProxyConfig.Text = "Ok";
this.SaveProxyConfig.UseVisualStyleBackColor = true;
this.SaveProxyConfig.Click += new System.EventHandler(this.SaveProxyConfig_Click);
//
// manualHttpProxyHost
//
this.manualHttpProxyHost.Location = new System.Drawing.Point(104, 141);
this.manualHttpProxyHost.Name = "manualHttpProxyHost";
this.manualHttpProxyHost.Size = new System.Drawing.Size(203, 22);
this.manualHttpProxyHost.TabIndex = 2;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(75, 114);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(103, 17);
this.label1.TabIndex = 3;
this.label1.Text = "Http proxy host";
this.label1.Click += new System.EventHandler(this.label1_Click);
//
// manualHttpProxyPort
//
this.manualHttpProxyPort.Location = new System.Drawing.Point(104, 208);
this.manualHttpProxyPort.Name = "manualHttpProxyPort";
this.manualHttpProxyPort.Size = new System.Drawing.Size(203, 22);
this.manualHttpProxyPort.TabIndex = 4;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(75, 181);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(101, 17);
this.label2.TabIndex = 5;
this.label2.Text = "Http proxy port";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(75, 257);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(139, 17);
this.label3.TabIndex = 7;
this.label3.Text = "Http proxy username";
//
// manualHttpProxyUsername
//
this.manualHttpProxyUsername.Location = new System.Drawing.Point(104, 286);
this.manualHttpProxyUsername.Name = "manualHttpProxyUsername";
this.manualHttpProxyUsername.Size = new System.Drawing.Size(203, 22);
this.manualHttpProxyUsername.TabIndex = 6;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(78, 332);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(136, 17);
this.label4.TabIndex = 9;
this.label4.Text = "Http proxy password";
//
// manualHttpProxyPassword
//
this.manualHttpProxyPassword.Location = new System.Drawing.Point(104, 362);
this.manualHttpProxyPassword.Name = "manualHttpProxyPassword";
this.manualHttpProxyPassword.PasswordChar = '*';
this.manualHttpProxyPassword.Size = new System.Drawing.Size(203, 22);
this.manualHttpProxyPassword.TabIndex = 8;
//
// label5
//
this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.label5.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.label5.Location = new System.Drawing.Point(13, 9);
this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(396, 69);
this.label5.TabIndex = 10;
this.label5.Text = "Manually configure an HTTP proxy server to use. Username and password are optiona" +
"l. Only basic auth is supported.";
this.label5.UseMnemonic = false;
//
// useManualProxySettings
//
this.useManualProxySettings.AutoSize = true;
this.useManualProxySettings.Location = new System.Drawing.Point(43, 81);
this.useManualProxySettings.Name = "useManualProxySettings";
this.useManualProxySettings.Size = new System.Drawing.Size(194, 21);
this.useManualProxySettings.TabIndex = 11;
this.useManualProxySettings.Text = "use manual proxy settings";
this.useManualProxySettings.UseVisualStyleBackColor = true;
this.useManualProxySettings.CheckedChanged += new System.EventHandler(this.useManualProxySettings_CheckedChanged);
//
// cancel
//
this.cancel.Location = new System.Drawing.Point(232, 404);
this.cancel.Name = "cancel";
this.cancel.Size = new System.Drawing.Size(75, 23);
this.cancel.TabIndex = 12;
this.cancel.Text = "Cancel";
this.cancel.UseVisualStyleBackColor = true;
this.cancel.Click += new System.EventHandler(this.cancel_Click);
//
// ProxySettings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(482, 450);
this.Controls.Add(this.cancel);
this.Controls.Add(this.useManualProxySettings);
this.Controls.Add(this.label5);
this.Controls.Add(this.label4);
this.Controls.Add(this.manualHttpProxyPassword);
this.Controls.Add(this.label3);
this.Controls.Add(this.manualHttpProxyUsername);
this.Controls.Add(this.label2);
this.Controls.Add(this.manualHttpProxyPort);
this.Controls.Add(this.label1);
this.Controls.Add(this.manualHttpProxyHost);
this.Controls.Add(this.SaveProxyConfig);
this.Name = "ProxySettings";
this.Text = "Proxy Settings";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button SaveProxyConfig;
private System.Windows.Forms.TextBox manualHttpProxyHost;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox manualHttpProxyPort;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox manualHttpProxyUsername;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.TextBox manualHttpProxyPassword;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.CheckBox useManualProxySettings;
private System.Windows.Forms.Button cancel;
}
}

69
ProxySettings.cs Normal file
View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MeshCentralRouter
{
public partial class ProxySettings : Form
{
public ProxySettings()
{
InitializeComponent();
useManualProxySettings.Checked = Settings.GetRegValue("Use_Manual_Http_proxy", false);
manualHttpProxyHost.Text = Settings.GetRegValue("Manual_Http_proxy_host", "");
manualHttpProxyPort.Text = Settings.GetRegValue("Manual_Http_proxy_port", "");
manualHttpProxyUsername.Text = Settings.GetRegValue("Manual_Http_proxy_username", "");
manualHttpProxyPassword.Text = Settings.GetRegValue("Manual_Http_proxy_password", "");
checkbox_refresh_form();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void SaveProxyConfig_Click(object sender, EventArgs e)
{
Settings.SetRegValue("Use_Manual_Http_proxy", useManualProxySettings.Checked);
Settings.SetRegValue("Manual_Http_proxy_host", manualHttpProxyHost.Text);
Settings.SetRegValue("Manual_Http_proxy_port", manualHttpProxyPort.Text);
Settings.SetRegValue("Manual_Http_proxy_username", manualHttpProxyUsername.Text);
Settings.SetRegValue("Manual_Http_proxy_password", manualHttpProxyPassword.Text);
DialogResult = DialogResult.OK;
}
private void checkbox_refresh_form()
{
if (useManualProxySettings.Checked)
{
manualHttpProxyHost.ReadOnly = false;
manualHttpProxyPort.ReadOnly = false;
manualHttpProxyUsername.ReadOnly = false;
manualHttpProxyPassword.ReadOnly = false;
}
else
{
manualHttpProxyHost.ReadOnly = true;
manualHttpProxyPort.ReadOnly = true;
manualHttpProxyUsername.ReadOnly = true;
manualHttpProxyPassword.ReadOnly = true;
}
}
private void useManualProxySettings_CheckedChanged(object sender, EventArgs e)
{
checkbox_refresh_form();
}
private void cancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
}
}
}

120
ProxySettings.resx Normal file
View File

@@ -0,0 +1,120 @@
<?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>
</root>

View File

@@ -49,6 +49,7 @@ namespace MeshCentralRouter
private bool accmask = false;
private int acclen = 0;
private bool proxyInUse = false;
private Uri proxyUri = null;
private string tlsCertFingerprint = null;
private string tlsCertFingerprint2 = null;
//private ConnectionErrors lastError = ConnectionErrors.NoError;
@@ -160,6 +161,13 @@ namespace MeshCentralRouter
private async Task ConnectAsync(Uri url)
{
if (state != ConnectionStates.Disconnected) return false;
SetState(ConnectionStates.Connecting);
this.url = url;
if (tlsCertFingerprint != null) { this.tlsCertFingerprint = tlsCertFingerprint.ToUpper(); }
Log("Websocket Start, URL=" + ((url == null) ? "(NULL)" : url.ToString()));
proxyUri = Win32Api.GetProxy(url);
if (CTS != null) CTS.Dispose();
CTS = new CancellationTokenSource();
try { await ws.ConnectAsync(url, CTS.Token); } catch (Exception) { SetState(0); return; }
@@ -188,7 +196,6 @@ namespace MeshCentralRouter
this.url = url;
if (tlsCertFingerprint != null) { this.tlsCertFingerprint = tlsCertFingerprint.ToUpper(); }
if (tlsCertFingerprint2 != null) { this.tlsCertFingerprint2 = tlsCertFingerprint2.ToUpper(); }
if (nativeWebSocketFirst) { try { ws = new ClientWebSocket(); } catch (Exception) { } }
if (ws != null)
{
@@ -279,7 +286,17 @@ namespace MeshCentralRouter
{
// Send proxy connection request
wsrawstream = wsclient.GetStream();
byte[] proxyRequestBuf = UTF8Encoding.UTF8.GetBytes("CONNECT " + url.Host + ":" + url.Port + " HTTP/1.1\r\nHost: " + url.Host + ":" + url.Port + "\r\n\r\n");
string userCreds = proxyUri.UserInfo;
string basicAuth = "";
if (userCreds.Length > 0)
{
// Base64 encode for basic authentication
userCreds = System.Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(userCreds));
basicAuth = "\r\nProxy-Authorization: Basic " + userCreds;
}
byte[] proxyRequestBuf = UTF8Encoding.UTF8.GetBytes("CONNECT " + url.Host + ":" + url.Port + " HTTP/1.1\r\nHost: " + url.Host + ":" + url.Port + basicAuth + "\r\n\r\n");
wsrawstream.Write(proxyRequestBuf, 0, proxyRequestBuf.Length);
wsrawstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnProxyResponseSink), this);
}

View File

@@ -78,6 +78,24 @@ namespace MeshCentralRouter
public static Uri GetProxy(Uri url)
{
// Manual http proxy with optional basic auth. Other auth type would be difficult to support as proxy authentication is done manually within the websocketclient
if(Settings.GetRegValue("Use_Manual_Http_proxy", false))
{
string proxyStr = "";
string proxyUserName = Settings.GetRegValue("Manual_Http_proxy_username", "");
string proxyPassword = Settings.GetRegValue("Manual_Http_proxy_password", "");
if (proxyUserName.Length > 0 && proxyPassword.Length > 0)
{
proxyStr = "http://" + proxyUserName + ":" + proxyPassword + "@" + Settings.GetRegValue("Manual_Http_proxy_host", "") + ":" + Settings.GetRegValue("Manual_Http_proxy_port", "");
}
else
{
proxyStr = "http://" + Settings.GetRegValue("Manual_Http_proxy_host", "") + ":" + Settings.GetRegValue("Manual_Http_proxy_port", "");
}
return new Uri(proxyStr);
}
// Check if we need to use a HTTP proxy (Auto-proxy way)
try
{
@@ -86,7 +104,10 @@ namespace MeshCentralRouter
if ((x != null) && (x.GetType() == typeof(string)))
{
string proxyStr = GetProxyForUrlUsingPac("http" + ((url.Port == 80) ? "" : "s") + "://" + url.Host + ":" + url.Port, x.ToString());
return new Uri("http://" + proxyStr);
if (proxyStr != null)
{
return new Uri("http://" + proxyStr);
}
}
}
catch (Exception) { }