diff --git a/Apps/NxDomainOverrideApp/App.cs b/Apps/NxDomainOverrideApp/App.cs
new file mode 100644
index 00000000..34999b73
--- /dev/null
+++ b/Apps/NxDomainOverrideApp/App.cs
@@ -0,0 +1,252 @@
+/*
+Technitium DNS Server
+Copyright (C) 2024 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 DnsServerCore.ApplicationCommon;
+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;
+using TechnitiumLibrary.Net.Dns.ResourceRecords;
+
+namespace NxDomainOverride
+{
+ public sealed class App : IDnsApplication, IDnsPostProcessor
+ {
+ #region variables
+
+ bool _enableOverride;
+ uint _defaultTtl;
+ Dictionary _domainSetMap;
+ Dictionary _sets;
+
+ #endregion
+
+ #region IDisposable
+
+ public void Dispose()
+ {
+ //do nothing
+ }
+
+ #endregion
+
+ #region private
+
+ private static string GetParentZone(string domain)
+ {
+ int i = domain.IndexOf('.');
+ if (i > -1)
+ return domain.Substring(i + 1);
+
+ //dont return root zone
+ return null;
+ }
+
+ private bool TryGetMappedSets(string domain, out string[] setNames)
+ {
+ domain = domain.ToLowerInvariant();
+
+ string parent;
+
+ do
+ {
+ if (_domainSetMap.TryGetValue(domain, out setNames))
+ return true;
+
+ parent = GetParentZone(domain);
+ if (parent is null)
+ {
+ if (_domainSetMap.TryGetValue("*", out setNames))
+ return true;
+
+ break;
+ }
+
+ domain = "*." + parent;
+
+ if (_domainSetMap.TryGetValue(domain, out setNames))
+ return true;
+
+ domain = parent;
+ }
+ while (true);
+
+ return false;
+ }
+
+ #endregion
+
+ #region public
+
+ public Task InitializeAsync(IDnsServer dnsServer, string config)
+ {
+ using JsonDocument jsonDocument = JsonDocument.Parse(config);
+ JsonElement jsonConfig = jsonDocument.RootElement;
+
+ _enableOverride = jsonConfig.GetPropertyValue("enableOverride", true);
+ _defaultTtl = jsonConfig.GetPropertyValue("defaultTtl", 300u);
+
+ _domainSetMap = jsonConfig.ReadObjectAsMap("domainSetMap", delegate (string domain, JsonElement jsonSets)
+ {
+ string[] sets = jsonSets.GetArray();
+
+ return new Tuple(domain.ToLowerInvariant(), sets);
+ });
+
+ _sets = jsonConfig.ReadArrayAsMap("sets", delegate (JsonElement jsonSet)
+ {
+ Set set = new Set(jsonSet);
+
+ return new Tuple(set.Name, set);
+ });
+
+ return Task.CompletedTask;
+ }
+
+ public Task PostProcessAsync(DnsDatagram request, IPEndPoint remoteEP, DnsTransportProtocol protocol, DnsDatagram response)
+ {
+ if (!_enableOverride)
+ return Task.FromResult(response);
+
+ if (response.DnssecOk)
+ return Task.FromResult(response);
+
+ if (response.OPCODE != DnsOpcode.StandardQuery)
+ return Task.FromResult(response);
+
+ if (response.RCODE != DnsResponseCode.NxDomain)
+ return Task.FromResult(response);
+
+ DnsQuestionRecord question = request.Question[0];
+
+ switch (question.Type)
+ {
+ case DnsResourceRecordType.A:
+ case DnsResourceRecordType.AAAA:
+ break;
+
+ default:
+ //NO DATA response
+ return Task.FromResult(new DnsDatagram(response.Identifier, true, response.OPCODE, response.AuthoritativeAnswer, response.Truncation, response.RecursionDesired, response.RecursionAvailable, response.AuthenticData, response.CheckingDisabled, DnsResponseCode.NoError, response.Question, response.Answer, response.Authority, response.Additional) { Tag = response.Tag });
+ }
+
+ string nxDomain = question.Name;
+
+ foreach (DnsResourceRecord record in response.Answer)
+ {
+ if (record.Type == DnsResourceRecordType.CNAME)
+ nxDomain = (record.RDATA as DnsCNAMERecordData).Domain;
+ }
+
+ if (!TryGetMappedSets(nxDomain, out string[] setNames))
+ return Task.FromResult(response);
+
+ List newAnswer = new List();
+ newAnswer.AddRange(response.Answer);
+
+ foreach (string setName in setNames)
+ {
+ if (_sets.TryGetValue(setName, out Set set))
+ {
+ switch (question.Type)
+ {
+ case DnsResourceRecordType.A:
+ foreach (DnsResourceRecordData rdata in set.RecordDataAddresses)
+ {
+ if (rdata is DnsARecordData)
+ newAnswer.Add(new DnsResourceRecord(nxDomain, DnsResourceRecordType.A, DnsClass.IN, _defaultTtl, rdata));
+ }
+ break;
+
+ case DnsResourceRecordType.AAAA:
+ foreach (DnsResourceRecordData rdata in set.RecordDataAddresses)
+ {
+ if (rdata is DnsAAAARecordData)
+ newAnswer.Add(new DnsResourceRecord(nxDomain, DnsResourceRecordType.AAAA, DnsClass.IN, _defaultTtl, rdata));
+ }
+ break;
+
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ return Task.FromResult(new DnsDatagram(response.Identifier, true, response.OPCODE, response.AuthoritativeAnswer, response.Truncation, response.RecursionDesired, response.RecursionAvailable, response.AuthenticData, response.CheckingDisabled, DnsResponseCode.NoError, response.Question, newAnswer) { Tag = response.Tag });
+ }
+
+ #endregion
+
+ #region properties
+
+ public string Description
+ { get { return "Overrides NX Domain response with custom A/AAAA record response for configured domain names."; } }
+
+ #endregion
+
+ class Set
+ {
+ #region variables
+
+ readonly string _name;
+ readonly DnsResourceRecordData[] _rdataAddresses;
+
+ #endregion
+
+ #region constructor
+
+ public Set(JsonElement jsonSet)
+ {
+ _name = jsonSet.GetProperty("name").GetString();
+ _rdataAddresses = jsonSet.ReadArray("addresses", delegate (string item)
+ {
+ IPAddress address = IPAddress.Parse(item);
+
+ switch (address.AddressFamily)
+ {
+ case AddressFamily.InterNetwork:
+ return new DnsARecordData(address);
+
+ case AddressFamily.InterNetworkV6:
+ return new DnsAAAARecordData(address);
+
+ default:
+ throw new NotSupportedException("Address family not supported: " + address.AddressFamily.ToString());
+ }
+ });
+ }
+
+ #endregion
+
+ #region properties
+
+ public string Name
+ { get { return _name; } }
+
+ public DnsResourceRecordData[] RecordDataAddresses
+ { get { return _rdataAddresses; } }
+
+ #endregion
+ }
+ }
+}
diff --git a/Apps/NxDomainOverrideApp/dnsApp.config b/Apps/NxDomainOverrideApp/dnsApp.config
new file mode 100644
index 00000000..b719056d
--- /dev/null
+++ b/Apps/NxDomainOverrideApp/dnsApp.config
@@ -0,0 +1,23 @@
+{
+ "enableOverride": true,
+ "defaultTtl": 300,
+ "domainSetMap": {
+ "*": ["set1"],
+ "example.com": ["set1", "set2"]
+ },
+ "sets": [
+ {
+ "name": "set1",
+ "addresses": [
+ "192.168.10.1"
+ ]
+ },
+ {
+ "name": "set2",
+ "addresses": [
+ "1.2.3.4",
+ "5.6.7.8"
+ ]
+ }
+ ]
+}