WebService: using independent task scheduler for web requests do that web panel stays responsive when dns server is under heavy load. Implemented async methods. Code refactoring done.

This commit is contained in:
Shreyas Zare
2020-08-29 14:40:51 +05:30
parent 46b576a166
commit d1b563e489

View File

@@ -29,11 +29,13 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TechnitiumLibrary.IO;
using TechnitiumLibrary.Net;
using TechnitiumLibrary.Net.Dns;
@@ -58,7 +60,7 @@ namespace DnsServerCore
#region variables
readonly string _currentVersion;
readonly Version _currentVersion;
readonly string _appFolder;
readonly string _configFolder;
readonly Uri _updateCheckUri;
@@ -71,6 +73,7 @@ namespace DnsServerCore
int _webServicePort;
HttpListener _webService;
Thread _webServiceThread;
readonly IndependentTaskScheduler _webServiceTaskScheduler = new IndependentTaskScheduler(ThreadPriority.AboveNormal);
string _webServiceHostname;
string _tlsCertificatePath;
@@ -94,7 +97,6 @@ namespace DnsServerCore
const int BLOCK_LIST_UPDATE_AFTER_HOURS = 24;
const int BLOCK_LIST_UPDATE_TIMER_INITIAL_INTERVAL = 5000;
const int BLOCK_LIST_UPDATE_TIMER_INTERVAL = 900000;
const int BLOCK_LIST_UPDATE_RETRIES = 3;
List<string> _configDisabledZones;
@@ -104,10 +106,10 @@ namespace DnsServerCore
public WebService(string configFolder = null, Uri updateCheckUri = null)
{
Assembly assembly = Assembly.GetEntryAssembly();
Assembly assembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = assembly.GetName();
_currentVersion = assemblyName.Version.ToString();
_currentVersion = assemblyName.Version;
_appFolder = Path.GetDirectoryName(assembly.Location);
if (configFolder == null)
@@ -180,7 +182,11 @@ namespace DnsServerCore
while (true)
{
HttpListenerContext context = _webService.GetContext();
ThreadPool.QueueUserWorkItem(ProcessRequestAsync, new object[] { context.Request, context.Response });
_ = Task.Factory.StartNew(delegate ()
{
return ProcessRequestAsync(context.Request, context.Response);
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, _webServiceTaskScheduler);
}
}
catch (Exception ex)
@@ -193,12 +199,8 @@ namespace DnsServerCore
}
}
private void ProcessRequestAsync(object state)
private async Task ProcessRequestAsync(HttpListenerRequest request, HttpListenerResponse response)
{
object[] parameters = state as object[];
HttpListenerRequest request = parameters[0] as HttpListenerRequest;
HttpListenerResponse response = parameters[1] as HttpListenerResponse;
response.AddHeader("Server", "");
response.AddHeader("X-Robots-Tag", "noindex, nofollow");
@@ -248,7 +250,7 @@ namespace DnsServerCore
break;
case "/api/checkForUpdate":
CheckForUpdate(request, jsonWriter);
await CheckForUpdateAsync(request, jsonWriter);
break;
case "/api/getDnsSettings":
@@ -260,7 +262,7 @@ namespace DnsServerCore
break;
case "/api/getStats":
GetStats(request, jsonWriter);
await GetStats(request, jsonWriter);
break;
case "/api/flushDnsCache":
@@ -320,7 +322,7 @@ namespace DnsServerCore
break;
case "/api/createZone":
CreateZone(request, jsonWriter);
await CreateZoneAsync(request, jsonWriter);
break;
case "/api/deleteZone":
@@ -352,7 +354,7 @@ namespace DnsServerCore
break;
case "/api/resolveQuery":
ResolveQuery(request, jsonWriter);
await ResolveQuery(request, jsonWriter);
break;
case "/api/listLogs":
@@ -781,9 +783,9 @@ namespace DnsServerCore
bW.WriteShortString(downloadLink);
}
private void CheckForUpdate(HttpListenerRequest request, JsonTextWriter jsonWriter)
private async Task CheckForUpdateAsync(HttpListenerRequest request, JsonTextWriter jsonWriter)
{
string updateVersion = null;
Version updateVersion = null;
string displayText = null;
string downloadLink = null;
@@ -793,11 +795,12 @@ namespace DnsServerCore
{
try
{
using (WebClientEx wc = new WebClientEx())
{
wc.Proxy = _dnsServer.Proxy;
HttpClientHandler handler = new HttpClientHandler();
handler.Proxy = _dnsServer.Proxy;
byte[] response = wc.DownloadData(_updateCheckUri);
using (HttpClient http = new HttpClient(handler))
{
byte[] response = await http.GetByteArrayAsync(_updateCheckUri);
using (MemoryStream mS = new MemoryStream(response, false))
{
@@ -809,7 +812,7 @@ namespace DnsServerCore
switch (bR.ReadByte()) //version
{
case 2:
updateVersion = bR.ReadShortString();
updateVersion = new Version(bR.ReadShortString());
displayText = bR.ReadShortString();
downloadLink = bR.ReadShortString();
break;
@@ -818,7 +821,7 @@ namespace DnsServerCore
throw new InvalidDataException("DNS Server update info version not supported.");
}
updateAvailable = IsUpdateAvailable(_currentVersion, updateVersion);
updateAvailable = updateVersion > _currentVersion;
}
}
@@ -846,46 +849,17 @@ namespace DnsServerCore
}
}
private static bool IsUpdateAvailable(string currentVersion, string updateVersion)
private static string GetCleanVersion(Version version)
{
if (updateVersion == null)
return false;
string strVersion = version.Major + "." + version.Minor;
string[] uVer = updateVersion.Split(new char[] { '.' });
string[] cVer = currentVersion.Split(new char[] { '.' });
if (version.Build > 0)
strVersion += "." + version.Build;
int x = uVer.Length;
if (x > cVer.Length)
x = cVer.Length;
if (version.Revision > 0)
strVersion += "." + version.Revision;
for (int i = 0; i < x; i++)
{
if (Convert.ToInt32(uVer[i]) > Convert.ToInt32(cVer[i]))
return true;
else if (Convert.ToInt32(uVer[i]) < Convert.ToInt32(cVer[i]))
return false;
}
if (uVer.Length > cVer.Length)
{
for (int i = x; i < uVer.Length; i++)
{
if (Convert.ToInt32(uVer[i]) > 0)
return true;
}
}
return false;
}
private static string GetCleanVersion(string version)
{
while (version.EndsWith(".0"))
{
version = version.Substring(0, version.Length - 2);
}
return version;
return strVersion;
}
private void GetDnsSettings(JsonTextWriter jsonWriter)
@@ -1245,7 +1219,12 @@ namespace DnsServerCore
_dnsServer.BlockListZoneManager.BlockListUrls.Clear();
foreach (string strBlockListUrl in strBlockListUrlList)
_dnsServer.BlockListZoneManager.BlockListUrls.Add(new Uri(strBlockListUrl));
{
Uri blockListUrl = new Uri(strBlockListUrl);
if (!_dnsServer.BlockListZoneManager.BlockListUrls.Contains(blockListUrl))
_dnsServer.BlockListZoneManager.BlockListUrls.Add(blockListUrl);
}
_blockListLastUpdatedOn = new DateTime();
@@ -1262,7 +1241,7 @@ namespace DnsServerCore
GetDnsSettings(jsonWriter);
}
private void GetStats(HttpListenerRequest request, JsonTextWriter jsonWriter)
private async Task GetStats(HttpListenerRequest request, JsonTextWriter jsonWriter)
{
string strType = request.QueryString["type"];
if (string.IsNullOrEmpty(strType))
@@ -1520,7 +1499,7 @@ namespace DnsServerCore
{
try
{
DnsDatagram ptrResponse = _dnsServer.DirectQuery(new DnsQuestionRecord(address, DnsClass.IN), 200);
DnsDatagram ptrResponse = await _dnsServer.DirectQueryAsync(new DnsQuestionRecord(address, DnsClass.IN), 200);
if ((ptrResponse != null) && (ptrResponse.Answer.Count > 0))
{
string ptrDomain = DnsClient.ParseResponsePTR(ptrResponse);
@@ -2030,7 +2009,7 @@ namespace DnsServerCore
jsonWriter.WriteEndArray();
}
private void CreateZone(HttpListenerRequest request, JsonTextWriter jsonWriter)
private async Task CreateZoneAsync(HttpListenerRequest request, JsonTextWriter jsonWriter)
{
string domain = request.QueryString["domain"];
if (string.IsNullOrEmpty(domain))
@@ -2062,11 +2041,11 @@ namespace DnsServerCore
switch (type)
{
case AuthZoneType.Primary:
if (_dnsServer.AuthZoneManager.CreatePrimaryZone(domain, _dnsServer.ServerDomain, false) != null)
{
_log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Authoritative primary zone was created: " + domain);
_dnsServer.AuthZoneManager.SaveZoneFile(domain);
}
if (_dnsServer.AuthZoneManager.CreatePrimaryZone(domain, _dnsServer.ServerDomain, false) == null)
throw new WebServiceException("Zone already exists: " + domain);
_log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Authoritative primary zone was created: " + domain);
_dnsServer.AuthZoneManager.SaveZoneFile(domain);
break;
case AuthZoneType.Secondary:
@@ -2075,11 +2054,11 @@ namespace DnsServerCore
if (string.IsNullOrEmpty(strPrimaryNameServerAddresses))
strPrimaryNameServerAddresses = null;
if (_dnsServer.AuthZoneManager.CreateSecondaryZone(domain, strPrimaryNameServerAddresses) != null)
{
_log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Authoritative secondary zone was created: " + domain);
_dnsServer.AuthZoneManager.SaveZoneFile(domain);
}
if (await _dnsServer.AuthZoneManager.CreateSecondaryZoneAsync(domain, strPrimaryNameServerAddresses) == null)
throw new WebServiceException("Zone already exists: " + domain);
_log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Authoritative secondary zone was created: " + domain);
_dnsServer.AuthZoneManager.SaveZoneFile(domain);
}
break;
@@ -2089,11 +2068,11 @@ namespace DnsServerCore
if (string.IsNullOrEmpty(strPrimaryNameServerAddresses))
strPrimaryNameServerAddresses = null;
if (_dnsServer.AuthZoneManager.CreateStubZone(domain, strPrimaryNameServerAddresses) != null)
{
_log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Stub zone was created: " + domain);
_dnsServer.AuthZoneManager.SaveZoneFile(domain);
}
if (await _dnsServer.AuthZoneManager.CreateStubZoneAsync(domain, strPrimaryNameServerAddresses) == null)
throw new WebServiceException("Zone already exists: " + domain);
_log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Stub zone was created: " + domain);
_dnsServer.AuthZoneManager.SaveZoneFile(domain);
}
break;
@@ -2108,11 +2087,11 @@ namespace DnsServerCore
if (string.IsNullOrEmpty(strForwarder))
throw new WebServiceException("Parameter 'forwarder' missing.");
if (_dnsServer.AuthZoneManager.CreateForwarderZone(domain, forwarderProtocol, strForwarder) != null)
{
_log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Forwarder zone was created: " + domain);
_dnsServer.AuthZoneManager.SaveZoneFile(domain);
}
if (_dnsServer.AuthZoneManager.CreateForwarderZone(domain, forwarderProtocol, strForwarder) == null)
throw new WebServiceException("Zone already exists: " + domain);
_log.Write(GetRequestRemoteEndPoint(request), "[" + GetSession(request).Username + "] Forwarder zone was created: " + domain);
_dnsServer.AuthZoneManager.SaveZoneFile(domain);
}
break;
@@ -3085,7 +3064,7 @@ namespace DnsServerCore
_dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
}
private void ResolveQuery(HttpListenerRequest request, JsonTextWriter jsonWriter)
private async Task ResolveQuery(HttpListenerRequest request, JsonTextWriter jsonWriter)
{
string server = request.QueryString["server"];
if (string.IsNullOrEmpty(server))
@@ -3135,7 +3114,7 @@ namespace DnsServerCore
else
question = new DnsQuestionRecord(domain, type, DnsClass.IN);
dnsResponse = DnsClient.RecursiveResolve(question, null, null, proxy, preferIPv6, RETRIES, TIMEOUT);
dnsResponse = await DnsClient.RecursiveResolveAsync(question, null, null, proxy, preferIPv6, RETRIES, TIMEOUT);
}
else
{
@@ -3183,9 +3162,9 @@ namespace DnsServerCore
if (proxy == null)
{
if (_dnsServer.AllowRecursion)
nameServer.ResolveIPAddress(new NameServerAddress[] { _dnsServer.ThisServer }, proxy, preferIPv6, RETRIES, TIMEOUT);
await nameServer.ResolveIPAddressAsync(new NameServerAddress[] { _dnsServer.ThisServer }, proxy, preferIPv6, RETRIES, TIMEOUT);
else
nameServer.RecursiveResolveIPAddress(_dnsServer.DnsCache, proxy, preferIPv6, RETRIES, TIMEOUT);
await nameServer.RecursiveResolveIPAddressAsync(_dnsServer.DnsCache, proxy, preferIPv6, RETRIES, TIMEOUT);
}
}
else if (protocol != DnsTransportProtocol.Tls)
@@ -3193,16 +3172,16 @@ namespace DnsServerCore
try
{
if (_dnsServer.AllowRecursion)
nameServer.ResolveDomainName(new NameServerAddress[] { _dnsServer.ThisServer }, proxy, preferIPv6, RETRIES, TIMEOUT);
await nameServer.ResolveDomainNameAsync(new NameServerAddress[] { _dnsServer.ThisServer }, proxy, preferIPv6, RETRIES, TIMEOUT);
else
nameServer.RecursiveResolveDomainName(_dnsServer.DnsCache, proxy, preferIPv6, RETRIES, TIMEOUT);
await nameServer.RecursiveResolveDomainNameAsync(_dnsServer.DnsCache, proxy, preferIPv6, RETRIES, TIMEOUT);
}
catch
{ }
}
}
dnsResponse = (new DnsClient(nameServer) { Proxy = proxy, PreferIPv6 = preferIPv6, Retries = RETRIES, Timeout = TIMEOUT }).Resolve(domain, type);
dnsResponse = await new DnsClient(nameServer) { Proxy = proxy, PreferIPv6 = preferIPv6, Retries = RETRIES, Timeout = TIMEOUT }.ResolveAsync(domain, type);
}
if (importRecords)
@@ -3861,13 +3840,13 @@ namespace DnsServerCore
{
if (_blockListUpdateTimer == null)
{
_blockListUpdateTimer = new Timer(delegate (object state)
_blockListUpdateTimer = new Timer(async delegate (object state)
{
try
{
if (DateTime.UtcNow > _blockListLastUpdatedOn.AddHours(BLOCK_LIST_UPDATE_AFTER_HOURS))
{
if (_dnsServer.BlockListZoneManager.UpdateBlockLists(BLOCK_LIST_UPDATE_RETRIES))
if (await _dnsServer.BlockListZoneManager.UpdateBlockListsAsync())
{
//block lists were updated
//save last updated on time
@@ -4425,16 +4404,17 @@ namespace DnsServerCore
_webService.IgnoreWriteExceptions = true;
_webServiceThread = new Thread(AcceptWebRequestAsync);
_webServiceThread.Name = "WebService";
_webServiceThread.IsBackground = true;
_webServiceThread.Start();
_state = ServiceState.Running;
_log.Write(new IPEndPoint(IPAddress.Any, _webServicePort), "Web Service (v" + _currentVersion + ") was started successfully.");
_log.Write(new IPEndPoint(IPAddress.Any, _webServicePort), "Web Service (v" + _currentVersion.ToString() + ") was started successfully.");
}
catch (Exception ex)
{
_log.Write("Failed to start Web Service (v" + _currentVersion + ")\r\n" + ex.ToString());
_log.Write("Failed to start Web Service (v" + _currentVersion.ToString() + ")\r\n" + ex.ToString());
throw;
}
}
@@ -4457,11 +4437,11 @@ namespace DnsServerCore
_state = ServiceState.Stopped;
_log.Write(new IPEndPoint(IPAddress.Loopback, _webServicePort), "Web Service (v" + _currentVersion + ") was stopped successfully.");
_log.Write(new IPEndPoint(IPAddress.Loopback, _webServicePort), "Web Service (v" + _currentVersion.ToString() + ") was stopped successfully.");
}
catch (Exception ex)
{
_log.Write("Failed to stop Web Service (v" + _currentVersion + ")\r\n" + ex.ToString());
_log.Write("Failed to stop Web Service (v" + _currentVersion.ToString() + ")\r\n" + ex.ToString());
throw;
}
}