talk is cheap, show me the code

      基本思路

      We use a unique generated hostname that forces your resolver to contact our auth DNS to get the IP of your resolver. Then we use the Host header of the subsequent HTTP request containing this unique hostname to return the discovered resolver IP. This is the same technique used by DNS leak test services.

      我们使用唯一生成的主机名,强制您的解析程序与我们的身份验证DNS联系,以获取您的解析程序的IP。然后,我们使用包含此唯一主机名的后续HTTP请求的主机头返回发现的解析程序IP。这与DNS泄漏测试服务使用的技术相同。

      实现细节

      看到上面的思路,很容易想到一个比较直观的解决方案(我一开始不是这样想的,但是思路基本一致,后者实现更为简单):浏览器发送一个类似下面的的请求:

      http://c71be2b5-f7b1-411b-b70c-58bc26e03945.ccc.cocofhu.com:8080/hello?name=c71be2b5-f7b1-411b-b70c-58bc26e03945.ccc.cocofhu.com

      很显然,这个域名c71be2b5-f7b1-411b-b70c-58bc26e03945.ccc.cocofhu.com是随机生成的,根本找不到实际的IP地址,对此,我们需要添加一条NS记录,使其指向我们自己的DNS服务器地址:

      NS ccc.cocofhu.com 我们自己的DNS服务器IP地址

      在这里,我的测试服务器IP地址是106.55.235.158,使用上次写的简易DNS服务器,并使该DNS服务器强制返回106.55.235.158(无论什么样的查询,都返回这个IP地址)。

      现在我们来分析上面那个奇怪的HTTP请求的详细过程:首先,由于我们添加了一条NS记录,这个奇怪的域名的解析将由我们自己的服务器进行,所以我们可以记录一下这个DNS查询的源IP,使其与c71be2b5-f7b1-411b-b70c-58bc26e03945绑定。

      mapping.put(question.getName(),packet.getAddress().getHostAddress());

      之后,将会有一个HTTP请求打向我们的服务器,我们只需要将刚才记录的IP地址访问给对方即可(这个IP地址就是运营商的IP地址,因为本地域名服务器找不到域名对应的IP会自动来我们的DNS服务器上来查询的)。

      Map<String,String> data = new HashMap<>();
      data.put("ip", DnsIspApplication.data.get(name));
      return data;

      这样一来,我们就很巧妙的获得了客户端DNS配置信息。简单Demo地址点这里。我们甚至还能通过这样的方式做负载均衡,当然这是以后的事情了。

      其他协议

      今天了解到了DOH协议,就想到这个服务器是不是还得兼容这个协议,但是仔细一想,DOH底层也会走原生的DNS协议,所以服务器根本不用改任何东西,不过感觉是可以针对特定的协议进行优化的。

      当然还有DOT和一些其他的DNS协议,我感觉都是要走底层DNS协议的,这样一看还是可能会有安全问题。