繁体   English   中英

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

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

我需要开发一个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可以解析作为“ uri”参数给定的主机,则一切正常(例如,如果我希望通过代理访问stackoverflow.com,它将起作用,因为我的本地dns可以解析stackoverflow.com)。

当存在本地dns未知的主机时,就会出现问题。 例如,代理后面的dns知道如何解析“ host1.private.dmz”之类的地址,因为这是仅在代理后面才知道的特殊主机(这里的代理实际上充当反向代理)。 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)
    (...)

由于无法解析ip,因此从不使用我的自定义ProxySelector。 是否有强制Java不通过localdns而是通过代理解析IP的选项?

如果我给出host1.private.dmz的IP地址(例如10.100.12.13),则一切正常。 通信被定向到我的自定义代理选择器,流量通过自定义代理毫无问题地通过。

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

如果我们看看stacktrace的最后一行,您会看到异常来自mysql jdbc驱动程序,因此是mysql驱动程序在实际打开与该主机的连接之前尝试将host1.private.dmz解析为IP地址。 因此,因为应用程序没有打开连接(因为当应用程序尝试解析dns时发生异常),所以没有调用代理选择器(“无连接” ==“无代理选择器”)。

在这种情况下我们该怎么办?

如果是您编写应用程序,则只需不通过调用InetAddress.getAllByName()解析IP并直接打开与主机域名(host1.private.dmz)的连接即可。 如果由于某种原因您需要一个IP来处理异常(如果发生异常,请尝试在不解析地址的情况下打开连接)。 如果您仍然无法接受,还有另一种选择。 您可以指示jvm使用额外的DNS服务器,该服务器能够解析该域的IP。 您可以通过设置以下属性来做到这一点:

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的上下文中配置了数据库连接池的Tomcat上运行Web应用程序。 在这种情况下,设置额外的dnses之前可能会发生“ UnknownHostException”异常。 在这种情况下,您可以通过“代理”来运行该应用程序。 在Java中,您可以通过使用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

暂无
暂无

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

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