You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/05/06 16:01:01 UTC

[2/2] mina-sshd git commit: [SSHD-327] Support for OpenSSH config file in ~/.ssh/config

[SSHD-327] Support for OpenSSH config file in ~/.ssh/config


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/99ebcc95
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/99ebcc95
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/99ebcc95

Branch: refs/heads/master
Commit: 99ebcc9511b5bd9d5c678f54c8833b7651562975
Parents: abec3d8
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Wed May 6 17:00:50 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Wed May 6 17:00:50 2015 +0300

----------------------------------------------------------------------
 pom.xml                                         |   1 +
 .../main/java/org/apache/sshd/SshBuilder.java   |   2 +-
 .../main/java/org/apache/sshd/SshClient.java    |   7 +-
 .../sshd/client/scp/AbstractScpClient.java      |  10 +-
 .../sshd/client/scp/DefaultScpClient.java       |  16 +-
 .../client/sftp/SftpFileSystemProvider.java     |   3 +-
 .../sshd/common/cipher/BuiltinCiphers.java      |  42 ++
 .../common/compression/BuiltinCompressions.java |   8 +-
 .../common/config/AllowTcpForwardingValue.java  |  62 +++
 .../common/config/CompressionConfigValue.java   |  78 ++++
 .../sshd/common/config/LogLevelValue.java       |  55 +++
 .../sshd/common/config/SshConfigFileReader.java | 460 +++++++++++++++++++
 .../sshd/common/config/SyslogFacilityValue.java |  50 ++
 .../sshd/common/config/TimeValueConfig.java     | 175 +++++++
 .../sshd/common/digest/BuiltinDigests.java      |   6 +-
 .../sshd/common/kex/BuiltinDHFactories.java     |  42 ++
 .../org/apache/sshd/common/mac/BuiltinMacs.java |  44 +-
 .../common/signature/BuiltinSignatures.java     |  45 +-
 .../apache/sshd/common/util/GenericUtils.java   |  90 ++++
 .../common/util/LoggingFilterOutputStream.java  |  57 ---
 .../sshd/common/util/NoCloseInputStream.java    |  39 --
 .../sshd/common/util/NoCloseOutputStream.java   |  41 --
 .../apache/sshd/common/util/ValidateUtils.java  |  28 +-
 .../util/io/LoggingFilterOutputStream.java      |  58 +++
 .../sshd/common/util/io/NoCloseInputStream.java |  47 ++
 .../common/util/io/NoCloseOutputStream.java     |  47 ++
 .../sshd/common/util/io/NoCloseReader.java      |  46 ++
 .../sshd/common/util/io/NoCloseWriter.java      |  46 ++
 .../sshd/server/channel/ChannelSession.java     |   2 +-
 .../sshd/server/channel/PipeDataReceiver.java   |   2 +-
 .../test/java/org/apache/sshd/ClientTest.java   |   2 +-
 .../java/org/apache/sshd/WindowAdjustTest.java  |   3 +-
 .../common/config/SshConfigFileReaderTest.java  | 120 +++++
 .../sshd/common/config/TimeValueConfigTest.java |  52 +++
 .../common/util/EventListenerUtilsTest.java     |   1 +
 .../sshd/common/util/GenericUtilsTest.java      |  58 +++
 .../org/apache/sshd/common/config/sshd_config   |  87 ++++
 37 files changed, 1754 insertions(+), 178 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 1cad3eb..7795da5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -457,6 +457,7 @@
                             <excludes>
                                 <exclude>**/*.iml</exclude>
                                 <exclude>src/docs/**</exclude>
+                                <exclude>src/test/resources/**</exclude>
                                 <exclude>**/stty-output-*.txt</exclude>
                                 <exclude>**/big-msg.txt</exclude>
                                 <exclude>sshd-sftp/**</exclude>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java b/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
index c486727..4daf088 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
@@ -82,7 +82,7 @@ public class SshBuilder {
     /**
      * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
      */
-    protected static class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder<T, S>> implements ObjectBuilder<T> {
+    public static class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder<T, S>> implements ObjectBuilder<T> {
         protected Factory<T> factory = null;
         protected List<NamedFactory<KeyExchange>> keyExchangeFactories = null;
         protected List<NamedFactory<Cipher>> cipherFactories = null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/SshClient.java
index b3c102e..12337a8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshClient.java
@@ -61,16 +61,17 @@ import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.SshdSocketAddress;
+import org.apache.sshd.common.config.SshConfigFileReader;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.DefaultIoServiceFactoryFactory;
 import org.apache.sshd.common.io.IoConnectFuture;
 import org.apache.sshd.common.io.IoConnector;
 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.session.AbstractSession;
-import org.apache.sshd.common.util.NoCloseInputStream;
-import org.apache.sshd.common.util.NoCloseOutputStream;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.ThreadUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.NoCloseOutputStream;
 import org.bouncycastle.openssl.PasswordFinder;
 
 /**
@@ -349,7 +350,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
         }
         root.addHandler(fh);
 
-        int port = 22;
+        int port = SshConfigFileReader.DEFAULT_PORT;
         String host = null;
         String login = System.getProperty("user.name");
         boolean agentForward = false;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java
index b249bc7..f005d2f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/scp/AbstractScpClient.java
@@ -49,8 +49,8 @@ public abstract class AbstractScpClient implements ScpClient {
 
     @Override
     public void download(String[] remote, String local, Collection<Option> options) throws IOException {
-        local = ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s");
-        remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s");
+        local = ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", local);
+        remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s", (Object) remote);
 
         if (remote.length > 1) {
             options = addTargetIsDirectory(options);
@@ -68,7 +68,7 @@ public abstract class AbstractScpClient implements ScpClient {
 
     @Override
     public void download(String[] remote, Path local, Collection<Option> options) throws IOException {
-        remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s");
+        remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s", (Object) remote);
 
         if (remote.length > 1) {
             options = addTargetIsDirectory(options);
@@ -91,7 +91,7 @@ public abstract class AbstractScpClient implements ScpClient {
 
     @Override
     public void upload(String local, String remote, Collection<Option> options) throws IOException {
-        upload(new String[] { ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s") }, remote, options);
+        upload(new String[] { ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", local) }, remote, options);
     }
 
     @Override
@@ -106,7 +106,7 @@ public abstract class AbstractScpClient implements ScpClient {
     
     @Override
     public void upload(Path local, String remote, Collection<Option> options) throws IOException {
-        upload(new Path[] { ValidateUtils.checkNotNull(local, "Invalid local argument: %s") }, remote, GenericUtils.isEmpty(options) ? Collections.<Option>emptySet() : GenericUtils.of(options));
+        upload(new Path[] { ValidateUtils.checkNotNull(local, "Invalid local argument: %s", local) }, remote, GenericUtils.isEmpty(options) ? Collections.<Option>emptySet() : GenericUtils.of(options));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
index ee2d01f..e42cf80 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
@@ -55,7 +55,7 @@ public class DefaultScpClient extends AbstractScpClient {
 
     @Override
     public void download(String remote, String local, Collection<Option> options) throws IOException {
-        local = ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s");
+        local = ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", local);
 
         FileSystemFactory factory = clientSession.getFactoryManager().getFileSystemFactory();
         FileSystem fs = factory.createFileSystem(clientSession);
@@ -72,13 +72,13 @@ public class DefaultScpClient extends AbstractScpClient {
 
     @Override
     public void download(String remote, Path local, Collection<Option> options) throws IOException {
-        local = ValidateUtils.checkNotNull(local, "Invalid argument local: %s");
+        local = ValidateUtils.checkNotNull(local, "Invalid argument local: %s", local);
         download(remote, local.getFileSystem(), local, options);
     }
 
     protected void download(String remote, FileSystem fs, Path local, Collection<Option> options) throws IOException {
-        local = ValidateUtils.checkNotNull(local, "Invalid argument local: %s");
-        remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s");
+        local = ValidateUtils.checkNotNull(local, "Invalid argument local: %s", local);
+        remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s", remote);
 
         LinkOption[]    opts = IoUtils.getLinkOptions(false);
         if (Files.isDirectory(local, opts)) {
@@ -133,7 +133,7 @@ public class DefaultScpClient extends AbstractScpClient {
 
     @Override
     public void upload(String[] local, String remote, Collection<Option> options) throws IOException {
-        final Collection<String>    paths=Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s"));
+        final Collection<String>    paths=Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", (Object) local));
         runUpload(remote, options, paths, new ScpOperationExecutor<String>() {
             public void execute(ScpHelper helper, Collection<String> local, Collection<Option> options) throws IOException {
                 helper.send(local,
@@ -146,7 +146,7 @@ public class DefaultScpClient extends AbstractScpClient {
 
     @Override
     public void upload(Path[] local, String remote, Collection<Option> options) throws IOException {
-        final Collection<Path>    paths=Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s"));
+        final Collection<Path>    paths=Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", local));
         runUpload(remote, options, paths, new ScpOperationExecutor<Path>() {
             public void execute(ScpHelper helper, Collection<Path> local, Collection<Option> options) throws IOException {
                 helper.sendPaths(local,
@@ -158,8 +158,8 @@ public class DefaultScpClient extends AbstractScpClient {
     }
 
     protected <T> void runUpload(String remote, Collection<Option> options, Collection<T> local, ScpOperationExecutor<T> executor) throws IOException {
-        local = ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s");
-        remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s");
+        local = ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", local);
+        remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s", remote);
         if (local.size() > 1) {
             options = addTargetIsDirectory(options);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
index 8ffa4d8..c765712 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/SftpFileSystemProvider.java
@@ -68,6 +68,7 @@ import org.apache.sshd.client.SftpClient;
 import org.apache.sshd.client.SftpClient.Attributes;
 import org.apache.sshd.client.SftpException;
 import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.config.SshConfigFileReader;
 import org.apache.sshd.common.sftp.SftpConstants;
 import org.apache.sshd.common.util.IoUtils;
 
@@ -124,7 +125,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
             String[] ui = userInfo.split(":");
             ClientSession session;
             try {
-                session = client.connect(ui[0], host, uri.getPort() > 0 ? uri.getPort() : 22)
+                session = client.connect(ui[0], host, uri.getPort() > 0 ? uri.getPort() : SshConfigFileReader.DEFAULT_PORT)
                         .await().getSession();
             } catch (InterruptedException e) {
                 throw new InterruptedIOException();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
index 52cef83..2ff52a0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
@@ -19,14 +19,19 @@
 
 package org.apache.sshd.common.cipher;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.sshd.common.Cipher;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.OptionalFeature;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Provides easy access to the currently implemented ciphers
@@ -72,6 +77,11 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
         return factoryName;
     }
 
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
     BuiltinCiphers(String factoryName, int ivsize, int blocksize, String algorithm, String transformation) {
         this.factoryName = factoryName;
         this.ivsize = ivsize;
@@ -192,6 +202,38 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
         return null;
     }
 
+    /**
+     * @param ciphers A comma-separated list of ciphers' names - ignored
+     * if {@code null}/empty
+     * @return A {@link List} of all the {@link NamedFactory}-ies whose
+     * name appears in the string and represent a built-in cipher. Any
+     * unknown name is <U>ignored</U>. The order of the returned result
+     * is the same as the original order - bar the unknown ciphers.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     */
+    public static final List<NamedFactory<Cipher>> parseCiphersList(String ciphers) {
+        return parseCiphersList(GenericUtils.split(ciphers, ','));
+    }
+
+    public static final List<NamedFactory<Cipher>> parseCiphersList(String ... ciphers) {
+        return parseCiphersList(GenericUtils.isEmpty((Object[]) ciphers) ? Collections.<String>emptyList() : Arrays.asList(ciphers));
+    }
+
+    public static final List<NamedFactory<Cipher>> parseCiphersList(Collection<String> ciphers) {
+        if (GenericUtils.isEmpty(ciphers)) {
+            return Collections.emptyList();
+        }
+        
+        List<NamedFactory<Cipher>>    result=new ArrayList<NamedFactory<Cipher>>(ciphers.size());
+        for (String name : ciphers) {
+            BuiltinCiphers  c=ValidateUtils.checkNotNull(fromFactoryName(name), "Bad factory name (%s) in %s", name, ciphers);
+            result.add(c);
+        }
+        
+        return result;
+    }
+
     public static final class Constants {
         public static final String NONE = "none";
         public static final String AES128_CBC = "aes128-cbc";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
index e745dfb..bb0c8f8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
@@ -51,11 +51,17 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
         };
     
     private final String    name;
+
     @Override
     public final String getName() {
         return name;
     }
-    
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
     public final boolean isSupported() {
         return true;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/config/AllowTcpForwardingValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/AllowTcpForwardingValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/AllowTcpForwardingValue.java
new file mode 100644
index 0000000..85383fb
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/AllowTcpForwardingValue.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5">sshd_config(5) section</A>
+ */
+public enum AllowTcpForwardingValue {
+    ALL,
+    NONE,
+    LOCAL,
+    REMOTE;
+
+    public static final Set<AllowTcpForwardingValue>    VALUES=
+            Collections.unmodifiableSet(EnumSet.allOf(AllowTcpForwardingValue.class));
+    // NOTE: it also interprets "yes" as "all" and "no" as "none"
+    public static final AllowTcpForwardingValue fromString(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        if ("yes".equalsIgnoreCase(s)) {
+            return ALL;
+        }
+
+        if ("no".equalsIgnoreCase(s)) {
+            return NONE;
+        }
+
+        for (AllowTcpForwardingValue v : VALUES) {
+            if (s.equalsIgnoreCase(v.name())) {
+                return v;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
new file mode 100644
index 0000000..bb94f57
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.compression.BuiltinCompressions;
+import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Provides a &quot;bridge&quot; between the configuration values and the
+ * actual {@link NamedFactory} for the {@link Compression}.
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum CompressionConfigValue implements NamedFactory<Compression> {
+    YES(BuiltinCompressions.zlib),
+    NO(BuiltinCompressions.none),
+    DELAYED(BuiltinCompressions.delayedZlib);
+
+    private final NamedFactory<Compression> factory;
+
+    @Override
+    public final String getName() { 
+        return factory.getName();
+    }
+    
+    @Override
+    public final Compression create() {
+        return factory.create();
+    }
+
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
+    CompressionConfigValue(NamedFactory<Compression> delegate) {
+        factory = delegate;
+    }
+
+    public static final Set<CompressionConfigValue> VALUES=
+            Collections.unmodifiableSet(EnumSet.allOf(CompressionConfigValue.class));
+
+    public static final CompressionConfigValue fromName(String n) {
+        if (GenericUtils.isEmpty(n)) {
+            return null;
+        }
+
+        for (CompressionConfigValue v : VALUES) {
+            if (n.equalsIgnoreCase(v.name())) {
+                return v;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
new file mode 100644
index 0000000..168c91e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html"><I>LogLevel</I> configuration value</A>
+ */
+public enum LogLevelValue {
+    /*
+     * NOTE(s):
+     * 1. DEBUG and DEBUG1 are EQUIVALENT
+     * 2. Order is important (!!!)
+     */
+    QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3;
+
+    public static final Set<LogLevelValue> VALUES=
+            Collections.unmodifiableSet(EnumSet.allOf(LogLevelValue.class));
+    public static final LogLevelValue fromName(String n) {
+        if (GenericUtils.isEmpty(n)) {
+            return null;
+        }
+
+        for (LogLevelValue l : VALUES) {
+            if (n.equalsIgnoreCase(l.name())) {
+                return l;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
new file mode 100644
index 0000000..45c7aff
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StreamCorruptedException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.AbstractFactoryManager;
+import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.Mac;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.kex.BuiltinDHFactories;
+import org.apache.sshd.common.kex.DHFactory;
+import org.apache.sshd.common.mac.BuiltinMacs;
+import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.NoCloseReader;
+
+/**
+ * Reads and interprets some useful configurations from an OpenSSH
+ * configuration file.
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <a href="https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5">ssh_config(5)</a>
+ */
+public class SshConfigFileReader {
+    public static final char    COMMENT_CHAR='#';
+
+    // Some well known configuration properties names and values
+    public static final String  BANNER_CONFIG_PROP="Banner";
+    public static final String  COMPRESSION_PROP="Compression";
+        public static final String  DEFAULT_COMPRESSION=CompressionConfigValue.NO.getName();
+    public static final String  ALLOW_TCP_FORWARDING_CONFIG_PROP="AllowTcpForwarding";
+        public static final String  DEFAULT_TCP_FORWARDING="yes";
+        public static final boolean DEFAULT_TCP_FORWARDING_VALUE=parseBooleanValue(DEFAULT_TCP_FORWARDING);
+    public static final String  ALLOW_AGENT_FORWARDING_CONFIG_PROP="AllowAgentForwarding";
+        public static final String  DEFAULT_AGENT_FORWARDING="yes";
+        public static final boolean DEFAULT_AGENT_FORWARDING_VALUE=parseBooleanValue(DEFAULT_AGENT_FORWARDING);
+    public static final String  ALLOW_X11_FORWARDING_CONFIG_PROP="X11Forwarding";
+        public static final String  DEFAULT_X11_FORWARDING="yes";
+        public static final boolean DEFAULT_X11_FORWARDING_VALUE=parseBooleanValue(DEFAULT_X11_FORWARDING);
+    public static final String  MAX_SESSIONS_CONFIG_PROP="MaxSessions";
+        public static final int DEFAULT_MAX_SESSIONS=10;
+    public static final String  HOST_KEY_CONFIG_PROP="HostKey";
+    public static final String  PASSWORD_AUTH_CONFIG_PROP="PasswordAuthentication";
+        public static final String  DEFAULT_PASSWORD_AUTH="no";
+        public static final boolean DEFAULT_PASSWORD_AUTH_VALUE=parseBooleanValue(DEFAULT_PASSWORD_AUTH);
+    public static final String  LISTEN_ADDRESS_CONFIG_PROP="ListenAddress";
+        public static final String  DEFAULT_BIND_ADDRESS="0.0.0.0";
+    public static final String  PORT_CONFIG_PROP="Port";
+        public static final int DEFAULT_PORT=22;
+    public static final String  KEEP_ALIVE_CONFIG_PROP="TCPKeepAlive";
+        public static final boolean DEFAULT_KEEP_ALIVE=true;
+    public static final String  USE_DNS_CONFIG_PROP="UseDNS";
+        // NOTE: the usual default is TRUE
+        public static final boolean DEFAULT_USE_DNS=true;
+    public static final String  PUBKEY_AUTH_CONFIG_PROP="PubkeyAuthentication";
+        public static final String  DEFAULT_PUBKEY_AUTH="yes";
+        public static final boolean DEFAULT_PUBKEY_AUTH_VALUE=parseBooleanValue(DEFAULT_PUBKEY_AUTH);
+    public static final String  AUTH_KEYS_FILE_CONFIG_PROP="AuthorizedKeysFile";
+    public static final String  MAX_AUTH_TRIES_CONFIG_PROP="MaxAuthTries";
+        public static final int DEFAULT_MAX_AUTH_TRIES=6;
+    public static final String  MAX_STARTUPS_CONFIG_PROP= "MaxStartups";
+        public static final int DEFAULT_MAX_STARTUPS=10;
+    public static final String  LOGIN_GRACE_TIME_CONFIG_PROP="LoginGraceTime";
+        public static final long    DEFAULT_LOGIN_GRACE_TIME=TimeUnit.SECONDS.toMillis(120);
+    public static final String  KEY_REGENERATE_INTERVAL_CONFIG_PROP="KeyRegenerationInterval";
+        public static final long    DEFAULT_REKEY_TIME_LIMIT=TimeUnit.HOURS.toMillis(1L);
+    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+    public static final String  CIPHERS_CONFIG_PROP="Ciphers";
+        public static final String  DEFAULT_CIPHERS=
+                "aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour";
+    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+    public static final String  MACS_CONFIG_PROP="MACs";
+        public static final String  DEFAULT_MACS=
+                "hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160,hmac-sha1-96,hmac-md5-96,hmac-sha2-256,hmac-sha2-256-96,hmac-sha2-512,hmac-sha2-512-96";
+    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+    public static final String  KEX_ALGORITHMS_CONFIG_PROP="KexAlgorithms";
+        public static final String  DEFAULT_KEX_ALGORITHMS=
+                        "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521"
+                + "," + "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1"
+                + "," + "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
+                        ;
+    // see http://linux.die.net/man/5/ssh_config
+    public static final String  HOST_KEY_ALGORITHMS_CONFIG_PROP="HostKeyAlgorithms";
+        // see https://tools.ietf.org/html/rfc5656
+        public static final String  DEFAULT_HOST_KEY_ALGORITHMS=
+                                    KeyPairProvider.SSH_RSA
+                            + "," + KeyPairProvider.SSH_DSS
+                                    ;
+    // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+    public static final String  LOG_LEVEL_CONFIG_PROP="LogLevel";
+        public static final LogLevelValue   DEFAULT_LOG_LEVEL=LogLevelValue.INFO;
+    // see https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5
+    public static final String  SYSLOG_FACILITY_CONFIG_PROP="SyslogFacility";
+        public static final SyslogFacilityValue DEFAULT_SYSLOG_FACILITY=SyslogFacilityValue.AUTH;
+    public static final String  SUBSYSTEM_CONFIG_PROP="Subsystem";
+
+    public static final Properties readConfigFile(File file) throws IOException {
+        try(InputStream input=new FileInputStream(file)) {
+            return readConfigFile(input, true);
+        }
+    }
+
+    public static final Properties readConfigFile(URL url) throws IOException {
+        try(InputStream input=url.openStream()) {
+            return readConfigFile(input, true);
+        }
+    }
+
+    public static final Properties readConfigFile(String path) throws IOException {
+        try(InputStream input=new FileInputStream(path)) {
+            return readConfigFile(input, true);
+        }
+    }
+
+    public static final Properties readConfigFile(InputStream input, boolean okToClose) throws IOException {
+        try(Reader      reader=new InputStreamReader(NoCloseInputStream.resolveInputStream(input, okToClose), StandardCharsets.UTF_8)) {
+            return readConfigFile(reader, true);
+        }
+    }
+
+    public static final Properties readConfigFile(Reader reader, boolean okToClose) throws IOException {
+        try(BufferedReader  buf=new BufferedReader(NoCloseReader.resolveReader(reader, okToClose))) {
+            return readConfigFile(buf);
+        }
+    }
+
+    /**
+     * Reads the configuration file contents into a {@link Properties} instance.
+     * <B>Note:</B> multiple keys value are concatenated using a comma - it is up to
+     * the caller to know which keys are expected to have multiple values and handle
+     * the split accordingly
+     * @param rdr The {@link BufferedReader} for reading the file
+     * @return The read properties
+     * @throws IOException If failed to read or malformed content
+     */
+    public static final Properties readConfigFile(BufferedReader rdr) throws IOException {
+        Properties  props=new Properties();
+        int         lineNumber=1;
+        for (String line=rdr.readLine(); line != null; line=rdr.readLine(), lineNumber++) {
+            line = GenericUtils.trimToEmpty(line);
+            if (GenericUtils.isEmpty(line)) {
+                continue;
+            }
+
+            int pos=line.indexOf(COMMENT_CHAR);
+            if (pos == 0) {
+                continue;
+            }
+
+            if (pos > 0) {
+                line = line.substring(0, pos);
+                line = line.trim();
+            }
+
+            /*
+             * Some options use '=', others use ' ' - try both
+             * NOTE: we do not validate the format for each option separately
+             */
+            if ((pos=line.indexOf(' ')) < 0) {
+                pos = line.indexOf('=');
+            }
+
+            if (pos < 0) {
+                throw new StreamCorruptedException("No delimiter at line " + lineNumber + ": " + line);
+            }
+
+            String  key=line.substring(0, pos);
+            String  value=line.substring(pos + 1).trim();
+            // see if need to concatenate multi-valued keys
+            String  prev=props.getProperty(key);
+            if (!GenericUtils.isEmpty(prev)) {
+                value = prev + "," + value;
+            }
+
+            props.setProperty(key, value);
+        }
+
+        return props;
+    }
+
+    /**
+     * @param props        The {@link Properties} - ignored if {@code null}/empty
+     * @param name         The property name
+     * @param defaultValue The default value to return if the specified property
+     *                     does not exist in the properties map or is an empty string
+     * @return The resolved property
+     * @throws NumberFormatException if malformed value
+     */
+    public static final long getLongProperty(Properties props, String name, long defaultValue) {
+        String value = (props == null) ? null : props.getProperty(name);
+        if (GenericUtils.isEmpty(value)) {
+            return defaultValue;
+        } else {
+            return Long.parseLong(value);
+        }
+    }
+
+    /**
+     * @param props The {@link Properties} - ignored if {@code null}/empty
+     * @param name  The property name
+     * @return The {@link Long} value or {@code null} if property not found or
+     * empty string
+     * @throws NumberFormatException if malformed value
+     */
+    public static final Long getLong(Properties props, String name) {
+        String value = (props == null) ? null : props.getProperty(name);
+        if (GenericUtils.isEmpty(value)) {
+            return null;
+        } else {
+            return Long.valueOf(value);
+        }
+    }
+
+    /**
+     * @param props        The {@link Properties} - ignored if {@code null}/empty
+     * @param name         The property name
+     * @param defaultValue The default value to return if the specified property
+     *                     does not exist in the properties map or is an empty string
+     * @return The resolved property
+     * @throws NumberFormatException if malformed value
+     */
+    public static final int getIntProperty(Properties props, String name, int defaultValue) {
+        String value = (props == null) ? null : props.getProperty(name);
+        if (GenericUtils.isEmpty(value)) {
+            return defaultValue;
+        } else {
+            return Integer.parseInt(value);
+        }
+    }
+
+    /**
+     * @param props The {@link Properties} - ignored if {@code null}/empty
+     * @param name  The property name
+     * @return The {@link Integer} value or {@code null} if property not found or
+     * empty string
+     * @throws NumberFormatException if malformed value
+     */
+    public static final Integer getInteger(Properties props, String name) {
+        String value = (props == null) ? null : props.getProperty(name);
+        if (GenericUtils.isEmpty(value)) {
+            return null;
+        } else {
+            return Integer.valueOf(value);
+        }
+    }
+
+    /**
+     * @param props        The {@link Properties} - ignored if {@code null}/empty
+     * @param name         The property name
+     * @param defaultValue The default value to return if the specified property
+     *                     does not exist in the properties map or is an empty string
+     * @return The resolved property
+     * @throws NumberFormatException if malformed value
+     */
+    public static final boolean getBooleanProperty(Properties props, String name, boolean defaultValue) {
+        String value = (props == null) ? null : props.getProperty(name);
+        if (GenericUtils.isEmpty(value)) {
+            return defaultValue;
+        } else {
+            return parseBooleanValue(value);
+        }
+    }
+
+    /**
+     * @param props The {@link Properties} - ignored if {@code null}/empty
+     * @param name  The property name
+     * @return The {@link Boolean} value or {@code null} if property not found or
+     * empty string
+     * @throws NumberFormatException if malformed value
+     */
+    public static final Boolean getBoolean(Properties props, String name) {
+        String value = (props == null) ? null : props.getProperty(name);
+        if (GenericUtils.isEmpty(value)) {
+            return null;
+        } else {
+            return Boolean.valueOf(parseBooleanValue(value));
+        }
+    }
+
+    /**
+     * @param v The value to parse - if {@code null}/empty then the default
+     * value is returned, otherwise {@link #parseBooleanValue(String)} is used
+     * @param defaultValue The default value to return if {@code null}/empty
+     * input string 
+     * @return The result
+     */
+    public static final boolean parseBooleanValue(String v, boolean defaultValue) {
+        if (GenericUtils.isEmpty(v)) {
+            return defaultValue;
+        } else {
+            return parseBooleanValue(v);
+        }
+    }
+
+    /**
+     * @param v Checks if the value is &quot;yes&quot;, &quot;y&quot;
+     * or &quot;on&quot; or &quot;true&quot;.
+     * @return The result - <B>Note:</B> {@code null}/empty values are
+     * intrepreted as {@code false}
+     */
+    public static final boolean parseBooleanValue(String v) {
+        if ("yes".equalsIgnoreCase(v)
+          || "y".equalsIgnoreCase(v)
+          || "on".equalsIgnoreCase(v)
+          || "true".equalsIgnoreCase(v)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * @param props The {@link Properties} - ignored if {@code null}/empty
+     * @return A {@link List} of all the {@link NamedFactory}-ies whose
+     * name appears in the string and represent a built-in cipher. Any
+     * unknown name is <U>ignored</U>. The order of the returned result
+     * is the same as the original order - bar the unknown ciphers.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     * @see #CIPHERS_CONFIG_PROP
+     * @see BuiltinCiphers#parseCiphersList(String)
+     */
+    public static final List<NamedFactory<Cipher>> getCiphers(Properties props) {
+        return BuiltinCiphers.parseCiphersList((props == null) ? null : props.getProperty(CIPHERS_CONFIG_PROP));
+    }
+    
+    /**
+     * @param props The {@link Properties} - ignored if {@code null}/empty
+     * @return A {@link List} of all the {@link NamedFactory}-ies whose
+     * name appears in the string and represent a built-in MAC. Any
+     * unknown name is <U>ignored</U>. The order of the returned result
+     * is the same as the original order - bar the unknown MACs.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     * @see #MACS_CONFIG_PROP
+     * @see BuiltinMacs#parseMacsList(String)
+     */
+    public static final List<NamedFactory<Mac>> getMacs(Properties props) {
+        return BuiltinMacs.parseMacsList((props == null) ? null : props.getProperty(MACS_CONFIG_PROP));
+    }
+    
+    /**
+     * @param props The {@link Properties} - ignored if {@code null}/empty
+     * @return A {@link List} of all the {@link NamedFactory} whose
+     * name appears in the string and represent a built-in signature. Any
+     * unknown name is <U>ignored</I>. The order of the returned result
+     * is the same as the original order - bar the unknown signatures.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     * @see #HOST_KEY_ALGORITHMS_CONFIG_PROP
+     * @see BuiltinSignatures#parseSignatureList(String)
+     */
+    public static final List<NamedFactory<Signature>> getSignatures(Properties props) {
+        return BuiltinSignatures.parseSignatureList((props == null) ? null : props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP));
+    }
+    
+    /**
+     * @param props The {@link Properties} - ignored if {@code null}/empty
+     * @return A {@link List} of all the {@link DHFactory}-ies whose
+     * name appears in the string and represent a built-in value. Any
+     * unknown name is <U>ignored</U>. The order of the returned result
+     * is the same as the original order - bar the unknown ones.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     * @see #KEX_ALGORITHMS_CONFIG_PROP
+     * @see BuiltinDHFactories#parseDHFactoriesList(String)
+     */
+    public static final List<DHFactory> getKexFactories(Properties props) {
+        return BuiltinDHFactories.parseDHFactoriesList((props == null) ? null : props.getProperty(KEX_ALGORITHMS_CONFIG_PROP));
+    }
+    
+    /**
+     * @param props The {@link Properties} - ignored if {@code null}/empty
+     * @return The matching {@link NamedFactory} for the configured value.
+     * {@code null} if no configuration or unknown name specified 
+     */
+    public static final NamedFactory<Compression> getCompression(Properties props) {
+        return CompressionConfigValue.fromName((props == null) ? null : props.getProperty(COMPRESSION_PROP));
+    }
+    
+    /**
+     * <P>Configures an {@link AbstractFactoryManager} with the values read from
+     * some configuration. Currently it configures:</P></BR>
+     * <UL>
+     *      <LI>The {@link Cipher}s - via the {@link #CIPHERS_CONFIG_PROP}</LI>
+     *      <LI>The {@link Mac}s - via the {@link #MACS_CONFIG_PROP}</LI>
+     *      <LI>The {@link Signature}s - via the {@link #HOST_KEY_ALGORITHMS_CONFIG_PROP}</LI>
+     *      <LI>The {@link Compression} - via the {@link #COMPRESSION_PROP}</LI>
+     * </UL>
+     * @param manager The {@link AbstractFactoryManager} to configure 
+     * @param props The {@link Properties} to use for configuration - <B>Note:</B>
+     * if any known configuration value has a default and does not appear in the
+     * properties, the default is used
+     * @return The configured manager
+     */
+    public static final <M extends AbstractFactoryManager> M configure(M manager, Properties props) {
+        ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+        
+        {
+            String  value=props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS);
+            manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(BuiltinSignatures.parseSignatureList(value), "Bad signatures: %s", value));
+        }
+
+        {
+            String  value=props.getProperty(MACS_CONFIG_PROP, DEFAULT_MACS);
+            manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(BuiltinMacs.parseMacsList(value), "Bad MAC(s): %s", value));
+        }
+
+        {
+            String  value=props.getProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS);
+            manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(BuiltinCiphers.parseCiphersList(value), "Bad ciphers(s): %s", value));
+        }
+
+        {
+            String  value=props.getProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION);
+            manager.setCompressionFactories(
+                    Collections.<NamedFactory<Compression>>singletonList(
+                            ValidateUtils.checkNotNull(CompressionConfigValue.fromName(value), "Bad compression: %s", value)));
+        }
+
+        return manager;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
new file mode 100644
index 0000000..7ae4b36
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5"><I>SyslogFacility</I> configuration value</A>
+ */
+public enum SyslogFacilityValue {
+    DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7;
+
+    public static final Set<SyslogFacilityValue> VALUES=
+            Collections.unmodifiableSet(EnumSet.allOf(SyslogFacilityValue.class));
+    public static final SyslogFacilityValue fromName(String n) {
+        if (GenericUtils.isEmpty(n)) {
+            return null;
+        }
+
+        for (SyslogFacilityValue f : VALUES) {
+            if (n.equalsIgnoreCase(f.name())) {
+                return f;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java b/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
new file mode 100644
index 0000000..6430325
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://unixhelp.ed.ac.uk/CGI/man-cgi?sshd_config+5">Time formats for SSH configuration values</A>
+ */
+public enum TimeValueConfig {
+    SECONDS('s', 'S', TimeUnit.SECONDS.toMillis(1L)),
+    MINUTES('m', 'M', TimeUnit.MINUTES.toMillis(1L)),
+    HOURS('h', 'H', TimeUnit.HOURS.toMillis(1L)),
+    DAYS('d', 'D', TimeUnit.DAYS.toMillis(1L)),
+    WEEKS('w', 'W', TimeUnit.DAYS.toMillis(7L));
+
+    private final char  loChar, hiChar;
+    public final char getLowerCaseValue() {
+        return loChar;
+    }
+
+    public final char getUpperCaseValue() {
+        return hiChar;
+    }
+
+    private final long  interval;
+    public final long getInterval() {
+        return interval;
+    }
+
+    TimeValueConfig(char lo, char hi, long duration) {
+        loChar = lo;
+        hiChar = hi;
+        interval = duration;
+    }
+
+    public static final Set<TimeValueConfig> VALUES=
+            Collections.unmodifiableSet(EnumSet.allOf(TimeValueConfig.class));
+    public static final TimeValueConfig fromValueChar(char ch) {
+        if ((ch <= ' ') || (ch >= 0x7F)) {
+            return null;
+        }
+
+        for (TimeValueConfig v : VALUES) {
+            if ((v.getLowerCaseValue() == ch) || (v.getUpperCaseValue() == ch)) {
+                return v;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param s A time specification
+     * @return The specified duration in milliseconds
+     * @see #parse(String)
+     * @see #durationOf(Map)
+     */
+    public static final long durationOf(String s) {
+        Map<TimeValueConfig,Long>   spec=parse(s);
+        return durationOf(spec);
+    }
+
+    /**
+     * @param s An input time specification containing possibly mixed numbers
+     * and units - e.g., {@code 3h10m} to indicate 3 hours and 10 minutes
+     * @return A {@link Map} specifying for each time unit its count
+     * @throws NumberFormatException If bad numbers found - e.g., negative counts
+     * @throws IllegalArgumentException If bad format - e.g., unknown unit
+     */
+    public static final Map<TimeValueConfig,Long> parse(String s) throws NumberFormatException, IllegalArgumentException {
+        if (GenericUtils.isEmpty(s)) {
+            return Collections.emptyMap();
+        }
+
+        int lastPos=0;
+        Map<TimeValueConfig,Long>   spec=new EnumMap<TimeValueConfig,Long>(TimeValueConfig.class);
+        for (int curPos=0; curPos < s.length(); curPos++) {
+            char    ch=s.charAt(curPos);
+            if ((ch >= '0') && (ch <= '9')) {
+                continue;
+            }
+
+            if (curPos <= lastPos) {
+                throw new IllegalArgumentException("parse(" + s + ") missing count value at index=" + curPos);
+            }
+
+            TimeValueConfig c=fromValueChar(ch);
+            if (c == null) {
+                throw new IllegalArgumentException("parse(" + s + ") unknown time value character: " + String.valueOf(ch));
+            }
+
+            String  v=s.substring(lastPos, curPos);
+            Long    count=Long.valueOf(v);
+            if (count.longValue() < 0L) {
+                throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for " + c.name());
+            }
+
+            Long    prev=spec.put(c, count);
+            if (prev != null) {
+                throw new IllegalArgumentException("parse(" + s + ") " + c.name() + " value re-specified: current=" + count + ", previous=" + prev);
+            }
+
+            if ((lastPos=curPos+1) >= s.length()) {
+                break;
+            }
+        }
+
+        if (lastPos < s.length()) {
+            String  v=s.substring(lastPos);
+            Long    count=Long.valueOf(v);
+            if (count.longValue() < 0L) {
+                throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for last component");
+            }
+
+            Long    prev=spec.put(SECONDS, count);
+            if (prev != null) {
+                throw new IllegalArgumentException("parse(" + s + ") last component (" + SECONDS.name() + ") value re-specified: current=" + count + ", previous=" + prev);
+            }
+        }
+
+        return spec;
+    }
+
+    /**
+     * @param spec The {@link Map} specifying the count for each {@link TimeValueConfig}
+     * @return The total duration in milliseconds
+     * @throws IllegalArgumentException If negative count for a time unit
+     */
+    public static final long durationOf(Map<TimeValueConfig,? extends Number> spec) throws IllegalArgumentException {
+        if (GenericUtils.isEmpty(spec)) {
+            return (-1L);
+        }
+
+        long    total=0L;
+        for (Map.Entry<TimeValueConfig,? extends Number> se : spec.entrySet()) {
+            TimeValueConfig v=se.getKey();
+            Number          c=se.getValue();
+            long            factor=c.longValue();
+            if (factor < 0L) {
+                throw new IllegalArgumentException("valueOf(" + spec + ") bad factor (" + c + ") for " + v.name());
+            }
+
+            long    added=v.getInterval() * factor;
+            total += added;
+        }
+
+        return total;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
index bb57f4c..8b342ba 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
@@ -71,11 +71,15 @@ public enum BuiltinDigests implements NamedFactory<Digest> {
         return factoryName;
     }
 
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
     BuiltinDigests(String facName) {
         factoryName = facName;
     }
 
-
     public static final Set<BuiltinDigests> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinDigests.class));
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
index 7dc630d..452dc5f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
@@ -20,8 +20,12 @@
 package org.apache.sshd.common.kex;
 
 import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.sshd.common.OptionalFeature;
@@ -29,6 +33,7 @@ import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.digest.BuiltinDigests;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -149,6 +154,11 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
         return true;
     }
 
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
     BuiltinDHFactories(String name) {
         factoryName = name;
     }
@@ -180,6 +190,38 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
         return false;
     }
 
+    /**
+     * @param factories A comma-separated list of ciphers' names - ignored
+     * if {@code null}/empty
+     * @return A {@link List} of all the {@link DHFactory}-ies whose
+     * name appears in the string and represent a built-in value. Any
+     * unknown name is <U>ignored</U>. The order of the returned result
+     * is the same as the original order - bar the unknown ones.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     */
+    public static final List<DHFactory> parseDHFactoriesList(String factories) {
+        return parseDHFactoriesList(GenericUtils.split(factories, ','));
+    }
+
+    public static final List<DHFactory> parseDHFactoriesList(String ... factories) {
+        return parseDHFactoriesList(GenericUtils.isEmpty((Object[]) factories) ? Collections.<String>emptyList() : Arrays.asList(factories));
+    }
+
+    public static final List<DHFactory> parseDHFactoriesList(Collection<String> factories) {
+        if (GenericUtils.isEmpty(factories)) {
+            return Collections.emptyList();
+        }
+        
+        List<DHFactory>    result=new ArrayList<DHFactory>(factories.size());
+        for (String name : factories) {
+            DHFactory  c=ValidateUtils.checkNotNull(fromFactoryName(name), "Unknown factory name (%s) in %s", name, factories);
+            result.add(c);
+        }
+        
+        return result;
+    }
+
     public static final class Constants {
         public static final String DIFFIE_HELLMAN_GROUP1_SHA1 = "diffie-hellman-group1-sha1";
         public static final String DIFFIE_HELLMAN_GROUP14_SHA1 = "diffie-hellman-group14-sha1";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
index 75f6c4c..e8d9239 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
@@ -19,8 +19,12 @@
 
 package org.apache.sshd.common.mac;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.sshd.common.Digest;
@@ -28,6 +32,7 @@ import org.apache.sshd.common.Mac;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.OptionalFeature;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Provides easy access to the currently implemented macs
@@ -84,6 +89,11 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
         return true;
     }
 
+    @Override
+    public final String toString() {
+        return getName();
+    }
+    
     BuiltinMacs(String facName) {
         factoryName = facName;
     }
@@ -111,7 +121,7 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
     }
 
     /**
-     * @param factory The {@link org.apache.sshd.common.NamedFactory} for the cipher - ignored if {@code null}
+     * @param factory The {@link org.apache.sshd.common.NamedFactory} for the Mac - ignored if {@code null}
      * @return The matching {@link org.apache.sshd.common.mac.BuiltinMacs} whose factory name matches
      * (case <U>insensitive</U>) the digest factory name
      * @see #fromFactoryName(String)
@@ -143,6 +153,38 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
         return null;
     }
 
+    /**
+     * @param macs A comma-separated list of MACs' names - ignored
+     * if {@code null}/empty
+     * @return A {@link List} of all the {@link NamedFactory}-ies whose
+     * name appears in the string and represent a built-in MAC. Any
+     * unknown name is <U>ignored</U>. The order of the returned result
+     * is the same as the original order - bar the unknown MACs.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     */
+    public static final List<NamedFactory<Mac>> parseMacsList(String macs) {
+        return parseMacsList(GenericUtils.split(macs, ','));
+    }
+
+    public static final List<NamedFactory<Mac>> parseMacsList(String ... macs) {
+        return parseMacsList(GenericUtils.isEmpty((Object[]) macs) ? Collections.<String>emptyList() : Arrays.asList(macs));
+    }
+
+    public static final List<NamedFactory<Mac>> parseMacsList(Collection<String> macs) {
+        if (GenericUtils.isEmpty(macs)) {
+            return Collections.emptyList();
+        }
+        
+        List<NamedFactory<Mac>>    result=new ArrayList<NamedFactory<Mac>>(macs.size());
+        for (String name : macs) {
+            BuiltinMacs  m=ValidateUtils.checkNotNull(fromFactoryName(name), "Bad factory name (%s) in %s", name, macs);
+            result.add(m);
+        }
+        
+        return result;
+    }
+
     public static final class Constants {
         public static final String HMAC_MD5 = "hmac-md5";
         public static final String HMAC_MD5_96 = "hmac-md5-96";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
index 86c85ad..2aee77d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
@@ -20,8 +20,12 @@
 package org.apache.sshd.common.signature;
 
 import java.security.spec.ECParameterSpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.sshd.common.Digest;
@@ -32,10 +36,10 @@ import org.apache.sshd.common.Signature;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
- * Provides easy access to the currently implemented macs
- *
+ * Provides easy access to the currently implemented signatures
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeature {
@@ -103,6 +107,11 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
         return factoryName;
     }
 
+    @Override
+    public final String toString() {
+        return getName();
+    }
+
     BuiltinSignatures(String facName) {
         factoryName = facName;
     }
@@ -166,4 +175,36 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
 
         return null;
     }
+    
+    /**
+     * @param sigs A comma-separated list of signatures' names - ignored
+     * if {@code null}/empty
+     * @return A {@link List} of all the {@link NamedFactory} whose
+     * name appears in the string and represent a built-in signature. Any
+     * unknown name is <U>ignored</I>. The order of the returned result
+     * is the same as the original order - bar the unknown signatures.
+     * <B>Note:</B> it is up to caller to ensure that the list does not
+     * contain duplicates
+     */
+    public static final List<NamedFactory<Signature>> parseSignatureList(String sigs) {
+        return parseSignatureList(GenericUtils.split(sigs, ','));
+    }
+
+    public static final List<NamedFactory<Signature>> parseSignatureList(String ... sigs) {
+        return parseSignatureList(GenericUtils.isEmpty((Object[]) sigs) ? Collections.<String>emptyList() : Arrays.asList(sigs));
+    }
+
+    public static final List<NamedFactory<Signature>> parseSignatureList(Collection<String> sigs) {
+        if (GenericUtils.isEmpty(sigs)) {
+            return Collections.emptyList();
+        }
+        
+        List<NamedFactory<Signature>>    result=new ArrayList<NamedFactory<Signature>>(sigs.size());
+        for (String name : sigs) {
+            BuiltinSignatures  s=ValidateUtils.checkNotNull(fromFactoryName(name), "Bad factory name (%s) in %s", name, sigs);
+            result.add(s);
+        }
+        
+        return result;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index e728c8c..345916a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -23,13 +23,18 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class GenericUtils {
+    public static final String[]    EMPTY_STRING_ARRAY={ };
+    public static final Object[]    EMPTY_OBJECT_ARRAY={ };
 
     public static final String trimToEmpty(String s) {
         if (s == null) {
@@ -54,6 +59,91 @@ public class GenericUtils {
             return false;
         }
     }
+
+    // a List would be better, but we want to be compatible with String.split(...)
+    public static final String[] split(String s, char ch) {
+        if (isEmpty(s)) {
+            return EMPTY_STRING_ARRAY;
+        }
+        
+        int lastPos=0, curPos=s.indexOf(ch);
+        if (curPos < 0) {
+            return new String[] { s };
+        }
+        
+        Collection<String>  values=new LinkedList<String>();
+        do {
+            String  v=s.substring(lastPos, curPos);
+            values.add(v);
+            
+            // skip separator
+            if ((lastPos = curPos + 1) >= s.length()) {
+                break;
+            }
+            
+            if ((curPos = s.indexOf(ch, lastPos)) < lastPos) {
+                break;  // no more separators
+            }
+        } while(curPos < s.length());
+        
+        // check if any leftovers
+        if (lastPos < s.length()) {
+            String  v=s.substring(lastPos);
+            values.add(v);
+        }
+
+        return values.toArray(new String[values.size()]);
+    }
+
+    public static final <T> String join(T[] values, char ch) {
+        return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), ch);
+    }
+
+    public static final String join(Iterable<?> iter, char ch) {
+        return join((iter == null) ? null : iter.iterator(), ch);
+    }
+    
+    public static final String join(Iterator<?> iter, char ch) {
+        if ((iter == null) || (!iter.hasNext())) {
+            return "";
+        }
+        
+        StringBuilder   sb=new StringBuilder();
+        do {    // we already asked hasNext...
+            Object  o=iter.next();
+            if (sb.length() > 0) {
+                sb.append(ch);
+            }
+            sb.append(Objects.toString(o));
+        } while(iter.hasNext());
+        
+        return sb.toString();
+    }
+
+    public static final <T> String join(T[] values, CharSequence sep) {
+        return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), sep);
+    }
+
+    public static final String join(Iterable<?> iter, CharSequence sep) {
+        return join((iter == null) ? null : iter.iterator(), sep);
+    }
+    
+    public static final String join(Iterator<?> iter, CharSequence sep) {
+        if ((iter == null) || (!iter.hasNext())) {
+            return "";
+        }
+        
+        StringBuilder   sb=new StringBuilder();
+        do {    // we already asked hasNext...
+            Object  o=iter.next();
+            if (sb.length() > 0) {
+                sb.append(sep);
+            }
+            sb.append(Objects.toString(o));
+        } while(iter.hasNext());
+        
+        return sb.toString();
+    }
     
     public static final int size(Collection<?> c) {
         if (c == null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java
deleted file mode 100644
index 772315f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.util;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.slf4j.Logger;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class LoggingFilterOutputStream extends FilterOutputStream {
-
-    private final String msg;
-    private final Logger log;
-
-    public LoggingFilterOutputStream(OutputStream out, String msg, Logger log) {
-        super(out);
-        this.msg = msg;
-        this.log = log;
-    }
-
-    @Override
-    public void write(int b) throws IOException {
-	    byte[] d = new byte[1];
-        d[0] = (byte) b;
-        write(d, 0, 1);
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException {
-        if (log != null && log.isTraceEnabled()) {
-            log.trace("{} {}", msg, BufferUtils.printHex(b, off, len));
-        }
-        out.write(b, off, len);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java
deleted file mode 100644
index 6692ff3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.util;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseInputStream extends FilterInputStream {
-
-    public NoCloseInputStream(InputStream in) {
-        super(in);
-    }
-
-    @Override
-    public void close() throws IOException {
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java
deleted file mode 100644
index 4b7e273..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.util;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseOutputStream extends FilterOutputStream {
-
-    public NoCloseOutputStream(OutputStream out) {
-        super(out);
-    }
-
-
-    @Override
-    public void close() throws IOException {
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
index 2d7685b..f856ea9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
@@ -26,43 +26,43 @@ import java.util.Map;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class ValidateUtils {
-    public static final <T> T checkNotNull(T t, String message) {
+    public static final <T> T checkNotNull(T t, String message, Object ... args) {
         if (t == null) {
-            throw new IllegalStateException(String.format(message, t));
+            throw new IllegalStateException(String.format(message, args));
         }
         return t;
     }
 
-    public static final String checkNotNullAndNotEmpty(String t, String message) {
-        t = checkNotNull(t, message).trim();
+    public static final String checkNotNullAndNotEmpty(String t, String message, Object ... args) {
+        t = checkNotNull(t, message, args).trim();
         if (t.isEmpty()) {
-            throw new IllegalArgumentException(String.format(message, t));
+            throw new IllegalArgumentException(String.format(message, args));
         }
         return t;
     }
 
-    public static final <K,V,M extends Map<K,V>> M checkNotNullAndNotEmpty(M t, String message) {
-        t = checkNotNull(t, message);
+    public static final <K,V,M extends Map<K,V>> M checkNotNullAndNotEmpty(M t, String message, Object ... args) {
+        t = checkNotNull(t, message, args);
         if (GenericUtils.size(t) <= 0) {
-            throw new IllegalArgumentException(String.format(message, t));
+            throw new IllegalArgumentException(String.format(message, args));
         }
         
         return t;
     }
 
-    public static final <T,C extends Collection<T>> C checkNotNullAndNotEmpty(C t, String message) {
-        t = checkNotNull(t, message);
+    public static final <T,C extends Collection<T>> C checkNotNullAndNotEmpty(C t, String message, Object ... args) {
+        t = checkNotNull(t, message, args);
         if (GenericUtils.size(t) <= 0) {
-            throw new IllegalArgumentException(String.format(message, t));
+            throw new IllegalArgumentException(String.format(message, args));
         }
         
         return t;
     }
 
-    public static final <T> T[] checkNotNullAndNotEmpty(T[] t, String message) {
-        t = checkNotNull(t, message);
+    public static final <T> T[] checkNotNullAndNotEmpty(T[] t, String message, Object ... args) {
+        t = checkNotNull(t, message, args);
         if (GenericUtils.length(t) <= 0) {
-            throw new IllegalArgumentException(String.format(message, t));
+            throw new IllegalArgumentException(String.format(message, t, args));
         }
         return t;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
new file mode 100644
index 0000000..ab1676e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.sshd.common.util.BufferUtils;
+import org.slf4j.Logger;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class LoggingFilterOutputStream extends FilterOutputStream {
+
+    private final String msg;
+    private final Logger log;
+
+    public LoggingFilterOutputStream(OutputStream out, String msg, Logger log) {
+        super(out);
+        this.msg = msg;
+        this.log = log;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+	    byte[] d = new byte[1];
+        d[0] = (byte) b;
+        write(d, 0, 1);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (log != null && log.isTraceEnabled()) {
+            log.trace("{} {}", msg, BufferUtils.printHex(b, off, len));
+        }
+        out.write(b, off, len);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/99ebcc95/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
new file mode 100644
index 0000000..2840c95
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseInputStream extends FilterInputStream {
+    public NoCloseInputStream(InputStream in) {
+        super(in);
+    }
+
+    @Override
+    public void close() throws IOException {
+        // ignored
+    }
+
+    public static final InputStream resolveInputStream(InputStream input, boolean okToClose) {
+        if ((input == null) || okToClose) {
+            return input;
+        } else {
+            return new NoCloseInputStream(input);
+        }
+    }
+}