diff --git a/DnsServerCore/DnsWebService.cs b/DnsServerCore/DnsWebService.cs
new file mode 100644
index 00000000..11cee5cd
--- /dev/null
+++ b/DnsServerCore/DnsWebService.cs
@@ -0,0 +1,605 @@
+/*
+Technitium Library
+Copyright (C) 2017 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.Text;
+using System.Threading;
+using TechnitiumLibrary.IO;
+using TechnitiumLibrary.Net;
+using TechnitiumLibrary.Net.Dns;
+
+namespace DnsServerCore
+{
+ public class DnsWebService
+ {
+ #region variables
+
+ readonly string _serverDomain;
+ readonly DnsServer _dnsServer;
+
+ readonly HttpListener _webService;
+ readonly Thread _webServiceThread;
+
+ #endregion
+
+ #region constructor
+
+ public DnsWebService(string serverDomain)
+ {
+ _serverDomain = serverDomain;
+ _dnsServer = new DnsServer();
+
+ _webService = new HttpListener();
+ _webService.Prefixes.Add("http://localhost:5380/");
+ _webService.Start();
+
+ _webServiceThread = new Thread(AcceptWebRequestAsync);
+ _webServiceThread.IsBackground = true;
+ _webServiceThread.Start();
+
+ CreateZone("technitium.com");
+ }
+
+ #endregion
+
+ #region private
+
+ private void AcceptWebRequestAsync(object state)
+ {
+ while (true)
+ {
+ HttpListenerContext context = _webService.GetContext();
+ ThreadPool.QueueUserWorkItem(ProcessRequestAsync, new object[] { context.Request, context.Response });
+ }
+ }
+
+ private void ProcessRequestAsync(object state)
+ {
+ object[] parameters = state as object[];
+ HttpListenerRequest request = parameters[0] as HttpListenerRequest;
+ HttpListenerResponse response = parameters[1] as HttpListenerResponse;
+
+ try
+ {
+ Uri url = request.Url;
+ string path = url.AbsolutePath;
+
+ if (!path.StartsWith("/"))
+ {
+ Send404(response);
+ return;
+ }
+
+ if (path.StartsWith("/api/"))
+ {
+ response.ContentType = "application/json; charset=utf-8";
+ response.ContentEncoding = Encoding.UTF8;
+
+ using (JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(response.OutputStream)))
+ {
+ jsonWriter.WriteStartObject();
+
+ try
+ {
+ jsonWriter.WritePropertyName("response");
+ jsonWriter.WriteStartObject();
+
+ switch (path)
+ {
+ case "/api/listZones":
+ ListZones(jsonWriter);
+ break;
+
+ case "/api/createZone":
+ CreateZone(request.QueryString["domain"]);
+ break;
+
+ case "/api/deleteZone":
+ DeleteZone(request.QueryString["domain"]);
+ break;
+
+ case "/api/setRecords":
+ SetRecords(request);
+ break;
+
+ case "/api/getRecords":
+ GetRecords(request.QueryString["domain"], jsonWriter);
+ break;
+
+ default:
+ throw new Exception("Invalid command: " + path);
+ }
+
+ jsonWriter.WriteEndObject();
+
+ jsonWriter.WritePropertyName("status");
+ jsonWriter.WriteValue("ok");
+ }
+ catch (Exception ex)
+ {
+ jsonWriter.WritePropertyName("status");
+ jsonWriter.WriteValue("error");
+
+ jsonWriter.WritePropertyName("errorMessage");
+ jsonWriter.WriteValue(ex.Message);
+
+ jsonWriter.WritePropertyName("stackTrace");
+ jsonWriter.WriteValue(ex.StackTrace);
+ }
+
+ jsonWriter.WriteEndObject();
+ }
+ }
+ else
+ {
+ if (path.Contains("/../"))
+ {
+ Send404(response);
+ return;
+ }
+
+ if (path == "/")
+ path = "/index.html";
+
+ path = "www" + path;
+
+ if (!File.Exists(path))
+ {
+ Send404(response);
+ return;
+ }
+
+ SendFile(response, path);
+ }
+ }
+ catch (Exception ex)
+ {
+ Send500(response, ex);
+ }
+ }
+
+ private void Send500(HttpListenerResponse response, Exception ex)
+ {
+ Send500(response, ex.ToString());
+ }
+
+ private void Send500(HttpListenerResponse response, string message)
+ {
+ byte[] buffer = Encoding.UTF8.GetBytes("
500 Internal Server Error
" + message + "
");
+
+ response.StatusCode = 500;
+ response.ContentType = "text/html";
+ response.ContentLength64 = buffer.Length;
+
+ using (Stream stream = response.OutputStream)
+ {
+ stream.Write(buffer, 0, buffer.Length);
+ }
+ }
+
+ private void Send404(HttpListenerResponse response)
+ {
+ byte[] buffer = Encoding.UTF8.GetBytes("404 Not Found
");
+
+ response.StatusCode = 404;
+ response.ContentType = "text/html";
+ response.ContentLength64 = buffer.Length;
+
+ using (Stream stream = response.OutputStream)
+ {
+ stream.Write(buffer, 0, buffer.Length);
+ }
+ }
+
+ private void SendFile(HttpListenerResponse response, string path)
+ {
+ using (FileStream fS = new FileStream(path, FileMode.Open, FileAccess.Read))
+ {
+ response.ContentType = WebUtilities.GetContentType(path).MediaType;
+ response.ContentLength64 = fS.Length;
+
+ using (Stream stream = response.OutputStream)
+ {
+ OffsetStream.StreamCopy(fS, stream);
+ }
+ }
+ }
+
+ private void ListZones(JsonTextWriter jsonWriter)
+ {
+ string[] zones = _dnsServer.AuthoritativeZoneRoot.ListAuthoritativeZones();
+
+ jsonWriter.WritePropertyName("zones");
+ jsonWriter.WriteStartArray();
+
+ foreach (string zone in zones)
+ {
+ jsonWriter.WriteValue(zone);
+ }
+
+ jsonWriter.WriteEndArray();
+ }
+
+ private void CreateZone(string domain)
+ {
+ if (string.IsNullOrEmpty(domain))
+ throw new Exception("Parameter 'domain' missing.");
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, DnsResourceRecordType.SOA, 14400, new DnsResourceRecordData[] { new DnsSOARecord(_serverDomain, "admin." + _serverDomain, uint.Parse(DateTime.UtcNow.ToString("yyyymmddHH")), 28800, 7200, 604800, 600) });
+ }
+
+ private void DeleteZone(string domain)
+ {
+ if (string.IsNullOrEmpty(domain))
+ throw new Exception("Parameter 'domain' missing.");
+
+ _dnsServer.AuthoritativeZoneRoot.DeleteZone(domain);
+ }
+
+ private void SetRecords(HttpListenerRequest request)
+ {
+ string domain = request.QueryString["domain"];
+ if (string.IsNullOrEmpty(domain))
+ throw new Exception("Parameter 'domain' missing.");
+
+ string strType = request.QueryString["type"];
+ if (string.IsNullOrEmpty(strType))
+ throw new Exception("Parameter 'type' missing.");
+
+ DnsResourceRecordType type = (DnsResourceRecordType)Enum.Parse(typeof(DnsResourceRecordType), strType);
+
+ uint ttl;
+ string strTtl = request.QueryString["ttl"];
+ if (string.IsNullOrEmpty(strTtl))
+ ttl = 3600;
+ else
+ ttl = uint.Parse(strTtl);
+
+ switch (type)
+ {
+ case DnsResourceRecordType.A:
+ {
+ string ip = request.QueryString["ip"];
+ if (string.IsNullOrEmpty(ip))
+ throw new Exception("Parameter 'ip' missing.");
+
+ string[] strIPs = ip.Split(',');
+ DnsARecord[] records = new DnsARecord[strIPs.Length];
+
+ for (int i = 0; i < strIPs.Length; i++)
+ records[i] = new DnsARecord(IPAddress.Parse(strIPs[i]));
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, type, ttl, records);
+ }
+ break;
+
+ case DnsResourceRecordType.AAAA:
+ {
+ string ip = request.QueryString["ip"];
+ if (string.IsNullOrEmpty(ip))
+ throw new Exception("Parameter 'ip' missing.");
+
+ string[] strIPs = ip.Split(',');
+ DnsAAAARecord[] records = new DnsAAAARecord[strIPs.Length];
+
+ for (int i = 0; i < strIPs.Length; i++)
+ records[i] = new DnsAAAARecord(IPAddress.Parse(strIPs[i]));
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, type, ttl, records);
+ }
+ break;
+
+ case DnsResourceRecordType.SOA:
+ {
+ string masterNameServer = request.QueryString["masterNameServer"];
+ if (string.IsNullOrEmpty(masterNameServer))
+ throw new Exception("Parameter 'masterNameServer' missing.");
+
+ string responsiblePerson = request.QueryString["responsiblePerson"];
+ if (string.IsNullOrEmpty(responsiblePerson))
+ throw new Exception("Parameter 'responsiblePerson' missing.");
+
+ string serial = request.QueryString["serial"];
+ if (string.IsNullOrEmpty(serial))
+ throw new Exception("Parameter 'serial' missing.");
+
+ string refresh = request.QueryString["refresh"];
+ if (string.IsNullOrEmpty(refresh))
+ throw new Exception("Parameter 'refresh' missing.");
+
+ string retry = request.QueryString["retry"];
+ if (string.IsNullOrEmpty(retry))
+ throw new Exception("Parameter 'retry' missing.");
+
+ string expire = request.QueryString["expire"];
+ if (string.IsNullOrEmpty(expire))
+ throw new Exception("Parameter 'expire' missing.");
+
+ string minimum = request.QueryString["minimum"];
+ if (string.IsNullOrEmpty(minimum))
+ throw new Exception("Parameter 'minimum' missing.");
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, type, ttl, new DnsResourceRecordData[] { new DnsSOARecord(masterNameServer, responsiblePerson, uint.Parse(serial), uint.Parse(refresh), uint.Parse(retry), uint.Parse(expire), uint.Parse(minimum)) });
+ }
+ break;
+
+ case DnsResourceRecordType.PTR:
+ {
+ string ptrDomain = request.QueryString["ptrDomain"];
+ if (string.IsNullOrEmpty(ptrDomain))
+ throw new Exception("Parameter 'ptrDomain' missing.");
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, type, ttl, new DnsResourceRecordData[] { new DnsPTRRecord(ptrDomain) });
+ }
+ break;
+
+ case DnsResourceRecordType.MX:
+ {
+ string mxDomain = request.QueryString["mxDomain"];
+ if (string.IsNullOrEmpty(mxDomain))
+ throw new Exception("Parameter 'mxDomain' missing.");
+
+ string[] mxDomainList = mxDomain.Split(',');
+ DnsMXRecord[] records = new DnsMXRecord[mxDomain.Length];
+
+ for (int i = 0; i < mxDomainList.Length; i++)
+ {
+ string[] strMxData = mxDomainList[i].Split(':');
+
+ records[i] = new DnsMXRecord(ushort.Parse(strMxData[0]), strMxData[1]);
+ }
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, type, ttl, records);
+ }
+ break;
+
+ case DnsResourceRecordType.TXT:
+ {
+ string txtData = request.QueryString["txtData"];
+ if (string.IsNullOrEmpty(txtData))
+ throw new Exception("Parameter 'txtData' missing.");
+
+ string[] txtDataList = txtData.Split(',');
+ DnsTXTRecord[] records = new DnsTXTRecord[txtData.Length];
+
+ for (int i = 0; i < txtDataList.Length; i++)
+ records[i] = new DnsTXTRecord(txtDataList[i]);
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, type, ttl, records);
+ }
+ break;
+
+ case DnsResourceRecordType.NS:
+ {
+ string nsDomain = request.QueryString["nsDomain"];
+ if (string.IsNullOrEmpty(nsDomain))
+ throw new Exception("Parameter 'nsDomain' missing.");
+
+ string[] nsDomains = nsDomain.Split(',');
+ DnsNSRecord[] records = new DnsNSRecord[nsDomain.Length];
+
+ for (int i = 0; i < nsDomains.Length; i++)
+ records[i] = new DnsNSRecord(nsDomains[i]);
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, type, ttl, records);
+ }
+ break;
+
+ case DnsResourceRecordType.CNAME:
+ {
+ string cnameDomain = request.QueryString["cnameDomain"];
+ if (string.IsNullOrEmpty(cnameDomain))
+ throw new Exception("Parameter 'cnameDomain' missing.");
+
+ _dnsServer.AuthoritativeZoneRoot.SetRecords(domain, type, ttl, new DnsResourceRecordData[] { new DnsCNAMERecord(cnameDomain) });
+ }
+ break;
+ }
+ }
+
+ private void GetRecords(string domain, JsonTextWriter jsonWriter)
+ {
+ if (string.IsNullOrEmpty(domain))
+ throw new Exception("Parameter 'domain' missing.");
+
+ DnsResourceRecord[] records = _dnsServer.AuthoritativeZoneRoot.GetRecords(domain);
+
+ Dictionary>> groupedByDomainRecords = Zone.GroupRecords(records);
+
+ jsonWriter.WritePropertyName("records");
+ jsonWriter.WriteStartArray();
+
+ foreach (KeyValuePair>> groupedByTypeRecords in groupedByDomainRecords)
+ {
+ string recordName = groupedByTypeRecords.Key;
+
+ foreach (KeyValuePair> groupedRecords in groupedByTypeRecords.Value)
+ {
+ DnsResourceRecordType type = groupedRecords.Key;
+ DnsResourceRecord[] resourceRecords = groupedRecords.Value.ToArray();
+
+ jsonWriter.WriteStartObject();
+
+ jsonWriter.WritePropertyName("name");
+ jsonWriter.WriteValue(recordName);
+
+ jsonWriter.WritePropertyName("type");
+ jsonWriter.WriteValue(type.ToString());
+
+ jsonWriter.WritePropertyName("ttl");
+ jsonWriter.WriteValue(resourceRecords[0].TTLValue);
+
+ jsonWriter.WritePropertyName("rData");
+ jsonWriter.WriteStartObject();
+
+ switch (type)
+ {
+ case DnsResourceRecordType.A:
+ {
+ jsonWriter.WritePropertyName("ipAddress");
+ jsonWriter.WriteStartArray();
+
+ foreach (DnsResourceRecord record in resourceRecords)
+ jsonWriter.WriteValue((record.RDATA as DnsARecord).IPAddress);
+
+ jsonWriter.WriteEndArray();
+ }
+ break;
+
+ case DnsResourceRecordType.AAAA:
+ {
+ jsonWriter.WritePropertyName("ipAddress");
+ jsonWriter.WriteStartArray();
+
+ foreach (DnsResourceRecord record in resourceRecords)
+ jsonWriter.WriteValue((record.RDATA as DnsAAAARecord).IPAddress);
+
+ jsonWriter.WriteEndArray();
+ }
+ break;
+
+ case DnsResourceRecordType.SOA:
+ {
+ DnsSOARecord rdata = resourceRecords[0].RDATA as DnsSOARecord;
+
+ jsonWriter.WritePropertyName("masterNameServer");
+ jsonWriter.WriteValue(rdata.MasterNameServer);
+
+ jsonWriter.WritePropertyName("responsiblePerson");
+ jsonWriter.WriteValue(rdata.ResponsiblePerson);
+
+ jsonWriter.WritePropertyName("serial");
+ jsonWriter.WriteValue(rdata.Serial);
+
+ jsonWriter.WritePropertyName("refresh");
+ jsonWriter.WriteValue(rdata.Refresh);
+
+ jsonWriter.WritePropertyName("retry");
+ jsonWriter.WriteValue(rdata.Retry);
+
+ jsonWriter.WritePropertyName("expire");
+ jsonWriter.WriteValue(rdata.Expire);
+
+ jsonWriter.WritePropertyName("minimum");
+ jsonWriter.WriteValue(rdata.Minimum);
+ }
+ break;
+
+ case DnsResourceRecordType.PTR:
+ {
+ DnsPTRRecord rdata = resourceRecords[0].RDATA as DnsPTRRecord;
+
+ jsonWriter.WritePropertyName("domain");
+ jsonWriter.WriteValue(rdata.PTRDomainName);
+ }
+ break;
+
+ case DnsResourceRecordType.MX:
+ {
+ jsonWriter.WritePropertyName("mxData");
+ jsonWriter.WriteStartArray();
+
+ foreach (DnsResourceRecord record in resourceRecords)
+ {
+ DnsMXRecord rdata = record.RDATA as DnsMXRecord;
+
+ jsonWriter.WriteStartObject();
+
+ jsonWriter.WritePropertyName("preference");
+ jsonWriter.WriteValue(rdata.Preference);
+
+ jsonWriter.WritePropertyName("exchange");
+ jsonWriter.WriteValue(rdata.Exchange);
+
+ jsonWriter.WriteEndObject();
+ }
+
+ jsonWriter.WriteEndArray();
+ }
+ break;
+
+ case DnsResourceRecordType.TXT:
+ {
+ jsonWriter.WritePropertyName("txtData");
+ jsonWriter.WriteStartArray();
+
+ foreach (DnsResourceRecord record in resourceRecords)
+ jsonWriter.WriteValue((record.RDATA as DnsTXTRecord).TXTData);
+
+ jsonWriter.WriteEndArray();
+ }
+ break;
+
+ case DnsResourceRecordType.NS:
+ {
+ jsonWriter.WritePropertyName("txtData");
+ jsonWriter.WriteStartArray();
+
+ foreach (DnsResourceRecord record in resourceRecords)
+ jsonWriter.WriteValue((record.RDATA as DnsNSRecord).NSDomainName);
+
+ jsonWriter.WriteEndArray();
+ }
+ break;
+
+ case DnsResourceRecordType.CNAME:
+ {
+ DnsCNAMERecord rdata = resourceRecords[0].RDATA as DnsCNAMERecord;
+
+ jsonWriter.WritePropertyName("domain");
+ jsonWriter.WriteValue(rdata.CNAMEDomainName);
+ }
+ break;
+
+ default:
+ {
+ jsonWriter.WritePropertyName("unknownData");
+ jsonWriter.WriteStartArray();
+
+ foreach (DnsResourceRecord record in resourceRecords)
+ {
+ using (MemoryStream mS = new MemoryStream())
+ {
+ record.RDATA.WriteTo(mS, new List());
+
+ jsonWriter.WriteValue(Convert.ToBase64String(mS.ToArray()));
+ }
+ }
+
+ jsonWriter.WriteEndArray();
+ }
+ break;
+ }
+
+ jsonWriter.WriteEndObject();
+
+ jsonWriter.WriteEndObject();
+ }
+ }
+
+ jsonWriter.WriteEndArray();
+ }
+
+ #endregion
+ }
+}