mirror of
https://github.com/fergalmoran/DnsServer.git
synced 2025-12-22 09:29:50 +00:00
added BlockPageWebServerApp
This commit is contained in:
551
Apps/BlockPageWebServerApp/App.cs
Normal file
551
Apps/BlockPageWebServerApp/App.cs
Normal file
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
using DnsServerCore.ApplicationCommon;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TechnitiumLibrary.IO;
|
||||
using TechnitiumLibrary.Net;
|
||||
using TechnitiumLibrary.Net.Http;
|
||||
|
||||
namespace BlockPageWebServer
|
||||
{
|
||||
public class App : IDnsApplication
|
||||
{
|
||||
#region enum
|
||||
|
||||
enum ServiceState
|
||||
{
|
||||
Stopped = 0,
|
||||
Starting = 1,
|
||||
Running = 2,
|
||||
Stopping = 3
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region variables
|
||||
|
||||
const int TCP_SEND_TIMEOUT = 10000;
|
||||
const int TCP_RECV_TIMEOUT = 10000;
|
||||
|
||||
IDnsServer _dnsServer;
|
||||
|
||||
IReadOnlyList<IPAddress> _webServerLocalAddresses = Array.Empty<IPAddress>();
|
||||
string _webServerTlsCertificateFilePath;
|
||||
string _webServerTlsCertificatePassword;
|
||||
string _webServerRootPath;
|
||||
bool _serveBlockPageFromWebServerRoot;
|
||||
|
||||
byte[] _blockPageContent;
|
||||
|
||||
readonly List<Socket> _httpListeners = new List<Socket>();
|
||||
readonly List<Socket> _httpsListeners = new List<Socket>();
|
||||
|
||||
X509Certificate2 _webServerTlsCertificate;
|
||||
DateTime _webServerTlsCertificateLastModifiedOn;
|
||||
|
||||
Timer _tlsCertificateUpdateTimer;
|
||||
const int TLS_CERTIFICATE_UPDATE_TIMER_INITIAL_INTERVAL = 60000;
|
||||
const int TLS_CERTIFICATE_UPDATE_TIMER_INTERVAL = 60000;
|
||||
|
||||
volatile ServiceState _state = ServiceState.Stopped;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopTlsCertificateUpdateTimer();
|
||||
StopWebServer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region private
|
||||
|
||||
private void StartWebServer()
|
||||
{
|
||||
if (_state != ServiceState.Stopped)
|
||||
throw new InvalidOperationException("Web server is already running.");
|
||||
|
||||
_state = ServiceState.Starting;
|
||||
|
||||
//bind to local addresses
|
||||
foreach (IPAddress localAddress in _webServerLocalAddresses)
|
||||
{
|
||||
//bind to HTTP port 80
|
||||
{
|
||||
IPEndPoint httpEP = new IPEndPoint(localAddress, 80);
|
||||
Socket httpListener = null;
|
||||
|
||||
try
|
||||
{
|
||||
httpListener = new Socket(httpEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
httpListener.Bind(httpEP);
|
||||
httpListener.Listen(100);
|
||||
|
||||
_httpListeners.Add(httpListener);
|
||||
|
||||
_dnsServer.WriteLog("Web server was bound successfully: " + httpEP.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dnsServer.WriteLog(ex);
|
||||
|
||||
if (httpListener is not null)
|
||||
httpListener.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
//bind to HTTPS port 443
|
||||
if (_webServerTlsCertificate is not null)
|
||||
{
|
||||
IPEndPoint httpsEP = new IPEndPoint(localAddress, 443);
|
||||
Socket httpsListener = null;
|
||||
|
||||
try
|
||||
{
|
||||
httpsListener = new Socket(httpsEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
httpsListener.Bind(httpsEP);
|
||||
httpsListener.Listen(100);
|
||||
|
||||
_httpsListeners.Add(httpsListener);
|
||||
|
||||
_dnsServer.WriteLog("Web server was bound successfully: " + httpsEP.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dnsServer.WriteLog(ex);
|
||||
|
||||
if (httpsListener is not null)
|
||||
httpsListener.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//start reading requests
|
||||
int listenerTaskCount = Math.Max(1, Environment.ProcessorCount);
|
||||
|
||||
foreach (Socket httpListener in _httpListeners)
|
||||
{
|
||||
for (int i = 0; i < listenerTaskCount; i++)
|
||||
{
|
||||
_ = Task.Factory.StartNew(delegate ()
|
||||
{
|
||||
return AcceptConnectionAsync(httpListener, false);
|
||||
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Current);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Socket httpsListener in _httpsListeners)
|
||||
{
|
||||
for (int i = 0; i < listenerTaskCount; i++)
|
||||
{
|
||||
_ = Task.Factory.StartNew(delegate ()
|
||||
{
|
||||
return AcceptConnectionAsync(httpsListener, true);
|
||||
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Current);
|
||||
}
|
||||
}
|
||||
|
||||
_state = ServiceState.Running;
|
||||
}
|
||||
|
||||
private void StopWebServer()
|
||||
{
|
||||
if (_state != ServiceState.Running)
|
||||
return;
|
||||
|
||||
_state = ServiceState.Stopping;
|
||||
|
||||
foreach (Socket httpListener in _httpListeners)
|
||||
httpListener.Dispose();
|
||||
|
||||
foreach (Socket httpsListener in _httpsListeners)
|
||||
httpsListener.Dispose();
|
||||
|
||||
_httpListeners.Clear();
|
||||
_httpsListeners.Clear();
|
||||
|
||||
_state = ServiceState.Stopped;
|
||||
}
|
||||
|
||||
private void LoadWebServiceTlsCertificate()
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(_webServerTlsCertificateFilePath);
|
||||
|
||||
if (!fileInfo.Exists)
|
||||
throw new ArgumentException("Web server TLS certificate file does not exists: " + _webServerTlsCertificateFilePath);
|
||||
|
||||
if (Path.GetExtension(_webServerTlsCertificateFilePath) != ".pfx")
|
||||
throw new ArgumentException("Web server TLS certificate file must be PKCS #12 formatted with .pfx extension: " + _webServerTlsCertificateFilePath);
|
||||
|
||||
_webServerTlsCertificate = new X509Certificate2(_webServerTlsCertificateFilePath, _webServerTlsCertificatePassword);
|
||||
_webServerTlsCertificateLastModifiedOn = fileInfo.LastWriteTimeUtc;
|
||||
|
||||
_dnsServer.WriteLog("Web server TLS certificate was loaded: " + _webServerTlsCertificateFilePath);
|
||||
}
|
||||
|
||||
private void StartTlsCertificateUpdateTimer()
|
||||
{
|
||||
if (_tlsCertificateUpdateTimer == null)
|
||||
{
|
||||
_tlsCertificateUpdateTimer = new Timer(delegate (object state)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_webServerTlsCertificateFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(_webServerTlsCertificateFilePath);
|
||||
|
||||
if (fileInfo.Exists && (fileInfo.LastWriteTimeUtc != _webServerTlsCertificateLastModifiedOn))
|
||||
LoadWebServiceTlsCertificate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dnsServer.WriteLog("Web server encountered an error while updating TLS Certificate: " + _webServerTlsCertificateFilePath + "\r\n" + ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
}, null, TLS_CERTIFICATE_UPDATE_TIMER_INITIAL_INTERVAL, TLS_CERTIFICATE_UPDATE_TIMER_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
private void StopTlsCertificateUpdateTimer()
|
||||
{
|
||||
if (_tlsCertificateUpdateTimer != null)
|
||||
{
|
||||
_tlsCertificateUpdateTimer.Dispose();
|
||||
_tlsCertificateUpdateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AcceptConnectionAsync(Socket tcpListener, bool usingHttps)
|
||||
{
|
||||
try
|
||||
{
|
||||
tcpListener.SendTimeout = TCP_SEND_TIMEOUT;
|
||||
tcpListener.ReceiveTimeout = TCP_RECV_TIMEOUT;
|
||||
tcpListener.NoDelay = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
Socket socket = await tcpListener.AcceptAsync();
|
||||
|
||||
_ = ProcessConnectionAsync(socket, usingHttps);
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
if (ex.SocketErrorCode == SocketError.OperationAborted)
|
||||
return; //server stopping
|
||||
|
||||
_dnsServer.WriteLog(ex);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
//server stopped
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if ((_state == ServiceState.Stopping) || (_state == ServiceState.Stopped))
|
||||
return; //server stopping
|
||||
|
||||
_dnsServer.WriteLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessConnectionAsync(Socket socket, bool usingHttps)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPEndPoint remoteEP = socket.RemoteEndPoint as IPEndPoint;
|
||||
Stream stream = new NetworkStream(socket);
|
||||
|
||||
if (usingHttps)
|
||||
{
|
||||
SslStream httpsStream = new SslStream(stream);
|
||||
await httpsStream.AuthenticateAsServerAsync(_webServerTlsCertificate);
|
||||
|
||||
stream = httpsStream;
|
||||
}
|
||||
|
||||
await ProcessHttpRequestAsync(stream, remoteEP, usingHttps);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
//ignore IO exceptions
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dnsServer.WriteLog(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (socket is not null)
|
||||
socket.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessHttpRequestAsync(Stream stream, IPEndPoint remoteEP, bool usingHttps)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
bool isSocketRemoteIpPrivate = NetUtilities.IsPrivateIP(remoteEP.Address);
|
||||
HttpRequest httpRequest = await HttpRequest.ReadRequestAsync(stream, 512).WithTimeout(TCP_RECV_TIMEOUT);
|
||||
if (httpRequest is null)
|
||||
return; //connection closed gracefully by client
|
||||
|
||||
string requestConnection = httpRequest.Headers[HttpRequestHeader.Connection];
|
||||
if (string.IsNullOrEmpty(requestConnection))
|
||||
requestConnection = "close";
|
||||
|
||||
string path = httpRequest.RequestPath;
|
||||
|
||||
if (!path.StartsWith("/") || path.Contains("/../") || path.Contains("/.../"))
|
||||
{
|
||||
await SendErrorAsync(stream, requestConnection, 404);
|
||||
break;
|
||||
}
|
||||
|
||||
if (path == "/")
|
||||
path = "/index.html";
|
||||
|
||||
string accept = httpRequest.Headers[HttpRequestHeader.Accept];
|
||||
if (string.IsNullOrEmpty(accept) || accept.Contains("text/html", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (path.Equals("/index.html", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
//send block page
|
||||
if (_serveBlockPageFromWebServerRoot)
|
||||
{
|
||||
path = Path.GetFullPath(_webServerRootPath + path.Replace('/', Path.DirectorySeparatorChar));
|
||||
|
||||
if (!path.StartsWith(_webServerRootPath) || !File.Exists(path))
|
||||
await SendErrorAsync(stream, requestConnection, 404);
|
||||
else
|
||||
await SendFileAsync(stream, requestConnection, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendContentAsync(stream, requestConnection, "text/html", _blockPageContent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//redirect to block page
|
||||
await RedirectAsync(stream, httpRequest.Protocol, requestConnection, (usingHttps ? "https://" : "http://") + httpRequest.Headers[HttpRequestHeader.Host]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_serveBlockPageFromWebServerRoot)
|
||||
{
|
||||
//serve files
|
||||
path = Path.GetFullPath(_webServerRootPath + path.Replace('/', Path.DirectorySeparatorChar));
|
||||
|
||||
if (!path.StartsWith(_webServerRootPath) || !File.Exists(path))
|
||||
await SendErrorAsync(stream, requestConnection, 404);
|
||||
else
|
||||
await SendFileAsync(stream, requestConnection, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendErrorAsync(stream, requestConnection, 404);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
//ignore timeout exception
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
//ignore IO exceptions
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dnsServer.WriteLog(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SendContentAsync(Stream outputStream, string connection, string contentType, byte[] content)
|
||||
{
|
||||
byte[] bufferHeader = Encoding.UTF8.GetBytes("HTTP/1.1 200 OK\r\nDate: " + DateTime.UtcNow.ToString("r") + "\r\nContent-Type: " + contentType + "\r\nContent-Length: " + content.Length + "\r\nX-Robots-Tag: noindex, nofollow\r\nConnection: " + connection + "\r\n\r\n");
|
||||
|
||||
await outputStream.WriteAsync(bufferHeader);
|
||||
await outputStream.WriteAsync(content);
|
||||
await outputStream.FlushAsync();
|
||||
}
|
||||
|
||||
private static async Task SendErrorAsync(Stream outputStream, string connection, int statusCode, string message = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string statusString = statusCode + " " + GetHttpStatusString((HttpStatusCode)statusCode);
|
||||
byte[] bufferContent = Encoding.UTF8.GetBytes("<html><head><title>" + statusString + "</title></head><body><h1>" + statusString + "</h1>" + (message is null ? "" : "<p>" + message + "</p>") + "</body></html>");
|
||||
byte[] bufferHeader = Encoding.UTF8.GetBytes("HTTP/1.1 " + statusString + "\r\nDate: " + DateTime.UtcNow.ToString("r") + "\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " + bufferContent.Length + "\r\nX-Robots-Tag: noindex, nofollow\r\nConnection: " + connection + "\r\n\r\n");
|
||||
|
||||
await outputStream.WriteAsync(bufferHeader);
|
||||
await outputStream.WriteAsync(bufferContent);
|
||||
await outputStream.FlushAsync();
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
|
||||
private static async Task RedirectAsync(Stream outputStream, string protocol, string connection, string location)
|
||||
{
|
||||
try
|
||||
{
|
||||
string statusString = "302 Found";
|
||||
byte[] bufferContent = Encoding.UTF8.GetBytes("<html><head><title>" + statusString + "</title></head><body><h1>" + statusString + "</h1><p>Location: <a href=\"" + location + "\">" + location + "</a></p></body></html>");
|
||||
byte[] bufferHeader = Encoding.UTF8.GetBytes(protocol + " " + statusString + "\r\nDate: " + DateTime.UtcNow.ToString("r") + "\r\nLocation: " + location + "\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " + bufferContent.Length + "\r\nX-Robots-Tag: noindex, nofollow\r\nConnection: " + connection + "\r\n\r\n");
|
||||
|
||||
await outputStream.WriteAsync(bufferHeader);
|
||||
await outputStream.WriteAsync(bufferContent);
|
||||
await outputStream.FlushAsync();
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
|
||||
private static async Task SendFileAsync(Stream outputStream, string connection, string filePath)
|
||||
{
|
||||
using (FileStream fS = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
byte[] bufferHeader = Encoding.UTF8.GetBytes("HTTP/1.1 200 OK\r\nDate: " + DateTime.UtcNow.ToString("r") + "\r\nContent-Type: " + WebUtilities.GetContentType(filePath).MediaType + "\r\nContent-Length: " + fS.Length + "\r\nCache-Control: private, max-age=300\r\nX-Robots-Tag: noindex, nofollow\r\nConnection: " + connection + "\r\n\r\n");
|
||||
|
||||
await outputStream.WriteAsync(bufferHeader);
|
||||
await fS.CopyToAsync(outputStream);
|
||||
await outputStream.FlushAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetHttpStatusString(HttpStatusCode statusCode)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (char c in statusCode.ToString().ToCharArray())
|
||||
{
|
||||
if (char.IsUpper(c) && sb.Length > 0)
|
||||
sb.Append(' ');
|
||||
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region public
|
||||
|
||||
public Task InitializeAsync(IDnsServer dnsServer, string config)
|
||||
{
|
||||
_dnsServer = dnsServer;
|
||||
|
||||
dynamic jsonConfig = JsonConvert.DeserializeObject(config);
|
||||
|
||||
{
|
||||
List<IPAddress> webServerLocalAddresses = new List<IPAddress>();
|
||||
|
||||
foreach (dynamic jsonAddress in jsonConfig.webServerLocalAddresses)
|
||||
webServerLocalAddresses.Add(IPAddress.Parse(jsonAddress.Value));
|
||||
|
||||
_webServerLocalAddresses = webServerLocalAddresses;
|
||||
}
|
||||
|
||||
_webServerTlsCertificateFilePath = jsonConfig.webServerTlsCertificateFilePath?.Value;
|
||||
_webServerTlsCertificatePassword = jsonConfig.webServerTlsCertificatePassword?.Value;
|
||||
|
||||
_webServerRootPath = jsonConfig.webServerRootPath?.Value;
|
||||
|
||||
if (!Path.IsPathRooted(_webServerRootPath))
|
||||
_webServerRootPath = Path.Combine(_dnsServer.ApplicationFolder, _webServerRootPath);
|
||||
|
||||
_serveBlockPageFromWebServerRoot = jsonConfig.serveBlockPageFromWebServerRoot?.Value;
|
||||
|
||||
string blockPageTitle = jsonConfig.blockPageTitle?.Value;
|
||||
string blockPageHeading = jsonConfig.blockPageHeading?.Value;
|
||||
string blockPageMessage = jsonConfig.blockPageMessage?.Value;
|
||||
|
||||
string blockPageContent = @"<html>
|
||||
<head>
|
||||
<title>" + (blockPageTitle is null ? "" : blockPageTitle) + @"</title>
|
||||
</head>
|
||||
<body>
|
||||
" + (blockPageHeading is null ? "" : " <h1>" + blockPageHeading + "</h1>") + @"
|
||||
" + (blockPageMessage is null ? "" : " <p>" + blockPageMessage + "</p>") + @"
|
||||
</body>
|
||||
</html>";
|
||||
|
||||
_blockPageContent = Encoding.UTF8.GetBytes(blockPageContent);
|
||||
|
||||
try
|
||||
{
|
||||
StopWebServer();
|
||||
|
||||
if (string.IsNullOrEmpty(_webServerTlsCertificateFilePath))
|
||||
{
|
||||
StopTlsCertificateUpdateTimer();
|
||||
_webServerTlsCertificate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadWebServiceTlsCertificate();
|
||||
StartTlsCertificateUpdateTimer();
|
||||
}
|
||||
|
||||
StartWebServer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dnsServer.WriteLog(ex);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region properties
|
||||
|
||||
public string Description
|
||||
{ get { return "Serves a block page from a built-in web server that can be displayed to the end user when a website is blocked by the DNS server.\n\nNote: You need to manually configure the custom IP addresses of this built-in web server in the blocking settings for the block page to be served."; } }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
55
Apps/BlockPageWebServerApp/BlockPageWebServerApp.csproj
Normal file
55
Apps/BlockPageWebServerApp/BlockPageWebServerApp.csproj
Normal file
@@ -0,0 +1,55 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<Version>1.0</Version>
|
||||
<Company>Technitium</Company>
|
||||
<Product>Technitium DNS Server</Product>
|
||||
<Authors>Shreyas Zare</Authors>
|
||||
<AssemblyName>BlockPageWebServerApp</AssemblyName>
|
||||
<RootNamespace>BlockPageWebServer</RootNamespace>
|
||||
<PackageProjectUrl>https://technitium.com/dns/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/TechnitiumSoftware/DnsServer</RepositoryUrl>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="wwwroot\index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="wwwroot\index.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\DnsServerCore.ApplicationCommon\DnsServerCore.ApplicationCommon.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="TechnitiumLibrary.IO">
|
||||
<HintPath>..\..\..\TechnitiumLibrary\bin\TechnitiumLibrary.IO.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="TechnitiumLibrary.Net">
|
||||
<HintPath>..\..\..\TechnitiumLibrary\bin\TechnitiumLibrary.Net.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="dnsApp.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
13
Apps/BlockPageWebServerApp/dnsApp.config
Normal file
13
Apps/BlockPageWebServerApp/dnsApp.config
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"webServerLocalAddresses": [
|
||||
"0.0.0.0",
|
||||
"::"
|
||||
],
|
||||
"webServerTlsCertificateFilePath": "z:\\dns.home.zare.im.pfx",
|
||||
"webServerTlsCertificatePassword": null,
|
||||
"webServerRootPath": "wwwroot",
|
||||
"serveBlockPageFromWebServerRoot": false,
|
||||
"blockPageTitle": "Website Blocked",
|
||||
"blockPageHeading": "Website Blocked",
|
||||
"blockPageMessage": "This website has been blocked by your network administrator."
|
||||
}
|
||||
9
Apps/BlockPageWebServerApp/wwwroot/index.html
Normal file
9
Apps/BlockPageWebServerApp/wwwroot/index.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Website Blocked</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Website Blocked</h1>
|
||||
<p>This website has been blocked by your network administrator.</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user