Program.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Text;
  9. namespace DnsForwarder
  10. {
  11. internal class Program : ProgramRev1
  12. {
  13. private static Socket Listener;
  14. private static BlockingCollection<string> ConsoleOutout;
  15. private static void Main(string[] args)
  16. {
  17. if (args.Length != 3)
  18. {
  19. Console.WriteLine("<default dns server ip> <china dns server ip> <path to dnsmasq-china-list file>");
  20. Environment.Exit(-1);
  21. return;
  22. }
  23. Console.WriteLine("Starting...");
  24. Console.WriteLine($"Default Server:{DefaultServer = args[0]}");
  25. Console.WriteLine($"China DNS Server:{ChinaServer = args[1]}");
  26. Console.WriteLine($"dnsmasq-china-list file:{args[2]}");
  27. Console.WriteLine("Loading list file...");
  28. ChinaList = LoadListFile(args[2]).ToDictionary(p => p[0], p => p[1]);
  29. Console.WriteLine("OK. Listening...");
  30. Listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  31. Listener.Bind(new IPEndPoint(IPAddress.Any, 53));
  32. ConsoleOutout = new BlockingCollection<string>();
  33. StartHandleRequest();
  34. while (true)
  35. {
  36. Console.WriteLine(ConsoleOutout.Take());
  37. }
  38. }
  39. private static void StartHandleRequest()
  40. {
  41. var buf = new byte[1500];
  42. EndPoint from = new IPEndPoint(IPAddress.Any, 0);
  43. Listener.BeginReceiveFrom(buf, 0, buf.Length, SocketFlags.None, ref from, Callback, buf);
  44. }
  45. private static void Callback(IAsyncResult ar)
  46. {
  47. EndPoint from = new IPEndPoint(IPAddress.Any, 0);
  48. var count = Listener.EndReceiveFrom(ar, ref from);
  49. var buf = (byte[])ar.AsyncState;
  50. StartHandleRequest();
  51. var domain = ExtractDomainName(buf, count);
  52. var target = MatchServer(domain);
  53. ConsoleOutout.Add($"{DateTime.Now:yyyyMMdd HH:mm:ss} {from} [{target}]\t{domain}");
  54. Listener.SendTo(GetDnsResponse(buf, count, target), from);
  55. }
  56. }
  57. internal class ProgramRev1
  58. {
  59. protected static string DefaultServer;
  60. protected static string ChinaServer;
  61. protected static Dictionary<string, string> ChinaList;
  62. private static void MainRev1(string[] args)
  63. {
  64. if (args.Length != 3)
  65. {
  66. Console.WriteLine("<default dns server ip> <china dns server ip> <path to dnsmasq-china-list file>");
  67. Environment.Exit(-1);
  68. return;
  69. }
  70. Console.WriteLine("Starting...");
  71. Console.WriteLine($"Default Server:{DefaultServer = args[0]}");
  72. Console.WriteLine($"China DNS Server:{ChinaServer = args[1]}");
  73. Console.WriteLine($"dnsmasq-china-list file:{args[2]}");
  74. Console.WriteLine("Loading list file...");
  75. ChinaList = LoadListFile(args[2]).ToDictionary(p => p[0], p => p[1]);
  76. Console.WriteLine("OK. Listing...");
  77. var listen = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  78. listen.Bind(new IPEndPoint(IPAddress.Any, 53));
  79. while (true)
  80. {
  81. EndPoint inEp = new IPEndPoint(IPAddress.Any, 0);
  82. var buf = new byte[1500];
  83. var count = listen.ReceiveFrom(buf, 0, buf.Length, SocketFlags.None, ref inEp);
  84. var domain = ExtractDomainName(buf, count);
  85. var target = MatchServer(domain);
  86. Console.WriteLine($"{DateTime.Now:yyyyMMdd HH:mm:ss} {inEp} [{target}]\t{domain}");
  87. listen.SendTo(GetDnsResponse(buf, count, target), inEp);
  88. }
  89. }
  90. protected static string ExtractDomainName(byte[] buf, int count)
  91. {
  92. var lst = new List<byte[]>();
  93. var ptr = 12;
  94. while (buf[ptr] != 0)
  95. {
  96. ptr += buf[ptr] + 1;
  97. }
  98. var bufDomain = new byte[ptr - 12];
  99. Array.Copy(buf, 12, bufDomain, 0, bufDomain.Length);
  100. //dot fill
  101. ptr = 0;
  102. while (ptr < bufDomain.Length)
  103. {
  104. var b = bufDomain[ptr];
  105. bufDomain[ptr] = (byte)'.';
  106. ptr += b + 1;
  107. }
  108. return Encoding.ASCII.GetString(bufDomain, 1, bufDomain.Length - 1);
  109. }
  110. protected static byte[] GetDnsResponse(byte[] buf, int count, string host, int port = 53)
  111. {
  112. using var to = new UdpClient();
  113. to.Connect(host, 53);
  114. to.Send(buf, count);
  115. IPEndPoint outEp = null;
  116. return to.Receive(ref outEp); //TODO: Handle Upstream TimeOut
  117. }
  118. protected static string MatchServer(string domain)
  119. {
  120. var lower = domain.ToLower();
  121. if (lower.EndsWith(".cn")) return ChinaServer;
  122. var parts = lower.Split('.').Reverse().ToArray();
  123. for (int i = parts.Length - 1; i >= 0; i--)
  124. {
  125. var d = string.Join(".", parts.Take(i + 1).Reverse());
  126. if (ChinaList.TryGetValue(d, out var tar)) return tar;
  127. }
  128. return DefaultServer;
  129. }
  130. protected static string[][] LoadListFile(string path)
  131. {
  132. var lines = File.ReadAllLines(path);
  133. var items = lines.Select(p =>
  134. {
  135. if (p.StartsWith("#")) return null;
  136. var parts = p.Split('/');
  137. return new[] { parts[1], parts[2] };
  138. }).Where(p => p != null).ToArray();
  139. return items;
  140. }
  141. }
  142. }