DomainTree.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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 System;
  16. using System.Text;
  17. using TechnitiumLibrary.ByteTree;
  18. namespace DnsServerCore.Dns.Trees
  19. {
  20. class DomainTree<T> : ByteTree<string, T> where T : class
  21. {
  22. #region variables
  23. readonly static byte[] _keyMap;
  24. readonly static byte[] _reverseKeyMap;
  25. #endregion
  26. #region constructor
  27. static DomainTree()
  28. {
  29. _keyMap = new byte[256];
  30. _reverseKeyMap = new byte[41];
  31. int keyCode;
  32. for (int i = 0; i < _keyMap.Length; i++)
  33. {
  34. if (i == 46) //[.]
  35. {
  36. keyCode = 0;
  37. _keyMap[i] = (byte)keyCode;
  38. _reverseKeyMap[keyCode] = (byte)i;
  39. }
  40. else if (i == 42) //[*]
  41. {
  42. keyCode = 1;
  43. _keyMap[i] = 0xff; //skipped value for optimization
  44. _reverseKeyMap[keyCode] = (byte)i;
  45. }
  46. else if (i == 45) //[-]
  47. {
  48. keyCode = 2;
  49. _keyMap[i] = (byte)keyCode;
  50. _reverseKeyMap[keyCode] = (byte)i;
  51. }
  52. else if (i == 47) //[/]
  53. {
  54. keyCode = 3;
  55. _keyMap[i] = (byte)keyCode;
  56. _reverseKeyMap[keyCode] = (byte)i;
  57. }
  58. else if ((i >= 48) && (i <= 57)) //[0-9]
  59. {
  60. keyCode = i - 44; //4 - 13
  61. _keyMap[i] = (byte)keyCode;
  62. _reverseKeyMap[keyCode] = (byte)i;
  63. }
  64. else if (i == 95) //[_]
  65. {
  66. keyCode = 14;
  67. _keyMap[i] = (byte)keyCode;
  68. _reverseKeyMap[keyCode] = (byte)i;
  69. }
  70. else if ((i >= 97) && (i <= 122)) //[a-z]
  71. {
  72. keyCode = i - 82; //15 - 40
  73. _keyMap[i] = (byte)keyCode;
  74. _reverseKeyMap[keyCode] = (byte)i;
  75. }
  76. else if ((i >= 65) && (i <= 90)) //[A-Z]
  77. {
  78. keyCode = i - 50; //15 - 40
  79. _keyMap[i] = (byte)keyCode;
  80. }
  81. else
  82. {
  83. _keyMap[i] = 0xff;
  84. }
  85. }
  86. }
  87. public DomainTree()
  88. : base(41)
  89. { }
  90. #endregion
  91. #region protected
  92. protected override byte[] ConvertToByteKey(string domain)
  93. {
  94. if (domain.Length == 0)
  95. return Array.Empty<byte>();
  96. if (domain.Length > 255)
  97. throw new InvalidDomainNameException("Invalid domain name [" + domain + "]: length cannot exceed 255 bytes.");
  98. byte[] key = new byte[domain.Length + 1];
  99. int keyOffset = 0;
  100. int labelStart;
  101. int labelEnd = domain.Length - 1;
  102. int labelLength;
  103. int labelChar;
  104. byte labelKeyCode;
  105. int i;
  106. do
  107. {
  108. if (labelEnd < 0)
  109. labelEnd = 0;
  110. labelStart = domain.LastIndexOf('.', labelEnd);
  111. labelLength = labelEnd - labelStart;
  112. if (labelLength == 0)
  113. throw new InvalidDomainNameException("Invalid domain name [" + domain + "]: label length cannot be 0 byte.");
  114. if (labelLength > 63)
  115. throw new InvalidDomainNameException("Invalid domain name [" + domain + "]: label length cannot exceed 63 bytes.");
  116. if (domain[labelStart + 1] == '-')
  117. throw new InvalidDomainNameException("Invalid domain name [" + domain + "]: label cannot start with hyphen.");
  118. if (domain[labelEnd] == '-')
  119. throw new InvalidDomainNameException("Invalid domain name [" + domain + "]: label cannot end with hyphen.");
  120. if ((labelLength == 1) && (domain[labelStart + 1] == '*')) //[*]
  121. {
  122. key[keyOffset++] = 1;
  123. }
  124. else
  125. {
  126. for (i = labelStart + 1; i <= labelEnd; i++)
  127. {
  128. labelChar = domain[i];
  129. if (labelChar >= _keyMap.Length)
  130. throw new InvalidDomainNameException("Invalid domain name [" + domain + "]: invalid character [" + labelChar + "] was found.");
  131. labelKeyCode = _keyMap[labelChar];
  132. if (labelKeyCode == 0xff)
  133. throw new InvalidDomainNameException("Invalid domain name [" + domain + "]: invalid character [" + labelChar + "] was found.");
  134. key[keyOffset++] = labelKeyCode;
  135. }
  136. }
  137. key[keyOffset++] = 0; //[.]
  138. labelEnd = labelStart - 1;
  139. }
  140. while (labelStart > -1);
  141. return key;
  142. }
  143. protected static string ConvertKeyToLabel(byte[] key, int startIndex)
  144. {
  145. int length = key.Length - startIndex;
  146. if (length < 1)
  147. return null;
  148. Span<byte> domain = stackalloc byte[length];
  149. int i;
  150. int k;
  151. for (i = 0; i < domain.Length; i++)
  152. {
  153. k = key[i + startIndex];
  154. if (k == 0) //[.]
  155. break;
  156. domain[i] = _reverseKeyMap[k];
  157. }
  158. return Encoding.ASCII.GetString(domain.Slice(0, i));
  159. }
  160. #endregion
  161. #region public
  162. public override bool TryRemove(string key, out T value)
  163. {
  164. if (TryRemove(key, out value, out Node currentNode))
  165. {
  166. currentNode.CleanThisBranch();
  167. return true;
  168. }
  169. return false;
  170. }
  171. #endregion
  172. }
  173. }