Merge branch 'secondary-zones' into develop

This commit is contained in:
Shreyas Zare
2019-12-14 16:58:12 +05:30
9 changed files with 101 additions and 63 deletions

View File

@@ -422,7 +422,7 @@ namespace DnsServerCore.Dhcp
}
}
if (string.IsNullOrEmpty(scope.DomainName))
if (string.IsNullOrWhiteSpace(scope.DomainName))
{
//update lease hostname
leaseOffer.SetHostName(request.HostName?.HostName);
@@ -441,13 +441,13 @@ namespace DnsServerCore.Dhcp
}
}
if (clientDomainName == null)
if (string.IsNullOrWhiteSpace(clientDomainName))
{
if (request.HostName != null)
clientDomainName = request.HostName.HostName.Replace(' ', '-') + "." + scope.DomainName;
}
if (clientDomainName != null)
if (!string.IsNullOrWhiteSpace(clientDomainName))
{
leaseOffer.SetHostName(clientDomainName.ToLower());
UpdateDnsAuthZone(true, scope, leaseOffer);
@@ -611,21 +611,23 @@ namespace DnsServerCore.Dhcp
if (_authoritativeZoneRoot == null)
return;
if (string.IsNullOrEmpty(scope.DomainName))
if (string.IsNullOrWhiteSpace(scope.DomainName))
return;
if (string.IsNullOrEmpty(lease.HostName))
if (string.IsNullOrWhiteSpace(lease.HostName))
return;
if (!DnsClient.IsDomainNameValid(lease.HostName))
return;
if (add)
{
//update forward zone
if (!string.IsNullOrEmpty(scope.DomainName))
{
if (!_authoritativeZoneRoot.ZoneExists(scope.DomainName))
{
//create forward zone
_authoritativeZoneRoot.SetRecords(scope.DomainName, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_authoritativeZoneRoot.ServerDomain, "hostmaster." + scope.DomainName, uint.Parse(DateTime.UtcNow.ToString("yyyyMMddHH")), 28800, 7200, 604800, 600) });
_authoritativeZoneRoot.SetRecords(scope.DomainName, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_authoritativeZoneRoot.ServerDomain, "hostmaster." + scope.DomainName, 1, 14400, 3600, 604800, 900) });
_authoritativeZoneRoot.SetRecords(scope.DomainName, DnsResourceRecordType.NS, 14400, new DnsResourceRecordData[] { new DnsNSRecord(_authoritativeZoneRoot.ServerDomain) });
_authoritativeZoneRoot.MakeZoneInternal(scope.DomainName);
@@ -639,7 +641,7 @@ namespace DnsServerCore.Dhcp
if (!_authoritativeZoneRoot.ZoneExists(scope.ReverseZone))
{
//create reverse zone
_authoritativeZoneRoot.SetRecords(scope.ReverseZone, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_authoritativeZoneRoot.ServerDomain, "hostmaster." + scope.ReverseZone, uint.Parse(DateTime.UtcNow.ToString("yyyyMMddHH")), 28800, 7200, 604800, 600) });
_authoritativeZoneRoot.SetRecords(scope.ReverseZone, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_authoritativeZoneRoot.ServerDomain, "hostmaster." + scope.ReverseZone, 1, 14400, 3600, 604800, 900) });
_authoritativeZoneRoot.SetRecords(scope.ReverseZone, DnsResourceRecordType.NS, 14400, new DnsResourceRecordData[] { new DnsNSRecord(_authoritativeZoneRoot.ServerDomain) });
_authoritativeZoneRoot.MakeZoneInternal(scope.ReverseZone);

View File

@@ -84,7 +84,7 @@ namespace DnsServerCore.Dhcp
_clientIdentifier.ParseOptionValue();
_hostName = bR.ReadShortString();
if (_hostName == "")
if (string.IsNullOrWhiteSpace(_hostName))
_hostName = null;
_hardwareAddress = bR.ReadBuffer();
@@ -93,7 +93,7 @@ namespace DnsServerCore.Dhcp
if (version >= 2)
{
_comments = bR.ReadShortString();
if (_comments == "")
if (string.IsNullOrWhiteSpace(_comments))
_comments = null;
}
@@ -142,7 +142,7 @@ namespace DnsServerCore.Dhcp
bW.Write((byte)_type);
_clientIdentifier.WriteTo(bW.BaseStream);
if (string.IsNullOrEmpty(_hostName))
if (string.IsNullOrWhiteSpace(_hostName))
bW.Write((byte)0);
else
bW.WriteShortString(_hostName);
@@ -150,7 +150,7 @@ namespace DnsServerCore.Dhcp
bW.WriteBuffer(_hardwareAddress);
_address.WriteTo(bW);
if (string.IsNullOrEmpty(_comments))
if (string.IsNullOrWhiteSpace(_comments))
bW.Write((byte)0);
else
bW.WriteShortString(_comments);
@@ -163,7 +163,7 @@ namespace DnsServerCore.Dhcp
{
string hardwareAddress = BitConverter.ToString(_hardwareAddress);
if (string.IsNullOrEmpty(_hostName))
if (string.IsNullOrWhiteSpace(_hostName))
return "[" + hardwareAddress + "]";
return _hostName + " [" + hardwareAddress + "]";

View File

@@ -112,7 +112,7 @@ namespace DnsServerCore.Dhcp
_offerDelayTime = bR.ReadUInt16();
_domainName = bR.ReadShortString();
if (_domainName == "")
if (string.IsNullOrWhiteSpace(_domainName))
_domainName = null;
_dnsTtl = bR.ReadUInt32();
@@ -313,7 +313,7 @@ namespace DnsServerCore.Dhcp
string clientDomainName;
if (request.ClientFullyQualifiedDomainName.DomainName == "")
if (string.IsNullOrWhiteSpace(request.ClientFullyQualifiedDomainName.DomainName))
{
//client domain empty and expects server for a fqdn domain name
if (request.HostName == null)
@@ -822,13 +822,13 @@ namespace DnsServerCore.Dhcp
public void ChangeNetwork(IPAddress startingAddress, IPAddress endingAddress, IPAddress subnetMask)
{
if (startingAddress.AddressFamily != AddressFamily.InterNetwork)
throw new ArgumentException("Address family not supported.", "startingAddress");
throw new ArgumentException("Address family not supported.", nameof(startingAddress));
if (endingAddress.AddressFamily != AddressFamily.InterNetwork)
throw new ArgumentException("Address family not supported.", "endingAddress");
throw new ArgumentException("Address family not supported.", nameof(endingAddress));
if (subnetMask.AddressFamily != AddressFamily.InterNetwork)
throw new ArgumentException("Address family not supported.", "subnetMask");
throw new ArgumentException("Address family not supported.", nameof(subnetMask));
uint startingAddressNumber = startingAddress.ConvertIpToNumber();
uint endingAddressNumber = endingAddress.ConvertIpToNumber();
@@ -876,7 +876,7 @@ namespace DnsServerCore.Dhcp
bW.Write(_leaseTimeMinutes);
bW.Write(_offerDelayTime);
if (string.IsNullOrEmpty(_domainName))
if (string.IsNullOrWhiteSpace(_domainName))
bW.Write((byte)0);
else
bW.WriteShortString(_domainName);

View File

@@ -875,7 +875,7 @@ namespace DnsServerCore.Dns
case DnsResourceRecordType.AXFR:
case DnsResourceRecordType.MAILB:
case DnsResourceRecordType.MAILA:
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.Refused, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null);
return new DnsDatagram(new DnsHeader(request.Header.Identifier, true, DnsOpcode.StandardQuery, false, false, request.Header.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NotImplemented, request.Header.QDCOUNT, 0, 0, 0), request.Question, null, null, null);
}
try
@@ -1570,23 +1570,25 @@ namespace DnsServerCore.Dns
{
IPEndPoint dnsEP = new IPEndPoint(_localIPs[i], 53);
Socket udpListener = new Socket(dnsEP.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
#region this code ignores ICMP port unreachable responses which creates SocketException in ReceiveFrom()
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
const uint IOC_IN = 0x80000000;
const uint IOC_VENDOR = 0x18000000;
const uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
udpListener.IOControl((IOControlCode)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
}
#endregion
Socket udpListener = null;
try
{
udpListener = new Socket(dnsEP.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
#region this code ignores ICMP port unreachable responses which creates SocketException in ReceiveFrom()
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
const uint IOC_IN = 0x80000000;
const uint IOC_VENDOR = 0x18000000;
const uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
udpListener.IOControl((IOControlCode)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
}
#endregion
udpListener.Bind(dnsEP);
_udpListeners.Add(udpListener);
@@ -1601,13 +1603,16 @@ namespace DnsServerCore.Dns
if (log != null)
log.Write(dnsEP, DnsTransportProtocol.Udp, "DNS Server failed to bind.\r\n" + ex.ToString());
udpListener.Dispose();
if (udpListener != null)
udpListener.Dispose();
}
Socket tcpListener = new Socket(dnsEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Socket tcpListener = null;
try
{
tcpListener = new Socket(dnsEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
tcpListener.Bind(dnsEP);
tcpListener.Listen(100);
@@ -1623,16 +1628,19 @@ namespace DnsServerCore.Dns
if (log != null)
log.Write(dnsEP, DnsTransportProtocol.Tcp, "DNS Server failed to bind.\r\n" + ex.ToString());
tcpListener.Dispose();
if (tcpListener != null)
tcpListener.Dispose();
}
if (_enableDnsOverHttp)
{
IPEndPoint httpEP = new IPEndPoint(_localIPs[i], 8053);
Socket httpListener = new Socket(httpEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Socket httpListener = null;
try
{
httpListener = new Socket(httpEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
httpListener.Bind(httpEP);
httpListener.Listen(100);
@@ -1650,17 +1658,20 @@ namespace DnsServerCore.Dns
if (log != null)
log.Write(httpEP, DnsTransportProtocol.Https, "DNS Server failed to bind.\r\n" + ex.ToString());
httpListener.Dispose();
if (httpListener != null)
httpListener.Dispose();
}
}
if (_enableDnsOverTls && (_certificate != null))
{
IPEndPoint tlsEP = new IPEndPoint(_localIPs[i], 853);
Socket tlsListener = new Socket(tlsEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Socket tlsListener = null;
try
{
tlsListener = new Socket(tlsEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
tlsListener.Bind(tlsEP);
tlsListener.Listen(100);
@@ -1676,17 +1687,20 @@ namespace DnsServerCore.Dns
if (log != null)
log.Write(tlsEP, DnsTransportProtocol.Tls, "DNS Server failed to bind.\r\n" + ex.ToString());
tlsListener.Dispose();
if (tlsListener != null)
tlsListener.Dispose();
}
}
if (_enableDnsOverHttps && (_certificate != null))
{
IPEndPoint httpsEP = new IPEndPoint(_localIPs[i], 443);
Socket httpsListener = new Socket(httpsEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Socket httpsListener = null;
try
{
httpsListener = new Socket(httpsEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
httpsListener.Bind(httpsEP);
httpsListener.Listen(100);
@@ -1704,7 +1718,8 @@ namespace DnsServerCore.Dns
if (log != null)
log.Write(httpsEP, DnsTransportProtocol.Https, "DNS Server failed to bind.\r\n" + ex.ToString());
httpsListener.Dispose();
if (httpsListener != null)
httpsListener.Dispose();
}
}
}
@@ -1713,11 +1728,11 @@ namespace DnsServerCore.Dns
{
string serverDomain = _authoritativeZoneRoot.ServerDomain;
_authoritativeZoneRoot.SetRecords("resolver-associated-doh.arpa", DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(serverDomain, "hostmaster." + serverDomain, uint.Parse(DateTime.UtcNow.ToString("yyyyMMddHH")), 28800, 7200, 604800, 600) });
_authoritativeZoneRoot.SetRecords("resolver-associated-doh.arpa", DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(serverDomain, "hostmaster." + serverDomain, 1, 14400, 3600, 604800, 900) });
_authoritativeZoneRoot.SetRecords("resolver-associated-doh.arpa", DnsResourceRecordType.NS, 14400, new DnsResourceRecordData[] { new DnsNSRecord(serverDomain) });
_authoritativeZoneRoot.SetRecords("resolver-associated-doh.arpa", DnsResourceRecordType.TXT, 60, new DnsResourceRecordData[] { new DnsTXTRecord("https://" + serverDomain + "/dns-query{?dns}") });
_authoritativeZoneRoot.SetRecords("resolver-addresses.arpa", DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(serverDomain, "hostmaster." + serverDomain, uint.Parse(DateTime.UtcNow.ToString("yyyyMMddHH")), 28800, 7200, 604800, 600) });
_authoritativeZoneRoot.SetRecords("resolver-addresses.arpa", DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(serverDomain, "hostmaster." + serverDomain, 1, 14400, 3600, 604800, 900) });
_authoritativeZoneRoot.SetRecords("resolver-addresses.arpa", DnsResourceRecordType.NS, 14400, new DnsResourceRecordData[] { new DnsNSRecord(serverDomain) });
_authoritativeZoneRoot.SetRecords("resolver-addresses.arpa", DnsResourceRecordType.CNAME, 60, new DnsResourceRecordData[] { new DnsCNAMERecord(serverDomain) });

View File

@@ -1085,7 +1085,7 @@ namespace DnsServerCore.Dns
case StatsResponseType.NoError:
if (!"blocked".Equals(responseTag)) //skip blocked domains
{
_queryDomains.GetOrAdd(query.Name, new Counter()).Increment();
_queryDomains.GetOrAdd(query.Name.ToLower(), new Counter()).Increment();
_queries.GetOrAdd(query, new Counter()).Increment();
}
@@ -1120,7 +1120,7 @@ namespace DnsServerCore.Dns
break;
case "blocked":
_queryBlockedDomains.GetOrAdd(query.Name, new Counter()).Increment();
_queryBlockedDomains.GetOrAdd(query.Name.ToLower(), new Counter()).Increment();
Interlocked.Increment(ref _totalBlocked);
break;
}

View File

@@ -454,6 +454,12 @@ namespace DnsServerCore
jsonWriter.WritePropertyName("stackTrace");
jsonWriter.WriteValue(ex.StackTrace);
if (ex.InnerException != null)
{
jsonWriter.WritePropertyName("innerErrorMessage");
jsonWriter.WriteValue(ex.InnerException.Message);
}
jsonWriter.WriteEndObject();
jsonWriter.Flush();
}
@@ -1312,6 +1318,10 @@ namespace DnsServerCore
}
}
string strForwarderProtocol = request.QueryString["forwarderProtocol"];
if (!string.IsNullOrEmpty(strForwarderProtocol))
_dnsServer.ForwarderProtocol = (DnsTransportProtocol)Enum.Parse(typeof(DnsTransportProtocol), strForwarderProtocol, true);
string strForwarders = request.QueryString["forwarders"];
if (!string.IsNullOrEmpty(strForwarders))
{
@@ -1325,16 +1335,17 @@ namespace DnsServerCore
NameServerAddress[] forwarders = new NameServerAddress[strForwardersList.Length];
for (int i = 0; i < strForwardersList.Length; i++)
{
if ((_dnsServer.ForwarderProtocol == DnsTransportProtocol.Tls) && IPAddress.TryParse(strForwardersList[i], out _))
strForwardersList[i] += ":853";
forwarders[i] = new NameServerAddress(strForwardersList[i]);
}
_dnsServer.Forwarders = forwarders;
}
}
string strForwarderProtocol = request.QueryString["forwarderProtocol"];
if (!string.IsNullOrEmpty(strForwarderProtocol))
_dnsServer.ForwarderProtocol = (DnsTransportProtocol)Enum.Parse(typeof(DnsTransportProtocol), strForwarderProtocol, true);
string strBlockListUrls = request.QueryString["blockListUrls"];
if (!string.IsNullOrEmpty(strBlockListUrls))
{
@@ -1986,7 +1997,7 @@ namespace DnsServerCore
private void AllowZone(string domain)
{
_dnsServer.AllowedZoneRoot.SetRecords(domain, DnsResourceRecordType.SOA, 60, new DnsResourceRecordData[] { new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, 1, 28800, 7200, 604800, 600) });
_dnsServer.AllowedZoneRoot.SetRecords(domain, DnsResourceRecordType.SOA, 60, new DnsResourceRecordData[] { new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, 1, 14400, 3600, 604800, 900) });
}
private void ListBlockedZones(HttpListenerRequest request, JsonTextWriter jsonWriter)
@@ -2165,7 +2176,7 @@ namespace DnsServerCore
{
blockedZoneRoot.SetRecords(new DnsResourceRecord[]
{
new DnsResourceRecord(domain, DnsResourceRecordType.SOA, DnsClass.IN, 60, new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, 1, 28800, 7200, 604800, 600)),
new DnsResourceRecord(domain, DnsResourceRecordType.SOA, DnsClass.IN, 60, new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, 1, 14400, 3600, 604800, 900)),
new DnsResourceRecord(domain, DnsResourceRecordType.A, DnsClass.IN, 60, new DnsARecord(IPAddress.Any)),
new DnsResourceRecord(domain, DnsResourceRecordType.AAAA, DnsClass.IN, 60, new DnsAAAARecord(IPAddress.IPv6Any))
});
@@ -2229,7 +2240,7 @@ namespace DnsServerCore
private void CreateZone(string domain)
{
_dnsServer.AuthoritativeZoneRoot.SetRecords(domain, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, uint.Parse(DateTime.UtcNow.ToString("yyyyMMddHH")), 28800, 7200, 604800, 600) });
_dnsServer.AuthoritativeZoneRoot.SetRecords(domain, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, 1, 14400, 3600, 604800, 900) });
_dnsServer.AuthoritativeZoneRoot.SetRecords(domain, DnsResourceRecordType.NS, 14400, new DnsResourceRecordData[] { new DnsNSRecord(_dnsServer.ServerDomain) });
}
@@ -2904,6 +2915,8 @@ namespace DnsServerCore
if (string.IsNullOrEmpty(domain))
throw new WebServiceException("Parameter 'domain' missing.");
domain = domain.Trim();
if (domain.EndsWith("."))
domain = domain.Substring(0, domain.Length - 1);
@@ -2934,8 +2947,8 @@ namespace DnsServerCore
{
DnsQuestionRecord question;
if (type == DnsResourceRecordType.PTR)
question = new DnsQuestionRecord(IPAddress.Parse(domain), DnsClass.IN);
if ((type == DnsResourceRecordType.PTR) && IPAddress.TryParse(domain, out IPAddress address))
question = new DnsQuestionRecord(address, DnsClass.IN);
else
question = new DnsQuestionRecord(domain, type, DnsClass.IN);
@@ -3012,7 +3025,7 @@ namespace DnsServerCore
}
if (!SOARecordExists)
_dnsServer.AuthoritativeZoneRoot.SetRecords(domain, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, uint.Parse(DateTime.UtcNow.ToString("yyyyMMddHH")), 28800, 7200, 604800, 600) });
_dnsServer.AuthoritativeZoneRoot.SetRecords(domain, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_dnsServer.ServerDomain, "hostmaster." + _dnsServer.ServerDomain, 1, 14400, 3600, 604800, 900) });
}
_dnsServer.AuthoritativeZoneRoot.SetRecords(recordsToSet);

View File

@@ -25,7 +25,7 @@ function htmlDecode(value) {
return $('<div/>').html(value).text();
}
function HTTPRequest(url, data, success, error, invalidToken, objAlertPlaceholder, objLoaderPlaceholder, dataIsFormData, dataContentType, dontHideAlert) {
function HTTPRequest(url, data, success, error, invalidToken, objAlertPlaceholder, objLoaderPlaceholder, dataIsFormData, dataContentType, dontHideAlert, showInnerError) {
var async = false;
var finalUrl;
@@ -61,6 +61,12 @@ function HTTPRequest(url, data, success, error, invalidToken, objAlertPlaceholde
if ((dontHideAlert == null) || !dontHideAlert)
hideAlert(objAlertPlaceholder);
if (showInnerError == null)
showInnerError = arguments[0].showInnerError;
if (showInnerError == null)
showInnerError = false;
if (objLoaderPlaceholder == null)
objLoaderPlaceholder = arguments[0].objLoaderPlaceholder;
@@ -117,7 +123,7 @@ function HTTPRequest(url, data, success, error, invalidToken, objAlertPlaceholde
break;
case "error":
showAlert("danger", "Error!", responseJson.errorMessage, objAlertPlaceholder);
showAlert("danger", "Error!", responseJson.errorMessage + (showInnerError && (responseJson.innerErrorMessage != null) ? " " + responseJson.innerErrorMessage : ""), objAlertPlaceholder);
if (error != null)
error();

View File

@@ -1563,7 +1563,8 @@ function resolveQuery(importRecords) {
btnOther.prop("disabled", false);
showPageLogin();
},
objLoaderPlaceholder: divDnsClientLoader
objLoaderPlaceholder: divDnsClientLoader,
showInnerError: true
});
//add server name to list if doesnt exists

View File

@@ -56,10 +56,11 @@ Make contribution to Technitium by becoming a Patron and help making new softwar
[Become a Patron now!](https://www.patreon.com/technitium)
# Blog Posts
- [Scott Hanselman: Exploring DNS with the .NET Core based Technitium DNS Server](https://www.hanselman.com/blog/ExploringDNSWithTheNETCoreBasedTechnitiumDNSServer.aspx) (April 2019)
- [phra's blog: Exfiltrate Like a Pro: Using DNS over HTTPS as a C2 Channel](https://iwantmore.pizza/posts/dnscat2-over-doh.html) (Aug 2019)
- [Scott Hanselman: Exploring DNS with the .NET Core based Technitium DNS Server](https://www.hanselman.com/blog/ExploringDNSWithTheNETCoreBasedTechnitiumDNSServer.aspx) (Apr 2019)
- [Technitium Blog: Turn Raspberry Pi Into Network Wide DNS Server](https://blog.technitium.com/2019/01/turn-raspberry-pi-into-network-wide-dns.html) (Jan 2019)
- [Technitium Blog: Blocking Internet Ads Using DNS Sinkhole](https://blog.technitium.com/2018/10/blocking-internet-ads-using-dns-sinkhole.html) (Oct 2018)
- [Technitium Blog: Configuring DNS Server For Privacy & Security](https://blog.technitium.com/2018/06/configuring-dns-server-for-privacy.html) (June 2018)
- [Technitium Blog: Technitium DNS Server v1.3 Released!](https://blog.technitium.com/2018/06/technitium-dns-server-v13-released.html) (June 2018)
- [Technitium Blog: Configuring DNS Server For Privacy & Security](https://blog.technitium.com/2018/06/configuring-dns-server-for-privacy.html) (Jun 2018)
- [Technitium Blog: Technitium DNS Server v1.3 Released!](https://blog.technitium.com/2018/06/technitium-dns-server-v13-released.html) (Jun 2018)
- [Technitium Blog: Running Technitium DNS Server on Ubuntu Linux](https://blog.technitium.com/2017/11/running-dns-server-on-ubuntu-linux.html) (Nov 2017)
- [Technitium Blog: Technitium DNS Server Released!](https://blog.technitium.com/2017/11/technitium-dns-server-released.html) (Nov 2017)