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

时间:2014-07-15 09:07:12

标签: java proxy dns

我需要开发一个java库,它允许通过代理仅为指定的主机定向流量。

库已经准备就绪并且正常工作,但是通过代理解析dns地址存在问题。

简而言之,我扩展了CustomProxySelector类,它具有以下逻辑:

public class CustomProxySelector extends ProxySelector {

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

如果本地dns可以解析主持人,那么所有工作都很好。&#34; uri&#34;参数(例如,如果我希望stackoverflow.com通过代理,它将工作,因为我的本地DNS可以解析stackoverflow.com)。

当我的本地dns不知道主机时问题就出现了。例如,代理后面的dns知道如何解析地址,如&#34; host1.private.dmz&#34;因为这是一个只在代理后面才知道的特殊主机(代理在这里充当反向代理)。 JVM似乎首先尝试解决&#34; host1.private.dmz&#34;到ip,当它失败时,它以下面的堆栈跟踪结束:

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)
    (...)

因为它无法解析ip,所以我从不使用自定义ProxySelector。有没有选项强制java不通过localdns但通过代理解析ip?

如果我给出host1.private.dmz的ip地址(例如10.100.12.13),那么一切正常。通信将定向到我的自定义代理选择器,流量通过自定义代理进行,没有问题。

1 个答案:

答案 0 :(得分:3)

我解决了这个问题。解决这个问题的重要一点是正确理解问题不在于jvm,而在于应用程序。 Jvm在调用自定义代理选择器之前不尝试解析host1.private.dmz,它是应用程序本身。

如果我们看看堆栈跟踪的最后一行你可以看到异常来自mysql jdbc驱动程序,所以在实际打开与该主机的连接之前,是mysql驱动程序试图将host1.private.dmz解析为IP地址。因此,因为应用程序不会打开连接(因为当应用程序尝试解析dns时发生异常),所以不会调用代理选择器(&#34;没有连接&#34; ==&#34;没有代理选择器&#34;)。 / p>

在这种情况下我们能做些什么?

如果您是编写应用程序的人,则只需通过调用InetAddress.getAllByName()解决IP并直接打开与主机域名(host1.private.dmz)的连接即可解析IP。如果由于某种原因你需要一个IP而不是处理异常(如果异常尝试打开连接而不解析地址)。如果仍然不能为您接受,还有一个选项。您可以指示jvm使用能够解析此域的IP的额外DNS服务器。您可以通过设置以下属性来执行此操作:

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);

这应该为您的应用程序设置额外的DNS服务器。

然而,可能会出现一个问题。在您有机会设置额外的DNS服务器之前,可能会尝试将域名解析为ip。例如,您可能正在Tomcat上运行Web应用程序,并在Tomcat的上下文中配置了数据库连接池。在这种情况下异常&#34; UnknownHostException&#34;可以在你设置额外的dnses之前发生。在这种情况下,您可以通过&#34;代理它来运行此应用程序&#34;。严格来说,你可以通过使用jProxyLoader库(http://jproxyloader.sourceforge.net)来实现这一点,例如通过使用以下参数运行应用程序:

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

上面的示例将在应用程序启动时将10.0.1.18设置为额外的dns服务器(能够解析未知的域名)。感谢这个额外的dns在应用程序启动时已经可用。

通过查看jProxyLoader问题排查页面了解有关此问题的更多信息:http://jproxyloader.sourceforge.net/troubleshooting.html