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 2018/09/06 16:03:58 UTC
[48/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
new file mode 100644
index 0000000..6677685
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
@@ -0,0 +1,482 @@
+/*
+ * 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.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.TreeMap;
+
+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 final class PropertyResolverUtils {
+ private PropertyResolverUtils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ /**
+ * @param resolver The {@link PropertyResolver} instance - ignored if {@code null}
+ * @param name The property name
+ * @param defaultValue The default value to return if the specified property
+ * does not exist in the properties map
+ * @return The resolved property
+ * @throws NumberFormatException if malformed value
+ * @see #toLong(Object, long)
+ */
+ public static long getLongProperty(PropertyResolver resolver, String name, long defaultValue) {
+ return toLong(resolvePropertyValue(resolver, name), defaultValue);
+ }
+
+ public static long getLongProperty(Map<String, ?> props, String name, long defaultValue) {
+ return toLong(resolvePropertyValue(props, name), defaultValue);
+ }
+
+ /**
+ * Converts a generic object value to a {@code long} if possible:
+ * <UL>
+ * <LI>
+ * If value is {@code null} the default is returned
+ * </LI>
+ *
+ * <LI>
+ * If value is a {@link Number} then its {@link Number#longValue()} is returned
+ * </LI>
+ *
+ * <LI>
+ * Otherwise, the value's {@link #toString()} is parsed as a {@code long}
+ * </LI>
+ * </UL>
+ *
+ * @param value The resolved value - may be {@code null}
+ * @param defaultValue The default to use if {@code null} resolved value
+ * @return The resolved value
+ * @throws NumberFormatException if malformed value
+ * @see Long#parseLong(String)
+ */
+ public static long toLong(Object value, long defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ } else if (value instanceof Number) {
+ return ((Number) value).longValue();
+ } else { // we parse the string in case it is not a valid long value
+ return Long.parseLong(value.toString());
+ }
+ }
+
+ /**
+ * @param resolver The {@link PropertyResolver} instance - ignored if {@code null}
+ * @param name The property name
+ * @return The {@link Long} value or {@code null} if property not found
+ * @throws NumberFormatException if malformed value
+ * @see #toLong(Object)
+ */
+ public static Long getLong(PropertyResolver resolver, String name) {
+ return toLong(resolvePropertyValue(resolver, name));
+ }
+
+ public static Long getLong(Map<String, ?> props, String name) {
+ return toLong(resolvePropertyValue(props, name));
+ }
+
+ /**
+ * Converts a generic object into a {@link Long}:
+ * <UL>
+ * <LI>
+ * If the value is {@code null} then returns {@code null}.
+ * </LI>
+ *
+ * <LI>
+ * If the value is already a {@link Long} then it is returned as such.
+ * </LI>
+
+ * <LI>
+ * If value is a {@link Number} then its {@link Number#longValue()} is
+ * wrapped as a {@link Long}
+ * </LI>
+ *
+ * <LI>
+ * Otherwise, the value's {@link #toString()} is parsed as a {@link Long}
+ * </LI>
+ * </UL>
+ *
+ * @param value The resolved value - may be {@code null}
+ * @return The {@link Long} value or {@code null} if property not found
+ * @throws NumberFormatException if malformed value
+ * @see Long#valueOf(long)
+ * @see Long#valueOf(String)
+ */
+ public static Long toLong(Object value) {
+ if (value == null) {
+ return null;
+ } else if (value instanceof Long) {
+ return (Long) value;
+ } else if (value instanceof Number) {
+ return ((Number) value).longValue();
+ } else { // we parse the string in case it is not a valid long value
+ return Long.valueOf(value.toString());
+ }
+ }
+
+ /**
+ * Converts an enumerated configuration value:
+ * <UL>
+ * <P><LI>
+ * If value is {@code null} then return {@code null}
+ * </LI></P>
+ *
+ * <P><LI>
+ * If value already of the expected type then simply
+ * cast and return it.
+ * </LI></P>
+ *
+ * <P><LI>
+ * If value is a {@link CharSequence} then convert it
+ * to a string and look for a matching enumerated value
+ * name - case <U>insensitive</U>.
+ * </LI></P>>
+ * </UL>
+ *
+ * @param <E> Type of enumerated value
+ * @param enumType The enumerated class type
+ * @param value The configured value - ignored if {@code null}
+ * @param failIfNoMatch Whether to fail if no matching name found
+ * @param available The available values to compare the name
+ * @return The matching enumerated value - {@code null} if no match found
+ * @throws IllegalArgumentException If value is neither {@code null},
+ * nor the enumerated type nor a {@link CharSequence}
+ * @throws NoSuchElementException If no matching string name found and
+ * <tt>failIfNoMatch</tt> is {@code true}
+ */
+ public static <E extends Enum<E>> E toEnum(Class<E> enumType, Object value, boolean failIfNoMatch, Collection<E> available) {
+ if (value == null) {
+ return null;
+ } else if (enumType.isInstance(value)) {
+ return enumType.cast(value);
+ } else if (value instanceof CharSequence) {
+ String name = value.toString();
+ if (GenericUtils.size(available) > 0) {
+ for (E v : available) {
+ if (name.equalsIgnoreCase(v.name())) {
+ return v;
+ }
+ }
+ }
+
+ if (failIfNoMatch) {
+ throw new NoSuchElementException("No match found for " + enumType.getSimpleName() + "[" + name + "]");
+ }
+
+ return null;
+ } else {
+ throw new IllegalArgumentException("Bad value type for enum conversion: " + value.getClass().getSimpleName());
+ }
+ }
+
+ public static Object updateProperty(PropertyResolver resolver, String name, long value) {
+ return updateProperty(resolver.getProperties(), name, value);
+ }
+
+ public static Object updateProperty(Map<String, Object> props, String name, long value) {
+ return updateProperty(props, name, Long.valueOf(value));
+ }
+
+ public static int getIntProperty(PropertyResolver resolver, String name, int defaultValue) {
+ return toInteger(resolvePropertyValue(resolver, name), defaultValue);
+ }
+
+ public static int getIntProperty(Map<String, ?> props, String name, int defaultValue) {
+ return toInteger(resolvePropertyValue(props, name), defaultValue);
+ }
+
+ public static int toInteger(Object value, int defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ } else if (value instanceof Number) {
+ return ((Number) value).intValue();
+ } else { // we parse the string in case this is NOT an integer
+ return Integer.parseInt(value.toString());
+ }
+ }
+
+ public static Integer getInteger(PropertyResolver resolver, String name) {
+ return toInteger(resolvePropertyValue(resolver, name));
+ }
+
+ public static Integer getInteger(Map<String, ?> props, String name) {
+ return toInteger(resolvePropertyValue(props, name));
+ }
+
+ public static Integer toInteger(Object value) {
+ if (value == null) {
+ return null;
+ } else if (value instanceof Integer) {
+ return (Integer) value;
+ } else if (value instanceof Number) {
+ return ((Number) value).intValue();
+ } else { // we parse the string in case this is NOT an integer
+ return Integer.valueOf(value.toString());
+ }
+ }
+
+ public static Object updateProperty(PropertyResolver resolver, String name, int value) {
+ return updateProperty(resolver.getProperties(), name, value);
+ }
+
+ public static Object updateProperty(Map<String, Object> props, String name, int value) {
+ return updateProperty(props, name, Integer.valueOf(value));
+ }
+
+ public static boolean getBooleanProperty(PropertyResolver resolver, String name, boolean defaultValue) {
+ return toBoolean(getObject(resolver, name), defaultValue);
+ }
+
+ public static boolean getBooleanProperty(Map<String, ?> props, String name, boolean defaultValue) {
+ return toBoolean(getObject(props, name), defaultValue);
+ }
+
+ public static boolean toBoolean(Object value, boolean defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return toBoolean(value);
+ }
+ }
+
+ public static Boolean getBoolean(PropertyResolver resolver, String name) {
+ return toBoolean(resolvePropertyValue(resolver, name));
+ }
+
+ public static Boolean getBoolean(Map<String, ?> props, String name) {
+ return toBoolean(resolvePropertyValue(props, name));
+ }
+
+ public static Boolean toBoolean(Object value) {
+ if (value == null) {
+ return null;
+ } else if (value instanceof Boolean) {
+ return (Boolean) value;
+ } else {
+ return Boolean.valueOf(value.toString());
+ }
+ }
+
+ public static Object updateProperty(PropertyResolver resolver, String name, boolean value) {
+ return updateProperty(resolver.getProperties(), name, value);
+ }
+
+ public static Object updateProperty(Map<String, Object> props, String name, boolean value) {
+ return updateProperty(props, name, Boolean.valueOf(value));
+ }
+
+ /**
+ * @param resolver The {@link PropertyResolver} to use - ignored if {@code null}
+ * @param name The property name
+ * @param defaultValue The default value to return if property not set or empty
+ * @return The set value (if not {@code null}/empty) or default one
+ */
+ public static String getStringProperty(PropertyResolver resolver, String name, String defaultValue) {
+ String value = getString(resolver, name);
+ if (GenericUtils.isEmpty(value)) {
+ return defaultValue;
+ } else {
+ return value;
+ }
+ }
+
+ public static String getStringProperty(Map<String, ?> props, String name, String defaultValue) {
+ Object value = resolvePropertyValue(props, name);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return Objects.toString(value);
+ }
+ }
+
+ public static Charset getCharset(PropertyResolver resolver, String name, Charset defaultValue) {
+ Object value = getObject(resolver, name);
+ return (value == null) ? defaultValue : toCharset(value);
+ }
+
+ public static Charset getCharset(Map<String, ?> props, String name, Charset defaultValue) {
+ Object value = getObject(props, name);
+ return (value == null) ? defaultValue : toCharset(value);
+ }
+
+ public static Charset toCharset(Object value) {
+ if (value == null) {
+ return null;
+ } else if (value instanceof Charset) {
+ return (Charset) value;
+ } else if (value instanceof CharSequence) {
+ return Charset.forName(value.toString());
+ } else {
+ throw new IllegalArgumentException("Invalid charset conversion value: " + value);
+ }
+ }
+
+ public static String getString(PropertyResolver resolver, String name) {
+ Object value = getObject(resolver, name);
+ return Objects.toString(value, null);
+ }
+
+ public static String getString(Map<String, ?> props, String name) {
+ Object value = getObject(props, name);
+ return Objects.toString(value, null);
+ }
+
+ public static Object getObject(PropertyResolver resolver, String name) {
+ return resolvePropertyValue(resolver, name);
+ }
+
+ // for symmetrical reasons...
+ public static Object getObject(Map<String, ?> props, String name) {
+ return resolvePropertyValue(props, name);
+ }
+
+ public static Object resolvePropertyValue(Map<String, ?> props, String name) {
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ return props != null ? props.get(key) : null;
+ }
+
+ /**
+ * @param resolver The {@link PropertyResolver} instance
+ * @param name The property name
+ * @param value The new value - if {@code null} or an empty {@link CharSequence}
+ * the property is <U>removed</U>
+ * @return The previous value - {@code null} if none
+ */
+ public static Object updateProperty(PropertyResolver resolver, String name, Object value) {
+ return updateProperty(resolver.getProperties(), name, value);
+ }
+
+ public static Object updateProperty(Map<String, Object> props, String name, Object value) {
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ if ((value == null) || ((value instanceof CharSequence) && GenericUtils.isEmpty((CharSequence) value))) {
+ return props.remove(key);
+ } else {
+ return props.put(key, value);
+ }
+ }
+
+ /**
+ * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
+ * for the requested property or reached top. If still no value found and the key
+ * starts with "org.apache.sshd" then the system properties are also
+ * consulted
+ *
+ * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
+ * @param name The requested property name
+ * @return The found value or {@code null}
+ */
+ public static Object resolvePropertyValue(PropertyResolver resolver, String name) {
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ for (PropertyResolver r = resolver; r != null; r = r.getParentPropertyResolver()) {
+ Map<String, ?> props = r.getProperties();
+ if (props != null) {
+ Object value = props.get(key);
+ if (value != null) {
+ return value;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Unwinds the resolvers hierarchy until found one with a non-{@code null} value
+ * for the requested property or reached top.
+ *
+ * @param resolver The {@link PropertyResolver} to start from - ignored if {@code null}
+ * @param name The requested property name
+ * @return The found properties {@link Map} or {@code null}
+ */
+ public static Map<String, Object> resolvePropertiesSource(PropertyResolver resolver, String name) {
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ for (PropertyResolver r = resolver; r != null; r = r.getParentPropertyResolver()) {
+ Map<String, Object> props = r.getProperties();
+ if (props != null) {
+ Object value = props.get(key);
+ if (value != null) {
+ return props;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static PropertyResolver toPropertyResolver(Properties props) {
+ if (GenericUtils.isEmpty(props)) {
+ return PropertyResolver.EMPTY;
+ }
+
+ Map<String, Object> propsMap = new TreeMap<>(Comparator.naturalOrder());
+ Collection<String> names = props.stringPropertyNames();
+ for (String key : names) {
+ String value = props.getProperty(key);
+ if (value == null) {
+ continue;
+ }
+ propsMap.put(key, value);
+ }
+
+ return toPropertyResolver(propsMap);
+ }
+
+ /**
+ * Wraps a {@link Map} into a {@link PropertyResolver} so it can be used
+ * with these utilities
+ *
+ * @param props The properties map - may be {@code null}/empty if no properties
+ * are updated
+ * @return The resolver wrapper
+ */
+ public static PropertyResolver toPropertyResolver(Map<String, Object> props) {
+ return toPropertyResolver(props, null);
+ }
+
+ public static PropertyResolver toPropertyResolver(Map<String, Object> props, PropertyResolver parent) {
+ return new PropertyResolver() {
+ @Override
+ public PropertyResolver getParentPropertyResolver() {
+ return parent;
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return props;
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toString(props);
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java b/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
new file mode 100644
index 0000000..8c9164f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/RuntimeSshException.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+ * Exception used in the SSH client or server.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RuntimeSshException extends RuntimeException {
+ private static final long serialVersionUID = -2423550196146939503L;
+
+ public RuntimeSshException() {
+ this(null, null);
+ }
+
+ public RuntimeSshException(String message) {
+ this(message, null);
+ }
+
+ public RuntimeSshException(Throwable cause) {
+ this(null, cause);
+ }
+
+ public RuntimeSshException(String message, Throwable cause) {
+ super(message);
+ if (cause != null) {
+ initCause(cause);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
new file mode 100644
index 0000000..01e8ae7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
@@ -0,0 +1,245 @@
+/*
+ * 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.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.LoggingUtils;
+
+/**
+ * This interface defines constants for the SSH protocol.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SshConstants {
+ //
+ // SSH message identifiers
+ //
+
+ public static final byte SSH_MSG_DISCONNECT = 1;
+ public static final byte SSH_MSG_IGNORE = 2;
+ public static final byte SSH_MSG_UNIMPLEMENTED = 3;
+ public static final byte SSH_MSG_DEBUG = 4;
+ public static final byte SSH_MSG_SERVICE_REQUEST = 5;
+ public static final byte SSH_MSG_SERVICE_ACCEPT = 6;
+ public static final byte SSH_MSG_KEXINIT = 20;
+ public static final int MSG_KEX_COOKIE_SIZE = 16;
+ public static final byte SSH_MSG_NEWKEYS = 21;
+
+ public static final byte SSH_MSG_KEX_FIRST = 30;
+ public static final byte SSH_MSG_KEX_LAST = 49;
+
+ public static final byte SSH_MSG_KEXDH_INIT = 30;
+ public static final byte SSH_MSG_KEXDH_REPLY = 31;
+
+ public static final byte SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
+ public static final byte SSH_MSG_KEX_DH_GEX_GROUP = 31;
+ public static final byte SSH_MSG_KEX_DH_GEX_INIT = 32;
+ public static final byte SSH_MSG_KEX_DH_GEX_REPLY = 33;
+ public static final byte SSH_MSG_KEX_DH_GEX_REQUEST = 34;
+
+ public static final byte SSH_MSG_USERAUTH_REQUEST = 50;
+ public static final byte SSH_MSG_USERAUTH_FAILURE = 51;
+ public static final byte SSH_MSG_USERAUTH_SUCCESS = 52;
+ public static final byte SSH_MSG_USERAUTH_BANNER = 53;
+
+ public static final byte SSH_MSG_USERAUTH_INFO_REQUEST = 60;
+ public static final byte SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
+
+ public static final byte SSH_MSG_USERAUTH_PK_OK = 60;
+
+ public static final byte SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60;
+
+ public static final byte SSH_MSG_USERAUTH_GSSAPI_MIC = 66;
+
+ public static final byte SSH_MSG_GLOBAL_REQUEST = 80;
+ public static final byte SSH_MSG_REQUEST_SUCCESS = 81;
+ public static final byte SSH_MSG_REQUEST_FAILURE = 82;
+ public static final byte SSH_MSG_CHANNEL_OPEN = 90;
+ public static final byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
+ public static final byte SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
+ public static final byte SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
+ public static final byte SSH_MSG_CHANNEL_DATA = 94;
+ public static final byte SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
+ public static final byte SSH_MSG_CHANNEL_EOF = 96;
+ public static final byte SSH_MSG_CHANNEL_CLOSE = 97;
+ public static final byte SSH_MSG_CHANNEL_REQUEST = 98;
+ public static final byte SSH_MSG_CHANNEL_SUCCESS = 99;
+ public static final byte SSH_MSG_CHANNEL_FAILURE = 100;
+
+ //
+ // Disconnect error codes
+ //
+ public static final int SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1;
+ public static final int SSH2_DISCONNECT_PROTOCOL_ERROR = 2;
+ public static final int SSH2_DISCONNECT_KEY_EXCHANGE_FAILED = 3;
+ public static final int SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED = 4;
+ public static final int SSH2_DISCONNECT_RESERVED = 4;
+ public static final int SSH2_DISCONNECT_MAC_ERROR = 5;
+ public static final int SSH2_DISCONNECT_COMPRESSION_ERROR = 6;
+ public static final int SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE = 7;
+ public static final int SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8;
+ public static final int SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9;
+ public static final int SSH2_DISCONNECT_CONNECTION_LOST = 10;
+ public static final int SSH2_DISCONNECT_BY_APPLICATION = 11;
+ public static final int SSH2_DISCONNECT_TOO_MANY_CONNECTIONS = 12;
+ public static final int SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER = 13;
+ public static final int SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14;
+ public static final int SSH2_DISCONNECT_ILLEGAL_USER_NAME = 15;
+
+ //
+ // Open error codes
+ //
+
+ public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
+ public static final int SSH_OPEN_CONNECT_FAILED = 2;
+ public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
+ public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
+
+ // Some more constants
+ public static final int SSH_EXTENDED_DATA_STDERR = 1; // see RFC4254 section 5.2
+ public static final int SSH_PACKET_HEADER_LEN = 5; // 32-bit length + 8-bit pad length
+
+ private SshConstants() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ private static final class LazyAmbiguousOpcodesHolder {
+ private static final Set<Integer> AMBIGUOUS_OPCODES =
+ Collections.unmodifiableSet(
+ new HashSet<>(
+ LoggingUtils.getAmbiguousMenmonics(SshConstants.class, "SSH_MSG_").values()));
+
+ private LazyAmbiguousOpcodesHolder() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ /**
+ * @param cmd The command value
+ * @return {@code true} if this value is used by several <U>different</U> messages
+ * @see #getAmbiguousOpcodes()
+ */
+ public static boolean isAmbiguousOpcode(int cmd) {
+ Collection<Integer> ambiguousOpcodes = getAmbiguousOpcodes();
+ return ambiguousOpcodes.contains(cmd);
+ }
+
+ /**
+ * @return A {@link Set} of opcodes that are used by several <U>different</U> messages
+ */
+ @SuppressWarnings("synthetic-access")
+ public static Set<Integer> getAmbiguousOpcodes() {
+ return LazyAmbiguousOpcodesHolder.AMBIGUOUS_OPCODES;
+ }
+
+ private static final class LazyMessagesMapHolder {
+ private static final Map<Integer, String> MESSAGES_MAP =
+ LoggingUtils.generateMnemonicMap(SshConstants.class, f -> {
+ String name = f.getName();
+ if (!name.startsWith("SSH_MSG_")) {
+ return false;
+ }
+
+ try {
+ return !isAmbiguousOpcode(f.getByte(null));
+ } catch (Exception e) {
+ return false;
+ }
+ });
+
+ private LazyMessagesMapHolder() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ /**
+ * Converts a command value to a user-friendly name
+ *
+ * @param cmd The command value
+ * @return The user-friendly name - if not one of the defined {@code SSH_MSG_XXX}
+ * values then returns the string representation of the command's value
+ */
+ public static String getCommandMessageName(int cmd) {
+ @SuppressWarnings("synthetic-access")
+ String name = LazyMessagesMapHolder.MESSAGES_MAP.get(cmd);
+ if (GenericUtils.isEmpty(name)) {
+ return Integer.toString(cmd);
+ } else {
+ return name;
+ }
+ }
+
+ private static final class LazyReasonsMapHolder {
+ private static final Map<Integer, String> REASONS_MAP =
+ LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH2_DISCONNECT_");
+
+ private LazyReasonsMapHolder() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ /**
+ * Converts a disconnect reason value to a user-friendly name
+ *
+ * @param reason The disconnect reason value
+ * @return The user-friendly name - if not one of the defined {@code SSH2_DISCONNECT_}
+ * values then returns the string representation of the reason's value
+ */
+ public static String getDisconnectReasonName(int reason) {
+ @SuppressWarnings("synthetic-access")
+ String name = LazyReasonsMapHolder.REASONS_MAP.get(reason);
+ if (GenericUtils.isEmpty(name)) {
+ return Integer.toString(reason);
+ } else {
+ return name;
+ }
+ }
+
+ private static final class LazyOpenCodesMapHolder {
+ private static final Map<Integer, String> OPEN_CODES_MAP =
+ LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH_OPEN_");
+
+ private LazyOpenCodesMapHolder() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ /**
+ * Converts an open error value to a user-friendly name
+ *
+ * @param code The open error value
+ * @return The user-friendly name - if not one of the defined {@code SSH_OPEN_}
+ * values then returns the string representation of the reason's value
+ */
+ public static String getOpenErrorCodeName(int code) {
+ @SuppressWarnings("synthetic-access")
+ String name = LazyOpenCodesMapHolder.OPEN_CODES_MAP.get(code);
+ if (GenericUtils.isEmpty(name)) {
+ return Integer.toString(code);
+ } else {
+ return name;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SshException.java b/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
new file mode 100644
index 0000000..4280d08
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SshException.java
@@ -0,0 +1,72 @@
+/*
+ * 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.io.IOException;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Represents an SSH related exception
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshException extends IOException {
+
+ private static final long serialVersionUID = -7349477687125144606L;
+
+ private final int disconnectCode;
+
+ public SshException(String message) {
+ this(message, null);
+ }
+
+ public SshException(Throwable cause) {
+ this(Objects.requireNonNull(cause, "No cause").getMessage(), cause);
+ }
+
+ public SshException(String message, Throwable cause) {
+ this(0, message, cause);
+ }
+
+ public SshException(int disconnectCode) {
+ this(disconnectCode, SshConstants.getDisconnectReasonName(disconnectCode));
+ }
+
+ public SshException(int disconnectCode, String message) {
+ this(disconnectCode, message, null);
+ }
+
+ public SshException(int disconnectCode, Throwable cause) {
+ this(disconnectCode, SshConstants.getDisconnectReasonName(disconnectCode), cause);
+ }
+
+ public SshException(int disconnectCode, String message, Throwable cause) {
+ super(GenericUtils.isEmpty(message) ? SshConstants.getDisconnectReasonName(disconnectCode) : message);
+ this.disconnectCode = disconnectCode;
+ if (cause != null) {
+ initCause(cause);
+ }
+ }
+
+ public int getDisconnectCode() {
+ return disconnectCode;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java b/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
new file mode 100644
index 0000000..4408be2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
@@ -0,0 +1,209 @@
+/*
+ * 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.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.MapEntryUtils;
+
+/**
+ * A wrapper that exposes a read-only {@link Map} access to the system
+ * properties. Any attempt to modify it will throw {@link UnsupportedOperationException}.
+ * The mapper uses the {@link #SYSPROPS_MAPPED_PREFIX} to filter and access'
+ * only these properties, ignoring all others
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SyspropsMapWrapper implements Map<String, Object> {
+ /**
+ * Prefix of properties used by the mapper to identify SSHD related settings
+ */
+ public static final String SYSPROPS_MAPPED_PREFIX = "org.apache.sshd.config";
+
+ /**
+ * The one and only wrapper instance
+ */
+ public static final SyspropsMapWrapper INSTANCE = new SyspropsMapWrapper();
+
+ /**
+ * A {@link PropertyResolver} with no parent that exposes the system properties
+ */
+ public static final PropertyResolver SYSPROPS_RESOLVER = new PropertyResolver() {
+ @Override
+ public Map<String, Object> getProperties() {
+ return SyspropsMapWrapper.INSTANCE;
+ }
+
+ @Override
+ public PropertyResolver getParentPropertyResolver() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "SYSPROPS";
+ }
+ };
+
+ private SyspropsMapWrapper() {
+ super();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException("sysprops#clear() N/A");
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return get(key) != null;
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ // not the most efficient implementation, but we do not expect it to be called much
+ Properties props = System.getProperties();
+ for (String key : props.stringPropertyNames()) {
+ if (!isMappedSyspropKey(key)) {
+ continue;
+ }
+
+ Object v = props.getProperty(key);
+ if (Objects.equals(v, value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public Set<Entry<String, Object>> entrySet() {
+ Properties props = System.getProperties();
+ // return a copy in order to avoid concurrent modifications
+ Set<Entry<String, Object>> entries = new TreeSet<>(MapEntryUtils.byKeyEntryComparator());
+ for (String key : props.stringPropertyNames()) {
+ if (!isMappedSyspropKey(key)) {
+ continue;
+ }
+
+ Object v = props.getProperty(key);
+ if (v != null) {
+ entries.add(new SimpleImmutableEntry<>(getUnmappedSyspropKey(key), v));
+ }
+ }
+
+ return entries;
+ }
+
+ @Override
+ public Object get(Object key) {
+ return (key instanceof String) ? System.getProperty(getMappedSyspropKey(key)) : null;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return GenericUtils.isEmpty(keySet());
+ }
+
+ @Override
+ public Set<String> keySet() {
+ return System.getProperties()
+ .stringPropertyNames().stream()
+ // filter out any non-SSHD properties
+ .filter(SyspropsMapWrapper::isMappedSyspropKey)
+ .map(SyspropsMapWrapper::getUnmappedSyspropKey)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Object put(String key, Object value) {
+ throw new UnsupportedOperationException("sysprops#put(" + key + ")[" + value + "] N/A");
+ }
+
+ @Override
+ public void putAll(Map<? extends String, ?> m) {
+ throw new UnsupportedOperationException("sysprops#putAll(" + m + ") N/A");
+ }
+
+ @Override
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException("sysprops#remove(" + key + ") N/A");
+ }
+
+ @Override
+ public int size() {
+ return GenericUtils.size(keySet());
+ }
+
+ @Override
+ public Collection<Object> values() {
+ Properties props = System.getProperties();
+ // return a copy in order to avoid concurrent modifications
+ return props
+ .stringPropertyNames().stream()
+ .filter(SyspropsMapWrapper::isMappedSyspropKey)
+ .map(props::get)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toString(System.getProperties(), null);
+ }
+
+ /**
+ * @param key Key to be tested
+ * @return {@code true} if key starts with {@link #SYSPROPS_MAPPED_PREFIX}
+ * and continues with a dot followed by some characters
+ */
+ public static boolean isMappedSyspropKey(String key) {
+ return (GenericUtils.length(key) > (SYSPROPS_MAPPED_PREFIX.length() + 1))
+ && key.startsWith(SYSPROPS_MAPPED_PREFIX)
+ && (key.charAt(SYSPROPS_MAPPED_PREFIX.length()) == '.');
+ }
+
+ /**
+ * @param key Key to be transformed
+ * @return The "pure" key name if a mapped one, same as input otherwise
+ * @see #isMappedSyspropKey(String)
+ */
+ public static String getUnmappedSyspropKey(Object key) {
+ String s = Objects.toString(key);
+ return isMappedSyspropKey(s) ? s.substring(SYSPROPS_MAPPED_PREFIX.length() + 1 /* skip dot */) : s;
+ }
+
+ /**
+ * @param key The original key
+ * @return A key prefixed by {@link #SYSPROPS_MAPPED_PREFIX}
+ * @see #isMappedSyspropKey(String)
+ */
+ public static String getMappedSyspropKey(Object key) {
+ return SYSPROPS_MAPPED_PREFIX + "." + key;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
new file mode 100644
index 0000000..485f2f2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
@@ -0,0 +1,27 @@
+/*
+ * 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.auth;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MutableUserHolder extends UsernameHolder {
+ void setUsername(String username);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
new file mode 100644
index 0000000..7ee76ad
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
@@ -0,0 +1,32 @@
+/*
+ * 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.auth;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface UsernameHolder {
+ /**
+ * @return The attached username - may be {@code null}/empty if holder
+ * not yet initialized
+ */
+ String getUsername();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
new file mode 100644
index 0000000..c3d9426
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
@@ -0,0 +1,113 @@
+/*
+ * 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 javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Base class for all Cipher implementations delegating to the JCE provider.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseCipher implements Cipher {
+
+ protected javax.crypto.Cipher cipher;
+ private final int ivsize;
+ private final int bsize;
+ private final String algorithm;
+ private final String transformation;
+ private String s;
+
+ public BaseCipher(int ivsize, int bsize, String algorithm, String transformation) {
+ this.ivsize = ivsize;
+ this.bsize = bsize;
+ this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
+ this.transformation = ValidateUtils.checkNotNullAndNotEmpty(transformation, "No transformation");
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public String getTransformation() {
+ return 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, getBlockSize());
+ iv = resize(iv, getIVSize());
+ try {
+ cipher = SecurityUtils.getCipher(getTransformation());
+ cipher.init(Mode.Encrypt.equals(mode) ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE,
+ new SecretKeySpec(key, getAlgorithm()),
+ new IvParameterSpec(iv));
+ } catch (Exception e) {
+ cipher = null;
+ throw new SshException("Unable to initialize cipher " + this, e);
+ }
+ }
+
+ @Override
+ public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
+ cipher.update(input, inputOffset, inputLen, input, inputOffset);
+ }
+
+ protected static byte[] resize(byte[] data, int size) {
+ if (data.length > size) {
+ byte[] tmp = new byte[size];
+ System.arraycopy(data, 0, tmp, 0, size);
+ data = tmp;
+ }
+ return data;
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this) {
+ if (s == null) {
+ s = getClass().getSimpleName()
+ + "[" + getAlgorithm()
+ + "," + getIVSize()
+ + "," + getBlockSize()
+ + "," + getTransformation()
+ + "]";
+ }
+ }
+
+ return s;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
new file mode 100644
index 0000000..2e3fc1e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
@@ -0,0 +1,54 @@
+/*
+ * 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 javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseRC4Cipher extends BaseCipher {
+
+ public static final int SKIP_SIZE = 1536;
+
+ public BaseRC4Cipher(int ivsize, int bsize) {
+ super(ivsize, bsize, "ARCFOUR", "RC4");
+ }
+
+ @Override
+ public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
+ key = resize(key, getBlockSize());
+ try {
+ cipher = SecurityUtils.getCipher(getTransformation());
+ cipher.init(Mode.Encrypt.equals(mode) ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE,
+ new SecretKeySpec(key, getAlgorithm()));
+
+ byte[] foo = new byte[1];
+ for (int i = 0; i < SKIP_SIZE; i++) {
+ cipher.update(foo, 0, 1, foo, 0);
+ }
+ } catch (Exception e) {
+ cipher = null;
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
new file mode 100644
index 0000000..8609d50
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
@@ -0,0 +1,348 @@
+/*
+ * 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 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.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.NamedFactory;
+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 CipherFactory {
+ none(Constants.NONE, 0, 0, "None", "None") {
+ @Override
+ public Cipher create() {
+ return new CipherNone();
+ }
+ },
+ aes128cbc(Constants.AES128_CBC, 16, 16, "AES", "AES/CBC/NoPadding"),
+ aes128ctr(Constants.AES128_CTR, 16, 16, "AES", "AES/CTR/NoPadding"),
+ aes192cbc(Constants.AES192_CBC, 16, 24, "AES", "AES/CBC/NoPadding"),
+ aes192ctr(Constants.AES192_CTR, 16, 24, "AES", "AES/CTR/NoPadding"),
+ aes256cbc(Constants.AES256_CBC, 16, 32, "AES", "AES/CBC/NoPadding"),
+ aes256ctr(Constants.AES256_CTR, 16, 32, "AES", "AES/CTR/NoPadding"),
+ arcfour128(Constants.ARCFOUR128, 8, 16, "ARCFOUR", "RC4") {
+ @Override
+ public Cipher create() {
+ return new BaseRC4Cipher(getIVSize(), getBlockSize());
+ }
+ },
+ arcfour256(Constants.ARCFOUR256, 8, 32, "ARCFOUR", "RC4") {
+ @Override
+ public Cipher create() {
+ return new BaseRC4Cipher(getIVSize(), getBlockSize());
+ }
+ },
+ blowfishcbc(Constants.BLOWFISH_CBC, 8, 16, "Blowfish", "Blowfish/CBC/NoPadding"),
+ tripledescbc(Constants.TRIPLE_DES_CBC, 8, 24, "DESede", "DESede/CBC/NoPadding");
+
+ public static final Set<BuiltinCiphers> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(BuiltinCiphers.class));
+
+ private static final Map<String, CipherFactory> EXTENSIONS =
+ new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private final String factoryName;
+ private final int ivsize;
+ private final int blocksize;
+ private final int keysize;
+ private final String algorithm;
+ private final String transformation;
+ private final boolean supported;
+
+ BuiltinCiphers(String factoryName, int ivsize, int blocksize, String algorithm, String transformation) {
+ this.factoryName = factoryName;
+ this.ivsize = ivsize;
+ this.blocksize = blocksize;
+ this.keysize = blocksize * Byte.SIZE;
+ this.algorithm = algorithm;
+ this.transformation = transformation;
+ /*
+ * This can be done once since in order to change the support the JVM
+ * needs to be stopped, some unlimited-strength files need be installed
+ * and then the JVM re-started. Therefore, the answer is not going to
+ * change while the JVM is running
+ */
+ this.supported = Constants.NONE.equals(factoryName) || Cipher.checkSupported(this.transformation, this.keysize);
+ }
+
+ @Override
+ public final String getName() {
+ return factoryName;
+ }
+
+ @Override
+ public final String toString() {
+ return getName();
+ }
+
+ /**
+ * @return {@code true} if the current JVM configuration supports this
+ * cipher - e.g., AES-256 requires the <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/">
+ * Java Cryptography Extension (JCE)</A>
+ */
+ @Override
+ public boolean isSupported() {
+ return supported;
+ }
+
+ /**
+ * @return The key size (in bits) for the cipher
+ */
+ public int getKeySize() {
+ return keysize;
+ }
+
+ @Override
+ public int getIVSize() {
+ return ivsize;
+ }
+
+ @Override
+ public int getBlockSize() {
+ return blocksize;
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public String getTransformation() {
+ return transformation;
+ }
+
+ @Override
+ public Cipher create() {
+ return new BaseCipher(getIVSize(), getBlockSize(), getAlgorithm(), getTransformation());
+ }
+
+ /**
+ * 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 void registerExtension(CipherFactory extension) {
+ String name = Objects.requireNonNull(extension, "No extension provided").getName();
+ ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+ synchronized (EXTENSIONS) {
+ ValidateUtils.checkTrue(!EXTENSIONS.containsKey(name), "Extension overrides existing: %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 NavigableSet<CipherFactory> getRegisteredExtensions() {
+ 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 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
+ * @return The matching {@link BuiltinCiphers} whose {@link Enum#name()} matches
+ * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+ */
+ public static BuiltinCiphers fromString(String s) {
+ if (GenericUtils.isEmpty(s)) {
+ return null;
+ }
+
+ for (BuiltinCiphers c : VALUES) {
+ if (s.equalsIgnoreCase(c.name())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param factory The {@link NamedFactory} for the cipher - ignored if {@code null}
+ * @return The matching {@link BuiltinCiphers} whose factory name matches
+ * (case <U>insensitive</U>) the cipher factory name
+ * @see #fromFactoryName(String)
+ */
+ public static BuiltinCiphers fromFactory(NamedFactory<Cipher> factory) {
+ if (factory == null) {
+ return null;
+ } else {
+ return fromFactoryName(factory.getName());
+ }
+ }
+
+ /**
+ * @param name The factory name - ignored if {@code null}/empty
+ * @return The matching {@link BuiltinCiphers} whose factory name matches
+ * (case <U>insensitive</U>) the provided name - {@code null} if no match
+ */
+ public static BuiltinCiphers fromFactoryName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ /**
+ * @param ciphers A comma-separated list of ciphers' 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 ParseResult parseCiphersList(String ciphers) {
+ return parseCiphersList(GenericUtils.split(ciphers, ','));
+ }
+
+ public static ParseResult parseCiphersList(String... ciphers) {
+ return parseCiphersList(GenericUtils.isEmpty((Object[]) ciphers) ? Collections.emptyList() : Arrays.asList(ciphers));
+ }
+
+ public static ParseResult parseCiphersList(Collection<String> ciphers) {
+ if (GenericUtils.isEmpty(ciphers)) {
+ return ParseResult.EMPTY;
+ }
+
+ List<CipherFactory> factories = new ArrayList<>(ciphers.size());
+ List<String> unknown = Collections.emptyList();
+ for (String name : ciphers) {
+ CipherFactory 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<>();
+ }
+ 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 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 class ParseResult extends NamedFactoriesListParseResult<Cipher, CipherFactory> {
+ public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+ public ParseResult(List<CipherFactory> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+ }
+
+ public static final class Constants {
+ public static final String NONE = "none";
+ public static final Pattern NONE_CIPHER_PATTERN =
+ Pattern.compile("(^|.*,)" + NONE + "($|,.*)");
+
+ public static final String AES128_CBC = "aes128-cbc";
+ public static final String AES128_CTR = "aes128-ctr";
+ public static final String AES192_CBC = "aes192-cbc";
+ public static final String AES192_CTR = "aes192-ctr";
+ public static final String AES256_CBC = "aes256-cbc";
+ public static final String AES256_CTR = "aes256-ctr";
+ public static final String ARCFOUR128 = "arcfour128";
+ public static final String ARCFOUR256 = "arcfour256";
+ public static final String BLOWFISH_CBC = "blowfish-cbc";
+ public static final String TRIPLE_DES_CBC = "3des-cbc";
+
+ private Constants() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ /**
+ * @param s A comma-separated list of ciphers - ignored if {@code null}/empty
+ * @return {@code true} if the {@link #NONE} cipher name appears in it
+ */
+ public static boolean isNoneCipherIncluded(String s) {
+ if (GenericUtils.isEmpty(s)) {
+ return false;
+ }
+ Matcher m = NONE_CIPHER_PATTERN.matcher(s);
+ return m.matches();
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
new file mode 100644
index 0000000..868e983
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/Cipher.java
@@ -0,0 +1,89 @@
+/*
+ * 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.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Wrapper for a cryptographic cipher, used either for encryption
+ * or decryption.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Cipher extends CipherInformation {
+
+ enum Mode {
+ Encrypt, Decrypt
+ }
+
+ /**
+ * Initialize the cipher for encryption or decryption with
+ * the given key and initialization vector
+ *
+ * @param mode Encrypt/Decrypt initialization
+ * @param key Key bytes
+ * @param iv Initialization vector bytes
+ * @throws Exception If failed to initialize
+ */
+ void init(Mode mode, byte[] key, byte[] iv) throws Exception;
+
+ /**
+ * Performs in-place encryption or decryption on the given data.
+ *
+ * @param input The input/output bytes
+ * @throws Exception If failed to execute
+ * @see #update(byte[], int, int)
+ */
+ default void update(byte[] input) throws Exception {
+ update(input, 0, NumberUtils.length(input));
+ }
+
+ /**
+ * Performs in-place encryption or decryption on the given data.
+ *
+ * @param input The input/output bytes
+ * @param inputOffset The offset of the data in the data buffer
+ * @param inputLen The number of bytes to update - starting at the given offset
+ * @throws Exception If failed to execute
+ */
+ void update(byte[] input, int inputOffset, int inputLen) throws Exception;
+
+ /**
+ * @param xform The full cipher transformation - e.g., AES/CBC/NoPadding -
+ * never {@code null}/empty
+ * @param keyLength The required key length in bits - always positive
+ * @return {@code true} if the cipher transformation <U>and</U> required
+ * key length are supported
+ * @see javax.crypto.Cipher#getMaxAllowedKeyLength(String)
+ */
+ static boolean checkSupported(String xform, int keyLength) {
+ ValidateUtils.checkNotNullAndNotEmpty(xform, "No transformation");
+ if (keyLength <= 0) {
+ throw new IllegalArgumentException("Bad key length (" + keyLength + ") for cipher=" + xform);
+ }
+
+ try {
+ int maxKeyLength = javax.crypto.Cipher.getMaxAllowedKeyLength(xform);
+ return maxKeyLength >= keyLength;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
new file mode 100644
index 0000000..36909f3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface CipherFactory extends BuiltinFactory<Cipher>, CipherInformation {
+ // nothing extra
+}
+//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
new file mode 100644
index 0000000..f17fd16
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CipherInformation {
+ /**
+ * @return The cipher's algorithm
+ */
+ String getAlgorithm();
+
+ /**
+ * @return The actual transformation used - e.g., AES/CBC/NoPadding
+ */
+ String getTransformation();
+
+ /**
+ * @return Size of the initialization vector (in bytes)
+ */
+ int getIVSize();
+
+ /**
+ * @return The block size (in bytes) for this cipher
+ */
+ int getBlockSize();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
new file mode 100644
index 0000000..15b6e9f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+/**
+ * Represents a no-op cipher.
+ * This cipher can not really be used during authentication and should only
+ * be used after, so that authentication remains secured, but not the remaining
+ * of the exchanges.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CipherNone implements Cipher {
+ public CipherNone() {
+ super();
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return "none";
+ }
+
+ @Override
+ public String getTransformation() {
+ return "none";
+ }
+
+ @Override
+ public int getIVSize() {
+ return 8; // dummy
+ }
+
+ @Override
+ public int getBlockSize() {
+ return 16; // dummy
+ }
+
+ @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
+ }
+}