Permalink
Browse files

fix: Added support for socksNonProxyHosts property (#975) (#985)

When a socksProxyHost is configured, there needs to be an escape value
so that some hosts are resolved immediately. The Java Networking spec[1]
does not specify such an option, but the socksNonProxyHosts is used in the
sun.net.spi.DefaultProxySelector. The behavior is mentioned in bug report
5001942[2]. This commit reimplements the logic in the DefaultProxySelector.select
method to determine if the host should be resolved immediately.

[1] http://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
[2] http://bugs.java.com/bugdatabase/view_bug.do?bug_id=5001942
  • Loading branch information...
jkutner authored and davecramer committed Oct 26, 2017
1 parent 2dcb91e commit 9813c685cae2cbfbc561a6220ba388cef08f34b0
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, PostgreSQL Global Development Group
* Copyright (c) 2017, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
@@ -21,7 +21,6 @@
import java.net.InetSocketAddress;
import java.net.Socket;
import java.sql.SQLException;
import javax.net.SocketFactory;
/**
@@ -63,7 +62,7 @@ public PGStream(SocketFactory socketFactory, HostSpec hostSpec, int timeout) thr
// When using a SOCKS proxy, the host might not be resolvable locally,
// thus we defer resolution until the traffic reaches the proxy. If there
// is no proxy, we must resolve the host to an IP to connect the socket.
InetSocketAddress address = System.getProperty("socksProxyHost") == null
InetSocketAddress address = hostSpec.shouldResolve()
? new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort())
: InetSocketAddress.createUnresolved(hostSpec.getHost(), hostSpec.getPort());
socket.connect(address, timeout);
@@ -5,10 +5,17 @@
package org.postgresql.util;
import static java.util.regex.Pattern.compile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Simple container for host and port.
*/
public class HostSpec {
public static String DEFAULT_NON_PROXY_HOSTS = "localhost|127.*|[::1]|0.0.0.0|[::0]";
protected final String host;
protected final int port;
@@ -39,4 +46,50 @@ public boolean equals(Object obj) {
public int hashCode() {
return port ^ host.hashCode();
}
public Boolean shouldResolve() {
if (System.getProperty("socksProxyHost") == null) {
return true;
}
return matchesNonProxyHosts();
}
private Boolean matchesNonProxyHosts() {
String nonProxyHosts = System.getProperty("socksNonProxyHosts", DEFAULT_NON_PROXY_HOSTS);
if (nonProxyHosts == null || this.host.isEmpty()) {
return false;
}
Pattern pattern = toPattern(nonProxyHosts);
Matcher matcher = pattern == null ? null : pattern.matcher(this.host);
return matcher != null && matcher.matches();
}
private Pattern toPattern(String mask) {
StringBuilder joiner = new StringBuilder();
String separator = "";
for (String disjunct : mask.split("\\|")) {
if (!disjunct.isEmpty()) {
String regex = disjunctToRegex(disjunct.toLowerCase());
joiner.append(separator).append(regex);
separator = "|";
}
}
return joiner.length() == 0 ? null : compile(joiner.toString());
}
private String disjunctToRegex(String disjunct) {
String regex;
if (disjunct.startsWith("*")) {
regex = ".*" + Pattern.quote(disjunct.substring(1));
} else if (disjunct.endsWith("*")) {
regex = Pattern.quote(disjunct.substring(0, disjunct.length() - 1)) + ".*";
} else {
regex = Pattern.quote(disjunct);
}
return regex;
}
}
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2017, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package org.postgresql.test.jre8.core;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/**
* @author Joe Kutner on 10/24/17.
* Twitter: @codefinger
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({SocksProxyTest.class})
public class Jre8TestSuite {
}
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2017, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package org.postgresql.test.jre8.core;
import static org.junit.Assert.assertNotNull;
import org.postgresql.test.TestUtil;
import org.junit.After;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* @author Joe Kutner on 10/9/17.
* Twitter: @codefinger
*/
public class SocksProxyTest {
@After
public void cleanup() {
System.clearProperty("socksProxyHost");
System.clearProperty("socksProxyPort");
System.clearProperty("socksNonProxyHosts");
}
/**
* Tests the connect method by connecting to the test database
*/
@Test
public void testConnectWithSocksNonProxyHost() throws Exception {
System.setProperty("socksProxyHost", "fake-socks-proxy");
System.setProperty("socksProxyPort", "9999");
System.setProperty("socksNonProxyHosts", TestUtil.getServer());
TestUtil.initDriver(); // Set up log levels, etc.
Connection con =
DriverManager.getConnection(TestUtil.getURL(), TestUtil.getUser(), TestUtil.getPassword());
assertNotNull(con);
con.close();
}
}
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2017, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package org.postgresql.test.util;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.postgresql.util.HostSpec;
import org.junit.After;
import org.junit.Test;
/**
* @author Joe Kutner on 10/19/17.
* Twitter: @codefinger
*/
public class HostSpecTest {
@After
public void cleanup() {
System.clearProperty("socksProxyHost");
System.clearProperty("socksProxyPort");
System.clearProperty("socksNonProxyHosts");
}
@Test
public void testShouldResolve() throws Exception {
HostSpec hostSpec = new HostSpec("localhost", 5432);
assertTrue(hostSpec.shouldResolve());
}
@Test
public void testShouldResolveWithSocksProxyHost() throws Exception {
System.setProperty("socksProxyHost", "fake-socks-proxy");
HostSpec hostSpec = new HostSpec("example.com", 5432);
assertFalse(hostSpec.shouldResolve());
}
@Test
public void testShouldResolveWithSocksProxyHostWithLocalhost() throws Exception {
System.setProperty("socksProxyHost", "fake-socks-proxy");
HostSpec hostSpec = new HostSpec("localhost", 5432);
assertTrue(hostSpec.shouldResolve());
}
@Test
public void testShouldResolveWithSocksNonProxyHost() throws Exception {
System.setProperty("socksProxyHost", "fake-socks-proxy");
System.setProperty("socksNonProxyHosts", "example.com");
HostSpec hostSpec = new HostSpec("example.com", 5432);
assertTrue(hostSpec.shouldResolve());
}
@Test
public void testShouldResolveWithSocksNonProxyHosts() throws Exception {
System.setProperty("socksProxyHost", "fake-socks-proxy");
System.setProperty("socksNonProxyHosts", "example.com|localhost");
HostSpec hostSpec = new HostSpec("example.com", 5432);
assertTrue(hostSpec.shouldResolve());
}
@Test
public void testShouldResolveWithSocksNonProxyHostsNotMatching() throws Exception {
System.setProperty("socksProxyHost", "fake-socks-proxy");
System.setProperty("socksNonProxyHosts", "example.com|localhost");
HostSpec hostSpec = new HostSpec("example.org", 5432);
assertFalse(hostSpec.shouldResolve());
}
}

0 comments on commit 9813c68

Please sign in to comment.