简体   繁体   English

使用自定义ProxySelector时Java通过代理解析dns

[英]Java resolve dns via proxy when using custom ProxySelector

I'm in need to develop a java library which allows a traffic to be directed via proxy only for specified hosts. 我需要开发一个Java库,该库仅允许通过代理针对指定主机定向流量。

The library is almost ready and working, but there is problem with resolving dns addresses via proxy. 该库几乎已准备就绪且可以正常工作,但是通过代理解析dns地址存在问题。

In short words I extended CustomProxySelector class which has following logic: 简而言之,我扩展了CustomProxySelector类,它具有以下逻辑:

public class CustomProxySelector extends ProxySelector {

  public List<Proxy> select(URI uri) {
    if (customProxyDefinedFor(uri)) {
      return getCustomProxyFor(uri);
    } else {
      // use direct connection
    }
  }
}

All works fine if local dns can resolve host given as "uri" parameter (for example if I want stackoverflow.com to go via proxy it will work because my local dns can resolve stackoverflow.com). 如果本地dns可以解析作为“ uri”参数给定的主机,则一切正常(例如,如果我希望通过代理访问stackoverflow.com,它将起作用,因为我的本地dns可以解析stackoverflow.com)。

The problem comes when there is a host which is not known to my local dns. 当存在本地dns未知的主机时,就会出现问题。 For example the dns behind proxy knows how to resolve address like "host1.private.dmz" because this is special host only known behind proxy (the proxy acts really as reverse proxy here). 例如,代理后面的dns知道如何解析“ host1.private.dmz”之类的地址,因为这是仅在代理后面才知道的特殊主机(这里的代理实际上充当反向代理)。 JVM seems to first try to resolve "host1.private.dmz" to ip, and when it fails it ends with folowing stacktrace: JVM似乎首先尝试将“ host1.private.dmz”解析为ip,当它失败时,它以以下stacktrace结尾:

Caused by: java.net.UnknownHostException: host1.private.dmz
    at java.net.InetAddress.getAllByName0(InetAddress.java:1259)
    at java.net.InetAddress.getAllByName(InetAddress.java:1171)
    at java.net.InetAddress.getAllByName(InetAddress.java:1105)
    at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:247)
    (...)

Because it fails to resolve the ip, my Custom ProxySelector is never used. 由于无法解析ip,因此从不使用我的自定义ProxySelector。 Is there any option to force java not to resolve ip via localdns but via proxy? 是否有强制Java不通过localdns而是通过代理解析IP的选项?

If I give the ip address of host1.private.dmz (for example 10.100.12.13) all works ok. 如果我给出host1.private.dmz的IP地址(例如10.100.12.13),则一切正常。 The communication is directed to my Custom Proxy Selector and the traffic goes via custom proxy without problem. 通信被定向到我的自定义代理选择器,流量通过自定义代理毫无问题地通过。

I solved this issue. 我解决了这个问题。 The important thing to fix this problem is correct understanding that the problem does not lay in jvm but in application. 解决此问题的重要之处在于正确理解问题不在于jvm,而在于应用程序。 Jvm does not try to resolve host1.private.dmz before calling custom proxy selector, it is the application itself. Jvm不会在调用自定义代理选择器之前尝试解析host1.private.dmz,它是应用程序本身。

If we have a look at last line of the stacktrace you can see that exception comes from mysql jdbc driver, so it is mysql driver who trys to resolve host1.private.dmz to IP address, before actually opening connection to that host. 如果我们看看stacktrace的最后一行,您会看到异常来自mysql jdbc驱动程序,因此是mysql驱动程序在实际打开与该主机的连接之前尝试将host1.private.dmz解析为IP地址。 Therefore because application does not open a connection (because exception occurs when application trys to resolve dns), no proxy selector is called ("no connection" == "no proxy selector"). 因此,因为应用程序没有打开连接(因为当应用程序尝试解析dns时发生异常),所以没有调用代理选择器(“无连接” ==“无代理选择器”)。

What can we do in such case? 在这种情况下我们该怎么办?

If it is you who writes the application, simply don't resolve the IP by calling InetAddress.getAllByName() and directly open connection to host domain name (host1.private.dmz). 如果是您编写应用程序,则只需不通过调用InetAddress.getAllByName()解析IP并直接打开与主机域名(host1.private.dmz)的连接即可。 If for some reason you need an IP than handle the exception (in case of exception try to open connection without resolving the address). 如果由于某种原因您需要一个IP来处理异常(如果发生异常,请尝试在不解析地址的情况下打开连接)。 If still this is not acceptable for you there is one more option. 如果您仍然无法接受,还有另一种选择。 You can instruct jvm to use extra DNS server which is able to resolve IP of this domain. 您可以指示jvm使用额外的DNS服务器,该服务器能够解析该域的IP。 You can do this by setting following properties: 您可以通过设置以下属性来做到这一点:

System.setProperty("sun.net.spi.nameservice.provider.1", "dns,sun");
System.setProperty("sun.net.spi.nameservice.nameservers", "10.200.2.3,100.40.70.5);

This should set extra dns server for your application. 这应该为您的应用程序设置额外的dns服务器。

There can however be one more problematic situation. 但是,可能还有另外一种问题。 An attempt to resolve domain name to ip might take place before you have the chance to set up extra dns servers. 在您有机会设置额外的DNS服务器之前,可能会尝试将域名解析为IP。 For example you might be running web application on Tomcat with database connection pool configured in Tomcat's context. 例如,您可能在Tomcat的上下文中配置了数据库连接池的Tomcat上运行Web应用程序。 In such case the exception "UnknownHostException" can happen before you set up extra dnses. 在这种情况下,设置额外的dnses之前可能会发生“ UnknownHostException”异常。 In such case you can run this application by "proxifying it". 在这种情况下,您可以通过“代理”来运行该应用程序。 Strictly in java you can do this by using jProxyLoader library ( http://jproxyloader.sourceforge.net ) , for example by running the application with following parameters: 在Java中,您可以通过使用jProxyLoader库( http://jproxyloader.sourceforge.net )来执行此操作,例如,通过使用以下参数运行应用程序:

-Djava.system.class.loader=net.sf.jproxyloader.JProxyLoader -DjplDnsServers=10.0.1.18

Above example will set up 10.0.1.18 as extra dns server (which is able to resolve the uknown domain name) at application startup. 上面的示例将在应用程序启动时将10.0.1.18设置为额外的dns服务器(能够解析未知域名)。 Thanks to this extra dns will already be available when application boots up. 由于此额外的dns,在应用程序启动时将已经可用。

You understand more about this problem, by having a look at jProxyLoader troubleshooting page: http://jproxyloader.sourceforge.net/troubleshooting.html 通过查看jProxyLoader疑难解答页面,您可以了解有关此问题的更多信息: http ://jproxyloader.sourceforge.net/troubleshooting.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM