Repository: mina-sshd Updated Branches: refs/heads/master df4f91240 -> 8a43a5028
[SSHD-746] IPv6 addresses are not supported as client sessions connection target Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/8a43a502 Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/8a43a502 Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/8a43a502 Branch: refs/heads/master Commit: 8a43a5028021dc676ecb1f89d423fcc9d17b59b1 Parents: 595c359 Author: Goldstein Lyor <[email protected]> Authored: Tue Jan 9 16:05:47 2018 +0200 Committer: Goldstein Lyor <[email protected]> Committed: Tue Jan 9 16:08:36 2018 +0200 ---------------------------------------------------------------------- .../java/org/apache/sshd/client/SshClient.java | 10 ++- .../client/config/hosts/KnownHostEntry.java | 8 +- .../org/apache/sshd/server/ServerBuilder.java | 6 +- .../java/org/apache/sshd/client/ClientTest.java | 88 ++++++++++++++++---- 4 files changed, 87 insertions(+), 25 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a43a502/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java index 6c5c291..f81dfc6 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java @@ -492,12 +492,18 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa HostConfigEntryResolver resolver = getHostConfigEntryResolver(); HostConfigEntry entry = resolver.resolveEffectiveHost(host, port, username); if (entry == null) { + // generate a synthetic entry if (log.isDebugEnabled()) { log.debug("connect({}@{}:{}) no overrides", username, host, port); } - // generate a synthetic entry - entry = new HostConfigEntry(host, host, port, username); + // IPv6 addresses have a format which means they need special treatment, separate from pattern validation + if (SshdSocketAddress.isIPv6Address(host)) { + // Not using a pattern as the host name passed in was a valid IPv6 address + entry = new HostConfigEntry("", host, port, username); + } else { + entry = new HostConfigEntry(host, host, port, username); + } } else { if (log.isDebugEnabled()) { log.debug("connect({}@{}:{}) effective: {}", username, host, port, entry); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a43a502/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java index 544a5ce..83ce690 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java @@ -253,8 +253,8 @@ public class KnownHostEntry extends HostPatternsHolder { if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) { KnownHostHashValue hash = - ValidateUtils.checkNotNull(KnownHostHashValue.parse(hostPattern), - "Failed to extract host hash value from line=%s", data); + ValidateUtils.checkNotNull(KnownHostHashValue.parse(hostPattern), + "Failed to extract host hash value from line=%s", data); entry.setHashedEntry(hash); entry.setPatterns(null); } else { @@ -263,8 +263,8 @@ public class KnownHostEntry extends HostPatternsHolder { } AuthorizedKeyEntry key = - ValidateUtils.checkNotNull(AuthorizedKeyEntry.parseAuthorizedKeyEntry(line), - "No valid key entry recovered from line=%s", data); + ValidateUtils.checkNotNull(AuthorizedKeyEntry.parseAuthorizedKeyEntry(line), + "No valid key entry recovered from line=%s", data); entry.setKeyEntry(key); return entry; } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a43a502/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java index 9069ab8..aa6c75b 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java @@ -142,9 +142,9 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> { /** * @param ignoreUnsupported If {@code true} then all the default - * key exchanges are included, regardless of whether they are currently - * supported by the JCE. Otherwise, only the supported ones out of the - * list are included + * key exchanges are included, regardless of whether they are currently + * supported by the JCE. Otherwise, only the supported ones out of the + * list are included * @return A {@link List} of the default {@link NamedFactory} * instances of the {@link KeyExchange}s according to the preference * order defined by {@link #DEFAULT_KEX_PREFERENCE}. http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a43a502/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java index cbfa56f..0e429cc 100644 --- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java @@ -26,13 +26,17 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import java.net.Inet6Address; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.NetworkInterface; import java.nio.charset.StandardCharsets; import java.security.KeyPair; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; +import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -236,8 +240,8 @@ public class ClientTest extends BaseTestSupport { @Test public void testPropertyResolutionHierarchy() throws Exception { - final String sessionPropName = getCurrentTestName() + "-session"; - final AtomicReference<Object> sessionConfigValueHolder = new AtomicReference<>(null); + String sessionPropName = getCurrentTestName() + "-session"; + AtomicReference<Object> sessionConfigValueHolder = new AtomicReference<>(null); client.addSessionListener(new SessionListener() { @Override public void sessionEvent(Session session, Event event) { @@ -260,8 +264,8 @@ public class ClientTest extends BaseTestSupport { } }); - final String channelPropName = getCurrentTestName() + "-channel"; - final AtomicReference<Object> channelConfigValueHolder = new AtomicReference<>(null); + String channelPropName = getCurrentTestName() + "-channel"; + AtomicReference<Object> channelConfigValueHolder = new AtomicReference<>(null); client.addChannelListener(new ChannelListener() { @Override public void channelOpenSuccess(Channel channel) { @@ -314,9 +318,9 @@ public class ClientTest extends BaseTestSupport { @Test public void testClientStillActiveIfListenerExceptions() throws Exception { - final Map<String, Integer> eventsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - final Collection<String> failuresSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - final Logger log = LoggerFactory.getLogger(getClass()); + Map<String, Integer> eventsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + Collection<String> failuresSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + Logger log = LoggerFactory.getLogger(getClass()); client.addChannelListener(new ChannelListener() { @Override public void channelInitialized(Channel channel) { @@ -412,7 +416,7 @@ public class ClientTest extends BaseTestSupport { @Test public void testSimpleClientListener() throws Exception { - final AtomicReference<Channel> channelHolder = new AtomicReference<>(null); + AtomicReference<Channel> channelHolder = new AtomicReference<>(null); client.addChannelListener(new ChannelListener() { @Override public void channelOpenSuccess(Channel channel) { @@ -506,7 +510,6 @@ public class ClientTest extends BaseTestSupport { final byte[] message = "0123456789\n".getBytes(StandardCharsets.UTF_8); final int nbMessages = 1000; - try (ByteArrayOutputStream baosOut = new ByteArrayOutputStream(); ByteArrayOutputStream baosErr = new ByteArrayOutputStream()) { AtomicInteger writes = new AtomicInteger(nbMessages); @@ -1124,8 +1127,8 @@ public class ClientTest extends BaseTestSupport { public void testDefaultKeyboardInteractiveWithFailures() throws Exception { client.setUserAuthFactories(Collections.singletonList(UserAuthKeyboardInteractiveFactory.INSTANCE)); - final AtomicInteger count = new AtomicInteger(); - final AtomicReference<ClientSession> interactionSessionHolder = new AtomicReference<>(null); + AtomicInteger count = new AtomicInteger(); + AtomicReference<ClientSession> interactionSessionHolder = new AtomicReference<>(null); client.setUserInteraction(new UserInteraction() { private final String[] badResponse = {"bad"}; @@ -1185,7 +1188,6 @@ public class ClientTest extends BaseTestSupport { @Test public void testDefaultKeyboardInteractiveInSessionUserInteractive() throws Exception { - final AtomicInteger count = new AtomicInteger(); final int maxPrompts = 3; PropertyResolverUtils.updateProperty(client, ClientAuthenticationManager.PASSWORD_PROMPTS, maxPrompts); @@ -1194,6 +1196,7 @@ public class ClientTest extends BaseTestSupport { try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { assertNotNull("Client session creation not signalled", clientSessionHolder.get()); + AtomicInteger count = new AtomicInteger(); session.setUserInteraction(new UserInteraction() { @Override public boolean isInteractionAllowed(ClientSession session) { @@ -1237,7 +1240,6 @@ public class ClientTest extends BaseTestSupport { @Test public void testKeyboardInteractiveInSessionUserInteractiveFailure() throws Exception { - final AtomicInteger count = new AtomicInteger(); final int maxPrompts = 3; PropertyResolverUtils.updateProperty(client, ClientAuthenticationManager.PASSWORD_PROMPTS, maxPrompts); client.setUserAuthFactories(Collections.singletonList(UserAuthKeyboardInteractiveFactory.INSTANCE)); @@ -1245,6 +1247,7 @@ public class ClientTest extends BaseTestSupport { try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { assertNotNull("Client session creation not signalled", clientSessionHolder.get()); + AtomicInteger count = new AtomicInteger(); session.setUserInteraction(new UserInteraction() { @Override public boolean isInteractionAllowed(ClientSession session) { @@ -1326,7 +1329,7 @@ public class ClientTest extends BaseTestSupport { @Test public void testWaitAuth() throws Exception { - final AtomicBoolean ok = new AtomicBoolean(); + AtomicBoolean ok = new AtomicBoolean(); client.setServerKeyVerifier( (sshClientSession, remoteAddress, serverKey) -> { outputDebugMessage("verifyServerKey(%s): %s", remoteAddress, serverKey); @@ -1415,6 +1418,44 @@ public class ClientTest extends BaseTestSupport { assertListenerSizes("ClientStop", clientListeners, 0, 1); } + @Test + public void testConnectUsingIPv6Address() throws IOException { + client.start(); + + try { + testConnectUsingIPv6Address(SshdSocketAddress.IPV6_SHORT_LOCALHOST); + + for (Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces(); (nets != null) && nets.hasMoreElements();) { + NetworkInterface netint = nets.nextElement(); + if (!netint.isUp()) { + continue; // ignore non-running interfaces + } + + for (Enumeration<InetAddress> inetAddresses = netint.getInetAddresses(); (inetAddresses != null) && inetAddresses.hasMoreElements();) { + InetAddress inetAddress = inetAddresses.nextElement(); + if (!(inetAddress instanceof Inet6Address)) { + continue; + } + + try { + testConnectUsingIPv6Address(inetAddress.getHostAddress()); + } catch (IOException e) { + outputDebugMessage("Failed (%s) to connect to %s: %s", + e.getClass().getSimpleName(), inetAddress, e.getMessage()); + } + } + } + } finally { + client.stop(); + } + } + + private void testConnectUsingIPv6Address(String address) throws IOException { + try (ClientSession session = createTestClientSession(address)) { + outputDebugMessage("Successfully connected to %s", address); + } + } + private static void assertListenerSizes(String phase, Map<String, ? extends TestChannelListener> listeners, int activeSize, int openSize) { assertListenerSizes(phase, listeners.values(), activeSize, openSize); } @@ -1444,7 +1485,23 @@ public class ClientTest extends BaseTestSupport { } private ClientSession createTestClientSession() throws IOException { - ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession(); + ClientSession session = createTestClientSession(TEST_LOCALHOST); + try { + InetSocketAddress addr = SshdSocketAddress.toInetSocketAddress(session.getConnectAddress()); + assertEquals("Mismatched connect host", TEST_LOCALHOST, addr.getHostString()); + + ClientSession returnValue = session; + session = null; // avoid 'finally' close + return returnValue; + } finally { + if (session != null) { + session.close(); + } + } + } + + private ClientSession createTestClientSession(String host) throws IOException { + ClientSession session = client.connect(getCurrentTestName(), host, port).verify(7L, TimeUnit.SECONDS).getSession(); try { assertNotNull("Client session creation not signalled", clientSessionHolder.get()); session.addPasswordIdentity(getCurrentTestName()); @@ -1452,7 +1509,6 @@ public class ClientTest extends BaseTestSupport { InetSocketAddress addr = SshdSocketAddress.toInetSocketAddress(session.getConnectAddress()); assertNotNull("No reported connect address", addr); - assertEquals("Mismatched connect host", TEST_LOCALHOST, addr.getHostString()); assertEquals("Mismatched connect port", port, addr.getPort()); ClientSession returnValue = session;
