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;

Reply via email to