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/07 13:59:01 UTC
[2/2] mina-sshd git commit: [SSHD-455] Allow users to register
"extensions" to some of the builtin factories
[SSHD-455] Allow users to register "extensions" to some of the builtin factories
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/7ccbd066
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/7ccbd066
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/7ccbd066
Branch: refs/heads/master
Commit: 7ccbd066355bde4240cc5ccee0ab06f891921310
Parents: cca7cc7
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Thu May 7 14:58:48 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Thu May 7 14:58:48 2015 +0300
----------------------------------------------------------------------
.../main/java/org/apache/sshd/SshBuilder.java | 84 +++++-----
.../main/java/org/apache/sshd/SshServer.java | 1 +
.../org/apache/sshd/common/NamedFactory.java | 15 +-
.../org/apache/sshd/common/NamedResource.java | 28 ++++
.../org/apache/sshd/common/Transformer.java | 58 +++++++
.../apache/sshd/common/cipher/BaseCipher.java | 4 +
.../sshd/common/cipher/BuiltinCiphers.java | 86 +++++++++-
.../sshd/common/cipher/CipherFactory.java | 38 +++++
.../apache/sshd/common/cipher/CipherNone.java | 5 +-
.../org/apache/sshd/common/cipher/ECCurves.java | 12 +-
.../common/compression/BuiltinCompressions.java | 133 +++++++++++++++-
.../compression/CompressionDelayedZlib.java | 1 +
.../common/compression/CompressionFactory.java | 37 +++++
.../common/compression/CompressionZlib.java | 4 +
.../common/config/CompressionConfigValue.java | 12 +-
.../sshd/common/config/SshConfigFileReader.java | 156 +++++++++++++++----
.../sshd/common/kex/BuiltinDHFactories.java | 76 ++++++++-
.../org/apache/sshd/common/kex/DHFactory.java | 3 +-
.../org/apache/sshd/common/mac/BaseMac.java | 5 +
.../org/apache/sshd/common/mac/BuiltinMacs.java | 92 +++++++++--
.../org/apache/sshd/common/mac/MacFactory.java | 38 +++++
.../common/signature/AbstractSignature.java | 2 +
.../common/signature/BuiltinSignatures.java | 100 +++++++++---
.../sshd/common/signature/SignatureDSA.java | 2 +
.../sshd/common/signature/SignatureECDSA.java | 8 +-
.../sshd/common/signature/SignatureFactory.java | 38 +++++
.../sshd/common/signature/SignatureRSA.java | 6 +-
.../apache/sshd/common/util/GenericUtils.java | 40 +++++
.../src/test/java/org/apache/sshd/LoadTest.java | 3 +-
.../org/apache/sshd/client/kex/KexTest.java | 2 +-
.../sshd/common/cipher/BaseCipherTest.java | 3 +-
.../sshd/common/cipher/BuiltinCiphersTest.java | 69 ++++++++
.../compression/BuiltinCompressionsTest.java | 114 ++++++++++++++
.../common/config/SshConfigFileReaderTest.java | 43 ++++-
.../sshd/common/kex/BuiltinDHFactoriesTest.java | 57 +++++++
.../apache/sshd/common/mac/BuiltinMacsTest.java | 70 +++++++++
.../common/signature/BuiltinSignaturesTest.java | 70 +++++++++
37 files changed, 1365 insertions(+), 150 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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 4daf088..89e9762 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
@@ -18,7 +18,6 @@
*/
package org.apache.sshd;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -39,6 +38,7 @@ import org.apache.sshd.common.Random;
import org.apache.sshd.common.RequestHandler;
import org.apache.sshd.common.Signature;
import org.apache.sshd.common.TcpipForwarderFactory;
+import org.apache.sshd.common.Transformer;
import org.apache.sshd.common.cipher.BuiltinCiphers;
import org.apache.sshd.common.compression.BuiltinCompressions;
import org.apache.sshd.common.compression.Compression;
@@ -83,17 +83,17 @@ public class SshBuilder {
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
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;
- protected List<NamedFactory<Compression>> compressionFactories = null;
- protected List<NamedFactory<Mac>> macFactories = null;
- protected List<NamedFactory<Signature>> signatureFactories = null;
- protected Factory<Random> randomFactory = null;
- protected List<NamedFactory<Channel>> channelFactories = null;
- protected FileSystemFactory fileSystemFactory = null;
- protected TcpipForwarderFactory tcpipForwarderFactory = null;
- protected List<RequestHandler<ConnectionService>> globalRequestHandlers = null;
+ protected Factory<T> factory;
+ protected List<NamedFactory<KeyExchange>> keyExchangeFactories;
+ protected List<NamedFactory<Cipher>> cipherFactories;
+ protected List<NamedFactory<Compression>> compressionFactories;
+ protected List<NamedFactory<Mac>> macFactories;
+ protected List<NamedFactory<Signature>> signatureFactories;
+ protected Factory<Random> randomFactory;
+ protected List<NamedFactory<Channel>> channelFactories;
+ protected FileSystemFactory fileSystemFactory;
+ protected TcpipForwarderFactory tcpipForwarderFactory;
+ protected List<RequestHandler<ConnectionService>> globalRequestHandlers;
protected S fillWithDefaultValues() {
if (signatureFactories == null) {
@@ -212,6 +212,7 @@ public class SshBuilder {
return ssh;
}
+ @Override
public T build() {
return build(true);
}
@@ -343,7 +344,19 @@ public class SshBuilder {
* SshClient builder
*/
public static class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> {
-
+ public static final Transformer<DHFactory,NamedFactory<KeyExchange>> DH2KEX =
+ new Transformer<DHFactory, NamedFactory<KeyExchange>>() {
+ @Override
+ public NamedFactory<KeyExchange> transform(DHFactory factory) {
+ if (factory == null) {
+ return null;
+ } else if (factory.isGroupExchange()) {
+ return DHGEXClient.newFactory(factory);
+ } else {
+ return DHGClient.newFactory(factory);
+ }
+ }
+ };
protected ServerKeyVerifier serverKeyVerifier;
public ClientBuilder serverKeyVerifier(ServerKeyVerifier serverKeyVerifier) {
@@ -390,21 +403,7 @@ public class SshBuilder {
* @see BuiltinDHFactories#isSupported()
*/
public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
- List<NamedFactory<KeyExchange>> avail = new ArrayList<>(DEFAULT_KEX_PREFERENCE.size());
- for (BuiltinDHFactories f : BuiltinDHFactories.VALUES) {
- if (ignoreUnsupported || f.isSupported()) {
- avail.add(getKeyExchangeFactory(f));
- }
- }
- return avail;
- }
-
- public static NamedFactory<KeyExchange> getKeyExchangeFactory(DHFactory factory) {
- if (factory.isGroupExchange()) {
- return DHGEXClient.newFactory(factory);
- } else {
- return DHGClient.newFactory(factory);
- }
+ return NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX);
}
}
@@ -412,6 +411,19 @@ public class SshBuilder {
* SshServer builder
*/
public static class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
+ public static final Transformer<DHFactory,NamedFactory<KeyExchange>> DH2KEX =
+ new Transformer<DHFactory, NamedFactory<KeyExchange>>() {
+ @Override
+ public NamedFactory<KeyExchange> transform(DHFactory factory) {
+ if (factory == null) {
+ return null;
+ } else if (factory.isGroupExchange()) {
+ return DHGEXServer.newFactory(factory);
+ } else {
+ return DHGServer.newFactory(factory);
+ }
+ }
+ };
@Override
protected ServerBuilder fillWithDefaultValues() {
@@ -450,21 +462,7 @@ public class SshBuilder {
* @see BuiltinDHFactories#isSupported()
*/
public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
- List<NamedFactory<KeyExchange>> avail = new ArrayList<>(DEFAULT_KEX_PREFERENCE.size());
- for (BuiltinDHFactories f : BuiltinDHFactories.VALUES) {
- if (ignoreUnsupported || f.isSupported()) {
- avail.add(getKeyExchangeFactory(f));
- }
- }
- return avail;
- }
-
- public static NamedFactory<KeyExchange> getKeyExchangeFactory(DHFactory factory) {
- if (factory.isGroupExchange()) {
- return DHGEXServer.newFactory(factory);
- } else {
- return DHGServer.newFactory(factory);
- }
+ return NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX);
}
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
index 9e45c44..269176a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
@@ -93,6 +93,7 @@ import org.apache.sshd.server.shell.ProcessShellFactory;
public class SshServer extends AbstractFactoryManager implements ServerFactoryManager, Closeable {
public static final Factory<SshServer> DEFAULT_SSH_SERVER_FACTORY = new Factory<SshServer>() {
+ @Override
public SshServer create() {
return new SshServer();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
index f12ca99..3337e46 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
@@ -112,7 +112,19 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
}
return null;
}
-
+
+ public static final <S extends OptionalFeature,T,E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories(
+ boolean ignoreUnsupported, Collection<? extends S> preferred, Transformer<? super S,? extends E> xform) {
+ List<NamedFactory<T>> avail=new ArrayList<>(preferred.size());
+ for (S f : preferred) {
+ if (ignoreUnsupported || f.isSupported()) {
+ avail.add(xform.transform(f));
+ }
+ }
+
+ return avail;
+ }
+
public static final <T,E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories(
boolean ignoreUnsupported, Collection<? extends E> preferred) {
List<NamedFactory<T>> avail=new ArrayList<>(preferred.size());
@@ -125,5 +137,4 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
return avail;
}
}
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
index 494a1a9..d57f433 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
@@ -22,6 +22,7 @@ package org.apache.sshd.common;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import org.apache.sshd.common.util.GenericUtils;
@@ -34,6 +35,33 @@ public interface NamedResource {
* @return The resource name
*/
String getName();
+
+ /**
+ * Compares 2 {@link NamedResource}s according to their {@link #getName()}
+ * value case <U>insensitive</U>
+ */
+ Comparator<NamedResource> BY_NAME_COMPARATOR=new Comparator<NamedResource>() {
+ @Override
+ public int compare(NamedResource r1, NamedResource r2) {
+ String n1=r1.getName(), n2=r2.getName();
+ return String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
+ }
+ };
+
+ /**
+ * Returns the value of {@link #getName()} - or {@code null} if argument is {@code null}
+ */
+ Transformer<NamedResource,String> NAME_EXTRACTOR=new Transformer<NamedResource,String>() {
+ @Override
+ public String transform(NamedResource input) {
+ if (input == null) {
+ return null;
+ } else {
+ return input.getName();
+ }
+ }
+ };
+
/**
* Utility class to help using {@link NamedResource}s
*/
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java b/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
new file mode 100644
index 0000000..952b2c0
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/Transformer.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;
+
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Transformer<I, O> {
+ // TODO in JDK-8 replace this with Function
+ /**
+ * @param input Input value
+ * @return Transformed output value
+ */
+ O transform(I input);
+
+ /**
+ * Invokes {@link Objects#toString(Object)} on the argument
+ */
+ Transformer<Object,String> TOSTRING=new Transformer<Object,String>() {
+ @Override
+ public String transform(Object input) {
+ return Objects.toString(input);
+ }
+ };
+
+ /**
+ * Returns {@link Enum#name()} or {@code null} if argument is {@code null}
+ */
+ Transformer<Enum<?>,String> ENUM_NAME_EXTRACTOR=new Transformer<Enum<?>,String>() {
+ @Override
+ public String transform(Enum<?> input) {
+ if (input == null) {
+ return null;
+ } else {
+ return input.name();
+ }
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
index 66a283c..58accbc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
@@ -45,14 +45,17 @@ public class BaseCipher implements Cipher {
this.transformation = transformation;
}
+ @Override
public int getIVSize() {
return ivsize;
}
+ @Override
public int getBlockSize() {
return bsize;
}
+ @Override
public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
key = resize(key, bsize);
iv = resize(iv, ivsize);
@@ -68,6 +71,7 @@ public class BaseCipher implements Cipher {
}
}
+ @Override
public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
cipher.update(input, inputOffset, inputLen, input, inputOffset);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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 da8550d..68db3cd 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
@@ -25,20 +25,24 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
import org.apache.sshd.common.Cipher;
import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.config.NamedFactoriesListParseResult;
import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
/**
* Provides easy access to the currently implemented ciphers
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
+public enum BuiltinCiphers implements CipherFactory {
none(Constants.NONE, 0, 0, "None", "None") {
@Override
public Cipher create() {
@@ -149,6 +153,52 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
public static final Set<BuiltinCiphers> VALUES =
Collections.unmodifiableSet(EnumSet.allOf(BuiltinCiphers.class));
+ private static final Map<String,CipherFactory> extensions =
+ new TreeMap<String,CipherFactory>(String.CASE_INSENSITIVE_ORDER);
+
+ /**
+ * Registered a {@link NamedFactory} to be available besides the built-in
+ * ones when parsing configuration
+ * @param extension The factory to register
+ * @throws IllegalArgumentException if factory instance is {@code null},
+ * or overrides a built-in one or overrides another registered factory
+ * with the same name (case <U>insensitive</U>).
+ */
+ public static final void registerExtension(CipherFactory extension) {
+ String name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+ ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+ synchronized(extensions) {
+ ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+ extensions.put(name, extension);
+ }
+ }
+
+ /**
+ * @return A {@link SortedSet} of the currently registered extensions, sorted
+ * according to the factory name (case <U>insensitive</U>)
+ */
+ public static final SortedSet<CipherFactory> getRegisteredExtensions() {
+ // TODO for JDK-8 return Collections.emptySortedSet()
+ synchronized(extensions) {
+ return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+ }
+ }
+
+ /**
+ * Unregisters specified extension
+ * @param name The factory name - ignored if {@code null}/empty
+ * @return The registered extension - {@code null} if not found
+ */
+ public static final NamedFactory<Cipher> unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized(extensions) {
+ return extensions.remove(name);
+ }
+ }
/**
* @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -222,10 +272,10 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
return ParseResult.EMPTY;
}
- List<NamedFactory<Cipher>> factories=new ArrayList<NamedFactory<Cipher>>(ciphers.size());
- List<String> unknown=Collections.<String>emptyList();
+ List<CipherFactory> factories=new ArrayList<CipherFactory>(ciphers.size());
+ List<String> unknown=Collections.<String>emptyList();
for (String name : ciphers) {
- BuiltinCiphers c=fromFactoryName(name);
+ CipherFactory c=resolveFactory(name);
if (c != null) {
factories.add(c);
} else {
@@ -241,13 +291,33 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
}
/**
+ * @param name The factory name
+ * @return The factory or {@code null} if it is neither a built-in one
+ * or a registered extension
+ */
+ public static final CipherFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ CipherFactory c=fromFactoryName(name);
+ if (c != null) {
+ return c;
+ }
+
+ synchronized(extensions) {
+ return extensions.get(name);
+ }
+ }
+
+ /**
* Holds the result of {@link BuiltinCiphers#parseCiphersList(String)}
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
- public static final class ParseResult extends NamedFactoriesListParseResult<Cipher,NamedFactory<Cipher>> {
- public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Cipher>>emptyList(), Collections.<String>emptyList());
+ public static final class ParseResult extends NamedFactoriesListParseResult<Cipher,CipherFactory> {
+ public static final ParseResult EMPTY=new ParseResult(Collections.<CipherFactory>emptyList(), Collections.<String>emptyList());
- public ParseResult(List<NamedFactory<Cipher>> parsed, List<String> unsupported) {
+ public ParseResult(List<CipherFactory> parsed, List<String> unsupported) {
super(parsed, unsupported);
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
new file mode 100644
index 0000000..e536ac0
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cipher;
+
+import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CipherFactory extends NamedFactory<Cipher>, OptionalFeature {
+ // required because of generics issues
+ Transformer<CipherFactory,NamedFactory<Cipher>> FAC2NAMED=new Transformer<CipherFactory,NamedFactory<Cipher>>() {
+ @Override
+ public NamedFactory<Cipher> transform(CipherFactory input) {
+ return input;
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
index 3484d93..41f467c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
@@ -19,7 +19,6 @@
package org.apache.sshd.common.cipher;
import org.apache.sshd.common.Cipher;
-import org.apache.sshd.common.NamedFactory;
/**
* Represents a no-op cipher.
@@ -31,18 +30,22 @@ import org.apache.sshd.common.NamedFactory;
*/
public class CipherNone implements Cipher {
+ @Override
public int getIVSize() {
return 8;
}
+ @Override
public int getBlockSize() {
return 16;
}
+ @Override
public void init(Mode mode, byte[] bytes, byte[] bytes1) throws Exception {
// ignored - always succeeds
}
+ @Override
public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
// ignored - always succeeds
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
index 927b94c..14d979b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -45,9 +45,9 @@ public class ECCurves {
private static final Map<Integer, String> CURVE_SIZES = new TreeMap<Integer, String>();
static {
- CURVE_SIZES.put(256, NISTP256);
- CURVE_SIZES.put(384, NISTP384);
- CURVE_SIZES.put(521, NISTP521);
+ CURVE_SIZES.put(Integer.valueOf(256), NISTP256);
+ CURVE_SIZES.put(Integer.valueOf(384), NISTP384);
+ CURVE_SIZES.put(Integer.valueOf(521), NISTP521);
}
public static String getCurveName(ECParameterSpec params) {
@@ -60,11 +60,7 @@ public class ECCurves {
}
public static String getCurveName(int fieldSize) {
- String curveName = CURVE_SIZES.get(fieldSize);
- if (curveName == null) {
- return null;
- }
- return curveName;
+ return CURVE_SIZES.get(Integer.valueOf(fieldSize));
}
public static int getCurveSize(ECParameterSpec params) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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 bb0c8f8..95128b8 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
@@ -19,18 +19,27 @@
package org.apache.sshd.common.compression;
+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.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFeature {
+public enum BuiltinCompressions implements CompressionFactory {
none(Constants.NONE) {
@Override
public Compression create() {
@@ -62,6 +71,7 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
return getName();
}
+ @Override
public final boolean isSupported() {
return true;
}
@@ -70,8 +80,55 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
name = n;
}
- public static final Set<BuiltinCompressions> VALUES=
+ public static final Set<BuiltinCompressions> VALUES =
Collections.unmodifiableSet(EnumSet.allOf(BuiltinCompressions.class));
+ private static final Map<String,CompressionFactory> extensions =
+ new TreeMap<String,CompressionFactory>(String.CASE_INSENSITIVE_ORDER);
+
+ /**
+ * Registered a {@link NamedFactory} to be available besides the built-in
+ * ones when parsing configuration
+ * @param extension The factory to register
+ * @throws IllegalArgumentException if factory instance is {@code null},
+ * or overrides a built-in one or overrides another registered factory
+ * with the same name (case <U>insensitive</U>).
+ */
+ public static final void registerExtension(CompressionFactory extension) {
+ String name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+ ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+ synchronized(extensions) {
+ ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+ extensions.put(name, extension);
+ }
+ }
+
+ /**
+ * @return A {@link SortedSet} of the currently registered extensions, sorted
+ * according to the factory name (case <U>insensitive</U>)
+ */
+ public static final SortedSet<CompressionFactory> getRegisteredExtensions() {
+ // TODO for JDK-8 return Collections.emptySortedSet()
+ synchronized(extensions) {
+ return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+ }
+ }
+
+ /**
+ * Unregisters specified extension
+ * @param name The factory name - ignored if {@code null}/empty
+ * @return The registered extension - {@code null} if not found
+ */
+ public static final CompressionFactory unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized(extensions) {
+ return extensions.remove(name);
+ }
+ }
+
public static final BuiltinCompressions fromFactoryName(String name) {
if (GenericUtils.isEmpty(name)) {
return null;
@@ -85,6 +142,76 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
return null;
}
+ /**
+ * @param Compressions A comma-separated list of Compressions' names - ignored
+ * if {@code null}/empty
+ * @return A {@link ParseResult} containing the successfully parsed
+ * factories and the unknown ones. <B>Note:</B> it is up to caller to
+ * ensure that the lists do not contain duplicates
+ */
+ public static final ParseResult parseCompressionsList(String Compressions) {
+ return parseCompressionsList(GenericUtils.split(Compressions, ','));
+ }
+
+ public static final ParseResult parseCompressionsList(String ... Compressions) {
+ return parseCompressionsList(GenericUtils.isEmpty((Object[]) Compressions) ? Collections.<String>emptyList() : Arrays.asList(Compressions));
+ }
+
+ public static final ParseResult parseCompressionsList(Collection<String> Compressions) {
+ if (GenericUtils.isEmpty(Compressions)) {
+ return ParseResult.EMPTY;
+ }
+
+ List<CompressionFactory> factories=new ArrayList<CompressionFactory>(Compressions.size());
+ List<String> unknown=Collections.<String>emptyList();
+ for (String name : Compressions) {
+ CompressionFactory c=resolveFactory(name);
+ if (c != null) {
+ factories.add(c);
+ } else {
+ // replace the (unmodifiable) empty list with a real one
+ if (unknown.isEmpty()) {
+ unknown = new ArrayList<String>();
+ }
+ unknown.add(name);
+ }
+ }
+
+ return new ParseResult(factories, unknown);
+ }
+
+ /**
+ * @param name The factory name
+ * @return The factory or {@code null} if it is neither a built-in one
+ * or a registered extension
+ */
+ public static final CompressionFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ CompressionFactory c=fromFactoryName(name);
+ if (c != null) {
+ return c;
+ }
+
+ synchronized(extensions) {
+ return extensions.get(name);
+ }
+ }
+
+ /**
+ * Holds the result of {@link BuiltinCompressions#parseCompressionsList(String)}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ public static final class ParseResult extends NamedFactoriesListParseResult<Compression,CompressionFactory> {
+ public static final ParseResult EMPTY=new ParseResult(Collections.<CompressionFactory>emptyList(), Collections.<String>emptyList());
+
+ public ParseResult(List<CompressionFactory> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+ }
+
public static final class Constants {
public static final String NONE="none";
public static final String ZLIB="zlib";
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
index 231c214..5c4e1a0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
@@ -34,6 +34,7 @@ public class CompressionDelayedZlib extends CompressionZlib {
super();
}
+ @Override
public boolean isDelayed() {
return true;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
new file mode 100644
index 0000000..3af29d7
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
@@ -0,0 +1,37 @@
+/*
+ * 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.compression;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CompressionFactory extends NamedFactory<Compression>, OptionalFeature {
+ // required because of generics issues
+ Transformer<CompressionFactory,NamedFactory<Compression>> FAC2NAMED=new Transformer<CompressionFactory,NamedFactory<Compression>>() {
+ @Override
+ public NamedFactory<Compression> transform(CompressionFactory input) {
+ return input;
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
index 1e8e921..5e9a56c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
@@ -43,15 +43,18 @@ public class CompressionZlib implements Compression {
public CompressionZlib() {
}
+ @Override
public boolean isDelayed() {
return false;
}
+ @Override
public void init(Type type, int level) {
compresser = new Deflater(level);
decompresser = new Inflater();
}
+ @Override
public void compress(Buffer buffer) throws IOException {
compresser.setInput(buffer.array(), buffer.rpos(), buffer.available());
buffer.wpos(buffer.rpos());
@@ -61,6 +64,7 @@ public class CompressionZlib implements Compression {
}
}
+ @Override
public void uncompress(Buffer from, Buffer to) throws IOException {
decompresser.setInput(from.array(), from.rpos(), from.available());
int len;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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
index bb94f57..fcad285 100644
--- 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
@@ -26,6 +26,7 @@ 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.compression.CompressionFactory;
import org.apache.sshd.common.util.GenericUtils;
/**
@@ -33,12 +34,12 @@ import org.apache.sshd.common.util.GenericUtils;
* 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> {
+public enum CompressionConfigValue implements CompressionFactory {
YES(BuiltinCompressions.zlib),
NO(BuiltinCompressions.none),
DELAYED(BuiltinCompressions.delayedZlib);
- private final NamedFactory<Compression> factory;
+ private final CompressionFactory factory;
@Override
public final String getName() {
@@ -51,11 +52,16 @@ public enum CompressionConfigValue implements NamedFactory<Compression> {
}
@Override
+ public boolean isSupported() {
+ return factory.isSupported();
+ }
+
+ @Override
public final String toString() {
return getName();
}
- CompressionConfigValue(NamedFactory<Compression> delegate) {
+ CompressionConfigValue(CompressionFactory delegate) {
factory = delegate;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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
index 38e0272..e31868c 100644
--- 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
@@ -31,21 +31,33 @@ import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
+import org.apache.sshd.SshBuilder.ClientBuilder;
+import org.apache.sshd.SshBuilder.ServerBuilder;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.SshServer;
import org.apache.sshd.common.AbstractFactoryManager;
import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.KeyExchange;
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.Transformer;
import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.CipherFactory;
+import org.apache.sshd.common.compression.BuiltinCompressions;
import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.compression.CompressionFactory;
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.mac.MacFactory;
import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.SignatureFactory;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.io.NoCloseInputStream;
@@ -408,10 +420,22 @@ public class SshConfigFileReader {
* @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) {
+ public static final CompressionFactory getCompression(Properties props) {
return CompressionConfigValue.fromName((props == null) ? null : props.getProperty(COMPRESSION_PROP));
}
+ public static final <S extends SshServer> S configure(S server, Properties props, boolean lenient, boolean ignoreUnsupported) {
+ configure((AbstractFactoryManager) server, props, lenient, ignoreUnsupported);
+ configureKeyExchanges(server, props, lenient, ServerBuilder.DH2KEX, ignoreUnsupported);
+ return server;
+ }
+
+ public static final <C extends SshClient> C configure(C client, Properties props, boolean lenient, boolean ignoreUnsupported) {
+ configure((AbstractFactoryManager) client, props, lenient, ignoreUnsupported);
+ configureKeyExchanges(client, props, lenient, ClientBuilder.DH2KEX, ignoreUnsupported);
+ return client;
+ }
+
/**
* <P>Configures an {@link AbstractFactoryManager} with the values read from
* some configuration. Currently it configures:</P></BR>
@@ -425,78 +449,158 @@ public class SshConfigFileReader {
* @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
- * @param lenient If {@code true} then any unknown/unsupported configuration
- * values are ignored. Otherwise an {@link IllegalArgumentException} is thrown
+ * @param lenient If {@code true} then any unknown configuration values are ignored.
+ * Otherwise an {@link IllegalArgumentException} is thrown
+ * @param ignoreUnsupported filter out unsupported configuration values (e.g., ciphers,
+ * key exchanges, etc..). <B>Note:</B> if after filtering out all the unknown
+ * or unsupported values there is an empty configuration exception is thrown
* @return The configured manager
*/
- public static final <M extends AbstractFactoryManager> M configure(M manager, Properties props, boolean lenient) {
- configureCiphers(manager, props, lenient);
- configureSignatures(manager, props, lenient);
- configureMacs(manager, props, lenient);
- configureCompression(manager, props, lenient);
+ public static final <M extends AbstractFactoryManager> M configure(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
+ configureCiphers(manager, props, lenient, ignoreUnsupported);
+ configureSignatures(manager, props, lenient, ignoreUnsupported);
+ configureMacs(manager, props, lenient, ignoreUnsupported);
+ configureCompression(manager, props, lenient, ignoreUnsupported);
return manager;
}
- public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, Properties props, boolean lenient) {
+ public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
- return configureCiphers(manager, props.getProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient);
+ return configureCiphers(manager, props.getProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient, ignoreUnsupported);
}
- public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient) {
+ public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
BuiltinCiphers.ParseResult result=BuiltinCiphers.parseCiphersList(value);
Collection<String> unsupported=result.getUnsupportedFactories();
ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported cipher(s) (%s) in %s", unsupported, value);
- manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known ciphers(s): %s", value));
+
+ List<NamedFactory<Cipher>> factories =
+ NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), CipherFactory.FAC2NAMED);
+ manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/unsupported ciphers(s): %s", value));
return manager;
}
- public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, Properties props, boolean lenient) {
+ public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
- return configureSignatures(manager, props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient);
+ return configureSignatures(manager, props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient, ignoreUnsupported);
}
- public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient) {
+ public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
BuiltinSignatures.ParseResult result=BuiltinSignatures.parseSignatureList(value);
Collection<String> unsupported=result.getUnsupportedFactories();
ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported signatures (%s) in %s", unsupported, value);
- manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known signatures: %s", value));
+
+ List<NamedFactory<Signature>> factories =
+ NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), SignatureFactory.FAC2NAMED);
+ manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported signatures: %s", value));
return manager;
}
- public static final <M extends AbstractFactoryManager> M configureMacs(M manager, Properties props, boolean lenient) {
+ public static final <M extends AbstractFactoryManager> M configureMacs(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
- return configureMacs(manager, props.getProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient);
+ return configureMacs(manager, props.getProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient, ignoreUnsupported);
}
- public static final <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient) {
+ public static final <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
BuiltinMacs.ParseResult result=BuiltinMacs.parseMacsList(value);
Collection<String> unsupported=result.getUnsupportedFactories();
ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported MAC(s) (%s) in %s", unsupported, value);
- manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known MAC(s): %s", value));
+
+ List<NamedFactory<Mac>> factories =
+ NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), MacFactory.FAC2NAMED);
+ manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported MAC(s): %s", value));
return manager;
}
- // NOTE: if no compression is resolved it is OK since SSH can function without it
- public static final <M extends AbstractFactoryManager> M configureCompression(M manager, Properties props, boolean lenient) {
+ /**
+ * @param manager The {@link AbstractFactoryManager} to set up (may not be {@code null})
+ * @param props The (non-{@code null}) {@link Properties} containing the configuration
+ * @param lenient If {@code true} then any unknown/unsupported configuration
+ * values are ignored. Otherwise an {@link IllegalArgumentException} is thrown
+ * @param xformer A {@link Transformer} to convert the configured {@link DHFactory}-ies
+ * to {@link NamedFactory}-ies of {@link KeyExchange}
+ * @param ignoreUnsupported Filter out any un-supported configurations - <B>Note:</B>
+ * if after ignoring the unknown and un-supported values the result is an empty
+ * list of factories and exception is thrown
+ * @return The configured manager
+ * @see #KEX_ALGORITHMS_CONFIG_PROP
+ * @see #DEFAULT_KEX_ALGORITHMS
+ */
+ public static final <M extends AbstractFactoryManager> M configureKeyExchanges(
+ M manager, Properties props, boolean lenient, Transformer<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
- return configureCompression(manager, props.getProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION), lenient);
+ return configureKeyExchanges(manager, props.getProperty(KEX_ALGORITHMS_CONFIG_PROP, DEFAULT_KEX_ALGORITHMS), lenient, xformer, ignoreUnsupported);
}
- public static final <M extends AbstractFactoryManager> M configureCompression(M manager, String value, boolean lenient) {
+ public static final <M extends AbstractFactoryManager> M configureKeyExchanges(
+ M manager, String value, boolean lenient, Transformer<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+ ValidateUtils.checkNotNull(xformer, "No DHFactory transformer", GenericUtils.EMPTY_OBJECT_ARRAY);
- NamedFactory<Compression> factory=CompressionConfigValue.fromName(value);
+ BuiltinDHFactories.ParseResult result=BuiltinDHFactories.parseDHFactoriesList(value);
+ Collection<String> unsupported=result.getUnsupportedFactories();
+ ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported KEX(s) (%s) in %s", unsupported, value);
+
+ List<NamedFactory<KeyExchange>> factories =
+ NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), xformer);
+ manager.setKeyExchangeFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported KEXS(s): %s", value));
+ return manager;
+ }
+
+ /**
+ * Configure the factory manager using one of the known {@link CompressionConfigValue}s.
+ * @param manager The {@link AbstractFactoryManager} to configure
+ * @param props The configuration {@link Properties}
+ * @param lenient If {@code true} and an unknown value is provided then
+ * it is ignored
+ * @param ignoreUnsupported If {@code false} then check if the compression
+ * is currently supported before setting it
+ * @return The configured manager - <B>Note:</B> if the result of filtering due
+ * to lenient mode or ignored unsupported value is empty then no factories are set
+ */
+ public static final <M extends AbstractFactoryManager> M configureCompression(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
+ 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(COMPRESSION_PROP, DEFAULT_COMPRESSION);
+ CompressionFactory factory=CompressionConfigValue.fromName(value);
ValidateUtils.checkTrue(lenient || (factory != null), "Unsupported compression value: %s", value);
- if (factory != null) {
+ if ((factory != null) && factory.isSupported()) {
manager.setCompressionFactories(Collections.<NamedFactory<Compression>>singletonList(factory));
}
+
+ return manager;
+ }
+
+ // accepts BOTH CompressionConfigValue(s) and/or BuiltinCompressions - including extensions
+ public static final <M extends AbstractFactoryManager> M configureCompression(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
+ ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+
+ CompressionFactory factory=CompressionConfigValue.fromName(value);
+ if (factory != null) {
+ // SSH can work without compression
+ if (ignoreUnsupported || factory.isSupported()) {
+ manager.setCompressionFactories(Collections.<NamedFactory<Compression>>singletonList(factory));
+ }
+ } else {
+ BuiltinCompressions.ParseResult result=BuiltinCompressions.parseCompressionsList(value);
+ Collection<String> unsupported=result.getUnsupportedFactories();
+ ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported compressions(s) (%s) in %s", unsupported, value);
+
+ List<NamedFactory<Compression>> factories =
+ NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), CompressionFactory.FAC2NAMED);
+ // SSH can work without compression
+ if (GenericUtils.size(factories) > 0) {
+ manager.setCompressionFactories(factories);
+ }
+ }
return manager;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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 be29fa2..9503486 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
@@ -26,19 +26,24 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.cipher.ECCurves;
import org.apache.sshd.common.config.NamedResourceListParseResult;
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>
*/
-public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
+public enum BuiltinDHFactories implements DHFactory {
dhg1(Constants.DIFFIE_HELLMAN_GROUP1_SHA1) {
@Override
public DHG create(Object... params) throws Exception {
@@ -165,6 +170,52 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
public static final Set<BuiltinDHFactories> VALUES =
Collections.unmodifiableSet(EnumSet.allOf(BuiltinDHFactories.class));
+ private static final Map<String,DHFactory> extensions =
+ new TreeMap<String,DHFactory>(String.CASE_INSENSITIVE_ORDER);
+
+ /**
+ * Registered a {@link NamedFactory} to be available besides the built-in
+ * ones when parsing configuration
+ * @param extension The factory to register
+ * @throws IllegalArgumentException if factory instance is {@code null},
+ * or overrides a built-in one or overrides another registered factory
+ * with the same name (case <U>insensitive</U>).
+ */
+ public static final void registerExtension(DHFactory extension) {
+ String name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+ ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+ synchronized(extensions) {
+ ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+ extensions.put(name, extension);
+ }
+ }
+
+ /**
+ * @return A {@link SortedSet} of the currently registered extensions, sorted
+ * according to the factory name (case <U>insensitive</U>)
+ */
+ public static final SortedSet<DHFactory> getRegisteredExtensions() {
+ // TODO for JDK-8 return Collections.emptySortedSet()
+ synchronized(extensions) {
+ return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+ }
+ }
+
+ /**
+ * Unregisters specified extension
+ * @param name The factory name - ignored if {@code null}/empty
+ * @return The registered extension - {@code null} if not found
+ */
+ public static final DHFactory unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized(extensions) {
+ return extensions.remove(name);
+ }
+ }
/**
* @param name The factory name - ignored if {@code null}/empty
@@ -216,7 +267,7 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
List<DHFactory> factories=new ArrayList<DHFactory>(dhList.size());
List<String> unknown=Collections.<String>emptyList();
for (String name : dhList) {
- DHFactory f=fromFactoryName(name);
+ DHFactory f=resolveFactory(name);
if (f != null) {
factories.add(f);
} else {
@@ -230,6 +281,25 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
return new ParseResult(factories, unknown);
}
+ /**
+ * @param name The factory name
+ * @return The factory or {@code null} if it is neither a built-in one
+ * or a registered extension
+ */
+ public static final DHFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ DHFactory s=fromFactoryName(name);
+ if (s != null) {
+ return s;
+ }
+
+ synchronized(extensions) {
+ return extensions.get(name);
+ }
+ }
/**
* Represents the result of {@link BuiltinDHFactories#parseDHFactoriesList(String)}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
index ad0e295..81ded7e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
@@ -19,11 +19,12 @@
package org.apache.sshd.common.kex;
import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.OptionalFeature;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface DHFactory extends NamedResource {
+public interface DHFactory extends NamedResource, OptionalFeature {
boolean isGroupExchange();
AbstractDH create(Object... params) throws Exception;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
index 9478a06..77a5006 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
@@ -43,10 +43,12 @@ public class BaseMac implements Mac {
this.tmp = new byte[defbsize];
}
+ @Override
public int getBlockSize() {
return bsize;
}
+ @Override
public void init(byte[] key) throws Exception {
if (key.length > defbsize) {
byte[] tmp = new byte[defbsize];
@@ -59,6 +61,7 @@ public class BaseMac implements Mac {
mac.init(skey);
}
+ @Override
public void updateUInt(long i) {
tmp[0] = (byte) (i >>> 24);
tmp[1] = (byte) (i >>> 16);
@@ -67,10 +70,12 @@ public class BaseMac implements Mac {
update(tmp, 0, 4);
}
+ @Override
public void update(byte foo[], int s, int l) {
mac.update(foo, s, l);
}
+ @Override
public void doFinal(byte[] buf, int offset) throws Exception {
if (bsize != defbsize) {
mac.doFinal(tmp, 0);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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 5756dc5..ef920e4 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
@@ -25,20 +25,25 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
import org.apache.sshd.common.Digest;
import org.apache.sshd.common.Mac;
import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
/**
* Provides easy access to the currently implemented macs
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
+public enum BuiltinMacs implements MacFactory {
hmacmd5(Constants.HMAC_MD5) {
@Override
public Mac create() {
@@ -99,6 +104,52 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
public static final Set<BuiltinMacs> VALUES =
Collections.unmodifiableSet(EnumSet.allOf(BuiltinMacs.class));
+ private static final Map<String,MacFactory> extensions =
+ new TreeMap<String,MacFactory>(String.CASE_INSENSITIVE_ORDER);
+
+ /**
+ * Registered a {@link NamedFactory} to be available besides the built-in
+ * ones when parsing configuration
+ * @param extension The factory to register
+ * @throws IllegalArgumentException if factory instance is {@code null},
+ * or overrides a built-in one or overrides another registered factory
+ * with the same name (case <U>insensitive</U>).
+ */
+ public static final void registerExtension(MacFactory extension) {
+ String name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+ ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+ synchronized(extensions) {
+ ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+ extensions.put(name, extension);
+ }
+ }
+
+ /**
+ * @return A {@link SortedSet} of the currently registered extensions, sorted
+ * according to the factory name (case <U>insensitive</U>)
+ */
+ public static final SortedSet<MacFactory> getRegisteredExtensions() {
+ // TODO for JDK-8 return Collections.emptySortedSet()
+ synchronized(extensions) {
+ return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+ }
+ }
+
+ /**
+ * Unregisters specified extension
+ * @param name The factory name - ignored if {@code null}/empty
+ * @return The registered extension - {@code null} if not found
+ */
+ public static final MacFactory unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized(extensions) {
+ return extensions.remove(name);
+ }
+ }
/**
* @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -172,10 +223,10 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
return ParseResult.EMPTY;
}
- List<NamedFactory<Mac>> factories=new ArrayList<NamedFactory<Mac>>(macs.size());
+ List<MacFactory> factories=new ArrayList<MacFactory>(macs.size());
List<String> unknown=Collections.<String>emptyList();
for (String name : macs) {
- BuiltinMacs m=fromFactoryName(name);
+ MacFactory m=resolveFactory(name);
if (m != null) {
factories.add(m);
} else {
@@ -190,22 +241,31 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
return new ParseResult(factories, unknown);
}
- public static final class ParseResult {
- public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Mac>>emptyList(), Collections.<String>emptyList());
- private final List<NamedFactory<Mac>> parsed;
- private final List<String> unsupported;
-
- public ParseResult(List<NamedFactory<Mac>> parsed, List<String> unsupported) {
- this.parsed = parsed;
- this.unsupported = unsupported;
+ /**
+ * @param name The factory name
+ * @return The factory or {@code null} if it is neither a built-in one
+ * or a registered extension
+ */
+ public static final MacFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ MacFactory m=fromFactoryName(name);
+ if (m != null) {
+ return m;
}
- public List<NamedFactory<Mac>> getParsedFactories() {
- return parsed;
+ synchronized(extensions) {
+ return extensions.get(name);
}
+ }
+
+ public static final class ParseResult extends NamedFactoriesListParseResult<Mac,MacFactory> {
+ public static final ParseResult EMPTY=new ParseResult(Collections.<MacFactory>emptyList(), Collections.<String>emptyList());
- public List<String> getUnsupportedFactories() {
- return unsupported;
+ public ParseResult(List<MacFactory> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
new file mode 100644
index 0000000..324405e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.mac;
+
+import org.apache.sshd.common.Mac;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MacFactory extends NamedFactory<Mac>, OptionalFeature {
+ // required because of generics issues
+ Transformer<MacFactory,NamedFactory<Mac>> FAC2NAMED=new Transformer<MacFactory,NamedFactory<Mac>>() {
+ @Override
+ public NamedFactory<Mac> transform(MacFactory input) {
+ return input;
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
index ba906b6..f129703 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
@@ -38,6 +38,7 @@ public abstract class AbstractSignature implements Signature {
this.algorithm = algorithm;
}
+ @Override
public void init(PublicKey pubkey, PrivateKey prvkey) throws Exception {
signature = SecurityUtils.getSignature(algorithm);
if (pubkey != null) {
@@ -48,6 +49,7 @@ public abstract class AbstractSignature implements Signature {
}
}
+ @Override
public void update(byte[] foo, int off, int len) throws Exception {
signature.update(foo, off, len);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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 23f72c0..1da1952 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
@@ -26,22 +26,27 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
import org.apache.sshd.common.Digest;
import org.apache.sshd.common.KeyPairProvider;
import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.Signature;
import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
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 signatures
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeature {
+public enum BuiltinSignatures implements SignatureFactory {
dsa(KeyPairProvider.SSH_DSS) {
@Override
public Signature create() {
@@ -120,8 +125,54 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
return true;
}
- public static final Set<BuiltinSignatures> VALUES =
+ public static final Set<BuiltinSignatures> VALUES =
Collections.unmodifiableSet(EnumSet.allOf(BuiltinSignatures.class));
+ private static final Map<String,SignatureFactory> extensions =
+ new TreeMap<String,SignatureFactory>(String.CASE_INSENSITIVE_ORDER);
+
+ /**
+ * Registered a {@link NamedFactory} to be available besides the built-in
+ * ones when parsing configuration
+ * @param extension The factory to register
+ * @throws IllegalArgumentException if factory instance is {@code null},
+ * or overrides a built-in one or overrides another registered factory
+ * with the same name (case <U>insensitive</U>).
+ */
+ public static final void registerExtension(SignatureFactory extension) {
+ String name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+ ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+ synchronized(extensions) {
+ ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+ extensions.put(name, extension);
+ }
+ }
+
+ /**
+ * @return A {@link SortedSet} of the currently registered extensions, sorted
+ * according to the factory name (case <U>insensitive</U>)
+ */
+ public static final SortedSet<SignatureFactory> getRegisteredExtensions() {
+ // TODO for JDK-8 return Collections.emptySortedSet()
+ synchronized(extensions) {
+ return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+ }
+ }
+
+ /**
+ * Unregisters specified extension
+ * @param name The factory name - ignored if {@code null}/empty
+ * @return The registered extension - {@code null} if not found
+ */
+ public static final SignatureFactory unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized(extensions) {
+ return extensions.remove(name);
+ }
+ }
/**
* @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -198,10 +249,10 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
return ParseResult.EMPTY;
}
- List<NamedFactory<Signature>> factories=new ArrayList<NamedFactory<Signature>>(sigs.size());
- List<String> unknown=Collections.<String>emptyList();
+ List<SignatureFactory> factories=new ArrayList<SignatureFactory>(sigs.size());
+ List<String> unknown=Collections.<String>emptyList();
for (String name : sigs) {
- BuiltinSignatures s=fromFactoryName(name);
+ SignatureFactory s=resolveFactory(name);
if (s != null) {
factories.add(s);
} else {
@@ -216,22 +267,35 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
return new ParseResult(factories, unknown);
}
- public static final class ParseResult {
- public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Signature>>emptyList(), Collections.<String>emptyList());
- private final List<NamedFactory<Signature>> parsed;
- private final List<String> unsupported;
-
- public ParseResult(List<NamedFactory<Signature>> parsed, List<String> unsupported) {
- this.parsed = parsed;
- this.unsupported = unsupported;
+ /**
+ * @param name The factory name
+ * @return The factory or {@code null} if it is neither a built-in one
+ * or a registered extension
+ */
+ public static final SignatureFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ SignatureFactory s=fromFactoryName(name);
+ if (s != null) {
+ return s;
}
- public List<NamedFactory<Signature>> getParsedFactories() {
- return parsed;
+ synchronized(extensions) {
+ return extensions.get(name);
}
+ }
+
+ /**
+ * Holds the result of the {@link BuiltinSignatures#parseSignatureList(String)}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ public static final class ParseResult extends NamedFactoriesListParseResult<Signature,SignatureFactory> {
+ public static final ParseResult EMPTY=new ParseResult(Collections.<SignatureFactory>emptyList(), Collections.<String>emptyList());
- public List<String> getUnsupportedFactories() {
- return unsupported;
+ public ParseResult(List<SignatureFactory> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
}
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
index d682b3a..b6ec18c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
@@ -30,6 +30,7 @@ public class SignatureDSA extends AbstractSignature {
super(algorithm);
}
+ @Override
public byte[] sign() throws Exception {
byte[] sig = signature.sign();
@@ -63,6 +64,7 @@ public class SignatureDSA extends AbstractSignature {
return result;
}
+ @Override
public boolean verify(byte[] sig) throws Exception {
sig = extractSig(sig);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
index b1e5a07..5801801 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
@@ -20,12 +20,6 @@ package org.apache.sshd.common.signature;
import java.io.IOException;
import java.math.BigInteger;
-import java.security.spec.ECParameterSpec;
-
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Signature;
-import org.apache.sshd.common.cipher.ECCurves;
import org.apache.sshd.common.util.Buffer;
/**
@@ -39,6 +33,7 @@ public class SignatureECDSA extends AbstractSignature {
super(algo);
}
+ @Override
public byte[] sign() throws Exception {
byte[] sig = signature.sign();
@@ -73,6 +68,7 @@ public class SignatureECDSA extends AbstractSignature {
return rsBuf.getCompactData();
}
+ @Override
public boolean verify(byte[] sig) throws Exception {
sig = extractSig(sig);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
new file mode 100644
index 0000000..99510e6
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.signature;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SignatureFactory extends NamedFactory<Signature>, OptionalFeature {
+ // required because of generics issues
+ Transformer<SignatureFactory,NamedFactory<Signature>> FAC2NAMED=new Transformer<SignatureFactory,NamedFactory<Signature>>() {
+ @Override
+ public NamedFactory<Signature> transform(SignatureFactory input) {
+ return input;
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
index 5c3558a..1cb117e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
@@ -18,10 +18,6 @@
*/
package org.apache.sshd.common.signature;
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Signature;
-
/**
* RSA <code>Signature</code>
*
@@ -33,10 +29,12 @@ public class SignatureRSA extends AbstractSignature {
super("SHA1withRSA");
}
+ @Override
public byte[] sign() throws Exception {
return signature.sign();
}
+ @Override
public boolean verify(byte[] sig) throws Exception {
sig = extractSig(sig);
return signature.verify(sig);