AuthZoneManager.cs 152 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663
  1. /*
  2. Technitium DNS Server
  3. Copyright (C) 2024 Shreyas Zare (shreyas@technitium.com)
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. using DnsServerCore.Dns.Dnssec;
  16. using DnsServerCore.Dns.ResourceRecords;
  17. using DnsServerCore.Dns.Trees;
  18. using DnsServerCore.Dns.Zones;
  19. using System;
  20. using System.Collections.Generic;
  21. using System.IO;
  22. using System.Net;
  23. using System.Net.Sockets;
  24. using System.Runtime.CompilerServices;
  25. using System.Text;
  26. using System.Threading;
  27. using System.Threading.Tasks;
  28. using TechnitiumLibrary;
  29. using TechnitiumLibrary.Net;
  30. using TechnitiumLibrary.Net.Dns;
  31. using TechnitiumLibrary.Net.Dns.ResourceRecords;
  32. namespace DnsServerCore.Dns.ZoneManagers
  33. {
  34. public sealed class AuthZoneManager : IDisposable
  35. {
  36. #region events
  37. public event EventHandler<SecondaryCatalogEventArgs> SecondaryCatalogZoneAdded;
  38. public event EventHandler<SecondaryCatalogEventArgs> SecondaryCatalogZoneRemoved;
  39. #endregion
  40. #region variables
  41. readonly DnsServer _dnsServer;
  42. string _serverDomain;
  43. bool _useSoaSerialDateScheme;
  44. readonly AuthZoneTree _root = new AuthZoneTree();
  45. readonly List<AuthZoneInfo> _zoneIndex = new List<AuthZoneInfo>(10);
  46. readonly List<AuthZoneInfo> _catalogZoneIndex = new List<AuthZoneInfo>(2);
  47. readonly ReaderWriterLockSlim _zoneIndexLock = new ReaderWriterLockSlim();
  48. readonly object _saveLock = new object();
  49. readonly Dictionary<string, object> _pendingSaveZones = new Dictionary<string, object>();
  50. readonly Timer _saveTimer;
  51. const int SAVE_TIMER_INITIAL_INTERVAL = 10000;
  52. #endregion
  53. #region constructor
  54. public AuthZoneManager(DnsServer dnsServer)
  55. {
  56. _dnsServer = dnsServer;
  57. _serverDomain = _dnsServer.ServerDomain;
  58. _saveTimer = new Timer(delegate (object state)
  59. {
  60. lock (_saveLock)
  61. {
  62. List<string> failedZones = new List<string>();
  63. foreach (KeyValuePair<string, object> pendingSaveZone in _pendingSaveZones)
  64. {
  65. try
  66. {
  67. SaveZoneFileInternal(pendingSaveZone.Key);
  68. }
  69. catch (Exception ex)
  70. {
  71. _dnsServer.LogManager.Write(ex);
  72. failedZones.Add(pendingSaveZone.Key);
  73. }
  74. }
  75. _pendingSaveZones.Clear();
  76. foreach (string zoneName in failedZones)
  77. _pendingSaveZones.TryAdd(zoneName, null);
  78. if (_pendingSaveZones.Count > 0)
  79. _saveTimer.Change(SAVE_TIMER_INITIAL_INTERVAL, Timeout.Infinite);
  80. }
  81. });
  82. }
  83. #endregion
  84. #region IDisposable
  85. bool _disposed;
  86. private void Dispose(bool disposing)
  87. {
  88. if (_disposed)
  89. return;
  90. if (disposing)
  91. {
  92. lock (_saveLock)
  93. {
  94. _saveTimer?.Dispose();
  95. try
  96. {
  97. foreach (KeyValuePair<string, object> pendingSaveZone in _pendingSaveZones)
  98. {
  99. try
  100. {
  101. SaveZoneFileInternal(pendingSaveZone.Key);
  102. }
  103. catch (Exception ex)
  104. {
  105. _dnsServer.LogManager.Write(ex);
  106. }
  107. }
  108. }
  109. finally
  110. {
  111. _pendingSaveZones.Clear();
  112. }
  113. }
  114. foreach (AuthZoneNode zoneNode in _root)
  115. zoneNode.Dispose();
  116. _zoneIndexLock.Dispose();
  117. }
  118. _disposed = true;
  119. }
  120. public void Dispose()
  121. {
  122. Dispose(true);
  123. }
  124. #endregion
  125. #region private
  126. internal void UpdateServerDomain(bool useBlockingAnswerTtl = false)
  127. {
  128. ThreadPool.QueueUserWorkItem(delegate (object state)
  129. {
  130. string serverDomain = _dnsServer.ServerDomain;
  131. //update authoritative zone SOA and NS records
  132. try
  133. {
  134. IReadOnlyList<AuthZoneInfo> zones = GetAllZones();
  135. foreach (AuthZoneInfo zone in zones)
  136. {
  137. if (zone.Type != AuthZoneType.Primary)
  138. continue;
  139. DnsResourceRecord record = zone.ApexZone.GetRecords(DnsResourceRecordType.SOA)[0];
  140. DnsSOARecordData soa = record.RDATA as DnsSOARecordData;
  141. uint ttl;
  142. uint minimum;
  143. if (useBlockingAnswerTtl)
  144. {
  145. ttl = _dnsServer.BlockingAnswerTtl;
  146. minimum = ttl;
  147. }
  148. else
  149. {
  150. ttl = record.TTL;
  151. minimum = soa.Minimum;
  152. }
  153. if (soa.PrimaryNameServer.Equals(_serverDomain, StringComparison.OrdinalIgnoreCase))
  154. {
  155. SetRecord(zone.Name, new DnsResourceRecord(record.Name, record.Type, DnsClass.IN, ttl, new DnsSOARecordData(serverDomain, soa.ResponsiblePerson, soa.Serial, soa.Refresh, soa.Retry, soa.Expire, minimum)));
  156. //update NS records
  157. IReadOnlyList<DnsResourceRecord> nsResourceRecords = zone.ApexZone.GetRecords(DnsResourceRecordType.NS);
  158. foreach (DnsResourceRecord nsResourceRecord in nsResourceRecords)
  159. {
  160. if ((nsResourceRecord.RDATA as DnsNSRecordData).NameServer.Equals(_serverDomain, StringComparison.OrdinalIgnoreCase))
  161. {
  162. UpdateRecord(zone.Name, nsResourceRecord, new DnsResourceRecord(nsResourceRecord.Name, nsResourceRecord.Type, nsResourceRecord.Class, nsResourceRecord.TTL, new DnsNSRecordData(serverDomain)) { Tag = nsResourceRecord.Tag });
  163. break;
  164. }
  165. }
  166. if (zone.Internal)
  167. continue; //dont save internal zones to disk
  168. SaveZoneFile(zone.Name);
  169. }
  170. }
  171. }
  172. catch (Exception ex)
  173. {
  174. _dnsServer.LogManager?.Write(ex);
  175. }
  176. //update server domain
  177. _serverDomain = serverDomain;
  178. });
  179. }
  180. private ApexZone CreateEmptyApexZone(AuthZoneInfo zoneInfo)
  181. {
  182. ApexZone apexZone;
  183. switch (zoneInfo.Type)
  184. {
  185. case AuthZoneType.Primary:
  186. apexZone = new PrimaryZone(_dnsServer, zoneInfo);
  187. break;
  188. case AuthZoneType.Secondary:
  189. apexZone = new SecondaryZone(_dnsServer, zoneInfo);
  190. break;
  191. case AuthZoneType.Stub:
  192. apexZone = new StubZone(_dnsServer, zoneInfo);
  193. break;
  194. case AuthZoneType.Forwarder:
  195. apexZone = new ForwarderZone(_dnsServer, zoneInfo);
  196. break;
  197. case AuthZoneType.SecondaryForwarder:
  198. apexZone = new SecondaryForwarderZone(_dnsServer, zoneInfo);
  199. break;
  200. case AuthZoneType.Catalog:
  201. apexZone = new CatalogZone(_dnsServer, zoneInfo);
  202. break;
  203. case AuthZoneType.SecondaryCatalog:
  204. SecondaryCatalogZone secondaryCatalogZone = new SecondaryCatalogZone(_dnsServer, zoneInfo);
  205. secondaryCatalogZone.ZoneAdded += SecondaryCatalogZoneAdded;
  206. secondaryCatalogZone.ZoneRemoved += SecondaryCatalogZoneRemoved;
  207. apexZone = secondaryCatalogZone;
  208. break;
  209. default:
  210. throw new InvalidDataException("DNS zone type not supported.");
  211. }
  212. if (_root.TryAdd(apexZone))
  213. return apexZone;
  214. throw new DnsServerException("Zone already exists: " + zoneInfo.DisplayName);
  215. }
  216. internal AuthZone GetOrAddSubDomainZone(string zoneName, string domain)
  217. {
  218. return _root.GetOrAddSubDomainZone(zoneName, domain, delegate ()
  219. {
  220. if (!_root.TryGet(zoneName, out ApexZone apexZone))
  221. throw new DnsServerException("Zone was not found for domain: " + domain);
  222. if (apexZone is PrimaryZone primaryZone)
  223. return new PrimarySubDomainZone(primaryZone, domain);
  224. else if (apexZone is SecondaryCatalogZone secondaryCatalogZone)
  225. return new SecondaryCatalogSubDomainZone(secondaryCatalogZone, domain);
  226. else if (apexZone is SecondaryZone secondaryZone)
  227. return new SecondarySubDomainZone(secondaryZone, domain);
  228. else if (apexZone is CatalogZone catalogZone)
  229. return new CatalogSubDomainZone(catalogZone, domain);
  230. else if (apexZone is ForwarderZone forwarderZone)
  231. return new ForwarderSubDomainZone(forwarderZone, domain);
  232. throw new DnsServerException("Zone cannot have sub domains.");
  233. });
  234. }
  235. internal IReadOnlyList<AuthZone> GetApexZoneWithSubDomainZones(string zoneName)
  236. {
  237. return _root.GetApexZoneWithSubDomainZones(zoneName);
  238. }
  239. internal AuthZone GetAuthZone(string zoneName, string domain)
  240. {
  241. return _root.GetAuthZone(zoneName, domain);
  242. }
  243. internal ApexZone GetApexZone(string zoneName)
  244. {
  245. return _root.GetApexZone(zoneName);
  246. }
  247. internal AuthZone FindPreviousSubDomainZone(string zoneName, string domain)
  248. {
  249. return _root.FindPreviousSubDomainZone(zoneName, domain);
  250. }
  251. internal AuthZone FindNextSubDomainZone(string zoneName, string domain)
  252. {
  253. return _root.FindNextSubDomainZone(zoneName, domain);
  254. }
  255. internal bool SubDomainExistsFor(string zoneName, string domain)
  256. {
  257. return _root.SubDomainExistsFor(zoneName, domain);
  258. }
  259. internal void RemoveSubDomainZone(string domain, bool removeAllSubDomains = false)
  260. {
  261. _root.TryRemove(domain, out SubDomainZone _, removeAllSubDomains);
  262. }
  263. internal static string GetParentZone(string domain)
  264. {
  265. int i = domain.IndexOf('.');
  266. if (i > -1)
  267. return domain.Substring(i + 1);
  268. //dont return root zone
  269. return null;
  270. }
  271. private static void ValidateZoneNameFor(string zoneName, string domain)
  272. {
  273. if (domain.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || domain.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase) || (zoneName.Length == 0))
  274. return;
  275. throw new DnsServerException("The domain name '" + domain + "' does not belong to the zone: " + zoneName);
  276. }
  277. private void ResolveCNAME(DnsQuestionRecord question, bool dnssecOk, DnsResourceRecord lastCNAME, List<DnsResourceRecord> answerRecords)
  278. {
  279. int queryCount = 0;
  280. do
  281. {
  282. string cnameDomain = (lastCNAME.RDATA as DnsCNAMERecordData).Domain;
  283. if (lastCNAME.Name.Equals(cnameDomain, StringComparison.OrdinalIgnoreCase))
  284. break; //loop detected
  285. if (!_root.TryGet(cnameDomain, out AuthZoneNode zoneNode))
  286. break;
  287. IReadOnlyList<DnsResourceRecord> records = zoneNode.QueryRecords(question.Type, dnssecOk);
  288. if (records.Count < 1)
  289. break;
  290. DnsResourceRecord lastRR = records[records.Count - 1];
  291. if (lastRR.Type != DnsResourceRecordType.CNAME)
  292. {
  293. answerRecords.AddRange(records);
  294. break;
  295. }
  296. foreach (DnsResourceRecord answerRecord in answerRecords)
  297. {
  298. if (answerRecord.Type != DnsResourceRecordType.CNAME)
  299. continue;
  300. if (answerRecord.RDATA.Equals(lastRR.RDATA))
  301. return; //loop detected
  302. }
  303. answerRecords.AddRange(records);
  304. lastCNAME = lastRR;
  305. }
  306. while (++queryCount < DnsServer.MAX_CNAME_HOPS);
  307. }
  308. private bool DoDNAMESubstitution(DnsQuestionRecord question, bool dnssecOk, IReadOnlyList<DnsResourceRecord> answer, out IReadOnlyList<DnsResourceRecord> newAnswer)
  309. {
  310. DnsResourceRecord dnameRR = answer[0];
  311. string result = (dnameRR.RDATA as DnsDNAMERecordData).Substitute(question.Name, dnameRR.Name);
  312. if (DnsClient.IsDomainNameValid(result))
  313. {
  314. DnsResourceRecord cnameRR = new DnsResourceRecord(question.Name, DnsResourceRecordType.CNAME, question.Class, dnameRR.TTL, new DnsCNAMERecordData(result));
  315. List<DnsResourceRecord> list = new List<DnsResourceRecord>(5);
  316. list.AddRange(answer);
  317. list.Add(cnameRR);
  318. ResolveCNAME(question, dnssecOk, cnameRR, list);
  319. newAnswer = list;
  320. return true;
  321. }
  322. else
  323. {
  324. newAnswer = answer;
  325. return false;
  326. }
  327. }
  328. private List<DnsResourceRecord> GetAdditionalRecords(IReadOnlyList<DnsResourceRecord> refRecords, bool dnssecOk)
  329. {
  330. List<DnsResourceRecord> additionalRecords = new List<DnsResourceRecord>(refRecords.Count);
  331. foreach (DnsResourceRecord refRecord in refRecords)
  332. {
  333. switch (refRecord.Type)
  334. {
  335. case DnsResourceRecordType.NS:
  336. IReadOnlyList<DnsResourceRecord> glueRecords = refRecord.GetAuthNSRecordInfo().GlueRecords;
  337. if (glueRecords is not null)
  338. {
  339. additionalRecords.AddRange(glueRecords);
  340. }
  341. else
  342. {
  343. ResolveAdditionalRecords(refRecord, (refRecord.RDATA as DnsNSRecordData).NameServer, dnssecOk, additionalRecords);
  344. }
  345. break;
  346. case DnsResourceRecordType.MX:
  347. ResolveAdditionalRecords(refRecord, (refRecord.RDATA as DnsMXRecordData).Exchange, dnssecOk, additionalRecords);
  348. break;
  349. case DnsResourceRecordType.SRV:
  350. ResolveAdditionalRecords(refRecord, (refRecord.RDATA as DnsSRVRecordData).Target, dnssecOk, additionalRecords);
  351. break;
  352. case DnsResourceRecordType.SVCB:
  353. case DnsResourceRecordType.HTTPS:
  354. DnsSVCBRecordData svcb = refRecord.RDATA as DnsSVCBRecordData;
  355. string targetName = svcb.TargetName;
  356. if (svcb.SvcPriority == 0)
  357. {
  358. //For AliasMode SVCB RRs, a TargetName of "." indicates that the service is not available or does not exist [draft-ietf-dnsop-svcb-https-12]
  359. if ((targetName.Length == 0) || targetName.Equals(refRecord.Name, StringComparison.OrdinalIgnoreCase))
  360. break;
  361. }
  362. else
  363. {
  364. //For ServiceMode SVCB RRs, if TargetName has the value ".", then the owner name of this record MUST be used as the effective TargetName [draft-ietf-dnsop-svcb-https-12]
  365. if (targetName.Length == 0)
  366. targetName = refRecord.Name;
  367. }
  368. ResolveAdditionalRecords(refRecord, targetName, dnssecOk, additionalRecords);
  369. break;
  370. }
  371. }
  372. return additionalRecords;
  373. }
  374. private void ResolveAdditionalRecords(DnsResourceRecord refRecord, string domain, bool dnssecOk, List<DnsResourceRecord> additionalRecords)
  375. {
  376. int count = 0;
  377. while (count++ < DnsServer.MAX_CNAME_HOPS)
  378. {
  379. AuthZone zone = _root.FindZone(domain, out _, out _, out _, out _);
  380. if ((zone is null) || !zone.IsActive)
  381. break;
  382. if (((refRecord.Type == DnsResourceRecordType.SVCB) || (refRecord.Type == DnsResourceRecordType.HTTPS)) && ((refRecord.RDATA as DnsSVCBRecordData).SvcPriority == 0))
  383. {
  384. //resolve SVCB/HTTPS for Alias mode refRecord
  385. IReadOnlyList<DnsResourceRecord> records = zone.QueryRecordsWildcard(refRecord.Type, dnssecOk, domain);
  386. if ((records.Count > 0) && (records[0].Type == refRecord.Type) && (records[0].RDATA is DnsSVCBRecordData svcb))
  387. {
  388. additionalRecords.AddRange(records);
  389. string targetName = svcb.TargetName;
  390. if (svcb.SvcPriority == 0)
  391. {
  392. //Alias mode
  393. if ((targetName.Length == 0) || targetName.Equals(records[0].Name, StringComparison.OrdinalIgnoreCase))
  394. break; //For AliasMode SVCB RRs, a TargetName of "." indicates that the service is not available or does not exist [draft-ietf-dnsop-svcb-https-12]
  395. foreach (DnsResourceRecord additionalRecord in additionalRecords)
  396. {
  397. if (additionalRecord.Name.Equals(targetName, StringComparison.OrdinalIgnoreCase))
  398. return; //loop detected
  399. }
  400. //continue to resolve SVCB/HTTPS further
  401. domain = targetName;
  402. refRecord = records[0];
  403. continue;
  404. }
  405. else
  406. {
  407. //Service mode
  408. if (targetName.Length > 0)
  409. {
  410. //continue to resolve A/AAAA for target name
  411. domain = targetName;
  412. refRecord = records[0];
  413. continue;
  414. }
  415. //resolve A/AAAA below
  416. }
  417. }
  418. }
  419. bool hasA = false;
  420. bool hasAAAA = false;
  421. if ((refRecord.Type == DnsResourceRecordType.SRV) || (refRecord.Type == DnsResourceRecordType.SVCB) || (refRecord.Type == DnsResourceRecordType.HTTPS))
  422. {
  423. foreach (DnsResourceRecord additionalRecord in additionalRecords)
  424. {
  425. if (additionalRecord.Name.Equals(domain, StringComparison.OrdinalIgnoreCase))
  426. {
  427. switch (additionalRecord.Type)
  428. {
  429. case DnsResourceRecordType.A:
  430. hasA = true;
  431. break;
  432. case DnsResourceRecordType.AAAA:
  433. hasAAAA = true;
  434. break;
  435. }
  436. }
  437. if (hasA && hasAAAA)
  438. break;
  439. }
  440. }
  441. if (!hasA)
  442. {
  443. IReadOnlyList<DnsResourceRecord> records = zone.QueryRecordsWildcard(DnsResourceRecordType.A, dnssecOk, domain);
  444. if ((records.Count > 0) && (records[0].Type == DnsResourceRecordType.A))
  445. additionalRecords.AddRange(records);
  446. }
  447. if (!hasAAAA)
  448. {
  449. IReadOnlyList<DnsResourceRecord> records = zone.QueryRecordsWildcard(DnsResourceRecordType.AAAA, dnssecOk, domain);
  450. if ((records.Count > 0) && (records[0].Type == DnsResourceRecordType.AAAA))
  451. additionalRecords.AddRange(records);
  452. }
  453. break;
  454. }
  455. }
  456. private DnsDatagram GetReferralResponse(DnsDatagram request, bool dnssecOk, AuthZone delegationZone, ApexZone apexZone, CancellationToken cancellationToken)
  457. {
  458. IReadOnlyList<DnsResourceRecord> authority;
  459. if (delegationZone is StubZone)
  460. {
  461. authority = delegationZone.GetRecords(DnsResourceRecordType.NS); //stub zone has no authority so cant query
  462. //update last used on
  463. DateTime utcNow = DateTime.UtcNow;
  464. foreach (DnsResourceRecord record in authority)
  465. record.GetAuthGenericRecordInfo().LastUsedOn = utcNow;
  466. }
  467. else
  468. {
  469. authority = delegationZone.QueryRecords(DnsResourceRecordType.NS, false);
  470. if (dnssecOk)
  471. {
  472. IReadOnlyList<DnsResourceRecord> dsRecords = delegationZone.QueryRecords(DnsResourceRecordType.DS, true);
  473. if (dsRecords.Count > 0)
  474. {
  475. List<DnsResourceRecord> newAuthority = new List<DnsResourceRecord>(authority.Count + dsRecords.Count);
  476. newAuthority.AddRange(authority);
  477. newAuthority.AddRange(dsRecords);
  478. authority = newAuthority;
  479. }
  480. else
  481. {
  482. //add proof of non existence (NODATA) to prove DS record does not exists
  483. IReadOnlyList<DnsResourceRecord> nsecRecords;
  484. if (apexZone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3)
  485. nsecRecords = _root.FindNSec3ProofOfNonExistenceNoData(delegationZone, apexZone, cancellationToken);
  486. else
  487. nsecRecords = AuthZoneTree.FindNSecProofOfNonExistenceNoData(delegationZone);
  488. if (nsecRecords.Count > 0)
  489. {
  490. List<DnsResourceRecord> newAuthority = new List<DnsResourceRecord>(authority.Count + nsecRecords.Count);
  491. newAuthority.AddRange(authority);
  492. newAuthority.AddRange(nsecRecords);
  493. authority = newAuthority;
  494. }
  495. }
  496. }
  497. }
  498. IReadOnlyList<DnsResourceRecord> additional = GetAdditionalRecords(authority, dnssecOk);
  499. return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, request.Question, null, authority, additional);
  500. }
  501. private DnsDatagram GetForwarderResponse(DnsDatagram request, AuthZone zone, SubDomainZone closestZone, ApexZone forwarderZone, CancellationToken cancellationToken)
  502. {
  503. IReadOnlyList<DnsResourceRecord> authority = null;
  504. if (zone is not null)
  505. {
  506. if (zone.ContainsNameServerRecords())
  507. return GetReferralResponse(request, false, zone, forwarderZone, cancellationToken);
  508. authority = zone.QueryRecords(DnsResourceRecordType.FWD, false);
  509. }
  510. if (((authority is null) || (authority.Count == 0)) && (closestZone is not null))
  511. {
  512. if (closestZone.ContainsNameServerRecords())
  513. return GetReferralResponse(request, false, closestZone, forwarderZone, cancellationToken);
  514. authority = closestZone.QueryRecords(DnsResourceRecordType.FWD, false);
  515. }
  516. if ((authority is null) || (authority.Count == 0))
  517. {
  518. if (forwarderZone.ContainsNameServerRecords())
  519. return GetReferralResponse(request, false, forwarderZone, forwarderZone, cancellationToken);
  520. authority = forwarderZone.QueryRecords(DnsResourceRecordType.FWD, false);
  521. }
  522. return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, false, false, false, DnsResponseCode.NoError, request.Question, null, authority);
  523. }
  524. internal void Flush()
  525. {
  526. _zoneIndexLock.EnterWriteLock();
  527. try
  528. {
  529. _root.Clear();
  530. _zoneIndex.Clear();
  531. _catalogZoneIndex.Clear();
  532. }
  533. finally
  534. {
  535. _zoneIndexLock.ExitWriteLock();
  536. }
  537. }
  538. private static List<DnsResourceRecord> CondenseIncrementalZoneTransferRecords(string zoneName, DnsResourceRecord currentSoaRecord, IReadOnlyList<DnsResourceRecord> xfrRecords)
  539. {
  540. DnsResourceRecord firstSoaRecord = xfrRecords[0];
  541. DnsResourceRecord lastSoaRecord = xfrRecords[xfrRecords.Count - 1];
  542. DnsResourceRecord firstDeletedSoaRecord = null;
  543. DnsResourceRecord lastAddedSoaRecord = null;
  544. List<DnsResourceRecord> deletedRecords = new List<DnsResourceRecord>();
  545. List<DnsResourceRecord> deletedGlueRecords = new List<DnsResourceRecord>();
  546. List<DnsResourceRecord> addedRecords = new List<DnsResourceRecord>();
  547. List<DnsResourceRecord> addedGlueRecords = new List<DnsResourceRecord>();
  548. //read and apply difference sequences
  549. int index = 1;
  550. int count = xfrRecords.Count - 1;
  551. DnsSOARecordData currentSoa = (DnsSOARecordData)currentSoaRecord.RDATA;
  552. while (index < count)
  553. {
  554. //read deleted records
  555. DnsResourceRecord deletedSoaRecord = xfrRecords[index];
  556. if ((deletedSoaRecord.Type != DnsResourceRecordType.SOA) || !deletedSoaRecord.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  557. throw new InvalidOperationException();
  558. if (firstDeletedSoaRecord is null)
  559. firstDeletedSoaRecord = deletedSoaRecord;
  560. index++;
  561. while (index < count)
  562. {
  563. DnsResourceRecord record = xfrRecords[index];
  564. if (record.Type == DnsResourceRecordType.SOA)
  565. break;
  566. if (zoneName.Length == 0)
  567. {
  568. //root zone case
  569. switch (record.Type)
  570. {
  571. case DnsResourceRecordType.A:
  572. case DnsResourceRecordType.AAAA:
  573. if (!addedGlueRecords.Remove(record))
  574. deletedGlueRecords.Add(record);
  575. break;
  576. default:
  577. if (!addedRecords.Remove(record))
  578. deletedRecords.Add(record);
  579. break;
  580. }
  581. }
  582. else
  583. {
  584. if (record.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase))
  585. {
  586. if (!addedRecords.Remove(record))
  587. deletedRecords.Add(record);
  588. }
  589. else
  590. {
  591. switch (record.Type)
  592. {
  593. case DnsResourceRecordType.A:
  594. case DnsResourceRecordType.AAAA:
  595. if (!addedGlueRecords.Remove(record))
  596. deletedGlueRecords.Add(record);
  597. break;
  598. }
  599. }
  600. }
  601. index++;
  602. }
  603. //read added records
  604. DnsResourceRecord addedSoaRecord = xfrRecords[index];
  605. if (!addedSoaRecord.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  606. throw new InvalidOperationException();
  607. lastAddedSoaRecord = addedSoaRecord;
  608. index++;
  609. while (index < count)
  610. {
  611. DnsResourceRecord record = xfrRecords[index];
  612. if (record.Type == DnsResourceRecordType.SOA)
  613. break;
  614. if (zoneName.Length == 0)
  615. {
  616. //root zone case
  617. switch (record.Type)
  618. {
  619. case DnsResourceRecordType.A:
  620. case DnsResourceRecordType.AAAA:
  621. if (!deletedGlueRecords.Remove(record))
  622. addedGlueRecords.Add(record);
  623. break;
  624. default:
  625. if (!deletedRecords.Remove(record))
  626. addedRecords.Add(record);
  627. break;
  628. }
  629. }
  630. else
  631. {
  632. if (record.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase))
  633. {
  634. if (!deletedRecords.Remove(record))
  635. addedRecords.Add(record);
  636. }
  637. else
  638. {
  639. switch (record.Type)
  640. {
  641. case DnsResourceRecordType.A:
  642. case DnsResourceRecordType.AAAA:
  643. if (!deletedGlueRecords.Remove(record))
  644. addedGlueRecords.Add(record);
  645. break;
  646. }
  647. }
  648. }
  649. index++;
  650. }
  651. //check sequence soa serial
  652. DnsSOARecordData deletedSoa = deletedSoaRecord.RDATA as DnsSOARecordData;
  653. if (currentSoa.Serial != deletedSoa.Serial)
  654. throw new InvalidOperationException("Current SOA serial does not match with the IXFR difference sequence deleted SOA.");
  655. //check next difference sequence
  656. currentSoa = addedSoaRecord.RDATA as DnsSOARecordData;
  657. }
  658. //create condensed records
  659. List<DnsResourceRecord> condensedRecords = new List<DnsResourceRecord>(2 + 2 + deletedRecords.Count + deletedGlueRecords.Count + addedRecords.Count + addedGlueRecords.Count);
  660. condensedRecords.Add(firstSoaRecord);
  661. condensedRecords.Add(firstDeletedSoaRecord);
  662. condensedRecords.AddRange(deletedRecords);
  663. condensedRecords.AddRange(deletedGlueRecords);
  664. condensedRecords.Add(lastAddedSoaRecord);
  665. condensedRecords.AddRange(addedRecords);
  666. condensedRecords.AddRange(addedGlueRecords);
  667. condensedRecords.Add(lastSoaRecord);
  668. return condensedRecords;
  669. }
  670. private void SaveZoneFileInternal(string zoneName)
  671. {
  672. zoneName = zoneName.ToLowerInvariant();
  673. using (MemoryStream mS = new MemoryStream())
  674. {
  675. //serialize zone
  676. WriteZoneTo(zoneName, mS);
  677. if (mS.Position == 0)
  678. return; //zone was not found
  679. //write to zone file
  680. mS.Position = 0;
  681. using (FileStream fS = new FileStream(Path.Combine(_dnsServer.ConfigFolder, "zones", zoneName + ".zone"), FileMode.Create, FileAccess.Write))
  682. {
  683. mS.CopyTo(fS);
  684. }
  685. }
  686. _dnsServer.LogManager?.Write("Saved zone file for domain: " + (zoneName == "" ? "<root>" : zoneName));
  687. }
  688. #endregion
  689. #region public
  690. public void LoadAllZoneFiles()
  691. {
  692. Flush();
  693. string zonesFolder = Path.Combine(_dnsServer.ConfigFolder, "zones");
  694. if (!Directory.Exists(zonesFolder))
  695. Directory.CreateDirectory(zonesFolder);
  696. //move zone files to new folder
  697. {
  698. string[] oldZoneFiles = Directory.GetFiles(_dnsServer.ConfigFolder, "*.zone");
  699. foreach (string oldZoneFile in oldZoneFiles)
  700. File.Move(oldZoneFile, Path.Combine(zonesFolder, Path.GetFileName(oldZoneFile)));
  701. }
  702. //remove old internal zones files
  703. {
  704. string[] oldZoneFiles = ["localhost.zone", "1.0.0.127.in-addr.arpa.zone", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.zone"];
  705. foreach (string oldZoneFile in oldZoneFiles)
  706. {
  707. string filePath = Path.Combine(zonesFolder, oldZoneFile);
  708. if (File.Exists(filePath))
  709. {
  710. try
  711. {
  712. File.Delete(filePath);
  713. }
  714. catch
  715. { }
  716. }
  717. }
  718. }
  719. //load system zones
  720. {
  721. {
  722. CreateInternalPrimaryZone("localhost");
  723. SetRecord("localhost", new DnsResourceRecord("localhost", DnsResourceRecordType.A, DnsClass.IN, 3600, new DnsARecordData(IPAddress.Loopback)));
  724. SetRecord("localhost", new DnsResourceRecord("localhost", DnsResourceRecordType.AAAA, DnsClass.IN, 3600, new DnsAAAARecordData(IPAddress.IPv6Loopback)));
  725. }
  726. {
  727. string ptrDomain = "0.in-addr.arpa";
  728. CreateInternalPrimaryZone(ptrDomain);
  729. }
  730. {
  731. string ptrDomain = "255.in-addr.arpa";
  732. CreateInternalPrimaryZone(ptrDomain);
  733. }
  734. {
  735. string ptrZoneName = "127.in-addr.arpa";
  736. CreateInternalPrimaryZone(ptrZoneName);
  737. SetRecord(ptrZoneName, new DnsResourceRecord("1.0.0.127.in-addr.arpa", DnsResourceRecordType.PTR, DnsClass.IN, 3600, new DnsPTRRecordData("localhost")));
  738. }
  739. {
  740. string ptrZoneName = IPAddress.IPv6Loopback.GetReverseDomain();
  741. CreateInternalPrimaryZone(ptrZoneName);
  742. SetRecord(ptrZoneName, new DnsResourceRecord(ptrZoneName, DnsResourceRecordType.PTR, DnsClass.IN, 3600, new DnsPTRRecordData("localhost")));
  743. }
  744. }
  745. //load zone files
  746. _zoneIndexLock.EnterWriteLock();
  747. try
  748. {
  749. string[] zoneFiles = Directory.GetFiles(zonesFolder, "*.zone");
  750. foreach (string zoneFile in zoneFiles)
  751. {
  752. try
  753. {
  754. using (FileStream fS = new FileStream(zoneFile, FileMode.Open, FileAccess.Read))
  755. {
  756. AuthZoneInfo zoneInfo = LoadZoneFrom(fS, File.GetLastWriteTimeUtc(fS.SafeFileHandle));
  757. _zoneIndex.Add(zoneInfo);
  758. if (zoneInfo.Type == AuthZoneType.Catalog)
  759. _catalogZoneIndex.Add(zoneInfo);
  760. }
  761. _dnsServer.LogManager?.Write("DNS Server successfully loaded zone file: " + zoneFile);
  762. }
  763. catch (Exception ex)
  764. {
  765. _dnsServer.LogManager?.Write("DNS Server failed to load zone file: " + zoneFile + "\r\n" + ex.ToString());
  766. }
  767. }
  768. _zoneIndex.Sort();
  769. _catalogZoneIndex.Sort();
  770. }
  771. finally
  772. {
  773. _zoneIndexLock.ExitWriteLock();
  774. }
  775. }
  776. internal AuthZoneInfo CreateSpecialPrimaryZone(string zoneName, DnsSOARecordData soaRecord, DnsNSRecordData ns)
  777. {
  778. PrimaryZone apexZone = new PrimaryZone(_dnsServer, zoneName, soaRecord, ns);
  779. _zoneIndexLock.EnterWriteLock();
  780. try
  781. {
  782. if (_root.TryAdd(apexZone))
  783. {
  784. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  785. _zoneIndex.Add(zoneInfo);
  786. _zoneIndex.Sort();
  787. return zoneInfo;
  788. }
  789. }
  790. finally
  791. {
  792. _zoneIndexLock.ExitWriteLock();
  793. }
  794. return null;
  795. }
  796. internal void LoadSpecialPrimaryZones(IReadOnlyList<string> zoneNames, DnsSOARecordData soaRecord, DnsNSRecordData ns)
  797. {
  798. _zoneIndexLock.EnterWriteLock();
  799. try
  800. {
  801. foreach (string zoneName in zoneNames)
  802. {
  803. PrimaryZone apexZone = new PrimaryZone(_dnsServer, zoneName, soaRecord, ns);
  804. if (_root.TryAdd(apexZone))
  805. {
  806. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  807. _zoneIndex.Add(zoneInfo);
  808. }
  809. }
  810. _zoneIndex.Sort();
  811. }
  812. finally
  813. {
  814. _zoneIndexLock.ExitWriteLock();
  815. }
  816. }
  817. internal void LoadSpecialPrimaryZones(Func<string> getZoneName, DnsSOARecordData soaRecord, DnsNSRecordData ns)
  818. {
  819. _zoneIndexLock.EnterWriteLock();
  820. try
  821. {
  822. string zoneName;
  823. while (true)
  824. {
  825. zoneName = getZoneName();
  826. if (zoneName is null)
  827. break;
  828. PrimaryZone apexZone = new PrimaryZone(_dnsServer, zoneName, soaRecord, ns);
  829. if (_root.TryAdd(apexZone))
  830. {
  831. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  832. _zoneIndex.Add(zoneInfo);
  833. }
  834. }
  835. _zoneIndex.Sort();
  836. }
  837. finally
  838. {
  839. _zoneIndexLock.ExitWriteLock();
  840. }
  841. }
  842. internal AuthZoneInfo CreateInternalPrimaryZone(string zoneName)
  843. {
  844. return CreatePrimaryZone(zoneName, true, _useSoaSerialDateScheme);
  845. }
  846. public AuthZoneInfo CreatePrimaryZone(string zoneName)
  847. {
  848. return CreatePrimaryZone(zoneName, false, _useSoaSerialDateScheme);
  849. }
  850. public AuthZoneInfo CreatePrimaryZone(string zoneName, bool useSoaSerialDateScheme)
  851. {
  852. return CreatePrimaryZone(zoneName, false, useSoaSerialDateScheme);
  853. }
  854. private AuthZoneInfo CreatePrimaryZone(string zoneName, bool @internal, bool useSoaSerialDateScheme)
  855. {
  856. PrimaryZone apexZone = new PrimaryZone(_dnsServer, zoneName, @internal, useSoaSerialDateScheme);
  857. _zoneIndexLock.EnterWriteLock();
  858. try
  859. {
  860. if (_root.TryAdd(apexZone))
  861. {
  862. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  863. _zoneIndex.Add(zoneInfo);
  864. _zoneIndex.Sort();
  865. if (!@internal)
  866. SaveZoneFile(zoneInfo.Name);
  867. return zoneInfo;
  868. }
  869. }
  870. finally
  871. {
  872. _zoneIndexLock.ExitWriteLock();
  873. }
  874. return null;
  875. }
  876. public Task<AuthZoneInfo> CreateSecondaryZoneAsync(string zoneName, string primaryNameServerAddresses = null, DnsTransportProtocol primaryZoneTransferProtocol = DnsTransportProtocol.Tcp, string primaryZoneTransferTsigKeyName = null, bool validateZone = false, bool ignoreSoaFailure = false)
  877. {
  878. NameServerAddress[] primaryNameServers;
  879. if (string.IsNullOrEmpty(primaryNameServerAddresses))
  880. primaryNameServers = null;
  881. else
  882. primaryNameServers = primaryNameServerAddresses.Split(NameServerAddress.Parse, ',');
  883. return CreateSecondaryZoneAsync(zoneName, primaryNameServers, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName, validateZone, ignoreSoaFailure);
  884. }
  885. public async Task<AuthZoneInfo> CreateSecondaryZoneAsync(string zoneName, IReadOnlyList<NameServerAddress> primaryNameServerAddresses = null, DnsTransportProtocol primaryZoneTransferProtocol = DnsTransportProtocol.Tcp, string primaryZoneTransferTsigKeyName = null, bool validateZone = false, bool ignoreSoaFailure = false)
  886. {
  887. SecondaryZone apexZone = await SecondaryZone.CreateAsync(_dnsServer, zoneName, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName, validateZone, ignoreSoaFailure);
  888. _zoneIndexLock.EnterWriteLock();
  889. try
  890. {
  891. if (_root.TryAdd(apexZone))
  892. {
  893. apexZone.TriggerRefresh(0);
  894. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  895. _zoneIndex.Add(zoneInfo);
  896. _zoneIndex.Sort();
  897. SaveZoneFile(zoneInfo.Name);
  898. return zoneInfo;
  899. }
  900. }
  901. finally
  902. {
  903. _zoneIndexLock.ExitWriteLock();
  904. }
  905. return null;
  906. }
  907. public Task<AuthZoneInfo> CreateStubZoneAsync(string zoneName, string primaryNameServerAddresses = null, bool ignoreSoaFailure = false)
  908. {
  909. NameServerAddress[] primaryNameServers;
  910. if (string.IsNullOrEmpty(primaryNameServerAddresses))
  911. primaryNameServers = null;
  912. else
  913. primaryNameServers = primaryNameServerAddresses.Split(NameServerAddress.Parse, ',');
  914. return CreateStubZoneAsync(zoneName, primaryNameServers, ignoreSoaFailure);
  915. }
  916. public async Task<AuthZoneInfo> CreateStubZoneAsync(string zoneName, IReadOnlyList<NameServerAddress> primaryNameServerAddresses = null, bool ignoreSoaFailure = false)
  917. {
  918. StubZone apexZone = await StubZone.CreateAsync(_dnsServer, zoneName, primaryNameServerAddresses, ignoreSoaFailure);
  919. _zoneIndexLock.EnterWriteLock();
  920. try
  921. {
  922. if (_root.TryAdd(apexZone))
  923. {
  924. apexZone.TriggerRefresh(0);
  925. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  926. _zoneIndex.Add(zoneInfo);
  927. _zoneIndex.Sort();
  928. SaveZoneFile(zoneInfo.Name);
  929. return zoneInfo;
  930. }
  931. }
  932. finally
  933. {
  934. _zoneIndexLock.ExitWriteLock();
  935. }
  936. return null;
  937. }
  938. private AuthZoneInfo CreateForwarderZone(string zoneName)
  939. {
  940. ForwarderZone apexZone = new ForwarderZone(_dnsServer, zoneName);
  941. _zoneIndexLock.EnterWriteLock();
  942. try
  943. {
  944. if (_root.TryAdd(apexZone))
  945. {
  946. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  947. _zoneIndex.Add(zoneInfo);
  948. _zoneIndex.Sort();
  949. SaveZoneFile(zoneInfo.Name);
  950. return zoneInfo;
  951. }
  952. }
  953. finally
  954. {
  955. _zoneIndexLock.ExitWriteLock();
  956. }
  957. return null;
  958. }
  959. public AuthZoneInfo CreateForwarderZone(string zoneName, DnsTransportProtocol forwarderProtocol, string forwarder, bool dnssecValidation, DnsForwarderRecordProxyType proxyType, string proxyAddress, ushort proxyPort, string proxyUsername, string proxyPassword, string fwdRecordComments)
  960. {
  961. ForwarderZone apexZone = new ForwarderZone(_dnsServer, zoneName, forwarderProtocol, forwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, fwdRecordComments);
  962. _zoneIndexLock.EnterWriteLock();
  963. try
  964. {
  965. if (_root.TryAdd(apexZone))
  966. {
  967. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  968. _zoneIndex.Add(zoneInfo);
  969. _zoneIndex.Sort();
  970. SaveZoneFile(zoneInfo.Name);
  971. return zoneInfo;
  972. }
  973. }
  974. finally
  975. {
  976. _zoneIndexLock.ExitWriteLock();
  977. }
  978. return null;
  979. }
  980. public AuthZoneInfo CreateSecondaryForwarderZone(string zoneName, string primaryNameServerAddresses = null, DnsTransportProtocol primaryZoneTransferProtocol = DnsTransportProtocol.Tcp, string primaryZoneTransferTsigKeyName = null)
  981. {
  982. NameServerAddress[] primaryNameServers;
  983. if (string.IsNullOrEmpty(primaryNameServerAddresses))
  984. primaryNameServers = null;
  985. else
  986. primaryNameServers = primaryNameServerAddresses.Split(NameServerAddress.Parse, ',');
  987. return CreateSecondaryForwarderZone(zoneName, primaryNameServers, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName);
  988. }
  989. public AuthZoneInfo CreateSecondaryForwarderZone(string zoneName, IReadOnlyList<NameServerAddress> primaryNameServerAddresses = null, DnsTransportProtocol primaryZoneTransferProtocol = DnsTransportProtocol.Tcp, string primaryZoneTransferTsigKeyName = null)
  990. {
  991. SecondaryForwarderZone apexZone = new SecondaryForwarderZone(_dnsServer, zoneName, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName);
  992. _zoneIndexLock.EnterWriteLock();
  993. try
  994. {
  995. if (_root.TryAdd(apexZone))
  996. {
  997. apexZone.TriggerRefresh(0);
  998. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  999. _zoneIndex.Add(zoneInfo);
  1000. _zoneIndex.Sort();
  1001. SaveZoneFile(zoneInfo.Name);
  1002. return zoneInfo;
  1003. }
  1004. }
  1005. finally
  1006. {
  1007. _zoneIndexLock.ExitWriteLock();
  1008. }
  1009. return null;
  1010. }
  1011. public AuthZoneInfo CreateCatalogZone(string zoneName)
  1012. {
  1013. CatalogZone apexZone = new CatalogZone(_dnsServer, zoneName);
  1014. _zoneIndexLock.EnterWriteLock();
  1015. try
  1016. {
  1017. if (_root.TryAdd(apexZone))
  1018. {
  1019. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  1020. _zoneIndex.Add(zoneInfo);
  1021. _zoneIndex.Sort();
  1022. _catalogZoneIndex.Add(zoneInfo);
  1023. _catalogZoneIndex.Sort();
  1024. apexZone.InitZoneProperties();
  1025. SaveZoneFile(zoneInfo.Name);
  1026. return zoneInfo;
  1027. }
  1028. }
  1029. finally
  1030. {
  1031. _zoneIndexLock.ExitWriteLock();
  1032. }
  1033. return null;
  1034. }
  1035. public AuthZoneInfo CreateSecondaryCatalogZone(string zoneName, string primaryNameServerAddresses, DnsTransportProtocol primaryZoneTransferProtocol = DnsTransportProtocol.Tcp, string primaryZoneTransferTsigKeyName = null)
  1036. {
  1037. NameServerAddress[] primaryNameServers;
  1038. if (string.IsNullOrEmpty(primaryNameServerAddresses))
  1039. primaryNameServers = null;
  1040. else
  1041. primaryNameServers = primaryNameServerAddresses.Split(NameServerAddress.Parse, ',');
  1042. return CreateSecondaryCatalogZone(zoneName, primaryNameServers, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName);
  1043. }
  1044. public AuthZoneInfo CreateSecondaryCatalogZone(string zoneName, IReadOnlyList<NameServerAddress> primaryNameServerAddresses, DnsTransportProtocol primaryZoneTransferProtocol = DnsTransportProtocol.Tcp, string primaryZoneTransferTsigKeyName = null)
  1045. {
  1046. SecondaryCatalogZone apexZone = new SecondaryCatalogZone(_dnsServer, zoneName, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName);
  1047. _zoneIndexLock.EnterWriteLock();
  1048. try
  1049. {
  1050. if (_root.TryAdd(apexZone))
  1051. {
  1052. apexZone.ZoneAdded += SecondaryCatalogZoneAdded;
  1053. apexZone.ZoneRemoved += SecondaryCatalogZoneRemoved;
  1054. apexZone.TriggerRefresh(0);
  1055. AuthZoneInfo zoneInfo = new AuthZoneInfo(apexZone);
  1056. _zoneIndex.Add(zoneInfo);
  1057. _zoneIndex.Sort();
  1058. SaveZoneFile(zoneInfo.Name);
  1059. return zoneInfo;
  1060. }
  1061. }
  1062. finally
  1063. {
  1064. _zoneIndexLock.ExitWriteLock();
  1065. }
  1066. return null;
  1067. }
  1068. public void AddCatalogMemberZone(string catalogZoneName, AuthZoneInfo memberZoneInfo, bool ignoreValidationErrors = false)
  1069. {
  1070. switch (memberZoneInfo.Type)
  1071. {
  1072. case AuthZoneType.Primary:
  1073. case AuthZoneType.Stub:
  1074. case AuthZoneType.Forwarder:
  1075. if (!ignoreValidationErrors)
  1076. {
  1077. string currentCatalogZoneName = memberZoneInfo.ApexZone.CatalogZoneName;
  1078. if (currentCatalogZoneName is not null)
  1079. throw new DnsServerException("The zone '" + memberZoneInfo.DisplayName + "' is already a member of Catalog zone '" + currentCatalogZoneName + "'.");
  1080. }
  1081. ApexZone apexZone = _root.GetApexZone(catalogZoneName);
  1082. if (apexZone is not CatalogZone catalogZone)
  1083. {
  1084. if (ignoreValidationErrors)
  1085. return;
  1086. throw new DnsServerException("No such Catalog zone was found: " + catalogZoneName);
  1087. }
  1088. catalogZone.AddMemberZone(memberZoneInfo.Name, memberZoneInfo.Type);
  1089. memberZoneInfo.ApexZone.CatalogZoneName = catalogZone.Name;
  1090. //update properties in catalog zone by settings member zone property values again
  1091. switch (memberZoneInfo.Type)
  1092. {
  1093. case AuthZoneType.Primary:
  1094. memberZoneInfo.QueryAccess = memberZoneInfo.QueryAccess;
  1095. memberZoneInfo.ZoneTransfer = memberZoneInfo.ZoneTransfer;
  1096. memberZoneInfo.ZoneTransferTsigKeyNames = memberZoneInfo.ZoneTransferTsigKeyNames;
  1097. break;
  1098. case AuthZoneType.Stub:
  1099. memberZoneInfo.PrimaryNameServerAddresses = memberZoneInfo.PrimaryNameServerAddresses;
  1100. memberZoneInfo.QueryAccess = memberZoneInfo.QueryAccess;
  1101. break;
  1102. case AuthZoneType.Forwarder:
  1103. memberZoneInfo.QueryAccess = memberZoneInfo.QueryAccess;
  1104. break;
  1105. }
  1106. SaveZoneFile(catalogZoneName);
  1107. break;
  1108. default:
  1109. throw new NotSupportedException();
  1110. }
  1111. }
  1112. public void RemoveCatalogMemberZone(AuthZoneInfo memberZoneInfo)
  1113. {
  1114. switch (memberZoneInfo.Type)
  1115. {
  1116. case AuthZoneType.Primary:
  1117. case AuthZoneType.Stub:
  1118. case AuthZoneType.Forwarder:
  1119. case AuthZoneType.Secondary:
  1120. case AuthZoneType.SecondaryForwarder:
  1121. string catalogZoneName = memberZoneInfo.ApexZone.CatalogZoneName;
  1122. if (catalogZoneName is null)
  1123. return;
  1124. memberZoneInfo.ApexZone.CatalogZone?.RemoveMemberZone(memberZoneInfo.Name);
  1125. memberZoneInfo.ApexZone.CatalogZoneName = null;
  1126. SaveZoneFile(catalogZoneName);
  1127. break;
  1128. default:
  1129. throw new NotSupportedException();
  1130. }
  1131. }
  1132. public void ChangeCatalogMemberZoneOwnership(AuthZoneInfo memberZoneInfo, string newCatalogZoneName)
  1133. {
  1134. switch (memberZoneInfo.Type)
  1135. {
  1136. case AuthZoneType.Primary:
  1137. case AuthZoneType.Stub:
  1138. case AuthZoneType.Forwarder:
  1139. string currentCatalogZoneName = memberZoneInfo.ApexZone.CatalogZoneName;
  1140. if (currentCatalogZoneName is null)
  1141. throw new DnsServerException("The zone '" + memberZoneInfo.DisplayName + "' is not a member of any Catalog zone.");
  1142. ApexZone apexZone = _root.GetApexZone(currentCatalogZoneName);
  1143. if (apexZone is not CatalogZone currentCatalogZone)
  1144. throw new DnsServerException("No such Catalog zone was found: " + currentCatalogZoneName);
  1145. AddCatalogMemberZone(newCatalogZoneName, memberZoneInfo, true);
  1146. currentCatalogZone.ChangeMemberZoneOwnership(memberZoneInfo.Name, newCatalogZoneName);
  1147. SaveZoneFile(currentCatalogZoneName);
  1148. break;
  1149. default:
  1150. throw new NotSupportedException();
  1151. }
  1152. }
  1153. public AuthZoneInfo CloneZone(string zoneName, string sourceZoneName)
  1154. {
  1155. AuthZoneInfo sourceZoneInfo = GetAuthZoneInfo(sourceZoneName);
  1156. if (sourceZoneInfo is null)
  1157. throw new DnsServerException("No such zone was found: " + (sourceZoneName.Length == 0 ? "." : sourceZoneName));
  1158. AuthZoneInfo zoneInfo;
  1159. switch (sourceZoneInfo.Type)
  1160. {
  1161. case AuthZoneType.Primary:
  1162. zoneInfo = CreatePrimaryZone(zoneName);
  1163. break;
  1164. case AuthZoneType.Forwarder:
  1165. zoneInfo = CreateForwarderZone(zoneName);
  1166. break;
  1167. default:
  1168. throw new DnsServerException("Cannot clone the zone: source zone must be a Primary or Conditional Forwarder zone.");
  1169. }
  1170. if (zoneInfo is null)
  1171. throw new DnsServerException("Failed to clone the zone: zone already exists.");
  1172. //copy zone options
  1173. zoneInfo.Disabled = sourceZoneInfo.Disabled;
  1174. if (zoneInfo.Type == AuthZoneType.Primary)
  1175. {
  1176. zoneInfo.ZoneTransfer = sourceZoneInfo.ZoneTransfer;
  1177. zoneInfo.ZoneTransferNetworkACL = sourceZoneInfo.ZoneTransferNetworkACL;
  1178. zoneInfo.ZoneTransferTsigKeyNames = sourceZoneInfo.ZoneTransferTsigKeyNames;
  1179. zoneInfo.Notify = sourceZoneInfo.Notify;
  1180. zoneInfo.NotifyNameServers = sourceZoneInfo.NotifyNameServers;
  1181. zoneInfo.Update = sourceZoneInfo.Update;
  1182. zoneInfo.UpdateNetworkACL = sourceZoneInfo.UpdateNetworkACL;
  1183. if (sourceZoneInfo.UpdateSecurityPolicies is not null)
  1184. {
  1185. Dictionary<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>> updateSecurityPolicies = new Dictionary<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>>(sourceZoneInfo.UpdateSecurityPolicies.Count);
  1186. foreach (KeyValuePair<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>> sourceSecurityPolicy in sourceZoneInfo.UpdateSecurityPolicies)
  1187. {
  1188. Dictionary<string, IReadOnlyList<DnsResourceRecordType>> policyMap = new Dictionary<string, IReadOnlyList<DnsResourceRecordType>>();
  1189. foreach (KeyValuePair<string, IReadOnlyList<DnsResourceRecordType>> sourcePolicyMap in sourceSecurityPolicy.Value)
  1190. policyMap.Add(string.Concat(sourcePolicyMap.Key.AsSpan(0, sourcePolicyMap.Key.Length - sourceZoneName.Length), zoneName), sourcePolicyMap.Value);
  1191. updateSecurityPolicies.Add(sourceSecurityPolicy.Key, policyMap);
  1192. }
  1193. zoneInfo.UpdateSecurityPolicies = updateSecurityPolicies;
  1194. }
  1195. }
  1196. //copy records
  1197. List<DnsResourceRecord> sourceRecords = new List<DnsResourceRecord>();
  1198. ListAllZoneRecords(sourceZoneName, sourceRecords);
  1199. List<DnsResourceRecord> newRecords = new List<DnsResourceRecord>(sourceRecords.Count);
  1200. foreach (DnsResourceRecord sourceRecord in sourceRecords)
  1201. {
  1202. switch (sourceRecord.Type)
  1203. {
  1204. case DnsResourceRecordType.DNSKEY:
  1205. case DnsResourceRecordType.RRSIG:
  1206. case DnsResourceRecordType.NSEC:
  1207. case DnsResourceRecordType.NSEC3:
  1208. case DnsResourceRecordType.NSEC3PARAM:
  1209. case DnsResourceRecordType.DS:
  1210. continue; //skip DNSSEC records
  1211. default:
  1212. DnsResourceRecord newRecord = new DnsResourceRecord(string.Concat(sourceRecord.Name.AsSpan(0, sourceRecord.Name.Length - sourceZoneName.Length), zoneName), sourceRecord.Type, sourceRecord.Class, sourceRecord.TTL, sourceRecord.RDATA);
  1213. if (sourceRecord.Tag is NSRecordInfo nsInfo)
  1214. {
  1215. NSRecordInfo nrInfo = new NSRecordInfo();
  1216. nrInfo.Disabled = nsInfo.Disabled;
  1217. nrInfo.Comments = nsInfo.Comments;
  1218. nrInfo.GlueRecords = nsInfo.GlueRecords;
  1219. newRecord.Tag = nrInfo;
  1220. }
  1221. else if (sourceRecord.Tag is SOARecordInfo soaInfo)
  1222. {
  1223. SOARecordInfo nrInfo = new SOARecordInfo();
  1224. nrInfo.Disabled = soaInfo.Disabled;
  1225. nrInfo.Comments = soaInfo.Comments;
  1226. nrInfo.UseSoaSerialDateScheme = soaInfo.UseSoaSerialDateScheme;
  1227. newRecord.Tag = nrInfo;
  1228. }
  1229. else if (sourceRecord.Tag is SVCBRecordInfo svcbInfo)
  1230. {
  1231. SVCBRecordInfo nrInfo = new SVCBRecordInfo();
  1232. nrInfo.Disabled = svcbInfo.Disabled;
  1233. nrInfo.Comments = svcbInfo.Comments;
  1234. nrInfo.AutoIpv4Hint = svcbInfo.AutoIpv4Hint;
  1235. nrInfo.AutoIpv6Hint = svcbInfo.AutoIpv6Hint;
  1236. newRecord.Tag = nrInfo;
  1237. }
  1238. else if (sourceRecord.Tag is GenericRecordInfo srInfo)
  1239. {
  1240. GenericRecordInfo nrInfo = new GenericRecordInfo();
  1241. nrInfo.Disabled = srInfo.Disabled;
  1242. nrInfo.Comments = srInfo.Comments;
  1243. newRecord.Tag = nrInfo;
  1244. }
  1245. newRecords.Add(newRecord);
  1246. break;
  1247. }
  1248. }
  1249. LoadZoneRecords(zoneInfo.ApexZone, newRecords);
  1250. SaveZoneFile(zoneInfo.Name);
  1251. return zoneInfo;
  1252. }
  1253. public void ConvertZoneType(string zoneName, AuthZoneType type)
  1254. {
  1255. AuthZoneInfo currentZoneInfo = GetAuthZoneInfo(zoneName);
  1256. if (currentZoneInfo is null)
  1257. throw new DnsServerException("No such zone was found: " + (zoneName.Length == 0 ? "." : zoneName));
  1258. if (currentZoneInfo.Type == type)
  1259. throw new DnsServerException("Cannot convert the zone '" + currentZoneInfo.DisplayName + "' from " + currentZoneInfo.TypeName + " to " + AuthZoneInfo.GetZoneTypeName(type) + " zone: the zone is already of the same type.");
  1260. switch (currentZoneInfo.Type)
  1261. {
  1262. case AuthZoneType.Primary:
  1263. switch (type)
  1264. {
  1265. case AuthZoneType.Forwarder:
  1266. if (currentZoneInfo.ApexZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)
  1267. throw new DnsServerException("Cannot convert the zone '" + currentZoneInfo.DisplayName + "' from " + currentZoneInfo.TypeName + " to " + AuthZoneInfo.GetZoneTypeName(type) + " zone: converting the zone will cause lose of DNSSEC private keys.");
  1268. break;
  1269. default:
  1270. throw new DnsServerException("Cannot convert the zone '" + currentZoneInfo.DisplayName + "' from " + currentZoneInfo.TypeName + " to " + AuthZoneInfo.GetZoneTypeName(type) + " zone: not supported.");
  1271. }
  1272. break;
  1273. case AuthZoneType.Secondary:
  1274. case AuthZoneType.SecondaryForwarder:
  1275. switch (type)
  1276. {
  1277. case AuthZoneType.Primary:
  1278. case AuthZoneType.Forwarder:
  1279. break;
  1280. default:
  1281. throw new DnsServerException("Cannot convert the zone '" + currentZoneInfo.DisplayName + "' from " + currentZoneInfo.TypeName + " to " + AuthZoneInfo.GetZoneTypeName(type) + " zone: not supported.");
  1282. }
  1283. break;
  1284. case AuthZoneType.Forwarder:
  1285. switch (type)
  1286. {
  1287. case AuthZoneType.Primary:
  1288. break;
  1289. default:
  1290. throw new DnsServerException("Cannot convert the zone '" + currentZoneInfo.DisplayName + "' from " + currentZoneInfo.TypeName + " to " + AuthZoneInfo.GetZoneTypeName(type) + " zone: not supported.");
  1291. }
  1292. break;
  1293. default:
  1294. throw new DnsServerException("Cannot convert the zone '" + currentZoneInfo.DisplayName + "' from " + currentZoneInfo.TypeName + " to " + AuthZoneInfo.GetZoneTypeName(type) + " zone: not supported.");
  1295. }
  1296. //read all current records
  1297. List<DnsResourceRecord> allRecords = new List<DnsResourceRecord>();
  1298. ListAllZoneRecords(currentZoneInfo.Name, allRecords);
  1299. try
  1300. {
  1301. //delete current zone
  1302. DeleteZone(currentZoneInfo);
  1303. //create new zone
  1304. AuthZoneInfo newZoneInfo;
  1305. switch (type)
  1306. {
  1307. case AuthZoneType.Primary:
  1308. switch (currentZoneInfo.Type)
  1309. {
  1310. case AuthZoneType.Secondary:
  1311. {
  1312. //reset SOA metadata and remove DNSSEC records
  1313. List<DnsResourceRecord> updateRecords = new List<DnsResourceRecord>(allRecords.Count);
  1314. foreach (DnsResourceRecord record in allRecords)
  1315. {
  1316. switch (record.Type)
  1317. {
  1318. case DnsResourceRecordType.SOA:
  1319. {
  1320. GenericRecordInfo recordInfo = record.GetAuthGenericRecordInfo();
  1321. record.Tag = null;
  1322. GenericRecordInfo newRecordInfo = record.GetAuthGenericRecordInfo();
  1323. newRecordInfo.Comments = recordInfo.Comments;
  1324. }
  1325. break;
  1326. case DnsResourceRecordType.DNSKEY:
  1327. case DnsResourceRecordType.RRSIG:
  1328. case DnsResourceRecordType.NSEC:
  1329. case DnsResourceRecordType.NSEC3:
  1330. case DnsResourceRecordType.NSEC3PARAM:
  1331. continue;
  1332. }
  1333. updateRecords.Add(record);
  1334. }
  1335. allRecords = updateRecords;
  1336. }
  1337. break;
  1338. case AuthZoneType.Forwarder:
  1339. case AuthZoneType.SecondaryForwarder:
  1340. {
  1341. //remove all FWD records
  1342. List<DnsResourceRecord> updateRecords = new List<DnsResourceRecord>(allRecords.Count);
  1343. foreach (DnsResourceRecord record in allRecords)
  1344. {
  1345. if (record.Type == DnsResourceRecordType.FWD)
  1346. continue;
  1347. updateRecords.Add(record);
  1348. }
  1349. allRecords = updateRecords;
  1350. }
  1351. break;
  1352. }
  1353. newZoneInfo = CreatePrimaryZone(currentZoneInfo.Name);
  1354. break;
  1355. case AuthZoneType.Forwarder:
  1356. switch (currentZoneInfo.Type)
  1357. {
  1358. case AuthZoneType.Primary:
  1359. case AuthZoneType.SecondaryForwarder:
  1360. {
  1361. //remove SOA and NS records
  1362. List<DnsResourceRecord> updateRecords = new List<DnsResourceRecord>(allRecords.Count);
  1363. foreach (DnsResourceRecord record in allRecords)
  1364. {
  1365. switch (record.Type)
  1366. {
  1367. case DnsResourceRecordType.SOA:
  1368. case DnsResourceRecordType.NS:
  1369. continue;
  1370. }
  1371. updateRecords.Add(record);
  1372. }
  1373. allRecords = updateRecords;
  1374. }
  1375. break;
  1376. case AuthZoneType.Secondary:
  1377. {
  1378. //remove SOA, NS and DNSSEC records
  1379. List<DnsResourceRecord> updateRecords = new List<DnsResourceRecord>(allRecords.Count);
  1380. foreach (DnsResourceRecord record in allRecords)
  1381. {
  1382. switch (record.Type)
  1383. {
  1384. case DnsResourceRecordType.SOA:
  1385. case DnsResourceRecordType.NS:
  1386. case DnsResourceRecordType.DNSKEY:
  1387. case DnsResourceRecordType.RRSIG:
  1388. case DnsResourceRecordType.NSEC:
  1389. case DnsResourceRecordType.NSEC3:
  1390. case DnsResourceRecordType.NSEC3PARAM:
  1391. case DnsResourceRecordType.DS:
  1392. continue;
  1393. }
  1394. updateRecords.Add(record);
  1395. }
  1396. allRecords = updateRecords;
  1397. }
  1398. break;
  1399. }
  1400. newZoneInfo = CreateForwarderZone(currentZoneInfo.Name);
  1401. break;
  1402. default:
  1403. throw new InvalidOperationException();
  1404. }
  1405. //load records
  1406. LoadZoneRecords(newZoneInfo.ApexZone, allRecords);
  1407. //save zone file
  1408. SaveZoneFile(newZoneInfo.Name);
  1409. }
  1410. catch (Exception ex)
  1411. {
  1412. _dnsServer.LogManager?.Write("DNS Server failed to convert the zone '" + currentZoneInfo.DisplayName + "' from " + currentZoneInfo.TypeName + " to " + AuthZoneInfo.GetZoneTypeName(type) + " zone.\r\n" + ex.ToString());
  1413. //delete the zone if it was created
  1414. DeleteZone(currentZoneInfo);
  1415. //reload old zone file
  1416. string zoneFile = Path.Combine(_dnsServer.ConfigFolder, "zones", currentZoneInfo.Name + ".zone");
  1417. _zoneIndexLock.EnterWriteLock();
  1418. try
  1419. {
  1420. using (FileStream fS = new FileStream(zoneFile, FileMode.Open, FileAccess.Read))
  1421. {
  1422. AuthZoneInfo zoneInfo = LoadZoneFrom(fS, File.GetLastWriteTimeUtc(fS.SafeFileHandle));
  1423. _zoneIndex.Add(zoneInfo);
  1424. _zoneIndex.Sort();
  1425. }
  1426. _dnsServer.LogManager?.Write("DNS Server successfully loaded zone file: " + zoneFile);
  1427. }
  1428. catch (Exception ex2)
  1429. {
  1430. _dnsServer.LogManager?.Write("DNS Server failed to load zone file: " + zoneFile + "\r\n" + ex2.ToString());
  1431. }
  1432. finally
  1433. {
  1434. _zoneIndexLock.ExitWriteLock();
  1435. }
  1436. throw;
  1437. }
  1438. }
  1439. public void SignPrimaryZoneWithRsaNSEC(string zoneName, string hashAlgorithm, int kskKeySize, int zskKeySize, uint dnsKeyTtl, ushort zskRolloverDays)
  1440. {
  1441. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1442. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1443. primaryZone.SignZoneWithRsaNSec(hashAlgorithm, kskKeySize, zskKeySize, dnsKeyTtl, zskRolloverDays);
  1444. SaveZoneFile(primaryZone.Name);
  1445. }
  1446. public void SignPrimaryZoneWithRsaNSEC3(string zoneName, string hashAlgorithm, int kskKeySize, int zskKeySize, ushort iterations, byte saltLength, uint dnsKeyTtl, ushort zskRolloverDays)
  1447. {
  1448. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1449. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1450. primaryZone.SignZoneWithRsaNSec3(hashAlgorithm, kskKeySize, zskKeySize, iterations, saltLength, dnsKeyTtl, zskRolloverDays);
  1451. SaveZoneFile(primaryZone.Name);
  1452. }
  1453. public void SignPrimaryZoneWithEcdsaNSEC(string zoneName, string curve, uint dnsKeyTtl, ushort zskRolloverDays)
  1454. {
  1455. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1456. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1457. primaryZone.SignZoneWithEcdsaNSec(curve, dnsKeyTtl, zskRolloverDays);
  1458. SaveZoneFile(primaryZone.Name);
  1459. }
  1460. public void SignPrimaryZoneWithEcdsaNSEC3(string zoneName, string curve, ushort iterations, byte saltLength, uint dnsKeyTtl, ushort zskRolloverDays)
  1461. {
  1462. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1463. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1464. primaryZone.SignZoneWithEcdsaNSec3(curve, iterations, saltLength, dnsKeyTtl, zskRolloverDays);
  1465. SaveZoneFile(primaryZone.Name);
  1466. }
  1467. public void UnsignPrimaryZone(string zoneName)
  1468. {
  1469. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1470. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1471. primaryZone.UnsignZone();
  1472. SaveZoneFile(primaryZone.Name);
  1473. }
  1474. public void ConvertPrimaryZoneToNSEC(string zoneName)
  1475. {
  1476. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1477. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1478. primaryZone.ConvertToNSec();
  1479. SaveZoneFile(primaryZone.Name);
  1480. }
  1481. public void ConvertPrimaryZoneToNSEC3(string zoneName, ushort iterations, byte saltLength)
  1482. {
  1483. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1484. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1485. primaryZone.ConvertToNSec3(iterations, saltLength);
  1486. SaveZoneFile(primaryZone.Name);
  1487. }
  1488. public void UpdatePrimaryZoneNSEC3Parameters(string zoneName, ushort iterations, byte saltLength)
  1489. {
  1490. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1491. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1492. primaryZone.UpdateNSec3Parameters(iterations, saltLength);
  1493. SaveZoneFile(primaryZone.Name);
  1494. }
  1495. public void UpdatePrimaryZoneDnsKeyTtl(string zoneName, uint dnsKeyTtl)
  1496. {
  1497. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1498. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1499. primaryZone.UpdateDnsKeyTtl(dnsKeyTtl);
  1500. SaveZoneFile(primaryZone.Name);
  1501. }
  1502. public void GenerateAndAddPrimaryZoneDnssecRsaPrivateKey(string zoneName, DnssecPrivateKeyType keyType, string hashAlgorithm, int keySize, ushort rolloverDays)
  1503. {
  1504. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1505. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1506. primaryZone.GenerateAndAddRsaKey(keyType, hashAlgorithm, keySize, rolloverDays);
  1507. SaveZoneFile(primaryZone.Name);
  1508. }
  1509. public void GenerateAndAddPrimaryZoneDnssecEcdsaPrivateKey(string zoneName, DnssecPrivateKeyType keyType, string curve, ushort rolloverDays)
  1510. {
  1511. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1512. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1513. primaryZone.GenerateAndAddEcdsaKey(keyType, curve, rolloverDays);
  1514. SaveZoneFile(primaryZone.Name);
  1515. }
  1516. public void UpdatePrimaryZoneDnssecPrivateKey(string zoneName, ushort keyTag, ushort rolloverDays)
  1517. {
  1518. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1519. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1520. primaryZone.UpdatePrivateKey(keyTag, rolloverDays);
  1521. SaveZoneFile(primaryZone.Name);
  1522. }
  1523. public void DeletePrimaryZoneDnssecPrivateKey(string zoneName, ushort keyTag)
  1524. {
  1525. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1526. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1527. primaryZone.DeletePrivateKey(keyTag);
  1528. SaveZoneFile(primaryZone.Name);
  1529. }
  1530. public void PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(string zoneName)
  1531. {
  1532. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1533. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1534. primaryZone.PublishAllGeneratedKeys();
  1535. SaveZoneFile(primaryZone.Name);
  1536. }
  1537. public void RolloverPrimaryZoneDnsKey(string zoneName, ushort keyTag)
  1538. {
  1539. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1540. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1541. primaryZone.RolloverDnsKey(keyTag);
  1542. SaveZoneFile(primaryZone.Name);
  1543. }
  1544. public void RetirePrimaryZoneDnsKey(string zoneName, ushort keyTag)
  1545. {
  1546. if (!_root.TryGet(zoneName, out ApexZone apexZone) || (apexZone is not PrimaryZone primaryZone) || primaryZone.Internal)
  1547. throw new DnsServerException("No such primary zone was found: " + zoneName);
  1548. primaryZone.RetireDnsKey(keyTag);
  1549. SaveZoneFile(primaryZone.Name);
  1550. }
  1551. public bool DeleteZone(string zoneName, bool deleteZoneFile = false)
  1552. {
  1553. AuthZoneInfo zoneInfo = GetAuthZoneInfo(zoneName);
  1554. if (zoneInfo is null)
  1555. return false;
  1556. return DeleteZone(zoneInfo, deleteZoneFile);
  1557. }
  1558. public bool DeleteZone(AuthZoneInfo zoneInfo, bool deleteZoneFile = false)
  1559. {
  1560. switch (zoneInfo.Type)
  1561. {
  1562. case AuthZoneType.Catalog:
  1563. //update all zone memberships for catalog zone to be deleted
  1564. foreach (string memberZoneName in (zoneInfo.ApexZone as CatalogZone).GetAllMemberZoneNames())
  1565. {
  1566. AuthZoneInfo memberZoneInfo = GetAuthZoneInfo(memberZoneName);
  1567. if (memberZoneInfo is null)
  1568. continue;
  1569. if (zoneInfo.Name.Equals(memberZoneInfo.CatalogZoneName, StringComparison.OrdinalIgnoreCase))
  1570. {
  1571. memberZoneInfo.ApexZone.CatalogZoneName = null;
  1572. SaveZoneFile(memberZoneInfo.Name);
  1573. }
  1574. }
  1575. break;
  1576. case AuthZoneType.SecondaryCatalog:
  1577. //delete all member zones for secondary catalog zone to be deleted
  1578. foreach (string memberZoneName in (zoneInfo.ApexZone as SecondaryCatalogZone).GetAllMemberZoneNames())
  1579. {
  1580. AuthZoneInfo memberZoneInfo = GetAuthZoneInfo(memberZoneName);
  1581. if (memberZoneInfo is null)
  1582. continue;
  1583. if (zoneInfo.Name.Equals(memberZoneInfo.CatalogZoneName, StringComparison.OrdinalIgnoreCase))
  1584. DeleteZone(memberZoneInfo, true);
  1585. }
  1586. break;
  1587. }
  1588. _zoneIndexLock.EnterWriteLock();
  1589. try
  1590. {
  1591. if (_root.TryRemove(zoneInfo.Name, out ApexZone removedApexZone))
  1592. {
  1593. removedApexZone.Dispose();
  1594. _zoneIndex.Remove(zoneInfo);
  1595. if (zoneInfo.Type == AuthZoneType.Catalog)
  1596. _catalogZoneIndex.Remove(zoneInfo);
  1597. if (zoneInfo.CatalogZoneName is not null)
  1598. RemoveCatalogMemberZone(zoneInfo); //remove catalog zone membership
  1599. if (deleteZoneFile)
  1600. {
  1601. File.Delete(Path.Combine(_dnsServer.ConfigFolder, "zones", zoneInfo.Name + ".zone"));
  1602. _dnsServer.LogManager?.Write("Deleted zone file for domain: " + zoneInfo.DisplayName);
  1603. }
  1604. return true;
  1605. }
  1606. }
  1607. finally
  1608. {
  1609. _zoneIndexLock.ExitWriteLock();
  1610. }
  1611. return false;
  1612. }
  1613. public AuthZoneInfo GetAuthZoneInfo(string zoneName, bool loadHistory = false)
  1614. {
  1615. if (_root.TryGet(zoneName, out AuthZoneNode authZoneNode) && (authZoneNode.ApexZone is not null))
  1616. return new AuthZoneInfo(authZoneNode.ApexZone, loadHistory);
  1617. return null;
  1618. }
  1619. public AuthZoneInfo FindAuthZoneInfo(string domain, bool loadHistory = false)
  1620. {
  1621. _ = _root.FindZone(domain, out _, out _, out ApexZone apexZone, out _);
  1622. if (apexZone is null)
  1623. return null;
  1624. return new AuthZoneInfo(apexZone, loadHistory);
  1625. }
  1626. public bool NameExists(string zoneName, string domain)
  1627. {
  1628. ValidateZoneNameFor(zoneName, domain);
  1629. return _root.TryGet(zoneName, domain, out _);
  1630. }
  1631. public void ListAllZoneRecords(string zoneName, List<DnsResourceRecord> records)
  1632. {
  1633. foreach (AuthZone authZone in _root.GetApexZoneWithSubDomainZones(zoneName))
  1634. authZone.ListAllRecords(records);
  1635. }
  1636. public void ListAllZoneRecords(string zoneName, DnsResourceRecordType[] types, List<DnsResourceRecord> records)
  1637. {
  1638. foreach (AuthZone authZone in _root.GetApexZoneWithSubDomainZones(zoneName))
  1639. {
  1640. foreach (DnsResourceRecordType type in types)
  1641. records.AddRange(authZone.GetRecords(type));
  1642. }
  1643. }
  1644. public void ListAllRecords(string zoneName, string domain, List<DnsResourceRecord> records)
  1645. {
  1646. ValidateZoneNameFor(zoneName, domain);
  1647. if (_root.TryGet(zoneName, domain, out AuthZone authZone))
  1648. authZone.ListAllRecords(records);
  1649. }
  1650. public IEnumerable<DnsResourceRecord> EnumerateAllRecords(string zoneName, string domain, bool includeAllSubDomainNames = false)
  1651. {
  1652. ValidateZoneNameFor(zoneName, domain);
  1653. if (includeAllSubDomainNames)
  1654. {
  1655. foreach (AuthZone authZone in _root.GetSubDomainZoneWithSubDomainZones(domain))
  1656. {
  1657. foreach (KeyValuePair<DnsResourceRecordType, IReadOnlyList<DnsResourceRecord>> entry in authZone.Entries)
  1658. {
  1659. foreach (DnsResourceRecord record in entry.Value)
  1660. yield return record;
  1661. }
  1662. }
  1663. }
  1664. else
  1665. {
  1666. if (_root.TryGet(zoneName, domain, out AuthZone authZone))
  1667. {
  1668. foreach (KeyValuePair<DnsResourceRecordType, IReadOnlyList<DnsResourceRecord>> entry in authZone.Entries)
  1669. {
  1670. foreach (DnsResourceRecord record in entry.Value)
  1671. yield return record;
  1672. }
  1673. }
  1674. }
  1675. }
  1676. public IReadOnlyList<DnsResourceRecord> GetRecords(string zoneName, string domain, DnsResourceRecordType type)
  1677. {
  1678. ValidateZoneNameFor(zoneName, domain);
  1679. if (_root.TryGet(zoneName, domain, out AuthZone authZone))
  1680. return authZone.GetRecords(type);
  1681. return Array.Empty<DnsResourceRecord>();
  1682. }
  1683. public IReadOnlyDictionary<DnsResourceRecordType, IReadOnlyList<DnsResourceRecord>> GetEntriesFor(string zoneName, string domain)
  1684. {
  1685. ValidateZoneNameFor(zoneName, domain);
  1686. if (_root.TryGet(zoneName, domain, out AuthZone authZone))
  1687. return authZone.Entries;
  1688. return new Dictionary<DnsResourceRecordType, IReadOnlyList<DnsResourceRecord>>(1);
  1689. }
  1690. public IReadOnlyList<DnsResourceRecord> QueryZoneTransferRecords(string zoneName)
  1691. {
  1692. AuthZoneInfo zoneInfo = GetAuthZoneInfo(zoneName);
  1693. if (zoneInfo is null)
  1694. throw new InvalidOperationException("Zone was not found: " + zoneName);
  1695. //primary, secondary, and forwarder zones support zone transfer
  1696. IReadOnlyList<DnsResourceRecord> soaRecords = zoneInfo.ApexZone.GetRecords(DnsResourceRecordType.SOA);
  1697. if (soaRecords.Count != 1)
  1698. throw new InvalidOperationException("Zone must be a primary, secondary, or forwarder zone.");
  1699. DnsResourceRecord soaRecord = soaRecords[0];
  1700. List<DnsResourceRecord> records = new List<DnsResourceRecord>();
  1701. ListAllZoneRecords(zoneName, records);
  1702. List<DnsResourceRecord> xfrRecords = new List<DnsResourceRecord>(records.Count + 1);
  1703. //start message
  1704. xfrRecords.Add(soaRecord);
  1705. foreach (DnsResourceRecord record in records)
  1706. {
  1707. GenericRecordInfo authRecordInfo = record.GetAuthGenericRecordInfo();
  1708. if (authRecordInfo.Disabled)
  1709. continue;
  1710. switch (record.Type)
  1711. {
  1712. case DnsResourceRecordType.SOA:
  1713. break; //skip record
  1714. case DnsResourceRecordType.NS:
  1715. xfrRecords.Add(record);
  1716. IReadOnlyList<DnsResourceRecord> glueRecords = (authRecordInfo as NSRecordInfo).GlueRecords;
  1717. if (glueRecords is not null)
  1718. {
  1719. foreach (DnsResourceRecord glueRecord in glueRecords)
  1720. xfrRecords.Add(glueRecord);
  1721. }
  1722. break;
  1723. default:
  1724. xfrRecords.Add(record);
  1725. break;
  1726. }
  1727. }
  1728. //end message
  1729. xfrRecords.Add(soaRecord);
  1730. return xfrRecords;
  1731. }
  1732. public IReadOnlyList<DnsResourceRecord> QueryIncrementalZoneTransferRecords(string zoneName, DnsResourceRecord clientSoaRecord)
  1733. {
  1734. AuthZoneInfo authZone = GetAuthZoneInfo(zoneName, true);
  1735. if (authZone is null)
  1736. throw new InvalidOperationException("Zone was not found: " + zoneName);
  1737. //primary, secondary, forwarder, and catalog zones support zone transfer
  1738. IReadOnlyList<DnsResourceRecord> soaRecords = authZone.ApexZone.GetRecords(DnsResourceRecordType.SOA);
  1739. if (soaRecords.Count != 1)
  1740. throw new InvalidOperationException("No SOA record was found for IXFR.");
  1741. DnsResourceRecord currentSoaRecord = soaRecords[0];
  1742. uint clientSerial = (clientSoaRecord.RDATA as DnsSOARecordData).Serial;
  1743. if (clientSerial == (currentSoaRecord.RDATA as DnsSOARecordData).Serial)
  1744. {
  1745. //zone not modified
  1746. return [currentSoaRecord];
  1747. }
  1748. //find history record start from client serial
  1749. IReadOnlyList<DnsResourceRecord> zoneHistory = authZone.ZoneHistory;
  1750. int index = 0;
  1751. while (index < zoneHistory.Count)
  1752. {
  1753. //check difference sequence
  1754. if ((zoneHistory[index].RDATA as DnsSOARecordData).Serial == clientSerial)
  1755. break; //found history for client's serial
  1756. //skip to next difference sequence
  1757. index++;
  1758. int soaCount = 1;
  1759. while (index < zoneHistory.Count)
  1760. {
  1761. if (zoneHistory[index].Type == DnsResourceRecordType.SOA)
  1762. {
  1763. soaCount++;
  1764. if (soaCount == 3)
  1765. break;
  1766. }
  1767. index++;
  1768. }
  1769. }
  1770. if (index == zoneHistory.Count)
  1771. {
  1772. //client's serial was not found in zone history
  1773. //do full zone transfer
  1774. return QueryZoneTransferRecords(zoneName);
  1775. }
  1776. List<DnsResourceRecord> xfrRecords = new List<DnsResourceRecord>();
  1777. //start incremental message
  1778. xfrRecords.Add(currentSoaRecord);
  1779. //write history
  1780. for (int i = index; i < zoneHistory.Count; i++)
  1781. xfrRecords.Add(zoneHistory[i]);
  1782. //end incremental message
  1783. xfrRecords.Add(currentSoaRecord);
  1784. //condense
  1785. return CondenseIncrementalZoneTransferRecords(zoneName, clientSoaRecord, xfrRecords);
  1786. }
  1787. public void SyncZoneTransferRecords(string zoneName, IReadOnlyList<DnsResourceRecord> xfrRecords)
  1788. {
  1789. if ((xfrRecords.Count < 2) || (xfrRecords[0].Type != DnsResourceRecordType.SOA) || !xfrRecords[0].Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || !xfrRecords[xfrRecords.Count - 1].Equals(xfrRecords[0]))
  1790. throw new DnsServerException("Invalid AXFR response was received.");
  1791. List<DnsResourceRecord> latestRecords = new List<DnsResourceRecord>(xfrRecords.Count);
  1792. List<DnsResourceRecord> allGlueRecords = new List<DnsResourceRecord>(4);
  1793. if (zoneName.Length == 0)
  1794. {
  1795. //root zone case
  1796. for (int i = 1; i < xfrRecords.Count; i++)
  1797. {
  1798. DnsResourceRecord record = xfrRecords[i];
  1799. switch (record.Type)
  1800. {
  1801. case DnsResourceRecordType.A:
  1802. case DnsResourceRecordType.AAAA:
  1803. if (!allGlueRecords.Contains(record))
  1804. allGlueRecords.Add(record);
  1805. break;
  1806. default:
  1807. if (!latestRecords.Contains(record))
  1808. latestRecords.Add(record);
  1809. break;
  1810. }
  1811. }
  1812. }
  1813. else
  1814. {
  1815. for (int i = 1; i < xfrRecords.Count; i++)
  1816. {
  1817. DnsResourceRecord record = xfrRecords[i];
  1818. if (record.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase))
  1819. {
  1820. if (!latestRecords.Contains(record))
  1821. latestRecords.Add(record);
  1822. }
  1823. else if (!allGlueRecords.Contains(record))
  1824. {
  1825. allGlueRecords.Add(record);
  1826. }
  1827. }
  1828. }
  1829. if (allGlueRecords.Count > 0)
  1830. {
  1831. foreach (DnsResourceRecord record in latestRecords)
  1832. {
  1833. if (record.Type == DnsResourceRecordType.NS)
  1834. record.SyncGlueRecords(allGlueRecords);
  1835. }
  1836. }
  1837. //sync records
  1838. List<DnsResourceRecord> currentRecords = new List<DnsResourceRecord>();
  1839. ListAllZoneRecords(zoneName, currentRecords);
  1840. Dictionary<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> currentRecordsGroupedByDomain = DnsResourceRecord.GroupRecords(currentRecords);
  1841. Dictionary<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> latestRecordsGroupedByDomain = DnsResourceRecord.GroupRecords(latestRecords);
  1842. //remove domains that do not exists in new records
  1843. foreach (KeyValuePair<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> currentDomain in currentRecordsGroupedByDomain)
  1844. {
  1845. if (!latestRecordsGroupedByDomain.ContainsKey(currentDomain.Key))
  1846. _root.TryRemove(currentDomain.Key, out SubDomainZone _);
  1847. }
  1848. //sync new records
  1849. foreach (KeyValuePair<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> latestEntries in latestRecordsGroupedByDomain)
  1850. {
  1851. AuthZone zone = GetOrAddSubDomainZone(zoneName, latestEntries.Key);
  1852. if (zone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  1853. zone.SyncRecords(latestEntries.Value);
  1854. else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  1855. zone.SyncRecords(latestEntries.Value);
  1856. }
  1857. if (!_root.TryGet(zoneName, out ApexZone apexZone))
  1858. throw new InvalidOperationException();
  1859. apexZone.UpdateDnssecStatus();
  1860. SaveZoneFile(apexZone.Name);
  1861. }
  1862. public IReadOnlyList<DnsResourceRecord> SyncIncrementalZoneTransferRecords(string zoneName, IReadOnlyList<DnsResourceRecord> xfrRecords)
  1863. {
  1864. if ((xfrRecords.Count < 2) || (xfrRecords[0].Type != DnsResourceRecordType.SOA) || !xfrRecords[0].Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || !xfrRecords[xfrRecords.Count - 1].Equals(xfrRecords[0]))
  1865. throw new DnsServerException("Invalid IXFR/AXFR response was received.");
  1866. if ((xfrRecords.Count < 4) || (xfrRecords[1].Type != DnsResourceRecordType.SOA))
  1867. {
  1868. //received AXFR response
  1869. SyncZoneTransferRecords(zoneName, xfrRecords);
  1870. return Array.Empty<DnsResourceRecord>();
  1871. }
  1872. if (!_root.TryGet(zoneName, out ApexZone apexZone))
  1873. throw new InvalidOperationException("No such zone was found: " + zoneName);
  1874. IReadOnlyList<DnsResourceRecord> soaRecords = apexZone.GetRecords(DnsResourceRecordType.SOA);
  1875. if (soaRecords.Count != 1)
  1876. throw new InvalidOperationException("No authoritative zone was found: " + zoneName);
  1877. //process IXFR response
  1878. DnsResourceRecord currentSoaRecord = soaRecords[0];
  1879. DnsSOARecordData currentSoa = currentSoaRecord.RDATA as DnsSOARecordData;
  1880. IReadOnlyList<DnsResourceRecord> condensedXfrRecords = CondenseIncrementalZoneTransferRecords(zoneName, currentSoaRecord, xfrRecords);
  1881. List<DnsResourceRecord> deletedRecords = new List<DnsResourceRecord>();
  1882. List<DnsResourceRecord> deletedGlueRecords = new List<DnsResourceRecord>();
  1883. List<DnsResourceRecord> addedRecords = new List<DnsResourceRecord>();
  1884. List<DnsResourceRecord> addedGlueRecords = new List<DnsResourceRecord>();
  1885. //read and apply difference sequences
  1886. int index = 1;
  1887. int count = condensedXfrRecords.Count - 1;
  1888. while (index < count)
  1889. {
  1890. //read deleted records
  1891. DnsResourceRecord deletedSoaRecord = condensedXfrRecords[index];
  1892. if ((deletedSoaRecord.Type != DnsResourceRecordType.SOA) || !deletedSoaRecord.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  1893. throw new InvalidOperationException();
  1894. index++;
  1895. while (index < count)
  1896. {
  1897. DnsResourceRecord record = condensedXfrRecords[index];
  1898. if (record.Type == DnsResourceRecordType.SOA)
  1899. break;
  1900. if (zoneName.Length == 0)
  1901. {
  1902. //root zone case
  1903. switch (record.Type)
  1904. {
  1905. case DnsResourceRecordType.A:
  1906. case DnsResourceRecordType.AAAA:
  1907. deletedGlueRecords.Add(record);
  1908. break;
  1909. default:
  1910. deletedRecords.Add(record);
  1911. break;
  1912. }
  1913. }
  1914. else
  1915. {
  1916. if (record.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase))
  1917. {
  1918. deletedRecords.Add(record);
  1919. }
  1920. else
  1921. {
  1922. switch (record.Type)
  1923. {
  1924. case DnsResourceRecordType.A:
  1925. case DnsResourceRecordType.AAAA:
  1926. deletedGlueRecords.Add(record);
  1927. break;
  1928. }
  1929. }
  1930. }
  1931. index++;
  1932. }
  1933. //read added records
  1934. DnsResourceRecord addedSoaRecord = condensedXfrRecords[index];
  1935. if (!addedSoaRecord.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  1936. throw new InvalidOperationException();
  1937. index++;
  1938. while (index < count)
  1939. {
  1940. DnsResourceRecord record = condensedXfrRecords[index];
  1941. if (record.Type == DnsResourceRecordType.SOA)
  1942. break;
  1943. if (zoneName.Length == 0)
  1944. {
  1945. //root zone case
  1946. switch (record.Type)
  1947. {
  1948. case DnsResourceRecordType.A:
  1949. case DnsResourceRecordType.AAAA:
  1950. addedGlueRecords.Add(record);
  1951. break;
  1952. default:
  1953. addedRecords.Add(record);
  1954. break;
  1955. }
  1956. }
  1957. else
  1958. {
  1959. if (record.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase) || record.Name.EndsWith("." + zoneName, StringComparison.OrdinalIgnoreCase))
  1960. {
  1961. addedRecords.Add(record);
  1962. }
  1963. else
  1964. {
  1965. switch (record.Type)
  1966. {
  1967. case DnsResourceRecordType.A:
  1968. case DnsResourceRecordType.AAAA:
  1969. addedGlueRecords.Add(record);
  1970. break;
  1971. }
  1972. }
  1973. }
  1974. index++;
  1975. }
  1976. //check sequence soa serial
  1977. DnsSOARecordData deletedSoa = deletedSoaRecord.RDATA as DnsSOARecordData;
  1978. if (currentSoa.Serial != deletedSoa.Serial)
  1979. throw new InvalidOperationException("Current SOA serial does not match with the IXFR difference sequence deleted SOA.");
  1980. //sync difference sequence
  1981. if (deletedRecords.Count > 0)
  1982. {
  1983. foreach (KeyValuePair<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> deletedEntry in DnsResourceRecord.GroupRecords(deletedRecords))
  1984. {
  1985. AuthZone zone = GetOrAddSubDomainZone(zoneName, deletedEntry.Key);
  1986. if (zone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  1987. {
  1988. zone.SyncRecords(deletedEntry.Value, null);
  1989. }
  1990. else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  1991. {
  1992. zone.SyncRecords(deletedEntry.Value, null);
  1993. if (zone.IsEmpty)
  1994. _root.TryRemove(deletedEntry.Key, out SubDomainZone _); //remove empty sub zone
  1995. }
  1996. }
  1997. }
  1998. if (addedRecords.Count > 0)
  1999. {
  2000. foreach (KeyValuePair<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> addedEntry in DnsResourceRecord.GroupRecords(addedRecords))
  2001. {
  2002. AuthZone zone = GetOrAddSubDomainZone(zoneName, addedEntry.Key);
  2003. if (zone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  2004. zone.SyncRecords(null, addedEntry.Value);
  2005. else if ((zone is SubDomainZone subDomainZone) && subDomainZone.AuthoritativeZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  2006. zone.SyncRecords(null, addedEntry.Value);
  2007. }
  2008. }
  2009. if ((deletedGlueRecords.Count > 0) || (addedGlueRecords.Count > 0))
  2010. {
  2011. foreach (AuthZone zone in _root.GetApexZoneWithSubDomainZones(zoneName))
  2012. zone.SyncGlueRecords(deletedGlueRecords, addedGlueRecords);
  2013. }
  2014. {
  2015. AuthZone zone = GetOrAddSubDomainZone(zoneName, zoneName);
  2016. addedSoaRecord.CopyRecordInfoFrom(currentSoaRecord);
  2017. zone.LoadRecords(DnsResourceRecordType.SOA, new DnsResourceRecord[] { addedSoaRecord });
  2018. }
  2019. //check next difference sequence
  2020. currentSoa = addedSoaRecord.RDATA as DnsSOARecordData;
  2021. deletedRecords.Clear();
  2022. deletedGlueRecords.Clear();
  2023. addedRecords.Clear();
  2024. addedGlueRecords.Clear();
  2025. }
  2026. apexZone.UpdateDnssecStatus();
  2027. SaveZoneFile(apexZone.Name);
  2028. //return history
  2029. List<DnsResourceRecord> historyRecords = new List<DnsResourceRecord>(xfrRecords.Count - 2);
  2030. for (int i = 1; i < xfrRecords.Count - 1; i++)
  2031. historyRecords.Add(xfrRecords[i]);
  2032. return historyRecords;
  2033. }
  2034. internal void ImportRecords(string zoneName, IReadOnlyList<DnsResourceRecord> records, bool overwrite, bool overwriteSoaSerial)
  2035. {
  2036. _ = _root.FindZone(zoneName, out _, out _, out ApexZone apexZone, out _);
  2037. if ((apexZone is null) || !apexZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
  2038. throw new DnsServerException("No such zone was found: " + zoneName);
  2039. if ((apexZone is not PrimaryZone) && (apexZone is not ForwarderZone))
  2040. throw new DnsServerException("Zone must be a primary or forwarder type: " + apexZone.ToString());
  2041. List<DnsResourceRecord> soaRRSet = null;
  2042. foreach (KeyValuePair<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> zoneEntry in DnsResourceRecord.GroupRecords(records))
  2043. {
  2044. if (zoneName.Equals(zoneEntry.Key, StringComparison.OrdinalIgnoreCase))
  2045. {
  2046. foreach (KeyValuePair<DnsResourceRecordType, List<DnsResourceRecord>> rrsetEntry in zoneEntry.Value)
  2047. {
  2048. switch (rrsetEntry.Key)
  2049. {
  2050. case DnsResourceRecordType.CNAME:
  2051. case DnsResourceRecordType.DNAME:
  2052. apexZone.SetRecords(rrsetEntry.Key, rrsetEntry.Value);
  2053. break;
  2054. case DnsResourceRecordType.SOA:
  2055. apexZone.SetRecords(rrsetEntry.Key, rrsetEntry.Value);
  2056. soaRRSet = rrsetEntry.Value;
  2057. break;
  2058. default:
  2059. if (overwrite)
  2060. {
  2061. apexZone.SetRecords(rrsetEntry.Key, rrsetEntry.Value);
  2062. }
  2063. else
  2064. {
  2065. foreach (DnsResourceRecord record in rrsetEntry.Value)
  2066. apexZone.AddRecord(record);
  2067. }
  2068. break;
  2069. }
  2070. }
  2071. }
  2072. else
  2073. {
  2074. ValidateZoneNameFor(zoneName, zoneEntry.Key);
  2075. AuthZone authZone = GetOrAddSubDomainZone(zoneName, zoneEntry.Key);
  2076. foreach (KeyValuePair<DnsResourceRecordType, List<DnsResourceRecord>> rrsetEntry in zoneEntry.Value)
  2077. {
  2078. switch (rrsetEntry.Key)
  2079. {
  2080. case DnsResourceRecordType.CNAME:
  2081. case DnsResourceRecordType.DNAME:
  2082. case DnsResourceRecordType.SOA:
  2083. authZone.SetRecords(rrsetEntry.Key, rrsetEntry.Value);
  2084. break;
  2085. default:
  2086. if (overwrite)
  2087. {
  2088. authZone.SetRecords(rrsetEntry.Key, rrsetEntry.Value);
  2089. }
  2090. else
  2091. {
  2092. foreach (DnsResourceRecord record in rrsetEntry.Value)
  2093. authZone.AddRecord(record);
  2094. }
  2095. break;
  2096. }
  2097. }
  2098. if (authZone is SubDomainZone subDomainZone)
  2099. subDomainZone.AutoUpdateState();
  2100. }
  2101. }
  2102. if (overwriteSoaSerial && (soaRRSet is not null) && ((apexZone is PrimaryZone) || (apexZone is ForwarderZone)))
  2103. apexZone.SetSoaSerial((soaRRSet[0].RDATA as DnsSOARecordData).Serial);
  2104. SaveZoneFile(apexZone.Name);
  2105. }
  2106. private void LoadZoneRecords(ApexZone apexZone, IReadOnlyList<DnsResourceRecord> records)
  2107. {
  2108. foreach (KeyValuePair<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> zoneEntry in DnsResourceRecord.GroupRecords(records))
  2109. {
  2110. if (apexZone.Name.Equals(zoneEntry.Key, StringComparison.OrdinalIgnoreCase))
  2111. {
  2112. foreach (KeyValuePair<DnsResourceRecordType, List<DnsResourceRecord>> rrsetEntry in zoneEntry.Value)
  2113. apexZone.LoadRecords(rrsetEntry.Key, rrsetEntry.Value);
  2114. }
  2115. else
  2116. {
  2117. ValidateZoneNameFor(apexZone.Name, zoneEntry.Key);
  2118. AuthZone authZone = GetOrAddSubDomainZone(apexZone.Name, zoneEntry.Key);
  2119. foreach (KeyValuePair<DnsResourceRecordType, List<DnsResourceRecord>> rrsetEntry in zoneEntry.Value)
  2120. authZone.LoadRecords(rrsetEntry.Key, rrsetEntry.Value);
  2121. if (authZone is SubDomainZone subDomainZone)
  2122. subDomainZone.AutoUpdateState();
  2123. }
  2124. }
  2125. apexZone.UpdateDnssecStatus();
  2126. }
  2127. public void SetRecords(string zoneName, IReadOnlyList<DnsResourceRecord> records)
  2128. {
  2129. for (int i = 1; i < records.Count; i++)
  2130. {
  2131. if (!records[i].Name.Equals(records[0].Name, StringComparison.OrdinalIgnoreCase))
  2132. throw new InvalidOperationException();
  2133. if (records[i].Type != records[0].Type)
  2134. throw new InvalidOperationException();
  2135. if (records[i].Class != records[0].Class)
  2136. throw new InvalidOperationException();
  2137. }
  2138. AuthZone authZone = GetOrAddSubDomainZone(zoneName, records[0].Name);
  2139. authZone.SetRecords(records[0].Type, records);
  2140. if (authZone is SubDomainZone subDomainZone)
  2141. subDomainZone.AutoUpdateState();
  2142. }
  2143. public void SetRecord(string zoneName, DnsResourceRecord record)
  2144. {
  2145. ValidateZoneNameFor(zoneName, record.Name);
  2146. AuthZone authZone = GetOrAddSubDomainZone(zoneName, record.Name);
  2147. authZone.SetRecords(record.Type, new DnsResourceRecord[] { record });
  2148. if (authZone is SubDomainZone subDomainZone)
  2149. subDomainZone.AutoUpdateState();
  2150. }
  2151. public void AddRecord(string zoneName, DnsResourceRecord record)
  2152. {
  2153. ValidateZoneNameFor(zoneName, record.Name);
  2154. AuthZone authZone = GetOrAddSubDomainZone(zoneName, record.Name);
  2155. authZone.AddRecord(record);
  2156. if (authZone is SubDomainZone subDomainZone)
  2157. subDomainZone.AutoUpdateState();
  2158. }
  2159. public void UpdateRecord(string zoneName, DnsResourceRecord oldRecord, DnsResourceRecord newRecord)
  2160. {
  2161. ValidateZoneNameFor(zoneName, oldRecord.Name);
  2162. ValidateZoneNameFor(zoneName, newRecord.Name);
  2163. if (oldRecord.Type != newRecord.Type)
  2164. throw new DnsServerException("Cannot update record: new record must be of same type.");
  2165. if (oldRecord.Type == DnsResourceRecordType.SOA)
  2166. throw new DnsServerException("Cannot update record: use SetRecords() for updating SOA record.");
  2167. if (!_root.TryGet(zoneName, oldRecord.Name, out AuthZone authZone))
  2168. throw new DnsServerException("Cannot update record: zone '" + zoneName + "' does not exists.");
  2169. switch (oldRecord.Type)
  2170. {
  2171. case DnsResourceRecordType.CNAME:
  2172. case DnsResourceRecordType.DNAME:
  2173. case DnsResourceRecordType.APP:
  2174. if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
  2175. {
  2176. authZone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
  2177. if (authZone is SubDomainZone subDomainZone)
  2178. subDomainZone.AutoUpdateState();
  2179. }
  2180. else
  2181. {
  2182. authZone.DeleteRecords(oldRecord.Type);
  2183. if (authZone is SubDomainZone subDomainZone)
  2184. {
  2185. if (authZone.IsEmpty)
  2186. _root.TryRemove(oldRecord.Name, out SubDomainZone _); //remove empty sub zone
  2187. else
  2188. subDomainZone.AutoUpdateState();
  2189. }
  2190. AuthZone newZone = GetOrAddSubDomainZone(zoneName, newRecord.Name);
  2191. newZone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });
  2192. if (newZone is SubDomainZone subDomainZone1)
  2193. subDomainZone1.AutoUpdateState();
  2194. }
  2195. break;
  2196. default:
  2197. if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
  2198. {
  2199. authZone.UpdateRecord(oldRecord, newRecord);
  2200. if (authZone is SubDomainZone subDomainZone)
  2201. subDomainZone.AutoUpdateState();
  2202. }
  2203. else
  2204. {
  2205. if (!authZone.DeleteRecord(oldRecord.Type, oldRecord.RDATA))
  2206. throw new DnsWebServiceException("Cannot update record: the old record does not exists.");
  2207. if (authZone is SubDomainZone subDomainZone)
  2208. {
  2209. if (authZone.IsEmpty)
  2210. _root.TryRemove(oldRecord.Name, out SubDomainZone _); //remove empty sub zone
  2211. else
  2212. subDomainZone.AutoUpdateState();
  2213. }
  2214. AuthZone newZone = GetOrAddSubDomainZone(zoneName, newRecord.Name);
  2215. newZone.AddRecord(newRecord);
  2216. if (newZone is SubDomainZone subDomainZone1)
  2217. subDomainZone1.AutoUpdateState();
  2218. }
  2219. break;
  2220. }
  2221. }
  2222. public bool DeleteRecord(string zoneName, DnsResourceRecord record)
  2223. {
  2224. return DeleteRecord(zoneName, record.Name, record.Type, record.RDATA);
  2225. }
  2226. public bool DeleteRecord(string zoneName, string domain, DnsResourceRecordType type, DnsResourceRecordData rdata)
  2227. {
  2228. ValidateZoneNameFor(zoneName, domain);
  2229. if (_root.TryGet(zoneName, domain, out AuthZone authZone))
  2230. {
  2231. if (authZone.DeleteRecord(type, rdata))
  2232. {
  2233. if (authZone is SubDomainZone subDomainZone)
  2234. {
  2235. if (authZone.IsEmpty)
  2236. _root.TryRemove(domain, out SubDomainZone _); //remove empty sub zone
  2237. else
  2238. subDomainZone.AutoUpdateState();
  2239. }
  2240. return true;
  2241. }
  2242. }
  2243. return false;
  2244. }
  2245. public bool DeleteRecords(string zoneName, string domain, DnsResourceRecordType type)
  2246. {
  2247. ValidateZoneNameFor(zoneName, domain);
  2248. if (_root.TryGet(zoneName, domain, out AuthZone authZone))
  2249. {
  2250. if (authZone.DeleteRecords(type))
  2251. {
  2252. if (authZone is SubDomainZone subDomainZone)
  2253. {
  2254. if (authZone.IsEmpty)
  2255. _root.TryRemove(domain, out SubDomainZone _); //remove empty sub zone
  2256. else
  2257. subDomainZone.AutoUpdateState();
  2258. }
  2259. return true;
  2260. }
  2261. }
  2262. return false;
  2263. }
  2264. public IReadOnlyList<AuthZoneInfo> GetAllZones()
  2265. {
  2266. _zoneIndexLock.EnterReadLock();
  2267. try
  2268. {
  2269. return _zoneIndex.ToArray();
  2270. }
  2271. finally
  2272. {
  2273. _zoneIndexLock.ExitReadLock();
  2274. }
  2275. }
  2276. public IReadOnlyList<AuthZoneInfo> GetZones(Func<AuthZoneInfo, bool> predicate)
  2277. {
  2278. _zoneIndexLock.EnterReadLock();
  2279. try
  2280. {
  2281. List<AuthZoneInfo> zoneInfoList = new List<AuthZoneInfo>();
  2282. foreach (AuthZoneInfo zoneInfo in _zoneIndex)
  2283. {
  2284. if (predicate(zoneInfo))
  2285. zoneInfoList.Add(zoneInfo);
  2286. }
  2287. return zoneInfoList;
  2288. }
  2289. finally
  2290. {
  2291. _zoneIndexLock.ExitReadLock();
  2292. }
  2293. }
  2294. public IReadOnlyList<AuthZoneInfo> GetAllCatalogZones()
  2295. {
  2296. _zoneIndexLock.EnterReadLock();
  2297. try
  2298. {
  2299. return _catalogZoneIndex.ToArray();
  2300. }
  2301. finally
  2302. {
  2303. _zoneIndexLock.ExitReadLock();
  2304. }
  2305. }
  2306. public IReadOnlyList<AuthZoneInfo> GetCatalogZones(Func<AuthZoneInfo, bool> predicate)
  2307. {
  2308. _zoneIndexLock.EnterReadLock();
  2309. try
  2310. {
  2311. List<AuthZoneInfo> catalogZoneInfoList = new List<AuthZoneInfo>();
  2312. foreach (AuthZoneInfo zone in _catalogZoneIndex)
  2313. {
  2314. if (predicate(zone))
  2315. catalogZoneInfoList.Add(zone);
  2316. }
  2317. return catalogZoneInfoList;
  2318. }
  2319. finally
  2320. {
  2321. _zoneIndexLock.ExitReadLock();
  2322. }
  2323. }
  2324. public void ListSubDomains(string domain, List<string> subDomains)
  2325. {
  2326. _root.ListSubDomains(domain, subDomains);
  2327. }
  2328. public DnsDatagram QueryClosestDelegation(DnsDatagram request)
  2329. {
  2330. _ = _root.FindZone(request.Question[0].Name, out _, out SubDomainZone delegation, out ApexZone apexZone, out _);
  2331. if (delegation is not null)
  2332. {
  2333. bool dnssecOk = request.DnssecOk && (apexZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned);
  2334. return GetReferralResponse(request, dnssecOk, delegation, apexZone, CancellationToken.None);
  2335. }
  2336. //no delegation found
  2337. return null;
  2338. }
  2339. public async Task<DnsDatagram> QueryAsync(DnsDatagram request, IPAddress remoteIP, bool isRecursionAllowed, CancellationToken cancellationToken = default)
  2340. {
  2341. AuthZone zone = _root.FindZone(request.Question[0].Name, out SubDomainZone closest, out SubDomainZone delegation, out ApexZone apexZone, out bool hasSubDomains);
  2342. if ((apexZone is null) || !apexZone.IsActive)
  2343. return null; //no authority for requested zone
  2344. if (!await IsQueryAllowedAsync(apexZone, remoteIP))
  2345. return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, false, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.Refused, request.Question);
  2346. return InternalQuery(request, isRecursionAllowed, zone, closest, delegation, apexZone, hasSubDomains, cancellationToken);
  2347. }
  2348. public DnsDatagram Query(DnsDatagram request, bool isRecursionAllowed)
  2349. {
  2350. AuthZone zone = _root.FindZone(request.Question[0].Name, out SubDomainZone closest, out SubDomainZone delegation, out ApexZone apexZone, out bool hasSubDomains);
  2351. if ((apexZone is null) || !apexZone.IsActive)
  2352. return null; //no authority for requested zone
  2353. return InternalQuery(request, isRecursionAllowed, zone, closest, delegation, apexZone, hasSubDomains, CancellationToken.None);
  2354. }
  2355. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  2356. private DnsDatagram InternalQuery(DnsDatagram request, bool isRecursionAllowed, AuthZone zone, SubDomainZone closest, SubDomainZone delegation, ApexZone apexZone, bool hasSubDomains, CancellationToken cancellationToken)
  2357. {
  2358. DnsQuestionRecord question = request.Question[0];
  2359. bool dnssecOk = request.DnssecOk && (apexZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned);
  2360. if ((zone is null) || !zone.IsActive)
  2361. {
  2362. //zone not found
  2363. if ((delegation is not null) && delegation.IsActive && (delegation.Name.Length > apexZone.Name.Length))
  2364. return GetReferralResponse(request, dnssecOk, delegation, apexZone, cancellationToken);
  2365. if (apexZone is StubZone)
  2366. return GetReferralResponse(request, false, apexZone, apexZone, cancellationToken);
  2367. DnsResponseCode rCode = DnsResponseCode.NoError;
  2368. IReadOnlyList<DnsResourceRecord> answer = null;
  2369. IReadOnlyList<DnsResourceRecord> authority = null;
  2370. if (closest is not null)
  2371. {
  2372. answer = closest.QueryRecords(DnsResourceRecordType.DNAME, dnssecOk);
  2373. if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME))
  2374. {
  2375. if (!DoDNAMESubstitution(question, dnssecOk, answer, out answer))
  2376. rCode = DnsResponseCode.YXDomain;
  2377. }
  2378. else
  2379. {
  2380. answer = null;
  2381. authority = closest.QueryRecords(DnsResourceRecordType.APP, false);
  2382. }
  2383. }
  2384. if (((answer is null) || (answer.Count == 0)) && ((authority is null) || (authority.Count == 0)))
  2385. {
  2386. answer = apexZone.QueryRecords(DnsResourceRecordType.DNAME, dnssecOk);
  2387. if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME))
  2388. {
  2389. if (!DoDNAMESubstitution(question, dnssecOk, answer, out answer))
  2390. rCode = DnsResponseCode.YXDomain;
  2391. }
  2392. else
  2393. {
  2394. answer = null;
  2395. authority = apexZone.QueryRecords(DnsResourceRecordType.APP, false);
  2396. if (authority.Count == 0)
  2397. {
  2398. if ((apexZone is ForwarderZone) || (apexZone is SecondaryForwarderZone))
  2399. return GetForwarderResponse(request, null, closest, apexZone, cancellationToken); //no DNAME or APP record available so process FWD response
  2400. if (!hasSubDomains)
  2401. rCode = DnsResponseCode.NxDomain;
  2402. authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, dnssecOk);
  2403. if (dnssecOk)
  2404. {
  2405. //add proof of non existence (NXDOMAIN) to prove the qname does not exists
  2406. IReadOnlyList<DnsResourceRecord> nsecRecords;
  2407. if (apexZone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3)
  2408. nsecRecords = _root.FindNSec3ProofOfNonExistenceNxDomain(question.Name, false, cancellationToken);
  2409. else
  2410. nsecRecords = _root.FindNSecProofOfNonExistenceNxDomain(question.Name, false);
  2411. if (nsecRecords.Count > 0)
  2412. {
  2413. List<DnsResourceRecord> newAuthority = new List<DnsResourceRecord>(authority.Count + nsecRecords.Count);
  2414. newAuthority.AddRange(authority);
  2415. newAuthority.AddRange(nsecRecords);
  2416. authority = newAuthority;
  2417. }
  2418. }
  2419. }
  2420. }
  2421. }
  2422. return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, true, false, request.RecursionDesired, isRecursionAllowed, false, false, rCode, request.Question, answer, authority);
  2423. }
  2424. else
  2425. {
  2426. //zone found
  2427. if (question.Type == DnsResourceRecordType.DS)
  2428. {
  2429. if (zone is ApexZone)
  2430. {
  2431. if ((delegation is null) || !delegation.IsActive || (delegation.Name.Length > apexZone.Name.Length))
  2432. return null; //no authoritative parent side delegation zone available to answer for DS
  2433. zone = delegation; //switch zone to parent side sub domain delegation zone for DS record
  2434. }
  2435. }
  2436. else if ((delegation is not null) && delegation.IsActive && (delegation.Name.Length > apexZone.Name.Length))
  2437. {
  2438. //zone is delegation
  2439. return GetReferralResponse(request, dnssecOk, delegation, apexZone, cancellationToken);
  2440. }
  2441. DnsResponseCode rCode = DnsResponseCode.NoError;
  2442. IReadOnlyList<DnsResourceRecord> answer = null;
  2443. IReadOnlyList<DnsResourceRecord> authority = null;
  2444. IReadOnlyList<DnsResourceRecord> additional = null;
  2445. if (closest is not null)
  2446. {
  2447. answer = closest.QueryRecords(DnsResourceRecordType.DNAME, dnssecOk);
  2448. if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME))
  2449. {
  2450. if (!DoDNAMESubstitution(question, dnssecOk, answer, out answer))
  2451. rCode = DnsResponseCode.YXDomain;
  2452. }
  2453. }
  2454. if ((answer is null) || (answer.Count == 0))
  2455. {
  2456. answer = apexZone.QueryRecords(DnsResourceRecordType.DNAME, dnssecOk);
  2457. if ((answer.Count > 0) && (answer[0].Type == DnsResourceRecordType.DNAME))
  2458. {
  2459. if (!DoDNAMESubstitution(question, dnssecOk, answer, out answer))
  2460. rCode = DnsResponseCode.YXDomain;
  2461. }
  2462. else
  2463. {
  2464. answer = zone.QueryRecords(question.Type, dnssecOk);
  2465. if (answer.Count == 0)
  2466. {
  2467. //record type not found
  2468. if (question.Type == DnsResourceRecordType.DS)
  2469. {
  2470. //check for correct auth zone
  2471. if (apexZone.Name.Equals(question.Name, StringComparison.OrdinalIgnoreCase))
  2472. {
  2473. //current auth zone is child side; find parent side auth zone for DS
  2474. string parentZone = GetParentZone(question.Name);
  2475. if (parentZone is null)
  2476. parentZone = string.Empty;
  2477. _ = _root.FindZone(parentZone, out _, out _, out apexZone, out _);
  2478. if ((apexZone is null) || !apexZone.IsActive)
  2479. return null; //no authority for requested zone
  2480. }
  2481. }
  2482. else
  2483. {
  2484. //check for delegation, stub & forwarder
  2485. if ((delegation is not null) && delegation.IsActive && (delegation.Name.Length > apexZone.Name.Length))
  2486. return GetReferralResponse(request, dnssecOk, delegation, apexZone, cancellationToken);
  2487. if (apexZone is StubZone)
  2488. return GetReferralResponse(request, false, apexZone, apexZone, cancellationToken);
  2489. }
  2490. authority = zone.QueryRecords(DnsResourceRecordType.APP, false);
  2491. if (authority.Count == 0)
  2492. {
  2493. if ((apexZone is ForwarderZone) || (apexZone is SecondaryForwarderZone))
  2494. return GetForwarderResponse(request, zone, closest, apexZone, cancellationToken); //no APP record available so process FWD response
  2495. authority = apexZone.QueryRecords(DnsResourceRecordType.SOA, dnssecOk);
  2496. if (dnssecOk)
  2497. {
  2498. //add proof of non existence (NODATA) to prove that no such type or record exists
  2499. IReadOnlyList<DnsResourceRecord> nsecRecords;
  2500. if (apexZone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3)
  2501. nsecRecords = _root.FindNSec3ProofOfNonExistenceNoData(zone, apexZone, cancellationToken);
  2502. else
  2503. nsecRecords = AuthZoneTree.FindNSecProofOfNonExistenceNoData(zone);
  2504. if (nsecRecords.Count > 0)
  2505. {
  2506. List<DnsResourceRecord> newAuthority = new List<DnsResourceRecord>(authority.Count + nsecRecords.Count);
  2507. newAuthority.AddRange(authority);
  2508. newAuthority.AddRange(nsecRecords);
  2509. authority = newAuthority;
  2510. }
  2511. }
  2512. }
  2513. additional = null;
  2514. }
  2515. else
  2516. {
  2517. //record type found
  2518. if (zone.Name.StartsWith('*') && !zone.Name.Equals(question.Name, StringComparison.OrdinalIgnoreCase))
  2519. {
  2520. //wildcard zone; generate new answer records
  2521. DnsResourceRecord[] wildcardAnswers = new DnsResourceRecord[answer.Count];
  2522. for (int i = 0; i < answer.Count; i++)
  2523. wildcardAnswers[i] = new DnsResourceRecord(question.Name, answer[i].Type, answer[i].Class, answer[i].TTL, answer[i].RDATA) { Tag = answer[i].Tag };
  2524. answer = wildcardAnswers;
  2525. //add proof of non existence (WILDCARD) to prove that the wildcard expansion was legit and the qname actually does not exists
  2526. if (dnssecOk)
  2527. {
  2528. IReadOnlyList<DnsResourceRecord> nsecRecords;
  2529. if (apexZone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3)
  2530. nsecRecords = _root.FindNSec3ProofOfNonExistenceNxDomain(question.Name, true, cancellationToken);
  2531. else
  2532. nsecRecords = _root.FindNSecProofOfNonExistenceNxDomain(question.Name, true);
  2533. if (nsecRecords.Count > 0)
  2534. authority = nsecRecords;
  2535. }
  2536. }
  2537. DnsResourceRecord lastRR = answer[answer.Count - 1];
  2538. if ((lastRR.Type != question.Type) && (question.Type != DnsResourceRecordType.ANY))
  2539. {
  2540. switch (lastRR.Type)
  2541. {
  2542. case DnsResourceRecordType.CNAME:
  2543. List<DnsResourceRecord> newAnswers = new List<DnsResourceRecord>(answer.Count + 1);
  2544. newAnswers.AddRange(answer);
  2545. ResolveCNAME(question, dnssecOk, lastRR, newAnswers);
  2546. answer = newAnswers;
  2547. break;
  2548. case DnsResourceRecordType.ANAME:
  2549. case DnsResourceRecordType.ALIAS:
  2550. authority = apexZone.GetRecords(DnsResourceRecordType.SOA); //adding SOA for use with NO DATA response
  2551. break;
  2552. }
  2553. }
  2554. switch (question.Type)
  2555. {
  2556. case DnsResourceRecordType.NS:
  2557. case DnsResourceRecordType.MX:
  2558. case DnsResourceRecordType.SRV:
  2559. case DnsResourceRecordType.SVCB:
  2560. case DnsResourceRecordType.HTTPS:
  2561. additional = GetAdditionalRecords(answer, dnssecOk);
  2562. break;
  2563. default:
  2564. additional = null;
  2565. break;
  2566. }
  2567. }
  2568. }
  2569. }
  2570. return new DnsDatagram(request.Identifier, true, DnsOpcode.StandardQuery, true, false, request.RecursionDesired, isRecursionAllowed, false, false, rCode, request.Question, answer, authority, additional);
  2571. }
  2572. }
  2573. private static async Task<bool> IsQueryAllowedAsync(ApexZone apexZone, IPAddress remoteIP)
  2574. {
  2575. async Task<bool> IsZoneNameServerAllowedAsync()
  2576. {
  2577. IReadOnlyList<NameServerAddress> zoneNameServers = await apexZone.GetAllResolvedNameServerAddressesAsync();
  2578. foreach (NameServerAddress nameServer in zoneNameServers)
  2579. {
  2580. if (nameServer.IPEndPoint.Address.Equals(remoteIP))
  2581. return true;
  2582. }
  2583. return false;
  2584. }
  2585. CatalogZone catalogZone = apexZone.CatalogZone;
  2586. if (catalogZone is not null)
  2587. {
  2588. if (!apexZone.OverrideCatalogQueryAccess)
  2589. apexZone = catalogZone; //use catalog query access options
  2590. }
  2591. else
  2592. {
  2593. SecondaryCatalogZone secondaryCatalogZone = apexZone.SecondaryCatalogZone;
  2594. if (secondaryCatalogZone is not null)
  2595. {
  2596. if (!apexZone.OverrideCatalogQueryAccess)
  2597. apexZone = secondaryCatalogZone; //use secondary query access options
  2598. }
  2599. }
  2600. switch (apexZone.QueryAccess)
  2601. {
  2602. case AuthZoneQueryAccess.Allow:
  2603. return true;
  2604. case AuthZoneQueryAccess.AllowOnlyPrivateNetworks:
  2605. if (IPAddress.IsLoopback(remoteIP) || IPAddress.Any.Equals(remoteIP))
  2606. return true;
  2607. switch (remoteIP.AddressFamily)
  2608. {
  2609. case AddressFamily.InterNetwork:
  2610. case AddressFamily.InterNetworkV6:
  2611. return NetUtilities.IsPrivateIP(remoteIP);
  2612. default:
  2613. return false;
  2614. }
  2615. case AuthZoneQueryAccess.AllowOnlyZoneNameServers:
  2616. if (IPAddress.IsLoopback(remoteIP) || IPAddress.Any.Equals(remoteIP))
  2617. return true;
  2618. return await IsZoneNameServerAllowedAsync();
  2619. case AuthZoneQueryAccess.UseSpecifiedNetworkACL:
  2620. if (IPAddress.IsLoopback(remoteIP) || IPAddress.Any.Equals(remoteIP))
  2621. return true;
  2622. return NetworkAccessControl.IsAddressAllowed(remoteIP, apexZone.QueryAccessNetworkACL);
  2623. case AuthZoneQueryAccess.AllowZoneNameServersAndUseSpecifiedNetworkACL:
  2624. if (IPAddress.IsLoopback(remoteIP) || IPAddress.Any.Equals(remoteIP))
  2625. return true;
  2626. return NetworkAccessControl.IsAddressAllowed(remoteIP, apexZone.QueryAccessNetworkACL) || await IsZoneNameServerAllowedAsync();
  2627. default:
  2628. if (IPAddress.IsLoopback(remoteIP) || IPAddress.Any.Equals(remoteIP))
  2629. return true;
  2630. return false;
  2631. }
  2632. }
  2633. public void LoadTrustAnchorsTo(DnsClient dnsClient, string domain, DnsResourceRecordType type)
  2634. {
  2635. if (type == DnsResourceRecordType.DS)
  2636. {
  2637. domain = GetParentZone(domain);
  2638. if (domain is null)
  2639. domain = "";
  2640. }
  2641. AuthZoneInfo zoneInfo = _dnsServer.AuthZoneManager.FindAuthZoneInfo(domain, false);
  2642. if ((zoneInfo is not null) && (zoneInfo.ApexZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned))
  2643. {
  2644. IReadOnlyList<DnsResourceRecord> dnsKeyRecords = zoneInfo.ApexZone.GetRecords(DnsResourceRecordType.DNSKEY);
  2645. foreach (DnsResourceRecord dnsKeyRecord in dnsKeyRecords)
  2646. {
  2647. DnsDNSKEYRecordData dnsKey = dnsKeyRecord.RDATA as DnsDNSKEYRecordData;
  2648. if (dnsKey.Flags.HasFlag(DnsDnsKeyFlag.SecureEntryPoint) && !dnsKey.Flags.HasFlag(DnsDnsKeyFlag.Revoke))
  2649. {
  2650. DnsDSRecordData dsRecord = dnsKey.CreateDS(dnsKeyRecord.Name, DnssecDigestType.SHA256);
  2651. dnsClient.AddTrustAnchor(zoneInfo.Name, dsRecord);
  2652. }
  2653. }
  2654. }
  2655. }
  2656. public AuthZoneInfo LoadZoneFrom(Stream s, DateTime lastModified)
  2657. {
  2658. BinaryReader bR = new BinaryReader(s);
  2659. if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "DZ")
  2660. throw new InvalidDataException("DnsServer zone file format is invalid.");
  2661. switch (bR.ReadByte())
  2662. {
  2663. case 2:
  2664. {
  2665. DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
  2666. if (records.Length == 0)
  2667. throw new InvalidDataException("Zone does not contain SOA record.");
  2668. DnsResourceRecord soaRecord = null;
  2669. for (int i = 0; i < records.Length; i++)
  2670. {
  2671. records[i] = new DnsResourceRecord(s);
  2672. if (records[i].Type == DnsResourceRecordType.SOA)
  2673. soaRecord = records[i];
  2674. }
  2675. if (soaRecord == null)
  2676. throw new InvalidDataException("Zone does not contain SOA record.");
  2677. //make zone info
  2678. AuthZoneType zoneType;
  2679. if (_dnsServer.ServerDomain.Equals((soaRecord.RDATA as DnsSOARecordData).PrimaryNameServer, StringComparison.OrdinalIgnoreCase))
  2680. zoneType = AuthZoneType.Primary;
  2681. else
  2682. zoneType = AuthZoneType.Stub;
  2683. AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, zoneType, false);
  2684. //create zone
  2685. ApexZone apexZone = CreateEmptyApexZone(zoneInfo);
  2686. try
  2687. {
  2688. //load records
  2689. LoadZoneRecords(apexZone, records);
  2690. }
  2691. catch
  2692. {
  2693. DeleteZone(zoneInfo);
  2694. throw;
  2695. }
  2696. //init zone
  2697. switch (zoneInfo.Type)
  2698. {
  2699. case AuthZoneType.Primary:
  2700. apexZone.TriggerNotify();
  2701. break;
  2702. case AuthZoneType.Stub:
  2703. {
  2704. StubZone stub = apexZone as StubZone;
  2705. soaRecord = stub.GetRecords(DnsResourceRecordType.SOA)[0];
  2706. SOARecordInfo soaInfo = soaRecord.GetAuthSOARecordInfo();
  2707. if (soaInfo.Version == 1)
  2708. stub.PrimaryNameServerAddresses = soaInfo.PrimaryNameServers;
  2709. stub.TriggerRefresh();
  2710. }
  2711. break;
  2712. }
  2713. return new AuthZoneInfo(apexZone);
  2714. }
  2715. case 3:
  2716. {
  2717. bool zoneDisabled = bR.ReadBoolean();
  2718. DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
  2719. if (records.Length == 0)
  2720. throw new InvalidDataException("Zone does not contain SOA record.");
  2721. DnsResourceRecord soaRecord = null;
  2722. for (int i = 0; i < records.Length; i++)
  2723. {
  2724. records[i] = new DnsResourceRecord(s);
  2725. records[i].Tag = AuthRecordInfo.ReadGenericRecordInfoFrom(bR, records[i].Type);
  2726. if (records[i].Type == DnsResourceRecordType.SOA)
  2727. soaRecord = records[i];
  2728. }
  2729. if (soaRecord == null)
  2730. throw new InvalidDataException("Zone does not contain SOA record.");
  2731. //make zone info
  2732. AuthZoneType zoneType;
  2733. if (_dnsServer.ServerDomain.Equals((soaRecord.RDATA as DnsSOARecordData).PrimaryNameServer, StringComparison.OrdinalIgnoreCase))
  2734. zoneType = AuthZoneType.Primary;
  2735. else
  2736. zoneType = AuthZoneType.Stub;
  2737. AuthZoneInfo zoneInfo = new AuthZoneInfo(records[0].Name, zoneType, zoneDisabled);
  2738. //create zone
  2739. ApexZone apexZone = CreateEmptyApexZone(zoneInfo);
  2740. try
  2741. {
  2742. //load records
  2743. LoadZoneRecords(apexZone, records);
  2744. }
  2745. catch
  2746. {
  2747. DeleteZone(zoneInfo);
  2748. throw;
  2749. }
  2750. //init zone
  2751. switch (zoneInfo.Type)
  2752. {
  2753. case AuthZoneType.Primary:
  2754. apexZone.TriggerNotify();
  2755. break;
  2756. case AuthZoneType.Stub:
  2757. {
  2758. StubZone stub = apexZone as StubZone;
  2759. soaRecord = stub.GetRecords(DnsResourceRecordType.SOA)[0];
  2760. SOARecordInfo soaInfo = soaRecord.GetAuthSOARecordInfo();
  2761. if (soaInfo.Version == 1)
  2762. stub.PrimaryNameServerAddresses = soaInfo.PrimaryNameServers;
  2763. stub.TriggerRefresh();
  2764. }
  2765. break;
  2766. }
  2767. return new AuthZoneInfo(apexZone);
  2768. }
  2769. case 4:
  2770. {
  2771. //read zone info
  2772. AuthZoneInfo zoneInfo = new AuthZoneInfo(bR, lastModified);
  2773. //create zone
  2774. ApexZone apexZone = CreateEmptyApexZone(zoneInfo);
  2775. try
  2776. {
  2777. //read all zone records
  2778. DnsResourceRecord[] records = new DnsResourceRecord[bR.ReadInt32()];
  2779. if (records.Length < 1)
  2780. throw new InvalidDataException("Failed to load DNS zone file: the zone file does not contain any records.");
  2781. uint minExpiryTtl = 0u;
  2782. for (int i = 0; i < records.Length; i++)
  2783. {
  2784. records[i] = new DnsResourceRecord(s);
  2785. records[i].Tag = AuthRecordInfo.ReadGenericRecordInfoFrom(bR, records[i].Type);
  2786. GenericRecordInfo recordInfo = records[i].GetAuthGenericRecordInfo();
  2787. if (recordInfo.ExpiryTtl > 0u)
  2788. {
  2789. uint pendingExpiryTtl = recordInfo.GetPendingExpiryTtl();
  2790. if (pendingExpiryTtl == 0)
  2791. {
  2792. //expired record found; set 10 sec ttl for timer to delete it
  2793. minExpiryTtl = 10;
  2794. }
  2795. else
  2796. {
  2797. if (minExpiryTtl == 0u)
  2798. minExpiryTtl = pendingExpiryTtl;
  2799. else
  2800. minExpiryTtl = Math.Min(minExpiryTtl, pendingExpiryTtl);
  2801. }
  2802. }
  2803. }
  2804. //load records
  2805. LoadZoneRecords(apexZone, records);
  2806. //init zone
  2807. switch (zoneInfo.Type)
  2808. {
  2809. case AuthZoneType.Primary:
  2810. apexZone.TriggerNotify();
  2811. if (minExpiryTtl > 0u)
  2812. apexZone.StartRecordExpiryTimer(minExpiryTtl);
  2813. break;
  2814. case AuthZoneType.Secondary:
  2815. {
  2816. SecondaryZone secondary = apexZone as SecondaryZone;
  2817. DnsResourceRecord soaRecord = secondary.GetRecords(DnsResourceRecordType.SOA)[0];
  2818. SOARecordInfo soaInfo = soaRecord.GetAuthSOARecordInfo();
  2819. if (soaInfo.Version == 1)
  2820. {
  2821. secondary.PrimaryNameServerAddresses = soaInfo.PrimaryNameServers;
  2822. secondary.PrimaryZoneTransferProtocol = soaInfo.ZoneTransferProtocol;
  2823. secondary.PrimaryZoneTransferTsigKeyName = soaInfo.TsigKeyName;
  2824. }
  2825. secondary.TriggerNotify();
  2826. secondary.TriggerRefresh();
  2827. }
  2828. break;
  2829. case AuthZoneType.Stub:
  2830. {
  2831. StubZone stub = apexZone as StubZone;
  2832. DnsResourceRecord soaRecord = stub.GetRecords(DnsResourceRecordType.SOA)[0];
  2833. SOARecordInfo soaInfo = soaRecord.GetAuthSOARecordInfo();
  2834. if (soaInfo.Version == 1)
  2835. stub.PrimaryNameServerAddresses = soaInfo.PrimaryNameServers;
  2836. stub.TriggerRefresh();
  2837. }
  2838. break;
  2839. case AuthZoneType.Forwarder:
  2840. {
  2841. IReadOnlyList<DnsResourceRecord> soaRecords = apexZone.GetRecords(DnsResourceRecordType.SOA);
  2842. if (soaRecords.Count == 0)
  2843. (apexZone as ForwarderZone).InitZone();
  2844. apexZone.TriggerNotify();
  2845. if (minExpiryTtl > 0u)
  2846. apexZone.StartRecordExpiryTimer(minExpiryTtl);
  2847. }
  2848. break;
  2849. case AuthZoneType.SecondaryForwarder:
  2850. {
  2851. (apexZone as SecondaryZone).TriggerRefresh();
  2852. }
  2853. break;
  2854. case AuthZoneType.Catalog:
  2855. {
  2856. (apexZone as CatalogZone).BuildMembersIndex();
  2857. apexZone.TriggerNotify();
  2858. if (minExpiryTtl > 0u)
  2859. apexZone.StartRecordExpiryTimer(minExpiryTtl);
  2860. }
  2861. break;
  2862. case AuthZoneType.SecondaryCatalog:
  2863. {
  2864. (apexZone as SecondaryZone).TriggerRefresh();
  2865. (apexZone as SecondaryCatalogZone).BuildMembersIndex();
  2866. }
  2867. break;
  2868. }
  2869. }
  2870. catch
  2871. {
  2872. DeleteZone(zoneInfo);
  2873. throw;
  2874. }
  2875. return new AuthZoneInfo(apexZone);
  2876. }
  2877. default:
  2878. throw new InvalidDataException("DNS Zone file version not supported.");
  2879. }
  2880. }
  2881. public void WriteZoneTo(string zoneName, Stream s)
  2882. {
  2883. AuthZoneInfo zoneInfo = GetAuthZoneInfo(zoneName, true);
  2884. if (zoneInfo is null)
  2885. return;
  2886. //serialize zone
  2887. BinaryWriter bW = new BinaryWriter(s);
  2888. bW.Write(Encoding.ASCII.GetBytes("DZ")); //format
  2889. bW.Write((byte)4); //version
  2890. //write zone info
  2891. if (zoneInfo.Internal)
  2892. throw new InvalidOperationException("Cannot save zones marked as internal.");
  2893. zoneInfo.WriteTo(bW);
  2894. //write all zone records
  2895. List<DnsResourceRecord> records = new List<DnsResourceRecord>();
  2896. ListAllZoneRecords(zoneInfo.Name, records);
  2897. bW.Write(records.Count);
  2898. foreach (DnsResourceRecord record in records)
  2899. {
  2900. record.WriteTo(s);
  2901. record.GetAuthGenericRecordInfo().WriteTo(bW);
  2902. }
  2903. }
  2904. public void SaveZoneFile(string zoneName)
  2905. {
  2906. zoneName = zoneName.ToLowerInvariant();
  2907. lock (_saveLock)
  2908. {
  2909. if (!_pendingSaveZones.TryAdd(zoneName, null))
  2910. return;
  2911. if (_pendingSaveZones.Count == 1)
  2912. _saveTimer.Change(SAVE_TIMER_INITIAL_INTERVAL, Timeout.Infinite);
  2913. }
  2914. }
  2915. #endregion
  2916. #region properties
  2917. public bool UseSoaSerialDateScheme
  2918. {
  2919. get { return _useSoaSerialDateScheme; }
  2920. set { _useSoaSerialDateScheme = value; }
  2921. }
  2922. public int TotalZones
  2923. { get { return _zoneIndex.Count; } }
  2924. #endregion
  2925. }
  2926. }