From 5d04668faa68bb5ed2129bfce4991f699306066c Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sat, 24 Dec 2022 11:41:49 +0530 Subject: [PATCH] Failover: Removed newtonsoft. Added retries for send alert email. --- Apps/FailoverApp/Address.cs | 77 +++++++++---------- Apps/FailoverApp/CNAME.cs | 51 ++++++------ Apps/FailoverApp/EmailAlert.cs | 99 ++++++++++-------------- Apps/FailoverApp/HealthCheck.cs | 65 ++++------------ Apps/FailoverApp/HealthService.cs | 89 ++++++++------------- Apps/FailoverApp/WebHook.cs | 124 +++++++++--------------------- 6 files changed, 189 insertions(+), 316 deletions(-) diff --git a/Apps/FailoverApp/Address.cs b/Apps/FailoverApp/Address.cs index cda6b414..cf60fd9e 100644 --- a/Apps/FailoverApp/Address.cs +++ b/Apps/FailoverApp/Address.cs @@ -18,11 +18,11 @@ along with this program. If not, see . */ using DnsServerCore.ApplicationCommon; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; +using System.Text.Json; using System.Threading.Tasks; using TechnitiumLibrary; using TechnitiumLibrary.Net.Dns; @@ -64,17 +64,14 @@ namespace Failover #region private - private void GetAnswers(dynamic jsonAddresses, DnsQuestionRecord question, uint appRecordTtl, string healthCheck, Uri healthCheckUrl, List answers) + private void GetAnswers(JsonElement jsonAddresses, DnsQuestionRecord question, uint appRecordTtl, string healthCheck, Uri healthCheckUrl, List answers) { - if (jsonAddresses == null) - return; - switch (question.Type) { case DnsResourceRecordType.A: - foreach (dynamic jsonAddress in jsonAddresses) + foreach (JsonElement jsonAddress in jsonAddresses.EnumerateArray()) { - IPAddress address = IPAddress.Parse(jsonAddress.Value); + IPAddress address = IPAddress.Parse(jsonAddress.GetString()); if (address.AddressFamily == AddressFamily.InterNetwork) { @@ -94,9 +91,9 @@ namespace Failover break; case DnsResourceRecordType.AAAA: - foreach (dynamic jsonAddress in jsonAddresses) + foreach (JsonElement jsonAddress in jsonAddresses.EnumerateArray()) { - IPAddress address = IPAddress.Parse(jsonAddress.Value); + IPAddress address = IPAddress.Parse(jsonAddress.GetString()); if (address.AddressFamily == AddressFamily.InterNetworkV6) { @@ -117,14 +114,11 @@ namespace Failover } } - private void GetStatusAnswers(dynamic jsonAddresses, FailoverType type, DnsQuestionRecord question, uint appRecordTtl, string healthCheck, Uri healthCheckUrl, List answers) + private void GetStatusAnswers(JsonElement jsonAddresses, FailoverType type, DnsQuestionRecord question, uint appRecordTtl, string healthCheck, Uri healthCheckUrl, List answers) { - if (jsonAddresses == null) - return; - - foreach (dynamic jsonAddress in jsonAddresses) + foreach (JsonElement jsonAddress in jsonAddresses.EnumerateArray()) { - IPAddress address = IPAddress.Parse(jsonAddress.Value); + IPAddress address = IPAddress.Parse(jsonAddress.GetString()); HealthCheckResponse response = _healthService.QueryStatus(address, healthCheck, healthCheckUrl, false); string text = "app=failover; addressType=" + type.ToString() + "; address=" + address.ToString() + "; healthCheck=" + healthCheck + (healthCheckUrl is null ? "" : "; healthCheckUrl=" + healthCheckUrl.AbsoluteUri) + "; healthStatus=" + response.Status.ToString() + ";"; @@ -145,7 +139,7 @@ namespace Failover if (_healthService is null) _healthService = HealthService.Create(dnsServer); - _healthService.Initialize(JsonConvert.DeserializeObject(config)); + _healthService.Initialize(config); return Task.CompletedTask; } @@ -158,17 +152,18 @@ namespace Failover case DnsResourceRecordType.A: case DnsResourceRecordType.AAAA: { - dynamic jsonAppRecordData = JsonConvert.DeserializeObject(appRecordData); + using JsonDocument jsonDocument = JsonDocument.Parse(appRecordData); + JsonElement jsonAppRecordData = jsonDocument.RootElement; - string healthCheck = jsonAppRecordData.healthCheck?.Value; + string healthCheck = jsonAppRecordData.GetPropertyValue("healthCheck", null); Uri healthCheckUrl = null; if (_healthService.HealthChecks.TryGetValue(healthCheck, out HealthCheck hc) && ((hc.Type == HealthCheckType.Https) || (hc.Type == HealthCheckType.Http)) && (hc.Url is null)) { //read health check url only for http/https type checks and only when app config does not have an url configured - if ((jsonAppRecordData.healthCheckUrl is not null) && (jsonAppRecordData.healthCheckUrl.Value is not null)) + if (jsonAppRecordData.TryGetProperty("healthCheckUrl", out JsonElement jsonHealthCheckUrl) && (jsonHealthCheckUrl.ValueKind != JsonValueKind.Null)) { - healthCheckUrl = new Uri(jsonAppRecordData.healthCheckUrl.Value); + healthCheckUrl = new Uri(jsonHealthCheckUrl.GetString()); } else { @@ -181,19 +176,23 @@ namespace Failover List answers = new List(); - GetAnswers(jsonAppRecordData.primary, question, appRecordTtl, healthCheck, healthCheckUrl, answers); + if (jsonAppRecordData.TryGetProperty("primary", out JsonElement jsonPrimary)) + GetAnswers(jsonPrimary, question, appRecordTtl, healthCheck, healthCheckUrl, answers); + if (answers.Count == 0) { - GetAnswers(jsonAppRecordData.secondary, question, appRecordTtl, healthCheck, healthCheckUrl, answers); + if (jsonAppRecordData.TryGetProperty("secondary", out JsonElement jsonSecondary)) + GetAnswers(jsonSecondary, question, appRecordTtl, healthCheck, healthCheckUrl, answers); + if (answers.Count == 0) { - if (jsonAppRecordData.serverDown is not null) + if (jsonAppRecordData.TryGetProperty("serverDown", out JsonElement jsonServerDown)) { if (question.Type == DnsResourceRecordType.A) { - foreach (dynamic jsonAddress in jsonAppRecordData.serverDown) + foreach (JsonElement jsonAddress in jsonServerDown.EnumerateArray()) { - IPAddress address = IPAddress.Parse(jsonAddress.Value); + IPAddress address = IPAddress.Parse(jsonAddress.GetString()); if (address.AddressFamily == AddressFamily.InterNetwork) answers.Add(new DnsResourceRecord(question.Name, DnsResourceRecordType.A, question.Class, 30, new DnsARecordData(address))); @@ -201,9 +200,9 @@ namespace Failover } else { - foreach (dynamic jsonAddress in jsonAppRecordData.serverDown) + foreach (JsonElement jsonAddress in jsonServerDown.EnumerateArray()) { - IPAddress address = IPAddress.Parse(jsonAddress.Value); + IPAddress address = IPAddress.Parse(jsonAddress.GetString()); if (address.AddressFamily == AddressFamily.InterNetworkV6) answers.Add(new DnsResourceRecord(question.Name, DnsResourceRecordType.AAAA, question.Class, 30, new DnsAAAARecordData(address))); @@ -224,27 +223,22 @@ namespace Failover case DnsResourceRecordType.TXT: { - dynamic jsonAppRecordData = JsonConvert.DeserializeObject(appRecordData); - - bool allowTxtStatus; - - if (jsonAppRecordData.allowTxtStatus == null) - allowTxtStatus = false; - else - allowTxtStatus = jsonAppRecordData.allowTxtStatus.Value; + using JsonDocument jsonDocument = JsonDocument.Parse(appRecordData); + JsonElement jsonAppRecordData = jsonDocument.RootElement; + bool allowTxtStatus = jsonAppRecordData.GetPropertyValue("allowTxtStatus", false); if (!allowTxtStatus) return Task.FromResult(null); - string healthCheck = jsonAppRecordData.healthCheck?.Value; + string healthCheck = jsonAppRecordData.GetPropertyValue("healthCheck", null); Uri healthCheckUrl = null; if (_healthService.HealthChecks.TryGetValue(healthCheck, out HealthCheck hc) && ((hc.Type == HealthCheckType.Https) || (hc.Type == HealthCheckType.Http)) && (hc.Url is null)) { //read health check url only for http/https type checks and only when app config does not have an url configured - if ((jsonAppRecordData.healthCheckUrl is not null) && (jsonAppRecordData.healthCheckUrl.Value is not null)) + if (jsonAppRecordData.TryGetProperty("healthCheckUrl", out JsonElement jsonHealthCheckUrl) && (jsonHealthCheckUrl.ValueKind != JsonValueKind.Null)) { - healthCheckUrl = new Uri(jsonAppRecordData.healthCheckUrl.Value); + healthCheckUrl = new Uri(jsonHealthCheckUrl.GetString()); } else { @@ -257,8 +251,11 @@ namespace Failover List answers = new List(); - GetStatusAnswers(jsonAppRecordData.primary, FailoverType.Primary, question, 30, healthCheck, healthCheckUrl, answers); - GetStatusAnswers(jsonAppRecordData.secondary, FailoverType.Secondary, question, 30, healthCheck, healthCheckUrl, answers); + if (jsonAppRecordData.TryGetProperty("primary", out JsonElement jsonPrimary)) + GetStatusAnswers(jsonPrimary, FailoverType.Primary, question, 30, healthCheck, healthCheckUrl, answers); + + if (jsonAppRecordData.TryGetProperty("secondary", out JsonElement jsonSecondary)) + GetStatusAnswers(jsonSecondary, FailoverType.Secondary, question, 30, healthCheck, healthCheckUrl, answers); return Task.FromResult(new DnsDatagram(request.Identifier, true, request.OPCODE, true, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NoError, request.Question, answers)); } diff --git a/Apps/FailoverApp/CNAME.cs b/Apps/FailoverApp/CNAME.cs index e6dcd076..785b7e43 100644 --- a/Apps/FailoverApp/CNAME.cs +++ b/Apps/FailoverApp/CNAME.cs @@ -18,11 +18,12 @@ along with this program. If not, see . */ using DnsServerCore.ApplicationCommon; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Net; +using System.Text.Json; using System.Threading.Tasks; +using TechnitiumLibrary; using TechnitiumLibrary.Net.Dns; using TechnitiumLibrary.Net.Dns.ResourceRecords; @@ -119,17 +120,18 @@ namespace Failover { DnsQuestionRecord question = request.Question[0]; - dynamic jsonAppRecordData = JsonConvert.DeserializeObject(appRecordData); + using JsonDocument jsonDocument = JsonDocument.Parse(appRecordData); + JsonElement jsonAppRecordData = jsonDocument.RootElement; - string healthCheck = jsonAppRecordData.healthCheck?.Value; + string healthCheck = jsonAppRecordData.GetPropertyValue("healthCheck", null); Uri healthCheckUrl = null; if (_healthService.HealthChecks.TryGetValue(healthCheck, out HealthCheck hc) && ((hc.Type == HealthCheckType.Https) || (hc.Type == HealthCheckType.Http)) && (hc.Url is null)) { //read health check url only for http/https type checks and only when app config does not have an url configured - if ((jsonAppRecordData.healthCheckUrl is not null) && (jsonAppRecordData.healthCheckUrl.Value is not null)) + if (jsonAppRecordData.TryGetProperty("healthCheckUrl", out JsonElement jsonHealthCheckUrl) && (jsonHealthCheckUrl.ValueKind != JsonValueKind.Null)) { - healthCheckUrl = new Uri(jsonAppRecordData.healthCheckUrl.Value); + healthCheckUrl = new Uri(jsonHealthCheckUrl.GetString()); } else { @@ -140,47 +142,50 @@ namespace Failover } } - IReadOnlyList answers; + IReadOnlyList answers = null; if (question.Type == DnsResourceRecordType.TXT) { - bool allowTxtStatus; - - if (jsonAppRecordData.allowTxtStatus == null) - allowTxtStatus = false; - else - allowTxtStatus = jsonAppRecordData.allowTxtStatus.Value; - + bool allowTxtStatus = jsonAppRecordData.GetPropertyValue("allowTxtStatus", false); if (!allowTxtStatus) return Task.FromResult(null); List txtAnswers = new List(); - GetStatusAnswers(jsonAppRecordData.primary.Value, FailoverType.Primary, question, 30, healthCheck, healthCheckUrl, txtAnswers); + if (jsonAppRecordData.TryGetProperty("primary", out JsonElement jsonPrimary)) + GetStatusAnswers(jsonPrimary.GetString(), FailoverType.Primary, question, 30, healthCheck, healthCheckUrl, txtAnswers); - foreach (dynamic jsonDomain in jsonAppRecordData.secondary) - GetStatusAnswers(jsonDomain.Value, FailoverType.Secondary, question, 30, healthCheck, healthCheckUrl, txtAnswers); + if (jsonAppRecordData.TryGetProperty("secondary", out JsonElement jsonSecondary)) + { + foreach (JsonElement jsonDomain in jsonSecondary.EnumerateArray()) + GetStatusAnswers(jsonDomain.GetString(), FailoverType.Secondary, question, 30, healthCheck, healthCheckUrl, txtAnswers); + } answers = txtAnswers; } else { - answers = GetAnswers(jsonAppRecordData.primary.Value, question, zoneName, appRecordTtl, healthCheck, healthCheckUrl); + if (jsonAppRecordData.TryGetProperty("primary", out JsonElement jsonPrimary)) + answers = GetAnswers(jsonPrimary.GetString(), question, zoneName, appRecordTtl, healthCheck, healthCheckUrl); + if (answers is null) { - foreach (dynamic jsonDomain in jsonAppRecordData.secondary) + if (jsonAppRecordData.TryGetProperty("secondary", out JsonElement jsonSecondary)) { - answers = GetAnswers(jsonDomain.Value, question, zoneName, appRecordTtl, healthCheck, healthCheckUrl); - if (answers is not null) - break; + foreach (JsonElement jsonDomain in jsonSecondary.EnumerateArray()) + { + answers = GetAnswers(jsonDomain.GetString(), question, zoneName, appRecordTtl, healthCheck, healthCheckUrl); + if (answers is not null) + break; + } } if (answers is null) { - if ((jsonAppRecordData.serverDown is null) || (jsonAppRecordData.serverDown.Value is null)) + if (!jsonAppRecordData.TryGetProperty("serverDown", out JsonElement jsonServerDown) || (jsonServerDown.ValueKind == JsonValueKind.Null)) return Task.FromResult(null); - string serverDown = jsonAppRecordData.serverDown.Value; + string serverDown = jsonServerDown.GetString(); if (question.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase)) //check for zone apex answers = new DnsResourceRecord[] { new DnsResourceRecord(question.Name, DnsResourceRecordType.ANAME, DnsClass.IN, 30, new DnsANAMERecordData(serverDown)) }; //use ANAME diff --git a/Apps/FailoverApp/EmailAlert.cs b/Apps/FailoverApp/EmailAlert.cs index dea45264..d45c8d56 100644 --- a/Apps/FailoverApp/EmailAlert.cs +++ b/Apps/FailoverApp/EmailAlert.cs @@ -23,8 +23,10 @@ using System.Collections.Generic; using System.Net; using System.Net.Mail; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using TechnitiumLibrary; using TechnitiumLibrary.Net.Dns; using TechnitiumLibrary.Net.Dns.ResourceRecords; using TechnitiumLibrary.Net.Mail; @@ -37,7 +39,7 @@ namespace Failover readonly HealthService _service; - string _name; + readonly string _name; bool _enabled; MailAddress[] _alertTo; string _smtpServer; @@ -54,13 +56,15 @@ namespace Failover #region constructor - public EmailAlert(HealthService service, dynamic jsonEmailAlert) + public EmailAlert(HealthService service, JsonElement jsonEmailAlert) { _service = service; _smtpClient = new SmtpClientEx(); _smtpClient.DnsClient = new DnsClientInternal(_service.DnsServer); + _name = jsonEmailAlert.GetPropertyValue("name", "default"); + Reload(jsonEmailAlert); } @@ -98,7 +102,24 @@ namespace Failover { try { - await _smtpClient.SendMailAsync(message); + const int MAX_RETRIES = 3; + const int WAIT_INTERVAL = 30000; + + for (int retries = 0; retries < MAX_RETRIES; retries++) + { + try + { + await _smtpClient.SendMailAsync(message); + break; + } + catch + { + if (retries == MAX_RETRIES - 1) + throw; + + await Task.Delay(WAIT_INTERVAL); + } + } } catch (Exception ex) { @@ -110,71 +131,33 @@ namespace Failover #region public - public void Reload(dynamic jsonEmailAlert) + public void Reload(JsonElement jsonEmailAlert) { - if (jsonEmailAlert.name is null) - _name = "default"; - else - _name = jsonEmailAlert.name.Value; + _enabled = jsonEmailAlert.GetPropertyValue("enabled", false); - if (jsonEmailAlert.enabled is null) - _enabled = false; + if (jsonEmailAlert.TryReadArray("alertTo", delegate (string emailAddress) { return new MailAddress(emailAddress); }, out MailAddress[] alertTo)) + _alertTo = alertTo; else - _enabled = jsonEmailAlert.enabled.Value; - - if (jsonEmailAlert.alertTo is null) - { _alertTo = null; - } - else + + _smtpServer = jsonEmailAlert.GetPropertyValue("smtpServer", null); + _smtpPort = jsonEmailAlert.GetPropertyValue("smtpPort", 25); + _startTls = jsonEmailAlert.GetPropertyValue("startTls", false); + _smtpOverTls = jsonEmailAlert.GetPropertyValue("smtpOverTls", false); + _username = jsonEmailAlert.GetPropertyValue("username", null); + _password = jsonEmailAlert.GetPropertyValue("password", null); + + if (jsonEmailAlert.TryGetProperty("mailFrom", out JsonElement jsonMailFrom)) { - _alertTo = new MailAddress[jsonEmailAlert.alertTo.Count]; - - for (int i = 0; i < _alertTo.Length; i++) - _alertTo[i] = new MailAddress(jsonEmailAlert.alertTo[i].Value); + if (jsonEmailAlert.TryGetProperty("mailFromName", out JsonElement jsonMailFromName)) + _mailFrom = new MailAddress(jsonMailFrom.GetString(), jsonMailFromName.GetString(), Encoding.UTF8); + else + _mailFrom = new MailAddress(jsonMailFrom.GetString()); } - - if (jsonEmailAlert.smtpServer is null) - _smtpServer = null; else - _smtpServer = jsonEmailAlert.smtpServer.Value; - - if (jsonEmailAlert.smtpPort is null) - _smtpPort = 25; - else - _smtpPort = Convert.ToInt32(jsonEmailAlert.smtpPort.Value); - - if (jsonEmailAlert.startTls is null) - _startTls = false; - else - _startTls = jsonEmailAlert.startTls.Value; - - if (jsonEmailAlert.smtpOverTls is null) - _smtpOverTls = false; - else - _smtpOverTls = jsonEmailAlert.smtpOverTls.Value; - - if (jsonEmailAlert.username is null) - _username = null; - else - _username = jsonEmailAlert.username.Value; - - if (jsonEmailAlert.password is null) - _password = null; - else - _password = jsonEmailAlert.password.Value; - - if (jsonEmailAlert.mailFrom is null) { _mailFrom = null; } - else - { - if (jsonEmailAlert.mailFromName is null) - _mailFrom = new MailAddress(jsonEmailAlert.mailFrom.Value); - else - _mailFrom = new MailAddress(jsonEmailAlert.mailFrom.Value, jsonEmailAlert.mailFromName.Value, Encoding.UTF8); - } //update smtp client settings _smtpClient.Host = _smtpServer; diff --git a/Apps/FailoverApp/HealthCheck.cs b/Apps/FailoverApp/HealthCheck.cs index ce202abd..85c02a2d 100644 --- a/Apps/FailoverApp/HealthCheck.cs +++ b/Apps/FailoverApp/HealthCheck.cs @@ -23,6 +23,7 @@ using System.Net; using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Text.Json; using System.Threading.Tasks; using TechnitiumLibrary; using TechnitiumLibrary.Net; @@ -49,7 +50,7 @@ namespace Failover readonly HealthService _service; - string _name; + readonly string _name; HealthCheckType _type; int _interval; int _retries; @@ -66,10 +67,12 @@ namespace Failover #region constructor - public HealthCheck(HealthService service, dynamic jsonHealthCheck) + public HealthCheck(HealthService service, JsonElement jsonHealthCheck) { _service = service; + _name = jsonHealthCheck.GetPropertyValue("name", "default"); + Reload(jsonHealthCheck); } @@ -200,63 +203,25 @@ namespace Failover #region public - public void Reload(dynamic jsonHealthCheck) + public void Reload(JsonElement jsonHealthCheck) { - if (jsonHealthCheck.name is null) - _name = "default"; - else - _name = jsonHealthCheck.name.Value; + _type = Enum.Parse(jsonHealthCheck.GetPropertyValue("type", "Tcp"), true); + _interval = jsonHealthCheck.GetPropertyValue("interval", 60) * 1000; + _retries = jsonHealthCheck.GetPropertyValue("retries", 3); + _timeout = jsonHealthCheck.GetPropertyValue("timeout", 10) * 1000; + _port = jsonHealthCheck.GetPropertyValue("port", 80); - if (jsonHealthCheck.type == null) - _type = HealthCheckType.Tcp; + if (jsonHealthCheck.TryGetProperty("url", out JsonElement jsonUrl) && (jsonUrl.ValueKind != JsonValueKind.Null)) + _url = new Uri(jsonUrl.GetString()); else - _type = Enum.Parse(jsonHealthCheck.type.Value, true); - - if (jsonHealthCheck.interval is null) - _interval = 60000; - else - _interval = Convert.ToInt32(jsonHealthCheck.interval.Value) * 1000; - - if (jsonHealthCheck.retries is null) - _retries = 3; - else - _retries = Convert.ToInt32(jsonHealthCheck.retries.Value); - - if (jsonHealthCheck.timeout is null) - _timeout = 10000; - else - _timeout = Convert.ToInt32(jsonHealthCheck.timeout.Value) * 1000; - - if (jsonHealthCheck.port is null) - _port = 80; - else - _port = Convert.ToInt32(jsonHealthCheck.port.Value); - - if ((jsonHealthCheck.url is null) || (jsonHealthCheck.url.Value is null)) _url = null; - else - _url = new Uri(jsonHealthCheck.url.Value); - string emailAlertName; - - if (jsonHealthCheck.emailAlert is null) - emailAlertName = null; - else - emailAlertName = jsonHealthCheck.emailAlert.Value; - - if ((emailAlertName is not null) && _service.EmailAlerts.TryGetValue(emailAlertName, out EmailAlert emailAlert)) + if (jsonHealthCheck.TryGetProperty("emailAlert", out JsonElement jsonEmailAlert) && _service.EmailAlerts.TryGetValue(jsonEmailAlert.GetString(), out EmailAlert emailAlert)) _emailAlert = emailAlert; else _emailAlert = null; - string webHookName; - - if (jsonHealthCheck.webHook is null) - webHookName = null; - else - webHookName = jsonHealthCheck.webHook.Value; - - if ((webHookName is not null) && _service.WebHooks.TryGetValue(webHookName, out WebHook webHook)) + if (jsonHealthCheck.TryGetProperty("webHook", out JsonElement jsonWebHook) && _service.WebHooks.TryGetValue(jsonWebHook.GetString(), out WebHook webHook)) _webHook = webHook; else _webHook = null; diff --git a/Apps/FailoverApp/HealthService.cs b/Apps/FailoverApp/HealthService.cs index adf55513..67b26511 100644 --- a/Apps/FailoverApp/HealthService.cs +++ b/Apps/FailoverApp/HealthService.cs @@ -1,6 +1,6 @@ /* Technitium DNS Server -Copyright (C) 2021 Shreyas Zare (shreyas@technitium.com) +Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,9 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net; +using System.Text.Json; using System.Threading; +using TechnitiumLibrary; using TechnitiumLibrary.Net; using TechnitiumLibrary.Net.Dns.ResourceRecords; @@ -178,19 +180,19 @@ namespace Failover #region public - public void Initialize(dynamic jsonConfig) + public void Initialize(string config) { + using JsonDocument jsonDocument = JsonDocument.Parse(config); + JsonElement jsonConfig = jsonDocument.RootElement; + //email alerts { - //add or update email alerts - foreach (dynamic jsonEmailAlert in jsonConfig.emailAlerts) - { - string name; + JsonElement jsonEmailAlerts = jsonConfig.GetProperty("emailAlerts"); - if (jsonEmailAlert.name is null) - name = "default"; - else - name = jsonEmailAlert.name.Value; + //add or update email alerts + foreach (JsonElement jsonEmailAlert in jsonEmailAlerts.EnumerateArray()) + { + string name = jsonEmailAlert.GetPropertyValue("name", "default"); if (_emailAlerts.TryGetValue(name, out EmailAlert existingEmailAlert)) { @@ -201,7 +203,6 @@ namespace Failover { //add EmailAlert emailAlert = new EmailAlert(this, jsonEmailAlert); - _emailAlerts.TryAdd(emailAlert.Name, emailAlert); } } @@ -211,15 +212,9 @@ namespace Failover { bool emailAlertExists = false; - foreach (dynamic jsonEmailAlert in jsonConfig.emailAlerts) + foreach (JsonElement jsonEmailAlert in jsonEmailAlerts.EnumerateArray()) { - string name; - - if (jsonEmailAlert.name is null) - name = "default"; - else - name = jsonEmailAlert.name.Value; - + string name = jsonEmailAlert.GetPropertyValue("name", "default"); if (name == emailAlert.Key) { emailAlertExists = true; @@ -237,15 +232,12 @@ namespace Failover //web hooks { - //add or update email alerts - foreach (dynamic jsonWebHook in jsonConfig.webHooks) - { - string name; + JsonElement jsonWebHooks = jsonConfig.GetProperty("webHooks"); - if (jsonWebHook.name is null) - name = "default"; - else - name = jsonWebHook.name.Value; + //add or update email alerts + foreach (JsonElement jsonWebHook in jsonWebHooks.EnumerateArray()) + { + string name = jsonWebHook.GetPropertyValue("name", "default"); if (_webHooks.TryGetValue(name, out WebHook existingWebHook)) { @@ -256,7 +248,6 @@ namespace Failover { //add WebHook webHook = new WebHook(this, jsonWebHook); - _webHooks.TryAdd(webHook.Name, webHook); } } @@ -266,15 +257,9 @@ namespace Failover { bool webHookExists = false; - foreach (dynamic jsonWebHook in jsonConfig.webHooks) + foreach (JsonElement jsonWebHook in jsonWebHooks.EnumerateArray()) { - string name; - - if (jsonWebHook.name is null) - name = "default"; - else - name = jsonWebHook.name.Value; - + string name = jsonWebHook.GetPropertyValue("name", "default"); if (name == webHook.Key) { webHookExists = true; @@ -292,15 +277,12 @@ namespace Failover //health checks { - //add or update health checks - foreach (dynamic jsonHealthCheck in jsonConfig.healthChecks) - { - string name; + JsonElement jsonHealthChecks = jsonConfig.GetProperty("healthChecks"); - if (jsonHealthCheck.name is null) - name = "default"; - else - name = jsonHealthCheck.name.Value; + //add or update health checks + foreach (JsonElement jsonHealthCheck in jsonHealthChecks.EnumerateArray()) + { + string name = jsonHealthCheck.GetPropertyValue("name", "default"); if (_healthChecks.TryGetValue(name, out HealthCheck existingHealthCheck)) { @@ -311,7 +293,6 @@ namespace Failover { //add HealthCheck healthCheck = new HealthCheck(this, jsonHealthCheck); - _healthChecks.TryAdd(healthCheck.Name, healthCheck); } } @@ -321,15 +302,9 @@ namespace Failover { bool healthCheckExists = false; - foreach (dynamic jsonHealthCheck in jsonConfig.healthChecks) + foreach (JsonElement jsonHealthCheck in jsonHealthChecks.EnumerateArray()) { - string name; - - if (jsonHealthCheck.name is null) - name = "default"; - else - name = jsonHealthCheck.name.Value; - + string name = jsonHealthCheck.GetPropertyValue("name", "default"); if (name == healthCheck.Key) { healthCheckExists = true; @@ -353,12 +328,12 @@ namespace Failover //under maintenance networks _underMaintenance.Clear(); - if (jsonConfig.underMaintenance is not null) + if (jsonConfig.TryGetProperty("underMaintenance", out JsonElement jsonUnderMaintenance)) { - foreach (dynamic jsonNetwork in jsonConfig.underMaintenance) + foreach (JsonElement jsonNetwork in jsonUnderMaintenance.EnumerateArray()) { - string network = jsonNetwork.network.Value; - bool enable = jsonNetwork.enable.Value; + string network = jsonNetwork.GetProperty("network").GetString(); + bool enable = jsonNetwork.GetProperty("enable").GetBoolean(); _underMaintenance.TryAdd(NetworkAddress.Parse(network), enable); } diff --git a/Apps/FailoverApp/WebHook.cs b/Apps/FailoverApp/WebHook.cs index 7aecc7f0..5356c972 100644 --- a/Apps/FailoverApp/WebHook.cs +++ b/Apps/FailoverApp/WebHook.cs @@ -17,14 +17,15 @@ along with this program. If not, see . */ -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Text.Json; using System.Threading.Tasks; +using TechnitiumLibrary; using TechnitiumLibrary.Net.Dns.ResourceRecords; using TechnitiumLibrary.Net.Proxy; @@ -36,7 +37,7 @@ namespace Failover readonly HealthService _service; - string _name; + readonly string _name; bool _enabled; Uri[] _urls; @@ -47,10 +48,12 @@ namespace Failover #region constructor - public WebHook(HealthService service, dynamic jsonWebHook) + public WebHook(HealthService service, JsonElement jsonWebHook) { _service = service; + _name = jsonWebHook.GetPropertyValue("name", "default"); + Reload(jsonWebHook); } @@ -176,29 +179,14 @@ namespace Failover #region public - public void Reload(dynamic jsonWebHook) + public void Reload(JsonElement jsonWebHook) { - if (jsonWebHook.name is null) - _name = "default"; - else - _name = jsonWebHook.name.Value; + _enabled = jsonWebHook.GetPropertyValue("enabled", false); - if (jsonWebHook.enabled is null) - _enabled = false; + if (jsonWebHook.TryReadArray("urls", delegate (string uri) { return new Uri(uri); }, out Uri[] urls)) + _urls = urls; else - _enabled = jsonWebHook.enabled.Value; - - if (jsonWebHook.urls is null) - { _urls = null; - } - else - { - _urls = new Uri[jsonWebHook.urls.Count]; - - for (int i = 0; i < _urls.Length; i++) - _urls[i] = new Uri(jsonWebHook.urls[i].Value); - } ConditionalHttpReload(); } @@ -212,26 +200,17 @@ namespace Failover { using (MemoryStream mS = new MemoryStream()) { - JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS)); + Utf8JsonWriter jsonWriter = new Utf8JsonWriter(mS); jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("address"); - jsonWriter.WriteValue(address.ToString()); - - jsonWriter.WritePropertyName("healthCheck"); - jsonWriter.WriteValue(healthCheck); - - jsonWriter.WritePropertyName("status"); - jsonWriter.WriteValue(healthCheckResponse.Status.ToString()); + jsonWriter.WriteString("address", address.ToString()); + jsonWriter.WriteString("healthCheck", healthCheck); + jsonWriter.WriteString("status", healthCheckResponse.Status.ToString()); if (healthCheckResponse.Status == HealthStatus.Failed) - { - jsonWriter.WritePropertyName("failureReason"); - jsonWriter.WriteValue(healthCheckResponse.FailureReason); - } + jsonWriter.WriteString("failureReason", healthCheckResponse.FailureReason); - jsonWriter.WritePropertyName("dateTime"); - jsonWriter.WriteValue(healthCheckResponse.DateTime); + jsonWriter.WriteString("dateTime", healthCheckResponse.DateTime); jsonWriter.WriteEndObject(); jsonWriter.Flush(); @@ -253,23 +232,14 @@ namespace Failover { using (MemoryStream mS = new MemoryStream()) { - JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS)); + Utf8JsonWriter jsonWriter = new Utf8JsonWriter(mS); jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("address"); - jsonWriter.WriteValue(address.ToString()); - - jsonWriter.WritePropertyName("healthCheck"); - jsonWriter.WriteValue(healthCheck); - - jsonWriter.WritePropertyName("status"); - jsonWriter.WriteValue("Error"); - - jsonWriter.WritePropertyName("failureReason"); - jsonWriter.WriteValue(ex.ToString()); - - jsonWriter.WritePropertyName("dateTime"); - jsonWriter.WriteValue(DateTime.UtcNow); + jsonWriter.WriteString("address", address.ToString()); + jsonWriter.WriteString("healthCheck", healthCheck); + jsonWriter.WriteString("status", "Error"); + jsonWriter.WriteString("failureReason", ex.ToString()); + jsonWriter.WriteString("dateTime", DateTime.UtcNow); jsonWriter.WriteEndObject(); jsonWriter.Flush(); @@ -291,29 +261,18 @@ namespace Failover { using (MemoryStream mS = new MemoryStream()) { - JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS)); + Utf8JsonWriter jsonWriter = new Utf8JsonWriter(mS); jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("domain"); - jsonWriter.WriteValue(domain); - - jsonWriter.WritePropertyName("recordType"); - jsonWriter.WriteValue(type.ToString()); - - jsonWriter.WritePropertyName("healthCheck"); - jsonWriter.WriteValue(healthCheck); - - jsonWriter.WritePropertyName("status"); - jsonWriter.WriteValue(healthCheckResponse.Status.ToString()); + jsonWriter.WriteString("domain", domain); + jsonWriter.WriteString("recordType", type.ToString()); + jsonWriter.WriteString("healthCheck", healthCheck); + jsonWriter.WriteString("status", healthCheckResponse.Status.ToString()); if (healthCheckResponse.Status == HealthStatus.Failed) - { - jsonWriter.WritePropertyName("failureReason"); - jsonWriter.WriteValue(healthCheckResponse.FailureReason); - } + jsonWriter.WriteString("failureReason", healthCheckResponse.FailureReason); - jsonWriter.WritePropertyName("dateTime"); - jsonWriter.WriteValue(healthCheckResponse.DateTime); + jsonWriter.WriteString("dateTime", healthCheckResponse.DateTime); jsonWriter.WriteEndObject(); jsonWriter.Flush(); @@ -335,26 +294,15 @@ namespace Failover { using (MemoryStream mS = new MemoryStream()) { - JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS)); + Utf8JsonWriter jsonWriter = new Utf8JsonWriter(mS); jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("domain"); - jsonWriter.WriteValue(domain); - - jsonWriter.WritePropertyName("recordType"); - jsonWriter.WriteValue(type.ToString()); - - jsonWriter.WritePropertyName("healthCheck"); - jsonWriter.WriteValue(healthCheck); - - jsonWriter.WritePropertyName("status"); - jsonWriter.WriteValue("Error"); - - jsonWriter.WritePropertyName("failureReason"); - jsonWriter.WriteValue(ex.ToString()); - - jsonWriter.WritePropertyName("dateTime"); - jsonWriter.WriteValue(DateTime.UtcNow); + jsonWriter.WriteString("domain", domain); + jsonWriter.WriteString("recordType", type.ToString()); + jsonWriter.WriteString("healthCheck", healthCheck); + jsonWriter.WriteString("status", "Error"); + jsonWriter.WriteString("failureReason", ex.ToString()); + jsonWriter.WriteString("dateTime", DateTime.UtcNow); jsonWriter.WriteEndObject(); jsonWriter.Flush();