CNAME.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /*
  2. Technitium DNS Server
  3. Copyright (C) 2021 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.ApplicationCommon;
  16. using MaxMind.GeoIP2.Responses;
  17. using Newtonsoft.Json;
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Net;
  21. using System.Threading.Tasks;
  22. using TechnitiumLibrary.Net.Dns;
  23. using TechnitiumLibrary.Net.Dns.ResourceRecords;
  24. namespace GeoCountry
  25. {
  26. public sealed class CNAME : IDnsApplication, IDnsAppRecordRequestHandler
  27. {
  28. #region variables
  29. MaxMind _maxMind;
  30. #endregion
  31. #region IDisposable
  32. bool _disposed;
  33. private void Dispose(bool disposing)
  34. {
  35. if (_disposed)
  36. return;
  37. if (disposing)
  38. {
  39. if (_maxMind is not null)
  40. _maxMind.Dispose();
  41. }
  42. _disposed = true;
  43. }
  44. public void Dispose()
  45. {
  46. Dispose(true);
  47. }
  48. #endregion
  49. #region public
  50. public Task InitializeAsync(IDnsServer dnsServer, string config)
  51. {
  52. _maxMind = MaxMind.Create(dnsServer);
  53. return Task.CompletedTask;
  54. }
  55. public Task<DnsDatagram> ProcessRequestAsync(DnsDatagram request, IPEndPoint remoteEP, DnsTransportProtocol protocol, bool isRecursionAllowed, string zoneName, uint appRecordTtl, string appRecordData)
  56. {
  57. dynamic jsonAppRecordData = JsonConvert.DeserializeObject(appRecordData);
  58. dynamic jsonCountry;
  59. if (_maxMind.DatabaseReader.TryCountry(remoteEP.Address, out CountryResponse response))
  60. {
  61. jsonCountry = jsonAppRecordData[response.Country.IsoCode];
  62. if (jsonCountry == null)
  63. jsonCountry = jsonAppRecordData["default"];
  64. }
  65. else
  66. {
  67. jsonCountry = jsonAppRecordData["default"];
  68. }
  69. if (jsonCountry == null)
  70. return Task.FromResult<DnsDatagram>(null);
  71. string cname = jsonCountry.Value;
  72. if (string.IsNullOrEmpty(cname))
  73. return Task.FromResult<DnsDatagram>(null);
  74. IReadOnlyList<DnsResourceRecord> answers;
  75. if (request.Question[0].Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase)) //check for zone apex
  76. answers = new DnsResourceRecord[] { new DnsResourceRecord(request.Question[0].Name, DnsResourceRecordType.ANAME, DnsClass.IN, appRecordTtl, new DnsANAMERecordData(cname)) }; //use ANAME
  77. else
  78. answers = new DnsResourceRecord[] { new DnsResourceRecord(request.Question[0].Name, DnsResourceRecordType.CNAME, DnsClass.IN, appRecordTtl, new DnsCNAMERecordData(cname)) };
  79. return Task.FromResult(new DnsDatagram(request.Identifier, true, request.OPCODE, true, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.NoError, request.Question, answers));
  80. }
  81. #endregion
  82. #region properties
  83. public string Description
  84. { get { return "Returns CNAME record based on the country the client queries from using MaxMind GeoIP2 Country database. Note that the app will return ANAME record for an APP record at zone apex. Use the two-character ISO 3166-1 alpha code for the country."; } }
  85. public string ApplicationRecordDataTemplate
  86. {
  87. get
  88. {
  89. return @"{
  90. ""IN"": ""in.example.com"",
  91. ""default"": ""example.com""
  92. }";
  93. }
  94. }
  95. #endregion
  96. }
  97. }