1
0
mirror of https://github.com/Ylianst/MeshCentralRouter synced 2025-12-06 00:13:33 +00:00

Added auto-update system.

This commit is contained in:
Ylian Saint-Hilaire
2020-11-02 21:45:50 -08:00
parent 552f5c00c1
commit 20d72cb66a
6 changed files with 2158 additions and 0 deletions

View File

@@ -150,6 +150,9 @@ namespace MeshCentralRouter
public MainForm(string[] args)
{
// Set TLS 1.2
ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
this.args = args;
InitializeComponent();
mainPanel.Controls.Add(panel1);
@@ -165,6 +168,8 @@ namespace MeshCentralRouter
title = this.Text;
int argflags = 0;
string update = null;
string delete = null;
foreach (string arg in this.args) {
if (arg.ToLower() == "-oldstyle") { deviceListViewMode = false; }
if (arg.ToLower() == "-install") { hookRouter(); forceExit = true; return; }
@@ -179,12 +184,31 @@ namespace MeshCentralRouter
if (arg.Length > 6 && arg.Substring(0, 6).ToLower() == "-user:") { userNameTextBox.Text = arg.Substring(6); argflags |= 2; }
if (arg.Length > 6 && arg.Substring(0, 6).ToLower() == "-pass:") { passwordTextBox.Text = arg.Substring(6); argflags |= 4; }
if (arg.Length > 8 && arg.Substring(0, 8).ToLower() == "-search:") { searchTextBox.Text = arg.Substring(8); }
if (arg.Length > 8 && arg.Substring(0, 8).ToLower() == "-update:") { update = arg.Substring(8); }
if (arg.Length > 8 && arg.Substring(0, 8).ToLower() == "-delete:") { delete = arg.Substring(8); }
if (arg.Length > 11 && arg.Substring(0, 11).ToLower() == "mcrouter://") { authLoginUrl = new Uri(arg); }
if ((arg.Length > 1) && (arg[0] != '-') && (arg.ToLower().EndsWith(".mcrouter"))) { try { argflags |= loadMappingFile(File.ReadAllText(arg), 1); } catch (Exception) { } }
if (arg.ToLower() == "-localfiles") { FileViewer fileViewer = new FileViewer(meshcentral, null); fileViewer.Show(); }
}
autoLogin = (argflags == 7);
if (update != null) {
// New args
ArrayList args2 = new ArrayList();
foreach (string a in args) { if (a.StartsWith("-update:") == false) { args2.Add(a); } }
// Remove ".update.exe" and copy
System.Threading.Thread.Sleep(1000);
File.Copy(Assembly.GetEntryAssembly().Location, update, true);
System.Threading.Thread.Sleep(1000);
Process.Start(update, string.Join(" ", args2 + " -delete:" + Assembly.GetEntryAssembly().Location));
this.forceExit = true;
Application.Exit();
return;
}
if (delete != null) { try { System.Threading.Thread.Sleep(1000); File.Delete(delete); } catch (Exception) { } }
// Set automatic port map values
if (authLoginUrl != null) {
string autoNodeId = null;
@@ -371,6 +395,7 @@ namespace MeshCentralRouter
meshcentral.onLoginTokenChanged += Meshcentral_onLoginTokenChanged;
meshcentral.onClipboardData += Meshcentral_onClipboardData;
meshcentral.onTwoFactorCookie += Meshcentral_onTwoFactorCookie;
meshcentral.onToolUpdate += Meshcentral_onToolUpdate;
if (lastBadConnectCert != null)
{
meshcentral.okCertHash = lastBadConnectCert.GetCertHashString();
@@ -442,6 +467,7 @@ namespace MeshCentralRouter
meshcentral.onLoginTokenChanged += Meshcentral_onLoginTokenChanged;
meshcentral.onClipboardData += Meshcentral_onClipboardData;
meshcentral.onTwoFactorCookie += Meshcentral_onTwoFactorCookie;
meshcentral.onToolUpdate += Meshcentral_onToolUpdate;
meshcentral.okCertHash = lastBadConnectCert.GetCertHashString();
Uri serverurl = null;
@@ -472,6 +498,13 @@ namespace MeshCentralRouter
}
}
private void Meshcentral_onToolUpdate(string url, string hash, int size)
{
if (this.InvokeRequired) { this.Invoke(new MeshCentralServer.toolUpdateHandler(Meshcentral_onToolUpdate), url, hash, size); return; }
UpdateForm f = new UpdateForm(url, hash, size, args);
if (f.ShowDialog(this) == DialogResult.OK) { }
}
private void Meshcentral_onLoginTokenChanged()
{
if (this.InvokeRequired) { this.Invoke(new MeshCentralServer.onLoginTokenChangedHandler(Meshcentral_onLoginTokenChanged)); return; }
@@ -1020,6 +1053,7 @@ namespace MeshCentralRouter
meshcentral.onLoginTokenChanged += Meshcentral_onLoginTokenChanged;
meshcentral.onClipboardData += Meshcentral_onClipboardData;
meshcentral.onTwoFactorCookie += Meshcentral_onTwoFactorCookie;
meshcentral.onToolUpdate += Meshcentral_onToolUpdate;
if (sendEmailToken == true)
{
sendEmailToken = false;

View File

@@ -227,6 +227,12 @@
<Compile Include="SettingsForm.Designer.cs">
<DependentUpon>SettingsForm.cs</DependentUpon>
</Compile>
<Compile Include="UpdateForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UpdateForm.Designer.cs">
<DependentUpon>UpdateForm.cs</DependentUpon>
</Compile>
<Compile Include="WebSocketClient.cs" />
<Compile Include="Win32Api.cs" />
<EmbeddedResource Include="AddPortMapForm.cs.resx">
@@ -742,6 +748,9 @@
<EmbeddedResource Include="SettingsForm.zh-chs.resx">
<DependentUpon>SettingsForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UpdateForm.resx">
<DependentUpon>UpdateForm.cs</DependentUpon>
</EmbeddedResource>
<None Include="app.manifest">
<SubType>Designer</SubType>
</None>

View File

@@ -218,6 +218,7 @@ namespace MeshCentralRouter
wc.WriteStringWebSocket("{\"action\":\"nodes\"}");
wc.WriteStringWebSocket("{\"action\":\"authcookie\"}");
wc.WriteStringWebSocket("{\"action\":\"logincookie\"}");
wc.WriteStringWebSocket("{\"action\":\"meshToolInfo\",\"name\":\"MeshCentralRouter\"}");
break;
}
case "authcookie":
@@ -582,6 +583,29 @@ namespace MeshCentralRouter
}
break;
}
case "meshToolInfo":
{
if (onToolUpdate == null) return;
if (jsonAction.ContainsKey("hash") && jsonAction.ContainsKey("url"))
{
// MeshCentral Router hash on the server
string hash = (string)jsonAction["hash"];
// Hash our own executable
byte[] selfHash;
using (var sha384 = SHA384Managed.Create()) { using (var stream = File.OpenRead(System.Reflection.Assembly.GetEntryAssembly().Location)) { selfHash = sha384.ComputeHash(stream); } }
string selfExecutableHashHex = BitConverter.ToString(selfHash).Replace("-", string.Empty).ToLower();
// Get login key
string url = jsonAction["url"] + "&auth=" + authCookie;
string loginkey = getValueFromQueryString(wsurl.Query, "key");
if (loginkey != null) { url += ("&key=" + loginkey); }
// If the hashes don't match, event the tool update with URL
if (selfExecutableHashHex != hash) { onToolUpdate((string)url, (string)jsonAction["hash"], (int)jsonAction["size"]); }
}
break;
}
default:
{
break;
@@ -589,6 +613,18 @@ namespace MeshCentralRouter
}
}
private static string getValueFromQueryString(string query, string name)
{
if ((query == null) || (name == null)) return null;
int i = query.IndexOf("?" + name + "=");
if (i == -1) { i = query.IndexOf("&" + name + "="); }
if (i == -1) return null;
string r = query.Substring(i + name.Length + 2);
i = r.IndexOf("&");
if (i >= 0) { r = r.Substring(0, i); }
return r;
}
public delegate void onStateChangedHandler(int state);
public event onStateChangedHandler onStateChanged;
public void changeState(int newState) { if (constate != newState) { constate = newState; if (onStateChanged != null) { onStateChanged(constate); } } }
@@ -601,6 +637,8 @@ namespace MeshCentralRouter
public event onClipboardDataHandler onClipboardData;
public delegate void twoFactorCookieHandler(string cookie);
public event twoFactorCookieHandler onTwoFactorCookie;
public delegate void toolUpdateHandler(string url, string hash, int size);
public event toolUpdateHandler onToolUpdate;
public class xwebclient : IDisposable
{

146
UpdateForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,146 @@
namespace MeshCentralRouter
{
partial class UpdateForm
{
/// <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()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UpdateForm));
this.cancelButton = new System.Windows.Forms.Button();
this.okButton = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.mainLabel = new System.Windows.Forms.Label();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.updateProgressBar = new System.Windows.Forms.ProgressBar();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(294, 155);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 0;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.Location = new System.Drawing.Point(213, 155);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 1;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
this.okButton.Click += new System.EventHandler(this.okButton_Click);
//
// groupBox1
//
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.Controls.Add(this.updateProgressBar);
this.groupBox1.Controls.Add(this.pictureBox1);
this.groupBox1.Controls.Add(this.mainLabel);
this.groupBox1.Location = new System.Drawing.Point(12, 12);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(357, 137);
this.groupBox1.TabIndex = 2;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "MeshCentral Router Update";
//
// mainLabel
//
this.mainLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.mainLabel.Font = new System.Drawing.Font("Arial", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.mainLabel.Location = new System.Drawing.Point(131, 19);
this.mainLabel.Name = "mainLabel";
this.mainLabel.Size = new System.Drawing.Size(220, 115);
this.mainLabel.TabIndex = 0;
this.mainLabel.Text = "This MeshCentral Server uses a different version of this tool. Click ok to downlo" +
"ad and update.";
//
// pictureBox1
//
this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.pictureBox1.Image = global::MeshCentralRouter.Properties.Resources.MeshCentral;
this.pictureBox1.Location = new System.Drawing.Point(6, 19);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(119, 112);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pictureBox1.TabIndex = 1;
this.pictureBox1.TabStop = false;
//
// updateProgressBar
//
this.updateProgressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.updateProgressBar.Location = new System.Drawing.Point(134, 110);
this.updateProgressBar.Name = "updateProgressBar";
this.updateProgressBar.Size = new System.Drawing.Size(217, 20);
this.updateProgressBar.TabIndex = 2;
this.updateProgressBar.Visible = false;
//
// UpdateForm
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(381, 190);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.okButton);
this.Controls.Add(this.cancelButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "UpdateForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Update";
this.groupBox1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label mainLabel;
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.ProgressBar updateProgressBar;
}
}

116
UpdateForm.cs Normal file
View File

@@ -0,0 +1,116 @@
using System;
using System.IO;
using System.Net;
using System.Diagnostics;
using System.Net.Security;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace MeshCentralRouter
{
public partial class UpdateForm : Form
{
private string url = null;
private string hash = null;
private int size = 0;
private string[] args = null;
public UpdateForm(string url, string hash, int size, string[] args)
{
InitializeComponent();
this.url = url;
this.hash = hash;
this.size = size;
this.args = args;
}
private void okButton_Click(object sender, EventArgs e)
{
DownloadUpdate();
}
private void Client_DownloadProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e)
{
updateProgressBar.Value = e.ProgressPercentage;
}
private void DownloadUpdate()
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
Uri x = webRequest.RequestUri;
webRequest.Method = "GET";
webRequest.Timeout = 10000;
webRequest.BeginGetResponse(new AsyncCallback(DownloadUpdateRespone), webRequest);
webRequest.ServerCertificateValidationCallback += RemoteCertificateValidationCallback;
updateProgressBar.Visible = true;
}
public static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
public delegate void updateProgressHandler(int ptr, int total);
public void updateProgress(int ptr, int total)
{
if (this.InvokeRequired) { this.Invoke(new updateProgressHandler(updateProgress), ptr, total); return; }
updateProgressBar.Visible = true;
updateProgressBar.Maximum = total;
if (ptr <= total) { updateProgressBar.Value = ptr; } else { updateProgressBar.Value = total; }
}
public delegate void updateMessageHandler(string msg, int buttons);
public void updateMessage(string msg, int buttons)
{
if (this.InvokeRequired) { this.Invoke(new updateMessageHandler(updateMessage), msg, buttons); return; }
mainLabel.Text = msg;
okButton.Enabled = ((buttons & 1) != 0);
cancelButton.Enabled = ((buttons & 2) != 0);
updateProgressBar.Visible = ((buttons & 4) != 0);
}
private void DownloadUpdateRespone(IAsyncResult asyncResult)
{
long received = 0;
HttpWebRequest webRequest = (HttpWebRequest)asyncResult.AsyncState;
try
{
// Hash our own executable
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(asyncResult))
{
byte[] buffer = new byte[4096];
FileStream fileStream = File.OpenWrite(System.Reflection.Assembly.GetEntryAssembly().Location + ".update.exe");
using (Stream input = webResponse.GetResponseStream())
{
int size = input.Read(buffer, 0, buffer.Length);
while (size > 0)
{
fileStream.Write(buffer, 0, size);
received += size;
updateProgress((int)received, (int)size);
size = input.Read(buffer, 0, buffer.Length);
}
}
fileStream.Flush();
fileStream.Close();
// Hash the resulting file
byte[] downloadHash;
using (var sha384 = SHA384Managed.Create()) { using (var stream = File.OpenRead(System.Reflection.Assembly.GetEntryAssembly().Location + ".update.exe")) { downloadHash = sha384.ComputeHash(stream); } }
string downloadHashHex = BitConverter.ToString(downloadHash).Replace("-", string.Empty).ToLower();
if (downloadHashHex != hash) {
updateMessage("Invalid download.", 2);
File.Delete(System.Reflection.Assembly.GetEntryAssembly().Location + ".update.exe");
} else {
updateMessage("Updating...", 0);
Process.Start(System.Reflection.Assembly.GetEntryAssembly().Location + ".update.exe", "-update:" + System.Reflection.Assembly.GetEntryAssembly().Location + " " + string.Join(" ", args));
Application.Exit();
}
}
}
catch (Exception ex) { updateMessage("Error: " + ex.ToString(), 2); }
}
}
}

1815
UpdateForm.resx Normal file

File diff suppressed because it is too large Load Diff