using Insight.Agent.Messages; using Insight.Agent.Network; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Diagnostics.Eventing.Reader; using System.Runtime.Versioning; using System.Threading.Channels; using Vaitr.Network; using static Insight.Agent.Messages.Event; using EventLevel = System.Diagnostics.Tracing.EventLevel; namespace Insight.Agent.Services { [SupportedOSPlatform("windows")] internal class EventService : BackgroundService { private readonly Channel _queue; private readonly TcpSessionPool _pool; private readonly ILogger _logger; public EventService(TcpSessionPool pool, ILogger logger) { _pool = pool; _logger = logger; _queue = Channel.CreateBounded(new BoundedChannelOptions(1000) { SingleReader = true, SingleWriter = true, AllowSynchronousContinuations = false, FullMode = BoundedChannelFullMode.DropOldest }); } protected override async Task ExecuteAsync(CancellationToken cancellationToken) { _logger.LogTrace("ExecuteAsync"); while (cancellationToken.IsCancellationRequested is false) { try { var tasks = new List { HandleQueueAsync(cancellationToken), WatchAsync("Application", "*", cancellationToken), WatchAsync("Security", "*", cancellationToken), WatchAsync("System", "*", cancellationToken), WatchAsync("Microsoft-Windows-PrintService/Admin", "*", cancellationToken), WatchAsync("Microsoft-Windows-TaskScheduler/Operational", "*", cancellationToken), WatchAsync("Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational", "*", cancellationToken), WatchAsync("Microsoft-Windows-TerminalServices-LocalSessionManager/Operational", "*", cancellationToken), WatchAsync("Microsoft-Windows-TerminalServices-RDPClient/Operational", "*", cancellationToken), WatchAsync("Microsoft-Windows-SmbClient/Connectivity", "*", cancellationToken), WatchAsync("Microsoft-Windows-SmbClient/Security", "*", cancellationToken), WatchAsync("Microsoft-Windows-SMBServer/Security", "*", cancellationToken), WatchAsync("Microsoft-Windows-StorageSpaces-Driver/Operational", "*", cancellationToken), WatchAsync("Microsoft-Windows-Diagnostics-Performance/Operational", "*", cancellationToken) }; await Task.WhenAll(tasks); } catch (OperationCanceledException) { } catch (Exception ex) { _logger.LogError("{ex}", ex); } } } private async Task WatchAsync(string path, string query, CancellationToken cancellationToken) { var config = new EventLogConfiguration(path); if (config is null) return; if (config.IsEnabled is false) { config.IsEnabled = true; config.SaveChanges(); } var watcher = new EventLogWatcher(new EventLogQuery(path, PathType.LogName, query) { TolerateQueryErrors = true, Session = EventLogSession.GlobalSession }); try { watcher.EventRecordWritten += new EventHandler(OnEvent); watcher.Enabled = true; using var semaphore = new SemaphoreSlim(0, 1); await semaphore.WaitAsync(cancellationToken); } catch (Exception) { } finally { watcher.EventRecordWritten -= new EventHandler(OnEvent); watcher.Enabled = false; watcher?.Dispose(); } } private async Task HandleQueueAsync(CancellationToken cancellationToken) { while (await _queue.Reader.WaitToReadAsync(cancellationToken)) { var session = _pool.FirstOrDefault(); if (session.Value is null) { await Task.Delay(10000, cancellationToken); continue; } if (_queue.Reader.TryRead(out var item) is false) { await Task.Delay(1000, cancellationToken); continue; } try { await session.Value.SendAsync(item, cancellationToken); } catch (OperationCanceledException) { } catch (Exception ex) { _logger.LogError("{ex}", ex); await Task.Delay(10000, cancellationToken); } } } private void OnEvent(object? sender, EventRecordWrittenEventArgs e) { if (e is null || e.EventRecord is null) return; try { var @event = new Event { Timestamp = e?.EventRecord?.TimeCreated, EventId = e?.EventRecord?.Id, Source = e?.EventRecord?.ProviderName, Category = e?.EventRecord?.LogName, Task = e?.EventRecord?.TaskDisplayName, Message = e?.EventRecord?.FormatDescription(), }; if (e?.EventRecord?.Level is not null) { @event.Status = (EventLevel)Convert.ToInt32(e?.EventRecord?.Level) switch { EventLevel.Informational => StatusType.Information, EventLevel.Warning => StatusType.Warning, EventLevel.Error => StatusType.Error, EventLevel.Critical => StatusType.Critical, _ => StatusType.Information, }; } _queue.Writer.WriteAsync(@event, default); } catch (Exception) { } // app crash } } }