/* Technitium DNS Server Copyright (C) 2021 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 the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License 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.Threading.Tasks; using TechnitiumLibrary.Net.Dns.ResourceRecords; using TechnitiumLibrary.Net.Proxy; namespace Failover { class WebHook : IDisposable { #region variables readonly HealthService _service; string _name; bool _enabled; Uri[] _urls; SocketsHttpHandler _httpHandler; HttpClient _httpClient; #endregion #region constructor public WebHook(HealthService service, dynamic jsonWebHook) { _service = service; Reload(jsonWebHook); } #endregion #region IDisposable bool _disposed; protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } if (_httpHandler != null) { _httpHandler.Dispose(); _httpHandler = null; } } _disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion #region private private void ConditionalHttpReload() { bool handlerChanged = false; NetProxy proxy = _service.DnsServer.Proxy; if (_httpHandler is null) { SocketsHttpHandler httpHandler = new SocketsHttpHandler(); httpHandler.Proxy = proxy; httpHandler.AllowAutoRedirect = true; httpHandler.MaxAutomaticRedirections = 10; _httpHandler = httpHandler; handlerChanged = true; } else { if (_httpHandler.Proxy != proxy) { SocketsHttpHandler httpHandler = new SocketsHttpHandler(); httpHandler.Proxy = proxy; httpHandler.AllowAutoRedirect = true; httpHandler.MaxAutomaticRedirections = 10; SocketsHttpHandler oldHttpHandler = _httpHandler; _httpHandler = httpHandler; handlerChanged = true; oldHttpHandler.Dispose(); } } if (_httpClient is null) { HttpClient httpClient = new HttpClient(_httpHandler); _httpClient = httpClient; } else { if (handlerChanged) { HttpClient httpClient = new HttpClient(_httpHandler); HttpClient oldHttpClient = _httpClient; _httpClient = httpClient; oldHttpClient.Dispose(); } } } private async Task CallAsync(HttpContent content) { ConditionalHttpReload(); async Task CallWebHook(Uri url) { try { HttpResponseMessage response = await _httpClient.PostAsync(url, content); response.EnsureSuccessStatusCode(); } catch (Exception ex) { _service.DnsServer.WriteLog(ex); } } List tasks = new List(); foreach (Uri url in _urls) tasks.Add(CallWebHook(url)); await Task.WhenAll(tasks); } #endregion #region public public void Reload(dynamic jsonWebHook) { if (jsonWebHook.name is null) _name = "default"; else _name = jsonWebHook.name.Value; if (jsonWebHook.enabled is null) _enabled = false; 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(); } public Task CallAsync(IPAddress address, string healthCheck, HealthCheckResponse healthCheckResponse) { if (!_enabled) return Task.CompletedTask; HttpContent content; { using (MemoryStream mS = new MemoryStream()) { JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(mS)); jsonWriter.WriteStartObject(); jsonWriter.WritePropertyName("address"); jsonWriter.WriteValue(address.ToString()); jsonWriter.WritePropertyName("healthCheck"); jsonWriter.WriteValue(healthCheck); jsonWriter.WritePropertyName("status"); jsonWriter.WriteValue(healthCheckResponse.Status.ToString()); if (healthCheckResponse.Status == HealthStatus.Failed) { jsonWriter.WritePropertyName("failureReason"); jsonWriter.WriteValue(healthCheckResponse.FailureReason); } jsonWriter.WritePropertyName("dateTime"); jsonWriter.WriteValue(healthCheckResponse.DateTime); jsonWriter.WriteEndObject(); jsonWriter.Flush(); content = new ByteArrayContent(mS.ToArray()); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); } } return CallAsync(content); } public Task CallAsync(IPAddress address, string healthCheck, Exception ex) { if (!_enabled) return Task.CompletedTask; HttpContent content; { using (MemoryStream mS = new MemoryStream()) { JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(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.WriteEndObject(); jsonWriter.Flush(); content = new ByteArrayContent(mS.ToArray()); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); } } return CallAsync(content); } public Task CallAsync(string domain, DnsResourceRecordType type, string healthCheck, HealthCheckResponse healthCheckResponse) { if (!_enabled) return Task.CompletedTask; HttpContent content; { using (MemoryStream mS = new MemoryStream()) { JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(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()); if (healthCheckResponse.Status == HealthStatus.Failed) { jsonWriter.WritePropertyName("failureReason"); jsonWriter.WriteValue(healthCheckResponse.FailureReason); } jsonWriter.WritePropertyName("dateTime"); jsonWriter.WriteValue(healthCheckResponse.DateTime); jsonWriter.WriteEndObject(); jsonWriter.Flush(); content = new ByteArrayContent(mS.ToArray()); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); } } return CallAsync(content); } public Task CallAsync(string domain, DnsResourceRecordType type, string healthCheck, Exception ex) { if (!_enabled) return Task.CompletedTask; HttpContent content; { using (MemoryStream mS = new MemoryStream()) { JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(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.WriteEndObject(); jsonWriter.Flush(); content = new ByteArrayContent(mS.ToArray()); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); } } return CallAsync(content); } #endregion #region properties public string Name { get { return _name; } } public bool Enabled { get { return _enabled; } } public IReadOnlyList Urls { get { return _urls; } } #endregion } }