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/12/08 06:12:15 UTC
[2/2] mina-sshd git commit: [SSHD-609] Session does not disconnect if
unknown compression factory requested
[SSHD-609] Session does not disconnect if unknown compression factory requested
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/53055cb5
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/53055cb5
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/53055cb5
Branch: refs/heads/master
Commit: 53055cb5330220d16681f8c94ca920b5130f3802
Parents: 9022808
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Dec 8 07:12:02 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Dec 8 07:12:02 2015 +0200
----------------------------------------------------------------------
.../common/compression/BaseCompression.java | 5 +
.../common/compression/BuiltinCompressions.java | 22 ++-
.../sshd/common/compression/Compression.java | 12 +-
.../common/compression/CompressionFactory.java | 2 +-
.../compression/CompressionInformation.java | 42 ++++++
.../common/compression/CompressionNone.java | 76 ++++++++++
.../common/config/CompressionConfigValue.java | 10 ++
.../sshd/common/session/AbstractSession.java | 51 +++++--
.../org/apache/sshd/common/session/Session.java | 33 +++++
.../compression/BuiltinCompressionsTest.java | 1 -
.../java/org/apache/sshd/server/ServerTest.java | 140 ++++++++++++++++---
.../org/apache/sshd/server/SshServerTest.java | 3 +
12 files changed, 350 insertions(+), 47 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
index 7feda60..ff31947 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
@@ -37,6 +37,11 @@ public abstract class BaseCompression implements Compression {
}
@Override
+ public boolean isCompressionExecuted() {
+ return true;
+ }
+
+ @Override
public String toString() {
return getName();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/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 f0a55c9..43213d8 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
@@ -42,7 +42,12 @@ public enum BuiltinCompressions implements CompressionFactory {
none(Constants.NONE) {
@Override
public Compression create() {
- return null;
+ return new CompressionNone();
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return false;
}
},
zlib(Constants.ZLIB) {
@@ -56,6 +61,11 @@ public enum BuiltinCompressions implements CompressionFactory {
public Compression create() {
return new CompressionDelayedZlib();
}
+
+ @Override
+ public boolean isDelayed() {
+ return true;
+ }
};
public static final Set<BuiltinCompressions> VALUES =
@@ -76,6 +86,16 @@ public enum BuiltinCompressions implements CompressionFactory {
}
@Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return true;
+ }
+
+ @Override
public final String toString() {
return getName();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
index 33f6fb2..0f1f809 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
@@ -20,7 +20,6 @@ package org.apache.sshd.common.compression;
import java.io.IOException;
-import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.util.buffer.Buffer;
/**
@@ -29,7 +28,7 @@ import org.apache.sshd.common.util.buffer.Buffer;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface Compression extends NamedResource {
+public interface Compression extends CompressionInformation {
/**
* Enum identifying if this object will be used to compress
@@ -41,15 +40,6 @@ public interface Compression extends NamedResource {
}
/**
- * Delayed compression is an Open-SSH specific feature which
- * informs both the client and server to not compress data before
- * the session has been authenticated.
- *
- * @return if the compression is delayed after authentication or not
- */
- boolean isDelayed();
-
- /**
* Initialize this object to either compress or uncompress data.
* This method must be called prior to any calls to either
* <code>compress</code> or <code>uncompress</code>.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/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
index a6dd4c9..355594a 100644
--- 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
@@ -25,7 +25,7 @@ import org.apache.sshd.common.BuiltinFactory;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
// CHECKSTYLE:OFF
-public interface CompressionFactory extends BuiltinFactory<Compression> {
+public interface CompressionFactory extends BuiltinFactory<Compression>, CompressionInformation {
// nothing extra
}
//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
new file mode 100644
index 0000000..428689f
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.NamedResource;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CompressionInformation extends NamedResource {
+ /**
+ * Delayed compression is an Open-SSH specific feature which
+ * informs both the client and server to not compress data before
+ * the session has been authenticated.
+ *
+ * @return if the compression is delayed after authentication or not
+ */
+ boolean isDelayed();
+
+ /**
+ * @return {@code true} if there is any compression executed by
+ * this "compressor" - special case for 'none'
+ */
+ boolean isCompressionExecuted();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
new file mode 100644
index 0000000..815e8ce
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
@@ -0,0 +1,76 @@
+/*
+ * 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 java.io.IOException;
+import java.io.StreamCorruptedException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CompressionNone extends BaseCompression {
+ private Type type;
+ private int level;
+
+ public CompressionNone() {
+ super(BuiltinCompressions.Constants.NONE);
+ }
+
+ @Override
+ public void init(Type type, int level) {
+ this.type = type;
+ this.level = level;
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return false;
+ }
+
+ @Override
+ public void compress(Buffer buffer) throws IOException {
+ if (!Type.Deflater.equals(type)) {
+ throw new StreamCorruptedException("Not set up for compression: " + type);
+ }
+ }
+
+ @Override
+ public void uncompress(Buffer from, Buffer to) throws IOException {
+ if (!Type.Inflater.equals(type)) {
+ throw new StreamCorruptedException("Not set up for de-compression: " + type);
+ }
+
+ if (from != to) {
+ throw new StreamCorruptedException("Separate de-compression buffers provided");
+ }
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[" + type + "/" + level + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/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 c3bbe74..e153adc 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
@@ -68,6 +68,16 @@ public enum CompressionConfigValue implements CompressionFactory {
return getName();
}
+ @Override
+ public boolean isDelayed() {
+ return factory.isDelayed();
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return factory.isCompressionExecuted();
+ }
+
public static CompressionConfigValue fromName(String n) {
if (GenericUtils.isEmpty(n)) {
return null;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 02af7cf..1347245 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -47,7 +47,9 @@ import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.channel.ChannelListener;
import org.apache.sshd.common.cipher.Cipher;
+import org.apache.sshd.common.cipher.CipherInformation;
import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.compression.CompressionInformation;
import org.apache.sshd.common.digest.Digest;
import org.apache.sshd.common.future.DefaultKeyExchangeFuture;
import org.apache.sshd.common.future.DefaultSshFuture;
@@ -60,6 +62,7 @@ import org.apache.sshd.common.kex.KexProposalOption;
import org.apache.sshd.common.kex.KexState;
import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.common.mac.Mac;
+import org.apache.sshd.common.mac.MacInformation;
import org.apache.sshd.common.random.Random;
import org.apache.sshd.common.util.EventListenerUtils;
import org.apache.sshd.common.util.GenericUtils;
@@ -319,6 +322,21 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
}
@Override
+ public CipherInformation getCipherInformation(boolean incoming) {
+ return incoming ? inCipher : outCipher;
+ }
+
+ @Override
+ public CompressionInformation getCompressionInformation(boolean incoming) {
+ return incoming ? inCompression : outCompression;
+ }
+
+ @Override
+ public MacInformation getMacInformation(boolean incoming) {
+ return incoming ? inMac : outMac;
+ }
+
+ @Override
public boolean isAuthenticated() {
return authed;
}
@@ -844,10 +862,11 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
log.trace("encode({}) Sending packet #{}: {}", this, Long.valueOf(seqo), buffer.printHex());
}
// Compress the packet if needed
- if ((outCompression != null) && (authed || !outCompression.isDelayed())) {
+ if ((outCompression != null) && outCompression.isCompressionExecuted() && (authed || (!outCompression.isDelayed()))) {
outCompression.compress(buffer);
len = buffer.available();
}
+
// Compute padding length
int bsize = outCipherSize;
int oldLen = len;
@@ -965,12 +984,13 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
Buffer buf;
int wpos = decoderBuffer.wpos();
// Decompress if needed
- if (inCompression != null && (authed || !inCompression.isDelayed())) {
+ if ((inCompression != null) && inCompression.isCompressionExecuted() && (authed || (!inCompression.isDelayed()))) {
if (uncompressBuffer == null) {
uncompressBuffer = new ByteArrayBuffer();
} else {
uncompressBuffer.clear();
}
+
decoderBuffer.wpos(decoderBuffer.rpos() + decoderLength - 1 - pad);
inCompression.uncompress(decoderBuffer, uncompressBuffer);
buf = uncompressBuffer;
@@ -978,9 +998,11 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
decoderBuffer.wpos(decoderLength + 4 - pad);
buf = decoderBuffer;
}
+
if (log.isTraceEnabled()) {
log.trace("decode({}) Received packet #{}: {}", this, Long.valueOf(seqi), buf.printHex());
}
+
// Update stats
inPacketsCount.incrementAndGet();
inBytesCount.addAndGet(buf.available());
@@ -1267,12 +1289,18 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
s2ccipher.init(isServer ? Cipher.Mode.Encrypt : Cipher.Mode.Decrypt, e_s2c, iv_s2c);
value = getNegotiatedKexParameter(KexProposalOption.S2CMAC);
- Mac s2cmac = ValidateUtils.checkNotNull(NamedFactory.Utils.create(getMacFactories(), value), "Unknown s2c mac: %s", value);
+ Mac s2cmac = NamedFactory.Utils.create(getMacFactories(), value);
+ if (s2cmac == null) {
+ throw new SshException(SshConstants.SSH2_DISCONNECT_MAC_ERROR, "Unknown s2c MAC: " + value);
+ }
mac_s2c = resizeKey(mac_s2c, s2cmac.getBlockSize(), hash, k, h);
s2cmac.init(mac_s2c);
value = getNegotiatedKexParameter(KexProposalOption.S2CCOMP);
Compression s2ccomp = NamedFactory.Utils.create(getCompressionFactories(), value);
+ if (s2ccomp == null) {
+ throw new SshException(SshConstants.SSH2_DISCONNECT_COMPRESSION_ERROR, "Unknown s2c compression: " + value);
+ }
value = getNegotiatedKexParameter(KexProposalOption.C2SENC);
Cipher c2scipher = ValidateUtils.checkNotNull(NamedFactory.Utils.create(getCipherFactories(), value), "Unknown c2s cipher: %s", value);
@@ -1280,12 +1308,18 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
c2scipher.init(isServer ? Cipher.Mode.Decrypt : Cipher.Mode.Encrypt, e_c2s, iv_c2s);
value = getNegotiatedKexParameter(KexProposalOption.C2SMAC);
- Mac c2smac = ValidateUtils.checkNotNull(NamedFactory.Utils.create(getMacFactories(), value), "Unknown c2s mac: %s", value);
+ Mac c2smac = NamedFactory.Utils.create(getMacFactories(), value);
+ if (c2smac == null) {
+ throw new SshException(SshConstants.SSH2_DISCONNECT_MAC_ERROR, "Unknown c2s MAC: " + value);
+ }
mac_c2s = resizeKey(mac_c2s, c2smac.getBlockSize(), hash, k, h);
c2smac.init(mac_c2s);
value = getNegotiatedKexParameter(KexProposalOption.C2SCOMP);
Compression c2scomp = NamedFactory.Utils.create(getCompressionFactories(), value);
+ if (c2scomp == null) {
+ throw new SshException(SshConstants.SSH2_DISCONNECT_COMPRESSION_ERROR, "Unknown c2s compression: " + value);
+ }
if (isServer) {
outCipher = s2ccipher;
@@ -1303,14 +1337,11 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
inCompression = s2ccomp;
}
outCipherSize = outCipher.getIVSize();
- if (outCompression != null) {
- outCompression.init(Compression.Type.Deflater, -1);
- }
+ outCompression.init(Compression.Type.Deflater, -1);
+
inCipherSize = inCipher.getIVSize();
inMacResult = new byte[inMac.getBlockSize()];
- if (inCompression != null) {
- inCompression.init(Compression.Type.Inflater, -1);
- }
+ inCompression.init(Compression.Type.Inflater, -1);
// see https://tools.ietf.org/html/rfc4344#section-3.2
int inBlockSize = inCipher.getBlockSize();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index 0ac2705..db9de87 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -27,12 +27,15 @@ import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.Service;
import org.apache.sshd.common.auth.UsernameHolder;
import org.apache.sshd.common.channel.ChannelListenerManager;
+import org.apache.sshd.common.cipher.CipherInformation;
+import org.apache.sshd.common.compression.CompressionInformation;
import org.apache.sshd.common.future.KeyExchangeFuture;
import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.io.IoWriteFuture;
import org.apache.sshd.common.kex.KexFactoryManager;
import org.apache.sshd.common.kex.KexProposalOption;
import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.mac.MacInformation;
import org.apache.sshd.common.util.buffer.Buffer;
/**
@@ -116,6 +119,36 @@ public interface Session
String getNegotiatedKexParameter(KexProposalOption paramType);
/**
+ * Retrieves current cipher information - <B>Note:</B> may change if
+ * key re-exchange executed
+ *
+ * @param incoming If {@code true} then the cipher for the incoming data,
+ * otherwise for the outgoing data
+ * @return The {@link CipherInformation} - or {@code null} if not negotiated yet.
+ */
+ CipherInformation getCipherInformation(boolean incoming);
+
+ /**
+ * Retrieves current compression information - <B>Note:</B> may change if
+ * key re-exchange executed
+ *
+ * @param incoming If {@code true} then the compression for the incoming data,
+ * otherwise for the outgoing data
+ * @return The {@link CompressionInformation} - or {@code null} if not negotiated yet.
+ */
+ CompressionInformation getCompressionInformation(boolean incoming);
+
+ /**
+ * Retrieves current MAC information - <B>Note:</B> may change if
+ * key re-exchange executed
+ *
+ * @param incoming If {@code true} then the MAC for the incoming data,
+ * otherwise for the outgoing data
+ * @return The {@link MacInformation} - or {@code null} if not negotiated yet.
+ */
+ MacInformation getMacInformation(boolean incoming);
+
+ /**
* Create a new buffer for the specified SSH packet and reserve the needed space
* (5 bytes) for the packet header.
*
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
index c33353c..266cf93 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
@@ -165,5 +165,4 @@ public class BuiltinCompressionsTest extends BaseTestSupport {
assertNull("Extension not un-registered", BuiltinCompressions.resolveFactory(name));
}
}
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
index a5dfd53..e45c51c 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -33,6 +33,7 @@ import java.util.EnumSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -45,7 +46,6 @@ import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.session.ClientConnectionServiceFactory;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.session.ClientSessionImpl;
-import org.apache.sshd.client.session.SessionFactory;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.PropertyResolverUtils;
@@ -63,6 +63,7 @@ import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.deprecated.ClientUserAuthServiceOld;
import org.apache.sshd.server.command.ScpCommandFactory;
+import org.apache.sshd.server.session.ServerSessionImpl;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.EchoShell;
@@ -84,7 +85,6 @@ import org.slf4j.LoggerFactory;
public class ServerTest extends BaseTestSupport {
private SshServer sshd;
private SshClient client;
- private int port;
public ServerTest() {
super();
@@ -94,9 +94,6 @@ public class ServerTest extends BaseTestSupport {
public void setUp() throws Exception {
sshd = setupTestServer();
sshd.setShellFactory(new TestEchoShellFactory());
- sshd.start();
- port = sshd.getPort();
-
client = setupTestClient();
}
@@ -120,13 +117,14 @@ public class ServerTest extends BaseTestSupport {
final int MAX_AUTH_REQUESTS = 10;
PropertyResolverUtils.updateProperty(sshd, ServerAuthenticationManager.MAX_AUTH_REQUESTS, MAX_AUTH_REQUESTS);
+ sshd.start();
client.setServiceFactories(Arrays.asList(
new ClientUserAuthServiceOld.Factory(),
ClientConnectionServiceFactory.INSTANCE
));
client.start();
- try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, sshd.getPort()).verify(7L, TimeUnit.SECONDS).getSession()) {
int nbTrials = 0;
Collection<ClientSession.ClientSessionEvent> res = Collections.emptySet();
Collection<ClientSession.ClientSessionEvent> mask =
@@ -149,12 +147,14 @@ public class ServerTest extends BaseTestSupport {
final int MAX_AUTH_REQUESTS = 10;
PropertyResolverUtils.updateProperty(sshd, ServerAuthenticationManager.MAX_AUTH_REQUESTS, MAX_AUTH_REQUESTS);
+ sshd.start();
+
client.setServiceFactories(Arrays.asList(
new ClientUserAuthServiceOld.Factory(),
ClientConnectionServiceFactory.INSTANCE
));
client.start();
- try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, sshd.getPort()).verify(7L, TimeUnit.SECONDS).getSession()) {
int nbTrials = 0;
AuthFuture authFuture;
do {
@@ -179,8 +179,9 @@ public class ServerTest extends BaseTestSupport {
final long AUTH_TIMEOUT = TimeUnit.SECONDS.toMillis(5L);
PropertyResolverUtils.updateProperty(sshd, FactoryManager.AUTH_TIMEOUT, AUTH_TIMEOUT);
+ sshd.start();
client.start();
- try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, sshd.getPort()).verify(7L, TimeUnit.SECONDS).getSession()) {
Collection<ClientSession.ClientSessionEvent> res = s.waitFor(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED), 2L * AUTH_TIMEOUT);
assertTrue("Session should be closed: " + res,
res.containsAll(EnumSet.of(ClientSession.ClientSessionEvent.CLOSED, ClientSession.ClientSessionEvent.WAIT_AUTH)));
@@ -216,9 +217,10 @@ public class ServerTest extends BaseTestSupport {
TestChannelListener channelListener = new TestChannelListener();
sshd.addChannelListener(channelListener);
+ sshd.start();
client.start();
- try (ClientSession s = createTestClientSession();
+ try (ClientSession s = createTestClientSession(sshd);
ChannelShell shell = s.createShellChannel();
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream()) {
@@ -282,10 +284,10 @@ public class ServerTest extends BaseTestSupport {
TestChannelListener channelListener = new TestChannelListener();
sshd.addChannelListener(channelListener);
+ sshd.start();
client.start();
-
- try (ClientSession s = createTestClientSession();
+ try (ClientSession s = createTestClientSession(sshd);
ChannelExec shell = s.createExecChannel("normal");
// Create a pipe that will block reading when the buffer is full
PipedInputStream pis = new PipedInputStream();
@@ -328,11 +330,12 @@ public class ServerTest extends BaseTestSupport {
@Test
public void testLanguageNegotiation() throws Exception {
- client.setSessionFactory(new SessionFactory(client) {
+ sshd.start();
+
+ client.setSessionFactory(new org.apache.sshd.client.session.SessionFactory(client) {
@Override
- @SuppressWarnings("synthetic-access")
- protected ClientSessionImpl createSession(IoSession ioSession) throws Exception {
- return new ClientSessionImpl(client, ioSession) {
+ protected ClientSessionImpl doCreateSession(IoSession ioSession) throws Exception {
+ return new ClientSessionImpl(getClient(), ioSession) {
@Override
protected Map<KexProposalOption, String> createProposal(String hostKeyTypes) {
Map<KexProposalOption, String> proposal = super.createProposal(hostKeyTypes);
@@ -344,9 +347,98 @@ public class ServerTest extends BaseTestSupport {
}
});
+ final Semaphore sigSem = new Semaphore(0, true);
+ client.addSessionListener(new SessionListener() {
+ @Override
+ public void sessionCreated(Session session) {
+ outputDebugMessage("Session created: %s", session);
+ }
+
+ @Override
+ public void sessionEvent(Session session, Event event) {
+ if (Event.KeyEstablished.equals(event)) {
+ for (KexProposalOption option : new KexProposalOption[]{ KexProposalOption.S2CLANG, KexProposalOption.C2SLANG}) {
+ assertNull("Unexpected negotiated language for " + option, session.getNegotiatedKexParameter(option));
+ }
+
+ sigSem.release();
+ }
+ }
+
+ @Override
+ public void sessionClosed(Session session) {
+ outputDebugMessage("Session closed: %s", session);
+ }
+ });
+
client.start();
- try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
- // do nothing
+ try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, sshd.getPort()).verify(7L, TimeUnit.SECONDS).getSession()) {
+ assertTrue("Failed to receive signal on time", sigSem.tryAcquire(11L, TimeUnit.SECONDS));
+ } finally {
+ client.stop();
+ }
+ }
+
+ @Test // see SSHD-609
+ public void testCompressionNegotiation() throws Exception {
+ sshd.setSessionFactory(new org.apache.sshd.server.session.SessionFactory(sshd) {
+ @Override
+ protected ServerSessionImpl doCreateSession(IoSession ioSession) throws Exception {
+ return new ServerSessionImpl(getServer(), ioSession) {
+ @Override
+ protected Map<KexProposalOption, String> createProposal(String hostKeyTypes) {
+ Map<KexProposalOption, String> proposal = super.createProposal(hostKeyTypes);
+ proposal.put(KexProposalOption.C2SCOMP, getCurrentTestName());
+ proposal.put(KexProposalOption.S2CCOMP, getCurrentTestName());
+ return proposal;
+ }
+ };
+ }
+ });
+ sshd.start();
+
+ client.setSessionFactory(new org.apache.sshd.client.session.SessionFactory(client) {
+ @Override
+ protected ClientSessionImpl doCreateSession(IoSession ioSession) throws Exception {
+ return new ClientSessionImpl(getClient(), ioSession) {
+ @Override
+ protected Map<KexProposalOption, String> createProposal(String hostKeyTypes) {
+ Map<KexProposalOption, String> proposal = super.createProposal(hostKeyTypes);
+ proposal.put(KexProposalOption.C2SCOMP, getCurrentTestName());
+ proposal.put(KexProposalOption.S2CCOMP, getCurrentTestName());
+ return proposal;
+ }
+ };
+ }
+ });
+
+ final Semaphore sigSem = new Semaphore(0, true);
+ client.addSessionListener(new SessionListener() {
+ @Override
+ public void sessionCreated(Session session) {
+ outputDebugMessage("Session created: %s", session);
+ }
+
+ @Override
+ public void sessionEvent(Session session, Event event) {
+ assertNotEquals("Unexpected key establishment success", Event.KeyEstablished, event);
+ }
+
+ @Override
+ public void sessionClosed(Session session) {
+ sigSem.release();
+ }
+ });
+
+ client.start();
+ try {
+ try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, sshd.getPort()).verify(7L, TimeUnit.SECONDS).getSession()) {
+ assertTrue("Session closing not signalled on time", sigSem.tryAcquire(5L, TimeUnit.SECONDS));
+ for (boolean incoming : new boolean[]{true, false}) {
+ assertNull("Unexpected compression information for incoming=" + incoming, s.getCompressionInformation(incoming));
+ }
+ assertFalse("Session unexpectedly still open", s.isOpen());
+ }
} finally {
client.stop();
}
@@ -373,8 +465,8 @@ public class ServerTest extends BaseTestSupport {
// ignored
}
});
+ sshd.start();
- client.start();
final AtomicInteger clientEventCount = new AtomicInteger(0);
client.addSessionListener(new SessionListener() {
@Override
@@ -394,8 +486,9 @@ public class ServerTest extends BaseTestSupport {
// ignored
}
});
+ client.start();
- try (ClientSession s = createTestClientSession()) {
+ try (ClientSession s = createTestClientSession(sshd)) {
assertEquals("Mismatched client events count", 1, clientEventCount.get());
assertEquals("Mismatched server events count", 1, serverEventCount.get());
s.close(false);
@@ -454,6 +547,7 @@ public class ServerTest extends BaseTestSupport {
TestChannelListener channelListener = new TestChannelListener();
sshd.addChannelListener(channelListener);
+ sshd.start();
@SuppressWarnings("synthetic-access")
Map<String, String> expected = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER) {
@@ -461,13 +555,13 @@ public class ServerTest extends BaseTestSupport {
{
put("test", getCurrentTestName());
- put("port", Integer.toString(port));
+ put("port", Integer.toString(sshd.getPort()));
put("user", OsUtils.getCurrentUser());
}
};
client.start();
- try (ClientSession s = createTestClientSession();
+ try (ClientSession s = createTestClientSession(sshd);
ChannelExec shell = s.createExecChannel(getCurrentTestName())) {
for (Map.Entry<String, String> ee : expected.entrySet()) {
shell.setEnv(ee.getKey(), ee.getValue());
@@ -505,8 +599,8 @@ public class ServerTest extends BaseTestSupport {
}
}
- private ClientSession createTestClientSession() throws Exception {
- ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession();
+ private ClientSession createTestClientSession(SshServer server) throws Exception {
+ ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, server.getPort()).verify(7L, TimeUnit.SECONDS).getSession();
try {
session.addPasswordIdentity(getCurrentTestName());
session.auth().verify(5L, TimeUnit.SECONDS);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/53055cb5/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java
index 09b9ddc..a683afb 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java
@@ -32,6 +32,9 @@ import org.junit.runners.MethodSorters;
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SshServerTest extends BaseTestSupport {
+ public SshServerTest() {
+ super();
+ }
@Test
public void stopMethodShouldBeIdempotent() throws Exception {