diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs
index 3e7f7ee..61cfffc 100644
--- a/MainForm.Designer.cs
+++ b/MainForm.Designer.cs
@@ -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;
}
diff --git a/MainForm.cs b/MainForm.cs
index 8dac6b4..23b2902 100644
--- a/MainForm.cs
+++ b/MainForm.cs
@@ -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());
diff --git a/MainForm.resx b/MainForm.resx
index f067527..081a548 100644
--- a/MainForm.resx
+++ b/MainForm.resx
@@ -121,20 +121,59 @@
FlatButtons
+
+ Bottom, Left
+
+
+ NoControl
+
+
+
+ 185, 354
+
+
+ 4, 4, 4, 4
+
+
+ 127, 28
+
+
+
+ 106
+
+
+ Manual Proxy settings
+
+
+ False
+
+
+ proxySettings
+
+
+ System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ panel1
+
+
+ 0
+
Bottom, Left
NoControl
-
- 11, 286
+ 15, 354
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
-
104
@@ -154,7 +193,7 @@
panel1
- 0
+ 1
Top, Left, Right
@@ -169,10 +208,13 @@
NoControl
- 241, 225
+ 321, 277
+
+
+ 4, 0, 4, 0
- 88, 13
+ 114, 17
19
@@ -193,7 +235,7 @@
panel1
- 1
+ 2
True
@@ -202,10 +244,13 @@
NoControl
- 238, 176
+ 317, 217
+
+
+ 4, 0, 4, 0
- 53, 13
+ 69, 17
18
@@ -223,7 +268,7 @@
panel1
- 2
+ 3
True
@@ -232,10 +277,13 @@
NoControl
- 238, 135
+ 317, 166
+
+
+ 4, 0, 4, 0
- 55, 13
+ 73, 17
17
@@ -253,7 +301,7 @@
panel1
- 3
+ 4
True
@@ -262,10 +310,13 @@
NoControl
- 238, 92
+ 317, 113
+
+
+ 4, 0, 4, 0
- 38, 13
+ 50, 17
16
@@ -283,19 +334,22 @@
panel1
- 4
+ 5
Top, Left, Right
- 241, 192
+ 321, 236
+
+
+ 4, 4, 4, 4
●
- 214, 20
+ 287, 22
102
@@ -310,7 +364,7 @@
panel1
- 5
+ 6
Top, Left, Right
@@ -319,10 +373,13 @@
meshcentral.com
- 241, 110
+ 321, 135
+
+
+ 4, 4, 4, 4
- 214, 21
+ 287, 24
100
@@ -337,16 +394,19 @@
panel1
- 6
+ 7
Top, Left, Right
- 241, 151
+ 321, 186
+
+
+ 4, 4, 4, 4
- 214, 20
+ 287, 22
101
@@ -361,7 +421,7 @@
panel1
- 7
+ 8
Bottom, Right
@@ -370,10 +430,13 @@
NoControl
- 174, 260
+ 235, 323
+
+
+ 4, 0, 4, 0
- 303, 13
+ 404, 16
103
@@ -394,7 +457,7 @@
panel1
- 8
+ 9
Bottom, Left
@@ -406,10 +469,13 @@
NoControl
- 3, 260
+ 4, 323
+
+
+ 4, 0, 4, 0
- 37, 13
+ 47, 17
11
@@ -427,7 +493,7 @@
panel1
- 9
+ 10
Bottom, Right
@@ -436,10 +502,13 @@
NoControl
- 372, 286
+ 498, 354
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
105
@@ -457,7 +526,7 @@
panel1
- 10
+ 11
Bottom, Left, Right
@@ -466,10 +535,13 @@
NoControl
- 0, 277
+ 0, 343
+
+
+ 4, 4, 4, 4
- 478, 40
+ 640, 49
StretchImage
@@ -487,7 +559,7 @@
panel1
- 11
+ 12
Top, Left, Right
@@ -496,10 +568,13 @@
NoControl
- 12, 9
+ 16, 11
+
+
+ 4, 0, 4, 0
- 454, 56
+ 608, 69
6
@@ -517,7 +592,7 @@
panel1
- 12
+ 13
Top, Bottom, Left, Right
@@ -526,10 +601,13 @@
NoControl
- 15, 68
+ 20, 84
+
+
+ 4, 4, 4, 4
- 217, 170
+ 292, 212
Zoom
@@ -547,16 +625,19 @@
panel1
- 13
+ 14
Fill
- 3, 3
+ 4, 4
+
+
+ 4, 4, 4, 4
- 478, 316
+ 640, 392
6
@@ -574,13 +655,16 @@
0
- 4, 25
+ 4, 28
+
+
+ 4, 4, 4, 4
- 3, 3, 3, 3
+ 4, 4, 4, 4
- 484, 322
+ 648, 400
0
@@ -607,10 +691,13 @@
NoControl
- 241, 221
+ 321, 272
+
+
+ 4, 4, 4, 4
- 131, 17
+ 170, 21
204
@@ -637,10 +724,13 @@
NoControl
- 241, 177
+ 321, 218
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
202
@@ -667,10 +757,13 @@
NoControl
- 342, 177
+ 456, 218
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
203
@@ -700,10 +793,13 @@
NoControl
- 238, 182
+ 317, 224
+
+
+ 4, 0, 4, 0
- 55, 13
+ 73, 17
21
@@ -733,10 +829,13 @@
NoControl
- 238, 135
+ 317, 166
+
+
+ 4, 0, 4, 0
- 38, 13
+ 48, 17
19
@@ -760,10 +859,13 @@
Top, Left, Right
- 241, 151
+ 321, 186
+
+
+ 4, 4, 4, 4
- 214, 20
+ 287, 22
201
@@ -787,10 +889,13 @@
NoControl
- 15, 68
+ 20, 84
+
+
+ 4, 4, 4, 4
- 202, 170
+ 272, 212
Zoom
@@ -817,10 +922,13 @@
NoControl
- 270, 286
+ 363, 354
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
205
@@ -847,10 +955,13 @@
NoControl
- 372, 286
+ 498, 354
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
206
@@ -877,10 +988,13 @@
NoControl
- 0, 277
+ 0, 343
+
+
+ 4, 4, 4, 4
- 478, 40
+ 640, 49
StretchImage
@@ -907,10 +1021,13 @@
NoControl
- 12, 9
+ 16, 11
+
+
+ 4, 0, 4, 0
- 454, 56
+ 608, 69
6
@@ -934,10 +1051,13 @@
Fill
- 3, 3
+ 4, 4
+
+
+ 4, 4, 4, 4
- 478, 316
+ 640, 392
7
@@ -955,13 +1075,16 @@
0
- 4, 25
+ 4, 28
+
+
+ 4, 4, 4, 4
- 3, 3, 3, 3
+ 4, 4, 4, 4
- 484, 322
+ 648, 400
1
@@ -988,10 +1111,13 @@
True
- 208, 256
+ 277, 318
+
+
+ 4, 4, 4, 4
- 145, 17
+ 190, 21
302
@@ -1015,7 +1141,10 @@
Top, Bottom, Left, Right
- 15, 82
+ 20, 101
+
+
+ 4, 4, 4, 4
True
@@ -1024,7 +1153,7 @@
Vertical
- 450, 162
+ 602, 202
25
@@ -1048,10 +1177,13 @@
NoControl
- 14, 251
+ 19, 313
+
+
+ 4, 4, 4, 4
- 188, 23
+ 251, 28
301
@@ -1081,10 +1213,13 @@
NoControl
- 8, 6
+ 11, 7
+
+
+ 4, 0, 4, 0
- 468, 22
+ 627, 27
23
@@ -1114,10 +1249,13 @@
NoControl
- 12, 34
+ 16, 42
+
+
+ 4, 0, 4, 0
- 454, 45
+ 608, 55
22
@@ -1144,10 +1282,13 @@
NoControl
- 276, 291
+ 371, 362
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
303
@@ -1174,10 +1315,13 @@
NoControl
- 378, 291
+ 506, 362
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
304
@@ -1204,10 +1348,13 @@
NoControl
- 0, 282
+ 0, 351
+
+
+ 4, 4, 4, 4
- 484, 40
+ 648, 49
StretchImage
@@ -1233,8 +1380,11 @@
0, 0
+
+ 4, 4, 4, 4
+
- 484, 322
+ 648, 400
8
@@ -1252,10 +1402,13 @@
0
- 4, 25
+ 4, 28
+
+
+ 4, 4, 4, 4
- 484, 322
+ 648, 400
2
@@ -1285,37 +1438,37 @@
613, 17
- 185, 22
+ 218, 26
Show &Group Names
- 185, 22
+ 218, 26
Show &Offline Devices
- 182, 6
+ 215, 6
- 185, 22
+ 218, 26
Sort by &Name
- 185, 22
+ 218, 26
Sort by G&roup
- 182, 6
+ 215, 6
- 185, 22
+ 218, 26
S&ettings...
@@ -1327,7 +1480,7 @@
&Custom Apps...
- 186, 170
+ 219, 146
mainContextMenuStrip
@@ -1342,10 +1495,13 @@
NoControl
- 454, 2
+ 608, 2
+
+
+ 4, 0, 4, 0
- 25, 25
+ 31, 31
403
@@ -1369,10 +1525,13 @@
Top, Right
- 306, 5
+ 411, 6
+
+
+ 4, 4, 4, 4
- 146, 20
+ 193, 22
402
@@ -1405,10 +1564,13 @@
Bottom, Left
- 4, 167
+ 5, 206
+
+
+ 4, 4, 4, 4
- 168, 35
+ 224, 43
405
@@ -1450,82 +1612,82 @@
Segoe UI, 9pt, style=Bold
- 170, 22
+ 198, 24
Add &Map...
- 170, 22
+ 198, 24
Add &Relay Map...
- 167, 6
+ 195, 6
- 171, 22
+ 212, 26
Ask Consent + Bar
- 171, 22
+ 212, 26
Ask Consent
- 171, 22
+ 212, 26
Privacy Bar
- 170, 22
+ 198, 24
Remote Desktop...
- 170, 22
+ 198, 24
Remote Files...
- 170, 22
+ 198, 24
HTTP
- 170, 22
+ 198, 24
HTTPS
- 170, 22
+ 198, 24
RDP
- 170, 22
+ 198, 24
SSH
- 170, 22
+ 198, 24
SCP
- 171, 208
+ 199, 226
devicesContextMenuStrip
@@ -1672,10 +1834,13 @@
- 10, 4
+ 13, 5
+
+
+ 4, 4, 4, 4
- 446, 210
+ 593, 258
404
@@ -1705,10 +1870,13 @@
NoControl
- 2, 88
+ 3, 108
+
+
+ 4, 0, 4, 0
- 392, 52
+ 526, 64
5
@@ -1744,10 +1912,13 @@
NoControl
- 2, 88
+ 3, 108
+
+
+ 4, 0, 4, 0
- 392, 52
+ 526, 64
4
@@ -1773,8 +1944,11 @@
0, 0
+
+ 4, 4, 4, 4
+
- 470, 248
+ 628, 305
50
@@ -1792,13 +1966,16 @@
0
- 4, 25
+ 4, 28
+
+
+ 4, 4, 4, 4
- 3, 3, 3, 3
+ 4, 4, 4, 4
- 470, 250
+ 632, 315
0
@@ -1825,10 +2002,13 @@
NoControl
- 4, 182
+ 5, 221
+
+
+ 4, 4, 4, 4
- 168, 35
+ 224, 43
55
@@ -1867,10 +2047,13 @@
NoControl
- 6, 79
+ 8, 97
+
+
+ 4, 0, 4, 0
- 446, 52
+ 594, 64
4
@@ -1898,8 +2081,11 @@ Click "Add" to get started.
0, 0
+
+ 4, 4, 4, 4
+
- 472, 221
+ 628, 268
49
@@ -1923,10 +2109,13 @@ Click "Add" to get started.
NoControl
- 1, 226
+ 1, 275
+
+
+ 4, 4, 4, 4
- 20, 20
+ 27, 25
54
@@ -1950,10 +2139,13 @@ Click "Add" to get started.
NoControl
- 240, 226
+ 320, 275
+
+
+ 4, 4, 4, 4
- 20, 20
+ 27, 25
5
@@ -1980,10 +2172,13 @@ Click "Add" to get started.
NoControl
- 372, 223
+ 496, 272
+
+
+ 4, 4, 4, 4
- 100, 23
+ 133, 28
50
@@ -2013,10 +2208,13 @@ Click "Add" to get started.
NoControl
- 266, 223
+ 355, 272
+
+
+ 4, 4, 4, 4
- 100, 23
+ 133, 28
52
@@ -2037,13 +2235,16 @@ Click "Add" to get started.
5
- 4, 25
+ 4, 28
+
+
+ 4, 4, 4, 4
- 3, 3, 3, 3
+ 4, 4, 4, 4
- 472, 253
+ 632, 315
1
@@ -2064,10 +2265,13 @@ Click "Add" to get started.
1
- 3, 3
+ 4, 4
+
+
+ 4, 4, 4, 4
- 478, 279
+ 640, 347
401
@@ -2091,10 +2295,13 @@ Click "Add" to get started.
NoControl
- 15, 291
+ 20, 362
+
+
+ 4, 4, 4, 4
- 109, 23
+ 145, 28
406
@@ -2124,10 +2331,13 @@ Click "Add" to get started.
NoControl
- 276, 291
+ 371, 362
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
407
@@ -2154,10 +2364,13 @@ Click "Add" to get started.
NoControl
- 378, 291
+ 506, 362
+
+
+ 4, 4, 4, 4
- 95, 23
+ 127, 28
408
@@ -2184,10 +2397,13 @@ Click "Add" to get started.
NoControl
- 0, 282
+ 0, 351
+
+
+ 4, 4, 4, 4
- 484, 40
+ 648, 49
StretchImage
@@ -2213,8 +2429,11 @@ Click "Add" to get started.
0, 0
+
+ 4, 4, 4, 4
+
- 484, 322
+ 648, 400
8
@@ -2232,10 +2451,13 @@ Click "Add" to get started.
0
- 4, 25
+ 4, 28
+
+
+ 4, 4, 4, 4
- 484, 322
+ 648, 400
4
@@ -2265,7 +2487,7 @@ Click "Add" to get started.
0, 0, 0, 0
- 492, 351
+ 656, 432
8
@@ -2289,10 +2511,13 @@ Click "Add" to get started.
Fill
- 0, 65
+ 0, 80
+
+
+ 4, 4, 4, 4
- 492, 351
+ 656, 432
9
@@ -2318,8 +2543,11 @@ Click "Add" to get started.
0, 0
+
+ 4, 4, 4, 4
+
- 492, 65
+ 656, 80
Zoom
@@ -2345,8 +2573,11 @@ Click "Add" to get started.
0, 0
+
+ 4, 4, 4, 4
+
- 492, 416
+ 656, 512
8
@@ -2376,22 +2607,22 @@ Click "Add" to get started.
418, 17
- 112, 22
+ 123, 24
&Open...
- 109, 6
+ 120, 6
- 112, 22
+ 123, 24
E&xit
- 113, 54
+ 124, 58
trayIconContextMenuStrip
@@ -4103,22 +4334,22 @@ Click "Add" to get started.
905, 17
- 168, 22
+ 193, 24
&Open Mappings...
- 168, 22
+ 193, 24
&Save Mappings...
- 165, 6
+ 190, 6
- 168, 22
+ 193, 24
S&ettings...
@@ -4130,7 +4361,7 @@ Click "Add" to get started.
&Custom Apps...
- 169, 98
+ 194, 82
mappingsContextMenuStrip
@@ -4160,10 +4391,10 @@ Click "Add" to get started.
True
- 6, 13
+ 8, 16
- 492, 416
+ 656, 512
@@ -5859,6 +6090,9 @@ Click "Add" to get started.
AADAPwAAwD8AAMA/AADAPwAA
+
+ 4, 4, 4, 4
+
MeshCentral Router
diff --git a/MeshCentralRouter.csproj b/MeshCentralRouter.csproj
index 4be9387..463f0db 100644
--- a/MeshCentralRouter.csproj
+++ b/MeshCentralRouter.csproj
@@ -18,6 +18,10 @@
3.5
+ false
+
+
+
publish\
true
Disk
@@ -30,12 +34,8 @@
true
0
1.0.0.%2a
- false
false
true
-
-
-
true
@@ -124,6 +124,11 @@
FileDialogMsgForm.cs
+
+ Form
+
+
+ ProxySettings.cs
Component
@@ -310,6 +315,9 @@
FileDialogMsgForm.cs
+
+ ProxySettings.cs
+
KVMViewerExtra.cs
diff --git a/MeshCentralServer.cs b/MeshCentralServer.cs
index a2363e7..2027dfe 100644
--- a/MeshCentralServer.cs
+++ b/MeshCentralServer.cs
@@ -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 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 ParseHttpHeader(string header)
+ {
+ string[] lines = header.Replace("\r\n", "\r").Split('\r');
+ if (lines.Length < 2) { return null; }
+ string[] directive = lines[0].Split(' ');
+ Dictionary values = new Dictionary();
+ 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)
diff --git a/ProxySettings.Designer.cs b/ProxySettings.Designer.cs
new file mode 100644
index 0000000..49a0bf5
--- /dev/null
+++ b/ProxySettings.Designer.cs
@@ -0,0 +1,194 @@
+namespace MeshCentralRouter
+{
+ partial class ProxySettings
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/ProxySettings.cs b/ProxySettings.cs
new file mode 100644
index 0000000..28d010b
--- /dev/null
+++ b/ProxySettings.cs
@@ -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;
+ }
+ }
+}
diff --git a/ProxySettings.resx b/ProxySettings.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/ProxySettings.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/WebSocketClient.cs b/WebSocketClient.cs
index b1bfe2f..2969a33 100644
--- a/WebSocketClient.cs
+++ b/WebSocketClient.cs
@@ -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);
}
diff --git a/Win32Api.cs b/Win32Api.cs
index 6e2643b..e2f8776 100644
--- a/Win32Api.cs
+++ b/Win32Api.cs
@@ -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) { }