2014-07-15 112 views
2

我需要开发一个Java库,它允许通过代理仅为指定主机定向流量。Java在使用自定义ProxySelector时通过代理解析DNS

该库几乎准备就绪并正在运行,但是通过代理解析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,而当它失败与如下因素堆栈跟踪结束:

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

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

如果我们看一下堆栈跟踪的最后一行,可以看到这个异常来自mysql jdbc驱动程序,所以它是在尝试将host1.private.dmz解析为IP地址的mysql驱动程序之前,实际打开连接之前主办。因此,因为应用程序不打开连接(因为应用程序尝试解析dns时发生异常),所以不会调用代理选择器(“no connection”==“no proxy selector”)。

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

如果是您编写应用程序,则不要通过调用InetAddress.getAllByName()来解析IP,并直接打开与主机域名(host1.private.dmz)的连接。如果由于某种原因,您需要一个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服务器。

然而,可以有一个更有问题的情况。尝试将域名解析为IP可能会在您有机会设置额外的dns服务器之前发生。例如,您可能正在Tomcat上运行Web应用程序,并在Tomcat的上下文中配置数据库连接池。在这种情况下,在设置额外的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