private void Run() { try { //Init Worker Thread LoadConfiguration(); //prepare datastructures Dictionary <string, List <LogTask> > requiredEventTypesToLogTasks = new Dictionary <string, List <LogTask> >(); foreach (LogTask l in _logTasks) { foreach (string s in l.EventPath) { if (!requiredEventTypesToLogTasks.ContainsKey(s)) { requiredEventTypesToLogTasks[s] = new List <LogTask>(); } requiredEventTypesToLogTasks[s].Add(l); } } var eventTypesToLastEvent = new Dictionary <string, DateTime>(); var eventTypesToMaxAge = new Dictionary <string, int>(); var eventTypesToNewEvents = new Dictionary <string, List <EventRecord> >(); var eventTypesToTimeFramedEvents = new Dictionary <string, List <EventRecord> >(); //load structure so that only required events are read foreach (string requiredEventType in requiredEventTypesToLogTasks.Keys) { eventTypesToLastEvent.Add(requiredEventType, DateTime.Now); eventTypesToMaxAge.Add(requiredEventType, 0); foreach (LogTask t in requiredEventTypesToLogTasks[requiredEventType]) { if (!t.OnlyNew && eventTypesToMaxAge[requiredEventType] < t.EventAge) { eventTypesToMaxAge[requiredEventType] = t.EventAge; } } } //start monitoring the logs while (true) { DateTime scanStart = DateTime.Now; if (_verbose) { Dump("Scanning the logs now, scanned logs are:", EventLogEntryType.Information, true); } DateTime referenceTimeForTimeFramedEvents = DateTime.Now; try { eventTypesToNewEvents.Clear(); eventTypesToTimeFramedEvents.Clear(); //first read all relevant events (events that are required by any of the tasks) foreach (string requiredEventType in requiredEventTypesToLogTasks.Keys) { eventTypesToNewEvents.Add(requiredEventType, new List <EventRecord>()); eventTypesToTimeFramedEvents.Add(requiredEventType, new List <EventRecord>()); var eventLogQuery = new EventLogQuery(requiredEventType, PathType.LogName) { ReverseDirection = true }; try { var eventLogReader = new EventLogReader(eventLogQuery); EventRecord r; while ((r = eventLogReader.ReadEvent()) != null) { if (!r.TimeCreated.HasValue) { continue; } bool canbreak = false; //fill new event list if (r.TimeCreated > eventTypesToLastEvent[requiredEventType]) { eventTypesToNewEvents[requiredEventType].Add(r); eventTypesToLastEvent[requiredEventType] = r.TimeCreated.Value; } else { canbreak = true; } //fill time framed event list if (r.TimeCreated > referenceTimeForTimeFramedEvents.Subtract(new TimeSpan(0, 0, eventTypesToMaxAge[requiredEventType]))) { eventTypesToTimeFramedEvents[requiredEventType].Add(r); } else if (canbreak) { break; } } } catch (EventLogNotFoundException) { Dump($"Event Log {requiredEventType} was not found, tasks that require these events will not work", EventLogEntryType.Warning); } } if (_verbose) { Dump($"Scanning finished in {DateTime.Now.Subtract(scanStart).TotalMilliseconds}[ms] ", EventLogEntryType.Information, true); } //then supply the events to the requesting tasks foreach (string key in requiredEventTypesToLogTasks.Keys) { foreach (LogTask t in requiredEventTypesToLogTasks[key]) { if (t.OnlyNew) { t.ProvideEvents(eventTypesToNewEvents[key]); } else { var eventsForThisTask = new List <EventRecord>(); foreach (EventRecord e in eventTypesToTimeFramedEvents[key]) { if (e.TimeCreated > referenceTimeForTimeFramedEvents.Subtract(new TimeSpan(0, 0, t.EventAge))) { eventsForThisTask.Add(e); } } if (_verbose) { Dump($"Provided {eventsForThisTask.Count} events for {t.Name}", EventLogEntryType.Information, true); } if (eventsForThisTask.Count > 0) { DateTime start = DateTime.Now; t.ProvideEvents(eventsForThisTask); if (DateTime.Now.Subtract(start).TotalMilliseconds > 500) { Dump($"Warning: Task {t.Name} takes a lot of resources. This can make your server vulnerable to DOS attacks. Try better boosters.", EventLogEntryType.Warning); } } } } } List <IPAddress> blackList = new List <IPAddress>(); //let the tasks poll which ips they want to have blocked / or permanently banned foreach (LogTask t in _logTasks) { if (t is IPBlockingLogTask ipTask) { foreach (IPAddress perma in ipTask.GetPermaBanVictims()) { SetPermanentBan(perma); } List <IPAddress> blockedIPs = ipTask.GetTempBanVictims(); if (_verbose) { Dump($"Polled {t.Name} and got {blockedIPs.Count} temporary and {_permaBannedIPs.Count} permanent ban(s)", EventLogEntryType.Information, true); } foreach (IPAddress blockedIP in blockedIPs) { if (!blackList.Contains(blockedIP)) { blackList.Add(blockedIP); } } } } if (_verbose) { Dump($"\r\n-----Cycle complete, sleeping {_eventLogInterval / 1000} s......\r\n", EventLogEntryType.Information); } _lastPolledTempBans = blackList; PushBanList(); } catch (Exception executionException) { Dump(executionException, EventLogEntryType.Error); } //wait for next iteration or kill signal try { Thread.Sleep(_eventLogInterval); } catch (ThreadInterruptedException) { } //check if need to terminate bool terminate; lock (_syncObject) { terminate = _stop; } if (terminate) { try { FirewallAPI.ClearIPBanList(); } catch (Exception ex) { Dump(ex, EventLogEntryType.Warning); } return; } } } catch (Exception e) { Dump(e, EventLogEntryType.Error); Stop(); } }
/// <summary> /// Pushes the current ban list down into the system API /// </summary> private void PushBanList() { lock (_syncObject) { List <IPAddress> banList = new List <IPAddress>(); if (_lastPolledTempBans != null) { foreach (IPAddress a in _lastPolledTempBans) { if (!banList.Contains(a)) { banList.Add(a); } } } if (_permaBannedIPs != null) { foreach (IPAddress p in _permaBannedIPs) { if (!banList.Contains(p)) { banList.Add(p); } } } List <IPAddress> unbanned = new List <IPAddress>(); foreach (IPAddress i in banList) { foreach (string pattern in _whiteListPatterns) { if (IsPatternMatch(i, pattern) && !unbanned.Contains(i)) { unbanned.Add(i); } } } foreach (IPAddress u in unbanned) { banList.Remove(u); } FirewallAPI.AdjustIPBanList(banList); foreach (IPAddress ip in _lastBannedIPs) { if (!banList.Contains(ip)) { Dump($"Removed {ip} from the ban list", EventLogEntryType.Information); } } foreach (IPAddress ip in banList) { if (!_lastBannedIPs.Contains(ip)) { Dump($"Banned {ip}", EventLogEntryType.Information); } } _lastBannedIPs = banList; } }