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 2016/02/26 06:17:52 UTC
[2/3] mina-sshd git commit: Moved common client/server session code
from the implementation classes to their respective abstract classes
Moved common client/server session code from the implementation classes to their respective abstract classes
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/4c9d3aeb
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/4c9d3aeb
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/4c9d3aeb
Branch: refs/heads/master
Commit: 4c9d3aebecdcc9100148037ddf84354690f2da48
Parents: 7406188
Author: Lyor Goldstein <ly...@gmail.com>
Authored: Fri Feb 26 07:16:39 2016 +0200
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Fri Feb 26 07:16:39 2016 +0200
----------------------------------------------------------------------
.../client/session/AbstractClientSession.java | 139 ++++++++++++
.../sshd/client/session/ClientSessionImpl.java | 191 +++-------------
.../common/session/helpers/AbstractSession.java | 7 +
.../server/session/AbstractServerSession.java | 216 ++++++++++++++++++
.../sshd/server/session/ServerSessionImpl.java | 223 +------------------
5 files changed, 402 insertions(+), 374 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
index 0a8a444..9193cec 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
@@ -23,7 +23,9 @@ import java.io.IOException;
import java.net.SocketAddress;
import java.nio.file.FileSystem;
import java.security.KeyPair;
+import java.util.EnumMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.sshd.client.ClientFactoryManager;
@@ -33,6 +35,7 @@ import org.apache.sshd.client.auth.keyboard.UserInteraction;
import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
import org.apache.sshd.client.channel.ChannelDirectTcpip;
import org.apache.sshd.client.channel.ChannelExec;
+import org.apache.sshd.client.channel.ChannelShell;
import org.apache.sshd.client.channel.ChannelSubsystem;
import org.apache.sshd.client.channel.ClientChannel;
import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
@@ -46,20 +49,32 @@ import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.CipherNone;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.forward.TcpipForwarder;
+import org.apache.sshd.common.future.DefaultKeyExchangeFuture;
+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.KexProposalOption;
+import org.apache.sshd.common.kex.KexState;
import org.apache.sshd.common.scp.ScpFileOpener;
import org.apache.sshd.common.scp.ScpTransferEventListener;
import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.helpers.AbstractConnectionService;
import org.apache.sshd.common.session.helpers.AbstractSession;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.net.SshdSocketAddress;
/**
+ * Provides default implementations of {@link ClientSession} related methods
+ *
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public abstract class AbstractClientSession extends AbstractSession implements ClientSession {
@@ -394,4 +409,128 @@ public abstract class AbstractClientSession extends AbstractSession implements C
public void startService(String name) throws Exception {
throw new IllegalStateException("Starting services is not supported on the client side: " + name);
}
+
+
+ @Override
+ public ChannelShell createShellChannel() throws IOException {
+ if ((inCipher instanceof CipherNone) || (outCipher instanceof CipherNone)) {
+ throw new IllegalStateException("Interactive channels are not supported with none cipher");
+ }
+ ChannelShell channel = new ChannelShell();
+ ConnectionService service = getConnectionService();
+ int id = service.registerChannel(channel);
+ if (log.isDebugEnabled()) {
+ log.debug("createShellChannel({}) created id={}", this, id);
+ }
+ return channel;
+ }
+
+ @Override
+ protected boolean readIdentification(Buffer buffer) throws IOException {
+ serverVersion = doReadIdentification(buffer, false);
+ if (serverVersion == null) {
+ return false;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("readIdentification({}) Server version string: {}", this, serverVersion);
+ }
+
+ if (!(serverVersion.startsWith(DEFAULT_SSH_VERSION_PREFIX) || serverVersion.startsWith("SSH-1.99-"))) {
+ throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+ "Unsupported protocol version: " + serverVersion);
+ }
+
+ return true;
+ }
+
+ @Override
+ protected byte[] sendKexInit(Map<KexProposalOption, String> proposal) throws IOException {
+ mergeProposals(clientProposal, proposal);
+ return super.sendKexInit(proposal);
+ }
+
+ @Override
+ protected void setKexSeed(byte... seed) {
+ i_c = ValidateUtils.checkNotNullAndNotEmpty(seed, "No KEX seed");
+ }
+
+ @Override
+ protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
+ mergeProposals(serverProposal, proposal);
+ i_s = seed;
+ }
+
+ @Override
+ protected void checkKeys() throws SshException {
+ ServerKeyVerifier serverKeyVerifier = ValidateUtils.checkNotNull(getServerKeyVerifier(), "No server key verifier");
+ SocketAddress remoteAddress = ioSession.getRemoteAddress();
+
+ if (!serverKeyVerifier.verifyServerKey(this, remoteAddress, kex.getServerKey())) {
+ throw new SshException(SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, "Server key did not validate");
+ }
+ }
+
+ @Override
+ public KeyExchangeFuture switchToNoneCipher() throws IOException {
+ if (!(currentService instanceof AbstractConnectionService<?>)
+ || !GenericUtils.isEmpty(((AbstractConnectionService<?>) currentService).getChannels())) {
+ throw new IllegalStateException("The switch to the none cipher must be done immediately after authentication");
+ }
+
+ if (kexState.compareAndSet(KexState.DONE, KexState.INIT)) {
+ DefaultKeyExchangeFuture kexFuture = new DefaultKeyExchangeFuture(null);
+ DefaultKeyExchangeFuture prev = kexFutureHolder.getAndSet(kexFuture);
+ if (prev != null) {
+ synchronized (prev) {
+ Object value = prev.getValue();
+ if (value == null) {
+ prev.setValue(new SshException("Switch to none cipher while previous KEX is ongoing"));
+ }
+ }
+ }
+
+ String c2sEncServer;
+ String s2cEncServer;
+ synchronized (serverProposal) {
+ c2sEncServer = serverProposal.get(KexProposalOption.C2SENC);
+ s2cEncServer = serverProposal.get(KexProposalOption.S2CENC);
+ }
+ boolean c2sEncServerNone = BuiltinCiphers.Constants.isNoneCipherIncluded(c2sEncServer);
+ boolean s2cEncServerNone = BuiltinCiphers.Constants.isNoneCipherIncluded(s2cEncServer);
+
+ String c2sEncClient;
+ String s2cEncClient;
+ synchronized (clientProposal) {
+ c2sEncClient = clientProposal.get(KexProposalOption.C2SENC);
+ s2cEncClient = clientProposal.get(KexProposalOption.S2CENC);
+ }
+
+ boolean c2sEncClientNone = BuiltinCiphers.Constants.isNoneCipherIncluded(c2sEncClient);
+ boolean s2cEncClientNone = BuiltinCiphers.Constants.isNoneCipherIncluded(s2cEncClient);
+
+ if ((!c2sEncServerNone) || (!s2cEncServerNone)) {
+ kexFuture.setValue(new SshException("Server does not support none cipher"));
+ } else if ((!c2sEncClientNone) || (!s2cEncClientNone)) {
+ kexFuture.setValue(new SshException("Client does not support none cipher"));
+ } else {
+ log.info("switchToNoneCipher({}) switching", this);
+
+ Map<KexProposalOption, String> proposal = new EnumMap<KexProposalOption, String>(KexProposalOption.class);
+ synchronized (clientProposal) {
+ proposal.putAll(clientProposal);
+ }
+
+ proposal.put(KexProposalOption.C2SENC, BuiltinCiphers.Constants.NONE);
+ proposal.put(KexProposalOption.S2CENC, BuiltinCiphers.Constants.NONE);
+
+ byte[] seed = sendKexInit(proposal);
+ setKexSeed(seed);
+ }
+
+ return ValidateUtils.checkNotNull(kexFutureHolder.get(), "No current KEX future");
+ } else {
+ throw new SshException("In flight key exchange");
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index 2bec6a3..d73a342 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -19,11 +19,9 @@
package org.apache.sshd.client.session;
import java.io.IOException;
-import java.net.SocketAddress;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
@@ -31,31 +29,22 @@ import java.util.Map;
import java.util.Set;
import org.apache.sshd.client.ClientFactoryManager;
-import org.apache.sshd.client.channel.ChannelShell;
import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.future.DefaultAuthFuture;
-import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.common.RuntimeSshException;
import org.apache.sshd.common.Service;
import org.apache.sshd.common.ServiceFactory;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.CipherNone;
-import org.apache.sshd.common.future.DefaultKeyExchangeFuture;
-import org.apache.sshd.common.future.KeyExchangeFuture;
import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.kex.KexProposalOption;
import org.apache.sshd.common.kex.KexState;
-import org.apache.sshd.common.session.ConnectionService;
import org.apache.sshd.common.session.SessionListener;
-import org.apache.sshd.common.session.helpers.AbstractConnectionService;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
/**
- * TODO Add javadoc
+ * The default implementation of a {@link ClientSession}
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@@ -73,10 +62,10 @@ public class ClientSessionImpl extends AbstractClientSession {
private Service nextService;
private ServiceFactory nextServiceFactory;
- public ClientSessionImpl(ClientFactoryManager client, IoSession session) throws Exception {
- super(client, session);
+ public ClientSessionImpl(ClientFactoryManager client, IoSession ioSession) throws Exception {
+ super(client, ioSession);
if (log.isDebugEnabled()) {
- log.debug("Client session created: {}", session);
+ log.debug("Client session created: {}", ioSession);
}
// Need to set the initial service early as calling code likes to start trying to
// manipulate it before the connection has even been established. For instance, to
@@ -103,6 +92,13 @@ public class ClientSessionImpl extends AbstractClientSession {
listener.sessionCreated(this);
} catch (Throwable t) {
Throwable e = GenericUtils.peelException(t);
+ if (log.isDebugEnabled()) {
+ log.debug("Failed ({}) to announce session={} created: {}",
+ e.getClass().getSimpleName(), ioSession, e.getMessage());
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Session=" + ioSession + " creation failure details", e);
+ }
if (e instanceof Exception) {
throw (Exception) e;
} else {
@@ -191,80 +187,34 @@ public class ClientSessionImpl extends AbstractClientSession {
}
@Override
- public KeyExchangeFuture switchToNoneCipher() throws IOException {
- if (!(currentService instanceof AbstractConnectionService<?>)
- || !GenericUtils.isEmpty(((AbstractConnectionService<?>) currentService).getChannels())) {
- throw new IllegalStateException("The switch to the none cipher must be done immediately after authentication");
+ protected void sendSessionEvent(SessionListener.Event event) throws IOException {
+ if (SessionListener.Event.KeyEstablished.equals(event)) {
+ sendInitialServiceRequest();
}
-
- if (kexState.compareAndSet(KexState.DONE, KexState.INIT)) {
- DefaultKeyExchangeFuture kexFuture = new DefaultKeyExchangeFuture(null);
- DefaultKeyExchangeFuture prev = kexFutureHolder.getAndSet(kexFuture);
- if (prev != null) {
- synchronized (prev) {
- Object value = prev.getValue();
- if (value == null) {
- prev.setValue(new SshException("Switch to none cipher while previous KEX is ongoing"));
- }
- }
- }
-
- String c2sEncServer;
- String s2cEncServer;
- synchronized (serverProposal) {
- c2sEncServer = serverProposal.get(KexProposalOption.C2SENC);
- s2cEncServer = serverProposal.get(KexProposalOption.S2CENC);
- }
- boolean c2sEncServerNone = BuiltinCiphers.Constants.isNoneCipherIncluded(c2sEncServer);
- boolean s2cEncServerNone = BuiltinCiphers.Constants.isNoneCipherIncluded(s2cEncServer);
-
- String c2sEncClient;
- String s2cEncClient;
- synchronized (clientProposal) {
- c2sEncClient = clientProposal.get(KexProposalOption.C2SENC);
- s2cEncClient = clientProposal.get(KexProposalOption.S2CENC);
- }
-
- boolean c2sEncClientNone = BuiltinCiphers.Constants.isNoneCipherIncluded(c2sEncClient);
- boolean s2cEncClientNone = BuiltinCiphers.Constants.isNoneCipherIncluded(s2cEncClient);
-
- if ((!c2sEncServerNone) || (!s2cEncServerNone)) {
- kexFuture.setValue(new SshException("Server does not support none cipher"));
- } else if ((!c2sEncClientNone) || (!s2cEncClientNone)) {
- kexFuture.setValue(new SshException("Client does not support none cipher"));
- } else {
- log.info("switchToNoneCipher({}) switching", this);
-
- Map<KexProposalOption, String> proposal = new EnumMap<KexProposalOption, String>(KexProposalOption.class);
- synchronized (clientProposal) {
- proposal.putAll(clientProposal);
- }
-
- proposal.put(KexProposalOption.C2SENC, BuiltinCiphers.Constants.NONE);
- proposal.put(KexProposalOption.S2CENC, BuiltinCiphers.Constants.NONE);
-
- byte[] seed = sendKexInit(proposal);
- setKexSeed(seed);
- }
-
- return ValidateUtils.checkNotNull(kexFutureHolder.get(), "No current KEX future");
- } else {
- throw new SshException("In flight key exchange");
+ synchronized (lock) {
+ lock.notifyAll();
}
+ super.sendSessionEvent(event);
}
- @Override
- public ChannelShell createShellChannel() throws IOException {
- if ((inCipher instanceof CipherNone) || (outCipher instanceof CipherNone)) {
- throw new IllegalStateException("Interactive channels are not supported with none cipher");
+ protected void sendInitialServiceRequest() throws IOException {
+ if (initialServiceRequestSent) {
+ return;
}
- ChannelShell channel = new ChannelShell();
- ConnectionService service = getConnectionService();
- int id = service.registerChannel(channel);
+ initialServiceRequestSent = true;
+ String serviceName = currentServiceFactory.getName();
if (log.isDebugEnabled()) {
- log.debug("createShellChannel({}) created id={}", this, id);
+ log.debug("sendInitialServiceRequest({}) Send SSH_MSG_SERVICE_REQUEST for {}", this, serviceName);
}
- return channel;
+
+ Buffer request = createBuffer(SshConstants.SSH_MSG_SERVICE_REQUEST, serviceName.length() + Byte.SIZE);
+ request.putString(serviceName);
+ writePacket(request);
+ // Assuming that MINA-SSHD only implements "explicit server authentication" it is permissible
+ // for the client's service to start sending data before the service-accept has been received.
+ // If "implicit authentication" were to ever be supported, then this would need to be
+ // called after service-accept comes back. See SSH-TRANSPORT.
+ currentService.start();
}
@Override
@@ -335,83 +285,6 @@ public class ClientSessionImpl extends AbstractClientSession {
}
@Override
- protected boolean readIdentification(Buffer buffer) throws IOException {
- serverVersion = doReadIdentification(buffer, false);
- if (serverVersion == null) {
- return false;
- }
-
- if (log.isDebugEnabled()) {
- log.debug("readIdentification({}) Server version string: {}", this, serverVersion);
- }
-
- if (!(serverVersion.startsWith(DEFAULT_SSH_VERSION_PREFIX) || serverVersion.startsWith("SSH-1.99-"))) {
- throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
- "Unsupported protocol version: " + serverVersion);
- }
-
- return true;
- }
-
- @Override
- protected byte[] sendKexInit(Map<KexProposalOption, String> proposal) throws IOException {
- mergeProposals(clientProposal, proposal);
- return super.sendKexInit(proposal);
- }
-
- @Override
- protected void setKexSeed(byte... seed) {
- i_c = ValidateUtils.checkNotNullAndNotEmpty(seed, "No KEX seed");
- }
-
- @Override
- protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
- mergeProposals(serverProposal, proposal);
- i_s = seed;
- }
-
- @Override
- protected void checkKeys() throws SshException {
- ServerKeyVerifier serverKeyVerifier = ValidateUtils.checkNotNull(getServerKeyVerifier(), "No server key verifier");
- SocketAddress remoteAddress = ioSession.getRemoteAddress();
-
- if (!serverKeyVerifier.verifyServerKey(this, remoteAddress, kex.getServerKey())) {
- throw new SshException(SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, "Server key did not validate");
- }
- }
-
- @Override
- protected void sendSessionEvent(SessionListener.Event event) throws IOException {
- if (SessionListener.Event.KeyEstablished.equals(event)) {
- sendInitialServiceRequest();
- }
- synchronized (lock) {
- lock.notifyAll();
- }
- super.sendSessionEvent(event);
- }
-
- protected void sendInitialServiceRequest() throws IOException {
- if (initialServiceRequestSent) {
- return;
- }
- initialServiceRequestSent = true;
- String serviceName = currentServiceFactory.getName();
- if (log.isDebugEnabled()) {
- log.debug("sendInitialServiceRequest({}) Send SSH_MSG_SERVICE_REQUEST for {}", this, serviceName);
- }
-
- Buffer request = createBuffer(SshConstants.SSH_MSG_SERVICE_REQUEST, serviceName.length() + Byte.SIZE);
- request.putString(serviceName);
- writePacket(request);
- // Assuming that MINA-SSHD only implements "explicit server authentication" it is permissible
- // for the client's service to start sending data before the service-accept has been received.
- // If "implicit authentication" were to ever be supported, then this would need to be
- // called after service-accept comes back. See SSH-TRANSPORT.
- currentService.start();
- }
-
- @Override
public Map<Object, Object> getMetadataMap() {
return metadataMap;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index 1627cf0..4b6488f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -1976,6 +1976,13 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
listener.sessionEvent(this, event);
} catch (Throwable e) {
Throwable t = GenericUtils.peelException(e);
+ if (log.isDebugEnabled()) {
+ log.debug("sendSessionEvent({})[{}] failed ({}) to inform listeners: {}",
+ this, event, t.getClass().getSimpleName(), t.getMessage());
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("sendSessionEvent(" + this + ")[" + event + "] listener inform details", t);
+ }
if (t instanceof IOException) {
throw (IOException) t;
} else if (t instanceof RuntimeException) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
index 1008ec4..137827b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
@@ -19,12 +19,33 @@
package org.apache.sshd.server.session;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.ServiceFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.IoService;
import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.kex.KexProposalOption;
+import org.apache.sshd.common.kex.KexState;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.session.helpers.AbstractSession;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.server.ServerFactoryManager;
import org.apache.sshd.server.auth.UserAuth;
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
@@ -34,6 +55,8 @@ import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
/**
+ * Provides default implementations for {@link ServerSession} related methods
+ *
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public abstract class AbstractServerSession extends AbstractSession implements ServerSession {
@@ -122,4 +145,197 @@ public abstract class AbstractServerSession extends AbstractSession implements S
protected void checkKeys() {
// nothing
}
+
+ @Override
+ public void startService(String name) throws Exception {
+ currentService = ServiceFactory.Utils.create(
+ getFactoryManager().getServiceFactories(),
+ ValidateUtils.checkNotNullAndNotEmpty(name, "No service name"),
+ this);
+ /*
+ * According to RFC4253:
+ *
+ * If the server rejects the service request, it SHOULD send an
+ * appropriate SSH_MSG_DISCONNECT message and MUST disconnect.
+ */
+ if (currentService == null) {
+ throw new SshException(SshConstants.SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE, "Unknown service: " + name);
+ }
+ }
+
+ @Override
+ protected void serviceAccept() throws IOException {
+ // TODO: can services be initiated by the server-side ?
+ disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Unsupported packet: SSH_MSG_SERVICE_ACCEPT");
+ }
+
+ @Override
+ protected byte[] sendKexInit(Map<KexProposalOption, String> proposal) throws IOException {
+ mergeProposals(serverProposal, proposal);
+ return super.sendKexInit(proposal);
+ }
+
+ @Override
+ protected void setKexSeed(byte... seed) {
+ i_s = ValidateUtils.checkNotNullAndNotEmpty(seed, "No KEX seed");
+ }
+
+ @Override
+ protected String resolveAvailableSignaturesProposal(FactoryManager proposedManager) {
+ /*
+ * Make sure we can provide key(s) for the available signatures
+ */
+ ValidateUtils.checkTrue(proposedManager == getFactoryManager(), "Mismatched signatures proposed factory manager");
+
+ KeyPairProvider kpp = getKeyPairProvider();
+ Collection<String> supported = NamedResource.Utils.getNameList(getSignatureFactories());
+ Iterable<String> provided;
+ try {
+ provided = (kpp == null) ? null : kpp.getKeyTypes();
+ } catch (Error e) {
+ log.warn("resolveAvailableSignaturesProposal({}) failed ({}) to get key types: {}",
+ this, e.getClass().getSimpleName(), e.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("resolveAvailableSignaturesProposal(" + this + ") fetch key types failure details", e);
+ }
+
+ throw new RuntimeSshException(e);
+ }
+
+ if ((provided == null) || GenericUtils.isEmpty(supported)) {
+ return resolveEmptySignaturesProposal(supported, provided);
+ }
+
+ StringBuilder resolveKeys = null;
+ for (String keyType : provided) {
+ if (!supported.contains(keyType)) {
+ if (log.isDebugEnabled()) {
+ log.debug("resolveAvailableSignaturesProposal({})[{}] {} not in suppored list: {}",
+ this, provided, keyType, supported);
+ }
+ continue;
+ }
+
+ if (resolveKeys == null) {
+ resolveKeys = new StringBuilder(supported.size() * 16 /* ecdsa-sha2-xxxx */);
+ }
+
+ if (resolveKeys.length() > 0) {
+ resolveKeys.append(',');
+ }
+
+ resolveKeys.append(keyType);
+ }
+
+ if (GenericUtils.isEmpty(resolveKeys)) {
+ return resolveEmptySignaturesProposal(supported, provided);
+ } else {
+ return resolveKeys.toString();
+ }
+ }
+
+ /**
+ * Called by {@link #resolveAvailableSignaturesProposal(FactoryManager)}
+ * if none of the provided keys is supported - last chance for the derived
+ * implementation to do something
+ *
+ * @param supported The supported key types - may be {@code null}/empty
+ * @param provided The available signature types - may be {@code null}/empty
+ * @return The resolved proposal - {@code null} by default
+ */
+ protected String resolveEmptySignaturesProposal(Iterable<String> supported, Iterable<String> provided) {
+ if (log.isDebugEnabled()) {
+ log.debug("resolveEmptySignaturesProposal({})[{}] none of the keys appears in supported list: {}",
+ this, provided, supported);
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean readIdentification(Buffer buffer) throws IOException {
+ clientVersion = doReadIdentification(buffer, true);
+ if (GenericUtils.isEmpty(clientVersion)) {
+ return false;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("readIdentification({}) client version string: {}", this, clientVersion);
+ }
+
+ if (!clientVersion.startsWith(DEFAULT_SSH_VERSION_PREFIX)) {
+ String msg = "Unsupported protocol version: " + clientVersion;
+ ioSession.write(new ByteArrayBuffer((msg + "\n").getBytes(StandardCharsets.UTF_8)))
+ .addListener(new SshFutureListener<IoWriteFuture>() {
+ @Override
+ public void operationComplete(IoWriteFuture future) {
+ close(true);
+ }
+ });
+ throw new SshException(msg);
+ } else {
+ kexState.set(KexState.INIT);
+ sendKexInit();
+ }
+ return true;
+ }
+
+ @Override
+ protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
+ mergeProposals(clientProposal, proposal);
+ i_c = seed;
+ }
+
+ @Override
+ public KeyPair getHostKey() {
+ String keyType = getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
+ KeyPairProvider provider = ValidateUtils.checkNotNull(getKeyPairProvider(), "No host keys provider");
+ try {
+ return provider.loadKey(keyType);
+ } catch (Error e) {
+ log.warn("getHostKey({}) failed ({}) to load key of type={}: {}",
+ this, e.getClass().getSimpleName(), keyType, e.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("getHostKey(" + this + ") " + keyType + " key load failure details", e);
+ }
+
+ throw new RuntimeSshException(e);
+ }
+ }
+
+ @Override
+ public int getActiveSessionCountForUser(String userName) {
+ if (GenericUtils.isEmpty(userName)) {
+ return 0;
+ }
+
+ IoService service = ioSession.getService();
+ Map<?, IoSession> sessionsMap = service.getManagedSessions();
+ if (GenericUtils.isEmpty(sessionsMap)) {
+ return 0;
+ }
+
+ int totalCount = 0;
+ for (IoSession is : sessionsMap.values()) {
+ ServerSession session = (ServerSession) getSession(is, true);
+ if (session == null) {
+ continue;
+ }
+
+ String sessionUser = session.getUsername();
+ if ((!GenericUtils.isEmpty(sessionUser)) && Objects.equals(sessionUser, userName)) {
+ totalCount++;
+ }
+ }
+
+ return totalCount;
+ }
+
+ /**
+ * Returns the session id.
+ *
+ * @return The session id.
+ */
+ public long getId() {
+ return ioSession.getId();
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4c9d3aeb/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
index ea8d658..2b3f54a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
@@ -18,35 +18,14 @@
*/
package org.apache.sshd.server.session;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.RuntimeSshException;
-import org.apache.sshd.common.ServiceFactory;
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.io.IoService;
import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.io.IoWriteFuture;
-import org.apache.sshd.common.kex.KexProposalOption;
-import org.apache.sshd.common.kex.KexState;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.session.SessionListener;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.server.ServerFactoryManager;
/**
- * TODO Add javadoc
+ * The default implementation for a {@link ServerSession}
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@@ -64,6 +43,13 @@ public class ServerSessionImpl extends AbstractServerSession {
listener.sessionCreated(this);
} catch (Throwable t) {
Throwable e = GenericUtils.peelException(t);
+ if (log.isDebugEnabled()) {
+ log.debug("Failed ({}) to announce session={} created: {}",
+ e.getClass().getSimpleName(), ioSession, e.getMessage());
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Session=" + ioSession + " creation failure details", e);
+ }
if (e instanceof Exception) {
throw (Exception) e;
} else {
@@ -73,197 +59,4 @@ public class ServerSessionImpl extends AbstractServerSession {
sendServerIdentification();
}
-
- @Override
- public void startService(String name) throws Exception {
- currentService = ServiceFactory.Utils.create(
- getFactoryManager().getServiceFactories(),
- ValidateUtils.checkNotNullAndNotEmpty(name, "No service name"),
- this);
- /*
- * According to RFC4253:
- *
- * If the server rejects the service request, it SHOULD send an
- * appropriate SSH_MSG_DISCONNECT message and MUST disconnect.
- */
- if (currentService == null) {
- throw new SshException(SshConstants.SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE, "Unknown service: " + name);
- }
- }
-
- @Override
- protected void serviceAccept() throws IOException {
- // TODO: can services be initiated by the server-side ?
- disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Unsupported packet: SSH_MSG_SERVICE_ACCEPT");
- }
-
- @Override
- protected byte[] sendKexInit(Map<KexProposalOption, String> proposal) throws IOException {
- mergeProposals(serverProposal, proposal);
- return super.sendKexInit(proposal);
- }
-
- @Override
- protected void setKexSeed(byte... seed) {
- i_s = ValidateUtils.checkNotNullAndNotEmpty(seed, "No KEX seed");
- }
-
- @Override
- protected String resolveAvailableSignaturesProposal(FactoryManager proposedManager) {
- /*
- * Make sure we can provide key(s) for the available signatures
- */
- ValidateUtils.checkTrue(proposedManager == getFactoryManager(), "Mismatched signatures proposed factory manager");
-
- KeyPairProvider kpp = getKeyPairProvider();
- Collection<String> supported = NamedResource.Utils.getNameList(getSignatureFactories());
- Iterable<String> provided;
- try {
- provided = (kpp == null) ? null : kpp.getKeyTypes();
- } catch (Error e) {
- log.warn("resolveAvailableSignaturesProposal({}) failed ({}) to get key types: {}",
- this, e.getClass().getSimpleName(), e.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("resolveAvailableSignaturesProposal(" + this + ") fetch key types failure details", e);
- }
-
- throw new RuntimeSshException(e);
- }
-
- if ((provided == null) || GenericUtils.isEmpty(supported)) {
- return resolveEmptySignaturesProposal(supported, provided);
- }
-
- StringBuilder resolveKeys = null;
- for (String keyType : provided) {
- if (!supported.contains(keyType)) {
- if (log.isDebugEnabled()) {
- log.debug("resolveAvailableSignaturesProposal({})[{}] {} not in suppored list: {}",
- this, provided, keyType, supported);
- }
- continue;
- }
-
- if (resolveKeys == null) {
- resolveKeys = new StringBuilder(supported.size() * 16 /* ecdsa-sha2-xxxx */);
- }
-
- if (resolveKeys.length() > 0) {
- resolveKeys.append(',');
- }
-
- resolveKeys.append(keyType);
- }
-
- if (GenericUtils.isEmpty(resolveKeys)) {
- return resolveEmptySignaturesProposal(supported, provided);
- } else {
- return resolveKeys.toString();
- }
- }
-
- /**
- * Called by {@link #resolveAvailableSignaturesProposal(FactoryManager)}
- * if none of the provided keys is supported - last chance for the derived
- * implementation to do something
- *
- * @param supported The supported key types - may be {@code null}/empty
- * @param provided The available signature types - may be {@code null}/empty
- * @return The resolved proposal - {@code null} by default
- */
- protected String resolveEmptySignaturesProposal(Iterable<String> supported, Iterable<String> provided) {
- if (log.isDebugEnabled()) {
- log.debug("resolveEmptySignaturesProposal({})[{}] none of the keys appears in supported list: {}",
- this, provided, supported);
- }
- return null;
- }
-
- @Override
- protected boolean readIdentification(Buffer buffer) throws IOException {
- clientVersion = doReadIdentification(buffer, true);
- if (GenericUtils.isEmpty(clientVersion)) {
- return false;
- }
-
- if (log.isDebugEnabled()) {
- log.debug("readIdentification({}) client version string: {}", this, clientVersion);
- }
-
- if (!clientVersion.startsWith(DEFAULT_SSH_VERSION_PREFIX)) {
- String msg = "Unsupported protocol version: " + clientVersion;
- ioSession.write(new ByteArrayBuffer((msg + "\n").getBytes(StandardCharsets.UTF_8)))
- .addListener(new SshFutureListener<IoWriteFuture>() {
- @Override
- public void operationComplete(IoWriteFuture future) {
- close(true);
- }
- });
- throw new SshException(msg);
- } else {
- kexState.set(KexState.INIT);
- sendKexInit();
- }
- return true;
- }
-
- @Override
- protected void receiveKexInit(Map<KexProposalOption, String> proposal, byte[] seed) throws IOException {
- mergeProposals(clientProposal, proposal);
- i_c = seed;
- }
-
- @Override
- public KeyPair getHostKey() {
- String keyType = getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
- KeyPairProvider provider = ValidateUtils.checkNotNull(getKeyPairProvider(), "No host keys provider");
- try {
- return provider.loadKey(keyType);
- } catch (Error e) {
- log.warn("getHostKey({}) failed ({}) to load key of type={}: {}",
- this, e.getClass().getSimpleName(), keyType, e.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("getHostKey(" + this + ") " + keyType + " key load failure details", e);
- }
-
- throw new RuntimeSshException(e);
- }
- }
-
- @Override
- public int getActiveSessionCountForUser(String userName) {
- if (GenericUtils.isEmpty(userName)) {
- return 0;
- }
-
- IoService service = ioSession.getService();
- Map<?, IoSession> sessionsMap = service.getManagedSessions();
- if (GenericUtils.isEmpty(sessionsMap)) {
- return 0;
- }
-
- int totalCount = 0;
- for (IoSession is : sessionsMap.values()) {
- ServerSession session = (ServerSession) getSession(is, true);
- if (session == null) {
- continue;
- }
-
- String sessionUser = session.getUsername();
- if ((!GenericUtils.isEmpty(sessionUser)) && Objects.equals(sessionUser, userName)) {
- totalCount++;
- }
- }
-
- return totalCount;
- }
-
- /**
- * Returns the session id.
- *
- * @return The session id.
- */
- public long getId() {
- return ioSession.getId();
- }
}