diff --git a/Protest/Front/about.js b/Protest/Front/about.js index 29d9f2d9..64067666 100644 --- a/Protest/Front/about.js +++ b/Protest/Front/about.js @@ -266,8 +266,8 @@ class About extends Tabs { link.style.borderRadius = "4px"; link.style.margin = "4px"; link.style.padding = "8px"; - link.style.paddingLeft = "32px"; - link.style.background = "url(mono/download.svg) 2px center / 24px 24px no-repeat"; + link.style.paddingLeft = "36px"; + link.style.background = "url(mono/download.svg) 4px center / 24px 24px no-repeat"; link.target = "_blank"; link.href = "https://github.com/openprotest/protest/releases/latest"; link.textContent = `Pro-test ${json.version}`; diff --git a/Protest/Front/deviceview.js b/Protest/Front/deviceview.js index 2e39ab0f..8c15644a 100644 --- a/Protest/Front/deviceview.js +++ b/Protest/Front/deviceview.js @@ -977,6 +977,7 @@ class DeviceView extends View { for (let i=0; i Pro-test - + diff --git a/Protest/Front/mono/listener.svg b/Protest/Front/mono/listener.svg new file mode 100644 index 00000000..d09d7621 --- /dev/null +++ b/Protest/Front/mono/listener.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Protest/Front/personalize.js b/Protest/Front/personalize.js index dd368444..3d8ac6b8 100644 --- a/Protest/Front/personalize.js +++ b/Protest/Front/personalize.js @@ -774,6 +774,9 @@ class Personalize extends Tabs { this.tabsPanel.appendChild(document.createElement("hr")); this.tabsPanel.appendChild(document.createElement("br")); + this.tabsPanel.appendChild(document.createElement("hr")); + this.tabsPanel.appendChild(document.createElement("br")); + const settingsButton = document.createElement("input"); settingsButton.type = "button"; settingsButton.value = "Prompt agent settings"; @@ -788,8 +791,8 @@ class Personalize extends Tabs { link.style.borderRadius = "4px"; link.style.margin = "4px"; link.style.padding = "8px"; - link.style.paddingLeft = "32px"; - link.style.background = "url(mono/download.svg) 2px center / 24px 24px no-repeat"; + link.style.paddingLeft = "36px"; + link.style.background = "url(mono/download.svg) 4px center / 24px 24px no-repeat"; link.target = "_blank"; link.href = "https://github.com/openprotest/protest/releases/latest"; link.textContent = `Download agent`; diff --git a/Protest/Tools/Monitor.cs b/Protest/Tools/Monitor.cs index a1448620..ba9a3d3e 100644 --- a/Protest/Tools/Monitor.cs +++ b/Protest/Tools/Monitor.cs @@ -35,11 +35,20 @@ public struct Query { public string value; } + public struct Answer { + public int index; + public Dictionary> data; + } + private static JsonSerializerOptions actionSerializerOptions; + private static JsonSerializerOptions answerSerializerOptions; static Monitor() { actionSerializerOptions = new JsonSerializerOptions(); actionSerializerOptions.Converters.Add(new ActionJsonConverter()); + + answerSerializerOptions = new JsonSerializerOptions(); + answerSerializerOptions.Converters.Add(new AnswerJsonConverter()); } private static async Task WsWriteText(WebSocket ws, [StringSyntax(StringSyntaxAttribute.Json)] string text) { @@ -73,7 +82,7 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { string target = null!; bool paused = false; bool ping = true; - int interval = 500; + int interval = 1000; ConcurrentDictionary wmi = new ConcurrentDictionary(); ConcurrentDictionary snmp = new ConcurrentDictionary(); byte[] buff = new byte[2048]; @@ -108,7 +117,7 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { Logger.Error(ex); } - Thread icmpThread = new Thread(async () => { //icmp thread + Func IcmpDelegate = async () => { while (ws.State == WebSocketState.Open) { if (paused) { await Task.Delay(interval); @@ -128,9 +137,9 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { await Task.Delay(calculatedInterval); } } - }); + }; - Thread wmiThread = new Thread(async() => { //wmi thread + Func WmiDelegate = async () => { ManagementScope scope = null; if (!OperatingSystem.IsWindows()) { await WsWriteText(ws, "{\"loglevel\":\"warning\",\"text\":\"WMI is not supported\"}"u8.ToArray()); @@ -176,9 +185,9 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { await Task.Delay(calculatedInterval); } } - }); + }; - Thread smtpThread = new Thread(async () => { //snmp thread + Func SnmpDelegate = async () => { while (ws.State == WebSocketState.Open) { if (paused) { await Task.Delay(interval); @@ -197,12 +206,15 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { await Task.Delay(calculatedInterval); } } - }); + }; + Thread icmpThread = null; + Thread wmiThread = null; + Thread smtpThread = null; + icmpThread = new Thread(() => IcmpDelegate()); icmpThread.Start(); - try { while (ws.State == WebSocketState.Open) { WebSocketReceiveResult receiveResult = await ws.ReceiveAsync(new ArraySegment(buff), CancellationToken.None); @@ -223,7 +235,10 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { switch (query.action) { case Action.start: paused = false; - //if (!icmpThread.IsAlive) { icmpThread.Start(); } + if (icmpThread is null) { + icmpThread = new Thread(() => IcmpDelegate()); + icmpThread.Start(); + } break; case Action.pause: @@ -236,12 +251,18 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { case Action.addwmi: wmi.TryAdd(query.index, query); - if (!wmiThread.IsAlive) { wmiThread.Start(); } + if (wmiThread is null) { + wmiThread = new Thread(() => WmiDelegate()); + wmiThread.Start(); + } break; case Action.addsnmp: snmp.TryAdd(query.index, query); - if (!smtpThread.IsAlive) { smtpThread.Start(); } + if (smtpThread is null) { + smtpThread = new Thread(() => SnmpDelegate()); + smtpThread.Start(); + } break; case Action.remove: @@ -303,42 +324,43 @@ private static long HandlePing(string host, int timeout) { [SupportedOSPlatform("windows")] private static async Task HandleWmi(WebSocket ws, ManagementScope scope, ConcurrentDictionary queries) { foreach (Query query in queries.Values) { - Console.WriteLine(query.value); - try { - await HandleWmiQuery(ws, scope, query.value); + await HandleWmiQuery(ws, scope, query); } catch { } } } [SupportedOSPlatform("windows")] - async private static Task HandleWmiQuery(WebSocket ws, ManagementScope scope, string query) { - using ManagementObjectCollection moc = new ManagementObjectSearcher(scope, new SelectQuery(query)).Get(); - Dictionary> dic = new Dictionary>(); + async private static Task HandleWmiQuery(WebSocket ws, ManagementScope scope, Query query) { + using ManagementObjectCollection moc = new ManagementObjectSearcher(scope, new SelectQuery(query.value)).Get(); + Dictionary> data = new Dictionary>(); foreach (ManagementObject o in moc.Cast()) { foreach (PropertyData p in o.Properties) { string name = p.Name.ToString(); string value = Protocols.Wmi.FormatProperty(p); - if (!dic.ContainsKey(name)) { - dic.Add(name, new List()); + if (!data.ContainsKey(name)) { + data.Add(name, new List()); } - Console.WriteLine(name + ": " + value); - - dic[name].Add(value); + data[name].Add(value); } } - string x = JsonSerializer.Serialize>>(dic); - byte[] bytes = JsonSerializer.SerializeToUtf8Bytes>>(dic); + Answer answer = new Answer() { + data = data, + index = query.index + }; + + string x = JsonSerializer.Serialize(answer, answerSerializerOptions); + byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(answer, answerSerializerOptions); Console.WriteLine(x); await WsWriteText(ws, bytes); - dic.Clear(); + data.Clear(); } private static void HandleSnmp(WebSocket ws, ConcurrentDictionary queries) { @@ -355,6 +377,78 @@ private static void HandleSnmpQuery(string query) { } } +file sealed class AnswerJsonConverter : JsonConverter { + public override Monitor.Answer Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + Monitor.Answer answer = new Monitor.Answer(); + + if (reader.TokenType != JsonTokenType.StartObject) { + throw new JsonException("Expected start of object"); + } + + reader.Read(); + + while (reader.TokenType == JsonTokenType.PropertyName) { + string propertyName = reader.GetString(); + + reader.Read(); + + switch (propertyName) { + case "index": + answer.index = reader.GetInt32(); + break; + + case "data": + reader.Read(); + while (reader.TokenType != JsonTokenType.EndObject) { + string key = reader.GetString(); + reader.Read(); + List values = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { + values.Add(reader.GetString()); + } + answer.data[key] = values; + + reader.Read(); + } + break; + + default: + break; + } + + reader.Read(); + } + + if (reader.TokenType != JsonTokenType.EndObject) { + throw new JsonException(); + } + + return answer; + } + + public override void Write(Utf8JsonWriter writer, Monitor.Answer value, JsonSerializerOptions options) { + ReadOnlySpan index = "index".AsSpan(); + ReadOnlySpan _data = "data".AsSpan(); + + writer.WriteStartObject(); + writer.WriteNumber(index, value.index); + + writer.WritePropertyName(_data); + writer.WriteStartObject(); + foreach (KeyValuePair> pair in value.data) { + writer.WritePropertyName(pair.Key.ToLower()); + writer.WriteStartArray(); + for (int i = 0; i < pair.Value.Count; i++) { + writer.WriteStringValue(pair.Value[i]); + } + writer.WriteEndArray(); + } + writer.WriteEndObject(); + + writer.WriteEndObject(); + } +} + file sealed class ActionJsonConverter : JsonConverter { public override Monitor.Query Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { Monitor.Query action = new Monitor.Query(); @@ -390,4 +484,4 @@ public override void Write(Utf8JsonWriter writer, Monitor.Query value, JsonSeria writer.WriteNumber(_index, value.index); writer.WriteEndObject(); } -} \ No newline at end of file +}