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:57 UTC
[47/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/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
new file mode 100644
index 0000000..ae39dd3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -0,0 +1,580 @@
+/*
+ * 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.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.interfaces.ECKey;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.digest.Digest;
+import org.apache.sshd.common.digest.DigestFactory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Utilities for working with elliptic curves.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum ECCurves implements NamedResource, OptionalFeature {
+ nistp256(Constants.NISTP256, new int[]{1, 2, 840, 10045, 3, 1, 7},
+ new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+ new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
+ new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
+ new ECPoint(
+ new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
+ new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
+ new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
+ 1),
+ 32,
+ BuiltinDigests.sha256),
+ nistp384(Constants.NISTP384, new int[]{1, 3, 132, 0, 34},
+ new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
+ new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)),
+ new ECPoint(
+ new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16),
+ new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
+ 1),
+ 48,
+ BuiltinDigests.sha384),
+ nistp521(Constants.NISTP521, new int[]{1, 3, 132, 0, 35},
+ new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),
+ new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951"
+ + "EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)),
+ new ECPoint(
+ new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77"
+ + "EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16),
+ new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE7299"
+ + "5EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)),
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B"
+ + "7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
+ 1),
+ 66,
+ BuiltinDigests.sha512);
+
+ /**
+ * A {@link Set} of all the known curves
+ */
+ public static final Set<ECCurves> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(ECCurves.class));
+
+ /**
+ * A {@link Set} of all the known curves names
+ */
+ public static final NavigableSet<String> NAMES =
+ Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
+ VALUES,
+ ECCurves::getName,
+ String.CASE_INSENSITIVE_ORDER));
+
+ /**
+ * A {@link Set} of all the known curves key types
+ */
+ public static final NavigableSet<String> KEY_TYPES =
+ Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
+ VALUES,
+ ECCurves::getKeyType,
+ String.CASE_INSENSITIVE_ORDER));
+
+ public static final Comparator<ECCurves> BY_KEY_SIZE = (o1, o2) -> {
+ int k1 = (o1 == null) ? Integer.MAX_VALUE : o1.getKeySize();
+ int k2 = (o2 == null) ? Integer.MAX_VALUE : o2.getKeySize();
+ return Integer.compare(k1, k2);
+ };
+
+ public static final List<ECCurves> SORTED_KEY_SIZE =
+ Collections.unmodifiableList(VALUES.stream()
+ .sorted(BY_KEY_SIZE)
+ .collect(Collectors.toList()));
+
+ private final String name;
+ private final String keyType;
+ private final String oidString;
+ private final List<Integer> oidValue;
+ private final ECParameterSpec params;
+ private final int keySize;
+ private final int numOctets;
+ private final DigestFactory digestFactory;
+
+ ECCurves(String name, int[] oid, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
+ this.oidString = NumberUtils.join('.', ValidateUtils.checkNotNullAndNotEmpty(oid, "No OID"));
+ this.oidValue = Collections.unmodifiableList(NumberUtils.asList(oid));
+ this.keyType = Constants.ECDSA_SHA2_PREFIX + name;
+ this.params = ValidateUtils.checkNotNull(params, "No EC params for %s", name);
+ this.keySize = getCurveSize(params);
+ this.numOctets = numOctets;
+ this.digestFactory = Objects.requireNonNull(digestFactory, "No digestFactory");
+ }
+
+ @Override // The curve name
+ public final String getName() {
+ return name;
+ }
+
+ public final String getOID() {
+ return oidString;
+ }
+
+ public final List<Integer> getOIDValue() {
+ return oidValue;
+ }
+
+ /**
+ * @return The standard key type used to represent this curve
+ */
+ public final String getKeyType() {
+ return keyType;
+ }
+
+ @Override
+ public final boolean isSupported() {
+ return SecurityUtils.isECCSupported() && digestFactory.isSupported();
+ }
+
+ public final ECParameterSpec getParameters() {
+ return params;
+ }
+
+ /**
+ * @return The size (in bits) of the key
+ */
+ public final int getKeySize() {
+ return keySize;
+ }
+
+ /**
+ * @return The number of octets used to represent the point(s) for the curve
+ */
+ public final int getNumPointOctets() {
+ return numOctets;
+ }
+
+ /**
+ * @return The {@link Digest} to use when hashing the curve's parameters
+ */
+ public final Digest getDigestForParams() {
+ return digestFactory.create();
+ }
+
+ /**
+ * @param type The key type value - ignored if {@code null}/empty
+ * @return The matching {@link ECCurves} constant - {@code null} if
+ * no match found case <U>insensitive</U>
+ */
+ public static ECCurves fromKeyType(String type) {
+ if (GenericUtils.isEmpty(type)) {
+ return null;
+ }
+
+ for (ECCurves c : VALUES) {
+ if (type.equalsIgnoreCase(c.getKeyType())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param name The curve name (case <U>insensitive</U> - ignored if
+ * {@code null}/empty
+ * @return The matching {@link ECCurves} instance - {@code null} if no
+ * match found
+ */
+ public static ECCurves fromCurveName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ /**
+ * @param key The {@link ECKey} - ignored if {@code null}
+ * @return The matching {@link ECCurves} instance - {@code null} if no
+ * match found
+ */
+ public static ECCurves fromECKey(ECKey key) {
+ return fromCurveParameters((key == null) ? null : key.getParams());
+ }
+
+ /**
+ * @param params The curve's {@link ECParameterSpec} - ignored if {@code null}
+ * @return The matching {@link ECCurves} value - {@code null} if no match found
+ * @see #getCurveSize(ECParameterSpec)
+ * @see #fromCurveSize(int)
+ */
+ public static ECCurves fromCurveParameters(ECParameterSpec params) {
+ if (params == null) {
+ return null;
+ } else {
+ return fromCurveSize(getCurveSize(params));
+ }
+ }
+
+ /**
+ * @param keySize The key size (in bits)
+ * @return The matching {@link ECCurves} value - {@code null} if no
+ * match found
+ */
+ public static ECCurves fromCurveSize(int keySize) {
+ if (keySize <= 0) {
+ return null;
+ }
+
+ for (ECCurves c : VALUES) {
+ if (keySize == c.getKeySize()) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ public static ECCurves fromOIDValue(List<? extends Number> oid) {
+ if (GenericUtils.isEmpty(oid)) {
+ return null;
+ }
+
+ for (ECCurves c : VALUES) {
+ List<? extends Number> v = c.getOIDValue();
+ if (oid.size() != v.size()) {
+ continue;
+ }
+
+ boolean matches = true;
+ for (int index = 0; index < v.size(); index++) {
+ Number exp = v.get(index);
+ Number act = oid.get(index);
+ if (exp.intValue() != act.intValue()) {
+ matches = false;
+ break;
+ }
+ }
+
+ if (matches) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ public static ECCurves fromOID(String oid) {
+ if (GenericUtils.isEmpty(oid)) {
+ return null;
+ }
+
+ for (ECCurves c : VALUES) {
+ if (oid.equalsIgnoreCase(c.getOID())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param params The curve's {@link ECParameterSpec}
+ * @return The curve's key size in bits
+ * @throws IllegalArgumentException if invalid parameters provided
+ */
+ public static int getCurveSize(ECParameterSpec params) {
+ EllipticCurve curve = Objects.requireNonNull(params, "No EC params").getCurve();
+ ECField field = Objects.requireNonNull(curve, "No EC curve").getField();
+ return Objects.requireNonNull(field, "No EC field").getFieldSize();
+ }
+
+ public static byte[] encodeECPoint(ECPoint group, ECParameterSpec params) {
+ return encodeECPoint(group, params.getCurve());
+ }
+
+ public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve) {
+ // M has len 2 ceil(log_2(q)/8) + 1 ?
+ int elementSize = (curve.getField().getFieldSize() + 7) / 8;
+ byte[] m = new byte[2 * elementSize + 1];
+
+ // Uncompressed format
+ m[0] = 0x04;
+
+ byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
+ System.arraycopy(affineX, 0, m, 1 + elementSize - affineX.length, affineX.length);
+
+ byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
+ System.arraycopy(affineY, 0, m, 1 + elementSize + elementSize - affineY.length, affineY.length);
+
+ return m;
+ }
+
+ private static byte[] removeLeadingZeroes(byte[] input) {
+ if (input[0] != 0x00) {
+ return input;
+ }
+
+ int pos = 1;
+ while (pos < input.length - 1 && input[pos] == 0x00) {
+ pos++;
+ }
+
+ byte[] output = new byte[input.length - pos];
+ System.arraycopy(input, pos, output, 0, output.length);
+ return output;
+ }
+
+ /**
+ * Converts the given octet string (defined by ASN.1 specifications) to a {@link BigInteger}
+ * As octet strings always represent positive integers, a zero-byte is prepended to
+ * the given array if necessary (if is MSB equal to 1), then this is converted to BigInteger
+ * The conversion is defined in the Section 2.3.8
+ *
+ * @param octets - octet string bytes to be converted
+ * @return The {@link BigInteger} representation of the octet string
+ */
+ public static BigInteger octetStringToInteger(byte... octets) {
+ if (octets == null) {
+ return null;
+ } else if (octets.length == 0) {
+ return BigInteger.ZERO;
+ } else {
+ return new BigInteger(1, octets);
+ }
+ }
+
+ public static ECPoint octetStringToEcPoint(byte... octets) {
+ if (NumberUtils.isEmpty(octets)) {
+ return null;
+ }
+
+ int startIndex = findFirstNonZeroIndex(octets);
+ if (startIndex < 0) {
+ throw new IllegalArgumentException("All zeroes ECPoint N/A");
+ }
+
+ byte indicator = octets[startIndex];
+ ECCurves.ECPointCompression compression = ECCurves.ECPointCompression.fromIndicatorValue(indicator);
+ if (compression == null) {
+ throw new UnsupportedOperationException("Unknown compression indicator value: 0x" + Integer.toHexString(indicator & 0xFF));
+ }
+
+ // The coordinates actually start after the compression indicator
+ return compression.octetStringToEcPoint(octets, startIndex + 1, octets.length - startIndex - 1);
+ }
+
+ private static int findFirstNonZeroIndex(byte... octets) {
+ if (NumberUtils.isEmpty(octets)) {
+ return -1;
+ }
+
+ for (int index = 0; index < octets.length; index++) {
+ if (octets[index] != 0) {
+ return index;
+ }
+ }
+
+ return -1; // all zeroes
+ }
+
+ public static final class Constants {
+ /**
+ * Standard prefix of NISTP key types when encoded
+ */
+ public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
+
+ public static final String NISTP256 = "nistp256";
+ public static final String NISTP384 = "nistp384";
+ public static final String NISTP521 = "nistp521";
+
+ private Constants() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ /**
+ * The various {@link ECPoint} representation compression indicators
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://www.ietf.org/rfc/rfc5480.txt">RFC-5480 - section 2.2</A>
+ */
+ public enum ECPointCompression {
+ // see http://tools.ietf.org/html/draft-jivsov-ecc-compact-00
+ // see http://crypto.stackexchange.com/questions/8914/ecdsa-compressed-public-key-point-back-to-uncompressed-public-key-point
+ VARIANT2((byte) 0x02) {
+ @Override
+ public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+ byte[] xp = new byte[len];
+ System.arraycopy(octets, startIndex, xp, 0, len);
+ BigInteger x = octetStringToInteger(xp);
+
+ // TODO derive even Y...
+ throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
+ }
+ },
+ VARIANT3((byte) 0x03) {
+ @Override
+ public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+ byte[] xp = new byte[len];
+ System.arraycopy(octets, startIndex, xp, 0, len);
+ BigInteger x = octetStringToInteger(xp);
+
+ // TODO derive odd Y...
+ throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
+ }
+ },
+ UNCOMPRESSED((byte) 0x04) {
+ @Override
+ public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+ int numElements = len / 2; /* x, y */
+ if (len != (numElements * 2)) { // make sure length is not odd
+ throw new IllegalArgumentException("octetStringToEcPoint(" + name() + ") "
+ + " invalid remainder octets representation: "
+ + " expected=" + (2 * numElements) + ", actual=" + len);
+ }
+
+ byte[] xp = new byte[numElements];
+ byte[] yp = new byte[numElements];
+ System.arraycopy(octets, startIndex, xp, 0, numElements);
+ System.arraycopy(octets, startIndex + numElements, yp, 0, numElements);
+
+ BigInteger x = octetStringToInteger(xp);
+ BigInteger y = octetStringToInteger(yp);
+ return new ECPoint(x, y);
+ }
+
+ @Override
+ public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
+ ECCurves curve = fromCurveName(curveName);
+ if (curve == null) {
+ throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + curveName + "] cannot determine octets count");
+ }
+
+ int numElements = curve.getNumPointOctets();
+ KeyEntryResolver.encodeInt(s, 1 /* the indicator */ + 2 * numElements);
+ s.write(getIndicatorValue());
+ writeCoordinate(s, "X", p.getAffineX(), numElements);
+ writeCoordinate(s, "Y", p.getAffineY(), numElements);
+ }
+ };
+
+ public static final Set<ECPointCompression> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(ECPointCompression.class));
+
+ private final byte indicatorValue;
+
+ ECPointCompression(byte indicator) {
+ indicatorValue = indicator;
+ }
+
+ public final byte getIndicatorValue() {
+ return indicatorValue;
+ }
+
+ public abstract ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len);
+
+ public byte[] ecPointToOctetString(String curveName, ECPoint p) {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream((2 * 66) + Long.SIZE)) {
+ writeECPoint(baos, curveName, p);
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException("ecPointToOctetString(" + curveName + ")"
+ + " failed (" + e.getClass().getSimpleName() + ")"
+ + " to write data: " + e.getMessage(),
+ e);
+ }
+ }
+
+ public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
+ if (s == null) {
+ throw new EOFException("No output stream");
+ }
+
+ throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + p + "] N/A");
+ }
+
+ protected void writeCoordinate(OutputStream s, String n, BigInteger v, int numElements) throws IOException {
+ byte[] vp = v.toByteArray();
+ int startIndex = 0;
+ int vLen = vp.length;
+ if (vLen > numElements) {
+ if (vp[0] == 0) { // skip artificial positive sign
+ startIndex++;
+ vLen--;
+ }
+ }
+
+ if (vLen > numElements) {
+ throw new StreamCorruptedException("writeCoordinate(" + name() + ")[" + n + "]"
+ + " value length (" + vLen + ") exceeds max. (" + numElements + ")"
+ + " for " + v);
+ }
+
+ if (vLen < numElements) {
+ byte[] tmp = new byte[numElements];
+ System.arraycopy(vp, startIndex, tmp, numElements - vLen, vLen);
+ vp = tmp;
+ }
+
+ s.write(vp, startIndex, vLen);
+ }
+
+ public static ECPointCompression fromIndicatorValue(int value) {
+ if ((value < 0) || (value > 0xFF)) {
+ return null; // must be a byte value
+ }
+
+ for (ECPointCompression c : VALUES) {
+ if (value == c.getIndicatorValue()) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html b/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
new file mode 100644
index 0000000..197a89d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/cipher/Cipher.html"><code>Cipher</code></a>
+implementations.
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
new file mode 100644
index 0000000..ff31947
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.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.compression;
+
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class BaseCompression implements Compression {
+ private final String name;
+
+ protected BaseCompression(String name) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No compression name");
+ }
+
+ @Override
+ public final String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
new file mode 100644
index 0000000..49ad0ab
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.compression;
+
+import java.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 org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinCompressions implements CompressionFactory {
+ none(Constants.NONE) {
+ @Override
+ public Compression create() {
+ return new CompressionNone();
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return false;
+ }
+ },
+ zlib(Constants.ZLIB) {
+ @Override
+ public Compression create() {
+ return new CompressionZlib();
+ }
+ },
+ delayedZlib(Constants.DELAYED_ZLIB) {
+ @Override
+ public Compression create() {
+ return new CompressionDelayedZlib();
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return true;
+ }
+ };
+
+ public static final Set<BuiltinCompressions> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(BuiltinCompressions.class));
+
+ private static final Map<String, CompressionFactory> EXTENSIONS =
+ new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private final String name;
+
+ BuiltinCompressions(String n) {
+ name = n;
+ }
+
+ @Override
+ public final String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return true;
+ }
+
+ @Override
+ public final String toString() {
+ return getName();
+ }
+
+ @Override
+ public final boolean isSupported() {
+ return true;
+ }
+
+ /**
+ * Registered a {@link org.apache.sshd.common.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(CompressionFactory 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<CompressionFactory> 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 CompressionFactory unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized (EXTENSIONS) {
+ return EXTENSIONS.remove(name);
+ }
+ }
+
+ public static BuiltinCompressions fromFactoryName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ /**
+ * @param compressions A comma-separated list of Compressions' names - ignored
+ * if {@code null}/empty
+ * @return A {@link ParseResult} containing the successfully parsed
+ * factories and the unknown ones. <B>Note:</B> it is up to caller to
+ * ensure that the lists do not contain duplicates
+ */
+ public static ParseResult parseCompressionsList(String compressions) {
+ return parseCompressionsList(GenericUtils.split(compressions, ','));
+ }
+
+ public static ParseResult parseCompressionsList(String... compressions) {
+ return parseCompressionsList(GenericUtils.isEmpty((Object[]) compressions) ? Collections.emptyList() : Arrays.asList(compressions));
+ }
+
+ public static ParseResult parseCompressionsList(Collection<String> compressions) {
+ if (GenericUtils.isEmpty(compressions)) {
+ return ParseResult.EMPTY;
+ }
+
+ List<CompressionFactory> factories = new ArrayList<>(compressions.size());
+ List<String> unknown = Collections.emptyList();
+ for (String name : compressions) {
+ CompressionFactory c = resolveFactory(name);
+ if (c != null) {
+ factories.add(c);
+ } else {
+ // replace the (unmodifiable) empty list with a real one
+ if (unknown.isEmpty()) {
+ unknown = new ArrayList<>();
+ }
+ 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 CompressionFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ CompressionFactory c = fromFactoryName(name);
+ if (c != null) {
+ return c;
+ }
+
+ synchronized (EXTENSIONS) {
+ return EXTENSIONS.get(name);
+ }
+ }
+
+ /**
+ * Holds the result of {@link BuiltinCompressions#parseCompressionsList(String)}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ public static class ParseResult extends NamedFactoriesListParseResult<Compression, CompressionFactory> {
+ public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+ public ParseResult(List<CompressionFactory> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+ }
+
+ public static final class Constants {
+ public static final String NONE = "none";
+ public static final String ZLIB = "zlib";
+ public static final String DELAYED_ZLIB = "zlib@openssh.com";
+
+ private Constants() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
new file mode 100644
index 0000000..91ac27e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.compression;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Interface used to compress the stream of data between the
+ * SSH server and clients.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Compression extends CompressionInformation {
+
+ /**
+ * Enum identifying if this object will be used to compress
+ * or uncompress data.
+ */
+ enum Type {
+ Inflater,
+ Deflater
+ }
+
+ /**
+ * Initialize this object to either compress or uncompress data.
+ * This method must be called prior to any calls to either
+ * <code>compress</code> or <code>uncompress</code>.
+ * Once the object has been initialized, only one of
+ * <code>compress</code> or <code>uncompress</code> methods can be
+ * called.
+ *
+ * @param type compression type
+ * @param level compression level
+ */
+ void init(Type type, int level);
+
+ /**
+ * Compress the given buffer in place.
+ *
+ * @param buffer the buffer containing the data to compress
+ * @throws IOException if an error occurs
+ */
+ void compress(Buffer buffer) throws IOException;
+
+ /**
+ * Uncompress the data in a buffer into another buffer.
+ *
+ * @param from the buffer containing the data to uncompress
+ * @param to the buffer receiving the uncompressed data
+ * @throws IOException if an error occurs
+ */
+ void uncompress(Buffer from, Buffer to) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
new file mode 100644
index 0000000..c062e38
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.compression;
+
+
+/**
+ * ZLib delayed compression.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see Compression#isDelayed()
+ */
+public class CompressionDelayedZlib extends CompressionZlib {
+ /**
+ * Create a new instance of a delayed ZLib compression
+ */
+ public CompressionDelayedZlib() {
+ super(BuiltinCompressions.Constants.DELAYED_ZLIB);
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
new file mode 100644
index 0000000..355594a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.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.compression;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface CompressionFactory extends BuiltinFactory<Compression>, CompressionInformation {
+ // 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/compression/CompressionInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
new file mode 100644
index 0000000..428689f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.compression;
+
+import org.apache.sshd.common.NamedResource;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CompressionInformation extends NamedResource {
+ /**
+ * Delayed compression is an Open-SSH specific feature which
+ * informs both the client and server to not compress data before
+ * the session has been authenticated.
+ *
+ * @return if the compression is delayed after authentication or not
+ */
+ boolean isDelayed();
+
+ /**
+ * @return {@code true} if there is any compression executed by
+ * this "compressor" - special case for 'none'
+ */
+ boolean isCompressionExecuted();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
new file mode 100644
index 0000000..815e8ce
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.compression;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CompressionNone extends BaseCompression {
+ private Type type;
+ private int level;
+
+ public CompressionNone() {
+ super(BuiltinCompressions.Constants.NONE);
+ }
+
+ @Override
+ public void init(Type type, int level) {
+ this.type = type;
+ this.level = level;
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return false;
+ }
+
+ @Override
+ public void compress(Buffer buffer) throws IOException {
+ if (!Type.Deflater.equals(type)) {
+ throw new StreamCorruptedException("Not set up for compression: " + type);
+ }
+ }
+
+ @Override
+ public void uncompress(Buffer from, Buffer to) throws IOException {
+ if (!Type.Inflater.equals(type)) {
+ throw new StreamCorruptedException("Not set up for de-compression: " + type);
+ }
+
+ if (from != to) {
+ throw new StreamCorruptedException("Separate de-compression buffers provided");
+ }
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[" + type + "/" + level + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
new file mode 100644
index 0000000..e365b91
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.compression;
+
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * ZLib based Compression.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CompressionZlib extends BaseCompression {
+
+ private static final int BUF_SIZE = 4096;
+
+ private byte[] tmpbuf = new byte[BUF_SIZE];
+ private Deflater compresser;
+ private Inflater decompresser;
+
+ /**
+ * Create a new instance of a ZLib base compression
+ */
+ public CompressionZlib() {
+ this(BuiltinCompressions.Constants.ZLIB);
+ }
+
+ protected CompressionZlib(String name) {
+ super(name);
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public void init(Type type, int level) {
+ compresser = new Deflater(level);
+ decompresser = new Inflater();
+ }
+
+ @Override
+ public void compress(Buffer buffer) throws IOException {
+ compresser.setInput(buffer.array(), buffer.rpos(), buffer.available());
+ buffer.wpos(buffer.rpos());
+ for (int len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH);
+ len > 0;
+ len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH)) {
+ buffer.putRawBytes(tmpbuf, 0, len);
+ }
+ }
+
+ @Override
+ public void uncompress(Buffer from, Buffer to) throws IOException {
+ decompresser.setInput(from.array(), from.rpos(), from.available());
+ try {
+ for (int len = decompresser.inflate(tmpbuf); len > 0; len = decompresser.inflate(tmpbuf)) {
+ to.putRawBytes(tmpbuf, 0, len);
+ }
+ } catch (DataFormatException e) {
+ throw new IOException("Error decompressing data", e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html b/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
new file mode 100644
index 0000000..9bd4652
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/compression/Compression.html"><code>Compression</code></a> implementations.
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
new file mode 100644
index 0000000..e153adc
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.compression.BuiltinCompressions;
+import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.compression.CompressionFactory;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Provides a "bridge" between the configuration values and the
+ * actual {@link org.apache.sshd.common.NamedFactory} for the {@link Compression}.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum CompressionConfigValue implements CompressionFactory {
+ YES(BuiltinCompressions.zlib),
+ NO(BuiltinCompressions.none),
+ DELAYED(BuiltinCompressions.delayedZlib);
+
+ public static final Set<CompressionConfigValue> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(CompressionConfigValue.class));
+
+ private final CompressionFactory factory;
+
+ CompressionConfigValue(CompressionFactory delegate) {
+ factory = delegate;
+ }
+
+ @Override
+ public final String getName() {
+ return factory.getName();
+ }
+
+ @Override
+ public final Compression create() {
+ return factory.create();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return factory.isSupported();
+ }
+
+ @Override
+ public final String toString() {
+ return getName();
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return factory.isDelayed();
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return factory.isCompressionExecuted();
+ }
+
+ public static CompressionConfigValue fromName(String n) {
+ if (GenericUtils.isEmpty(n)) {
+ return null;
+ }
+
+ for (CompressionConfigValue v : VALUES) {
+ if (n.equalsIgnoreCase(v.name())) {
+ return v;
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
new file mode 100644
index 0000000..10b131c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StreamCorruptedException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.NoCloseReader;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <a href="https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5">ssh_config(5)</a>
+ */
+public final class ConfigFileReaderSupport {
+
+ public static final char COMMENT_CHAR = '#';
+
+ public static final String COMPRESSION_PROP = "Compression";
+ public static final String DEFAULT_COMPRESSION = CompressionConfigValue.NO.getName();
+ public static final String MAX_SESSIONS_CONFIG_PROP = "MaxSessions";
+ public static final int DEFAULT_MAX_SESSIONS = 10;
+ public static final String PASSWORD_AUTH_CONFIG_PROP = "PasswordAuthentication";
+ public static final String DEFAULT_PASSWORD_AUTH = "no";
+ public static final boolean DEFAULT_PASSWORD_AUTH_VALUE = parseBooleanValue(DEFAULT_PASSWORD_AUTH);
+ public static final String LISTEN_ADDRESS_CONFIG_PROP = "ListenAddress";
+ public static final String DEFAULT_BIND_ADDRESS = SshdSocketAddress.IPV4_ANYADDR;
+ public static final String PORT_CONFIG_PROP = "Port";
+ public static final int DEFAULT_PORT = 22;
+ public static final String KEEP_ALIVE_CONFIG_PROP = "TCPKeepAlive";
+ public static final boolean DEFAULT_KEEP_ALIVE = true;
+ public static final String USE_DNS_CONFIG_PROP = "UseDNS";
+ // NOTE: the usual default is TRUE
+ public static final boolean DEFAULT_USE_DNS = true;
+ public static final String PUBKEY_AUTH_CONFIG_PROP = "PubkeyAuthentication";
+ public static final String DEFAULT_PUBKEY_AUTH = "yes";
+ public static final boolean DEFAULT_PUBKEY_AUTH_VALUE = parseBooleanValue(DEFAULT_PUBKEY_AUTH);
+ public static final String AUTH_KEYS_FILE_CONFIG_PROP = "AuthorizedKeysFile";
+ public static final String MAX_AUTH_TRIES_CONFIG_PROP = "MaxAuthTries";
+ public static final int DEFAULT_MAX_AUTH_TRIES = 6;
+ public static final String MAX_STARTUPS_CONFIG_PROP = "MaxStartups";
+ public static final int DEFAULT_MAX_STARTUPS = 10;
+ public static final String LOGIN_GRACE_TIME_CONFIG_PROP = "LoginGraceTime";
+ public static final long DEFAULT_LOGIN_GRACE_TIME = TimeUnit.SECONDS.toMillis(120);
+ public static final String KEY_REGENERATE_INTERVAL_CONFIG_PROP = "KeyRegenerationInterval";
+ public static final long DEFAULT_REKEY_TIME_LIMIT = TimeUnit.HOURS.toMillis(1L);
+ // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+ public static final String CIPHERS_CONFIG_PROP = "Ciphers";
+ public static final String DEFAULT_CIPHERS =
+ "aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour";
+ // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+ public static final String MACS_CONFIG_PROP = "MACs";
+ public static final String DEFAULT_MACS =
+ "hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160,hmac-sha1-96,hmac-md5-96,hmac-sha2-256,hmac-sha2-256-96,hmac-sha2-512,hmac-sha2-512-96";
+ // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+ public static final String KEX_ALGORITHMS_CONFIG_PROP = "KexAlgorithms";
+ public static final String DEFAULT_KEX_ALGORITHMS =
+ "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521"
+ + "," + "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1"
+ // RFC-8268 groups
+ + "," + "diffie-hellman-group18-sha512,diffie-hellman-group17-sha512"
+ + "," + "diffie-hellman-group16-sha512,diffie-hellman-group15-sha512"
+ + "," + "diffie-hellman-group14-sha256"
+ // Legacy groups
+ + "," + "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1";
+ // see http://linux.die.net/man/5/ssh_config
+ public static final String HOST_KEY_ALGORITHMS_CONFIG_PROP = "HostKeyAlgorithms";
+ // see https://tools.ietf.org/html/rfc5656
+ public static final String DEFAULT_HOST_KEY_ALGORITHMS =
+ KeyPairProvider.SSH_RSA + "," + KeyPairProvider.SSH_DSS;
+ // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html
+ public static final String LOG_LEVEL_CONFIG_PROP = "LogLevel";
+ public static final LogLevelValue DEFAULT_LOG_LEVEL = LogLevelValue.INFO;
+ // see https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5
+ public static final String SYSLOG_FACILITY_CONFIG_PROP = "SyslogFacility";
+ public static final SyslogFacilityValue DEFAULT_SYSLOG_FACILITY = SyslogFacilityValue.AUTH;
+ public static final String SUBSYSTEM_CONFIG_PROP = "Subsystem";
+
+ private ConfigFileReaderSupport() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static Properties readConfigFile(File file) throws IOException {
+ return readConfigFile(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
+ }
+
+ public static Properties readConfigFile(Path path, OpenOption... options) throws IOException {
+ try (InputStream input = Files.newInputStream(path, options)) {
+ return readConfigFile(input, true);
+ }
+ }
+
+ public static Properties readConfigFile(URL url) throws IOException {
+ try (InputStream input = url.openStream()) {
+ return readConfigFile(input, true);
+ }
+ }
+
+ public static Properties readConfigFile(String path) throws IOException {
+ try (InputStream input = new FileInputStream(path)) {
+ return readConfigFile(input, true);
+ }
+ }
+
+ public static Properties readConfigFile(InputStream input, boolean okToClose) throws IOException {
+ try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(input, okToClose), StandardCharsets.UTF_8)) {
+ return readConfigFile(reader, true);
+ }
+ }
+
+ public static Properties readConfigFile(Reader reader, boolean okToClose) throws IOException {
+ try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(reader, okToClose))) {
+ return readConfigFile(buf);
+ }
+ }
+
+ /**
+ * Reads the configuration file contents into a {@link Properties} instance.
+ * <B>Note:</B> multiple keys value are concatenated using a comma - it is up to
+ * the caller to know which keys are expected to have multiple values and handle
+ * the split accordingly
+ *
+ * @param rdr The {@link BufferedReader} for reading the file
+ * @return The read properties
+ * @throws IOException If failed to read or malformed content
+ */
+ public static Properties readConfigFile(BufferedReader rdr) throws IOException {
+ Properties props = new Properties();
+ int lineNumber = 1;
+ for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
+ line = GenericUtils.replaceWhitespaceAndTrim(line);
+ if (GenericUtils.isEmpty(line)) {
+ continue;
+ }
+
+ int pos = line.indexOf(COMMENT_CHAR);
+ if (pos == 0) {
+ continue;
+ }
+
+ if (pos > 0) {
+ line = line.substring(0, pos);
+ line = line.trim();
+ }
+
+ /*
+ * Some options use '=', others use ' ' - try both
+ * NOTE: we do not validate the format for each option separately
+ */
+ pos = line.indexOf(' ');
+ if (pos < 0) {
+ pos = line.indexOf('=');
+ }
+
+ if (pos < 0) {
+ throw new StreamCorruptedException("No delimiter at line " + lineNumber + ": " + line);
+ }
+
+ String key = line.substring(0, pos);
+ String value = line.substring(pos + 1).trim();
+ // see if need to concatenate multi-valued keys
+ String prev = props.getProperty(key);
+ if (!GenericUtils.isEmpty(prev)) {
+ value = prev + "," + value;
+ }
+
+ props.setProperty(key, value);
+ }
+
+ return props;
+ }
+
+ /**
+ * @param v Checks if the value is "yes", "y"
+ * or "on" or "true".
+ * @return The result - <B>Note:</B> {@code null}/empty values are
+ * interpreted as {@code false}
+ */
+ public static boolean parseBooleanValue(String v) {
+ return "yes".equalsIgnoreCase(v)
+ || "y".equalsIgnoreCase(v)
+ || "on".equalsIgnoreCase(v)
+ || "true".equalsIgnoreCase(v);
+ }
+
+ /**
+ * Returns a "yes" or "no" value based on the input
+ * parameter
+ *
+ * @param flag The required state
+ * @return "yes" if {@code true}, "no" otherwise
+ */
+ public static String yesNoValueOf(boolean flag) {
+ return flag ? "yes" : "no";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
new file mode 100644
index 0000000..7a1cee1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.List;
+
+import org.apache.sshd.common.Factory;
+
+/**
+ * @param <T> Result type
+ * @param <F> Factory type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class FactoriesListParseResult<T, F extends Factory<T>> extends ListParseResult<F> {
+ protected FactoriesListParseResult(List<F> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+
+ /**
+ * @return The {@link List} of successfully parsed {@link Factory} instances
+ * in the <U>same order</U> as they were encountered during parsing
+ */
+ public final List<F> getParsedFactories() {
+ return getParsedValues();
+ }
+
+ /**
+ * @return A {@link List} of unknown/unsupported configuration values for
+ * the factories
+ */
+ public List<String> getUnsupportedFactories() {
+ return getUnsupportedValues();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
new file mode 100644
index 0000000..97590cb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Used to hold the result of parsing a list of value. Such result contains known
+ * and unknown values - which are accessible via the respective {@link #getParsedValues()}
+ * and {@link #getUnsupportedValues()} methods. <B>Note:</B> the returned {@link List}s may
+ * be un-modifiable, so it is recommended to avoid attempting changing the, returned
+ * list(s)
+ *
+ * @param <E> Type of list item
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class ListParseResult<E> {
+ private final List<E> parsed;
+ private final List<String> unsupported;
+
+ protected ListParseResult(List<E> parsed, List<String> unsupported) {
+ this.parsed = parsed;
+ this.unsupported = unsupported;
+ }
+
+ /**
+ * @return The {@link List} of successfully parsed value instances
+ * in the <U>same order</U> as they were encountered during parsing
+ */
+ public final List<E> getParsedValues() {
+ return parsed;
+ }
+
+ /**
+ * @return A {@link List} of unknown/unsupported configuration values for
+ * the factories
+ */
+ public List<String> getUnsupportedValues() {
+ return unsupported;
+ }
+
+ @Override
+ public String toString() {
+ return "parsed=" + GenericUtils.join(getParsedValues(), ',')
+ + ";unsupported=" + GenericUtils.join(getUnsupportedValues(), ',');
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
new file mode 100644
index 0000000..2fbdc80
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html"><I>LogLevel</I> configuration value</A>
+ */
+public enum LogLevelValue {
+ /*
+ * NOTE(s):
+ * 1. DEBUG and DEBUG1 are EQUIVALENT
+ * 2. Order is important (!!!)
+ */
+ QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3;
+
+ public static final Set<LogLevelValue> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(LogLevelValue.class));
+
+ public static LogLevelValue fromName(String n) {
+ if (GenericUtils.isEmpty(n)) {
+ return null;
+ }
+
+ for (LogLevelValue l : VALUES) {
+ if (n.equalsIgnoreCase(l.name())) {
+ return l;
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
new file mode 100644
index 0000000..246cae0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.List;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Holds the result of parsing a list of {@link NamedFactory}ies
+ *
+ * @param <T> Result type
+ * @param <F> Factory type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class NamedFactoriesListParseResult<T, F extends NamedFactory<T>>
+ extends FactoriesListParseResult<T, F> {
+
+ protected NamedFactoriesListParseResult(List<F> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+
+ @Override
+ public String toString() {
+ return "parsed=" + NamedResource.getNames(getParsedFactories())
+ + ";unknown=" + GenericUtils.join(getUnsupportedFactories(), ',');
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
new file mode 100644
index 0000000..feb45f4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.List;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @param <R> Type of result {@link NamedResource}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class NamedResourceListParseResult<R extends NamedResource> extends ListParseResult<R> {
+ protected NamedResourceListParseResult(List<R> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+
+ /**
+ * @return The {@link List} of successfully parsed {@link NamedResource} instances
+ * in the <U>same order</U> as they were encountered during parsing
+ */
+ public final List<R> getParsedResources() {
+ return getParsedValues();
+ }
+
+ /**
+ * @return A {@link List} of unknown/unsupported configuration values for
+ * the resources
+ */
+ public List<String> getUnsupportedResources() {
+ return getUnsupportedValues();
+ }
+
+ @Override
+ public String toString() {
+ return "parsed=" + NamedResource.getNames(getParsedResources())
+ + ";unknown=" + GenericUtils.join(getUnsupportedResources(), ',');
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
new file mode 100644
index 0000000..f52ae36
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5"><I>SyslogFacility</I> configuration value</A>
+ */
+public enum SyslogFacilityValue {
+ DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7;
+
+ public static final Set<SyslogFacilityValue> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(SyslogFacilityValue.class));
+
+ public static SyslogFacilityValue fromName(String n) {
+ if (GenericUtils.isEmpty(n)) {
+ return null;
+ }
+
+ for (SyslogFacilityValue f : VALUES) {
+ if (n.equalsIgnoreCase(f.name())) {
+ return f;
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java b/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
new file mode 100644
index 0000000..222cf84
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config;
+
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://unixhelp.ed.ac.uk/CGI/man-cgi?sshd_config+5">Time formats for SSH configuration values</A>
+ */
+public enum TimeValueConfig {
+ SECONDS('s', 'S', TimeUnit.SECONDS.toMillis(1L)),
+ MINUTES('m', 'M', TimeUnit.MINUTES.toMillis(1L)),
+ HOURS('h', 'H', TimeUnit.HOURS.toMillis(1L)),
+ DAYS('d', 'D', TimeUnit.DAYS.toMillis(1L)),
+ WEEKS('w', 'W', TimeUnit.DAYS.toMillis(7L));
+
+ public static final Set<TimeValueConfig> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(TimeValueConfig.class));
+
+ private final char loChar;
+ private final char hiChar;
+ private final long interval;
+
+ TimeValueConfig(char lo, char hi, long interval) {
+ loChar = lo;
+ hiChar = hi;
+ this.interval = interval;
+ }
+
+ public final char getLowerCaseValue() {
+ return loChar;
+ }
+
+ public final char getUpperCaseValue() {
+ return hiChar;
+ }
+
+ public final long getInterval() {
+ return interval;
+ }
+
+ public static TimeValueConfig fromValueChar(char ch) {
+ if ((ch <= ' ') || (ch >= 0x7F)) {
+ return null;
+ }
+
+ for (TimeValueConfig v : VALUES) {
+ if ((v.getLowerCaseValue() == ch) || (v.getUpperCaseValue() == ch)) {
+ return v;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param s A time specification
+ * @return The specified duration in milliseconds
+ * @see #parse(String)
+ * @see #durationOf(Map)
+ */
+ public static long durationOf(String s) {
+ Map<TimeValueConfig, Long> spec = parse(s);
+ return durationOf(spec);
+ }
+
+ /**
+ * @param s An input time specification containing possibly mixed numbers
+ * and units - e.g., {@code 3h10m} to indicate 3 hours and 10 minutes
+ * @return A {@link Map} specifying for each time unit its count
+ * @throws NumberFormatException If bad numbers found - e.g., negative counts
+ * @throws IllegalArgumentException If bad format - e.g., unknown unit
+ */
+ public static Map<TimeValueConfig, Long> parse(String s) throws IllegalArgumentException {
+ if (GenericUtils.isEmpty(s)) {
+ return Collections.emptyMap();
+ }
+
+ int lastPos = 0;
+ Map<TimeValueConfig, Long> spec = new EnumMap<>(TimeValueConfig.class);
+ for (int curPos = 0; curPos < s.length(); curPos++) {
+ char ch = s.charAt(curPos);
+ if ((ch >= '0') && (ch <= '9')) {
+ continue;
+ }
+
+ if (curPos <= lastPos) {
+ throw new IllegalArgumentException("parse(" + s + ") missing count value at index=" + curPos);
+ }
+
+ TimeValueConfig c = fromValueChar(ch);
+ if (c == null) {
+ throw new IllegalArgumentException("parse(" + s + ") unknown time value character: '" + ch + "'");
+ }
+
+ String v = s.substring(lastPos, curPos);
+ long count = Long.parseLong(v);
+ if (count < 0L) {
+ throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for " + c.name());
+ }
+
+ Long prev = spec.put(c, count);
+ if (prev != null) {
+ throw new IllegalArgumentException("parse(" + s + ") " + c.name() + " value re-specified: current=" + count + ", previous=" + prev);
+ }
+
+ lastPos = curPos + 1;
+ if (lastPos >= s.length()) {
+ break;
+ }
+ }
+
+ if (lastPos < s.length()) {
+ String v = s.substring(lastPos);
+ long count = Long.parseLong(v);
+ if (count < 0L) {
+ throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for last component");
+ }
+
+ Long prev = spec.put(SECONDS, count);
+ if (prev != null) {
+ throw new IllegalArgumentException("parse(" + s + ") last component (" + SECONDS.name() + ") value re-specified: current=" + count + ", previous=" + prev);
+ }
+ }
+
+ return spec;
+ }
+
+ /**
+ * @param spec The {@link Map} specifying the count for each {@link TimeValueConfig}
+ * @return The total duration in milliseconds
+ * @throws IllegalArgumentException If negative count for a time unit
+ */
+ public static long durationOf(Map<TimeValueConfig, ? extends Number> spec) throws IllegalArgumentException {
+ if (GenericUtils.isEmpty(spec)) {
+ return -1L;
+ }
+
+ long total = 0L;
+ // Cannot use forEach because the total value is not effectively final
+ for (Map.Entry<TimeValueConfig, ? extends Number> se : spec.entrySet()) {
+ TimeValueConfig v = se.getKey();
+ Number c = se.getValue();
+ long factor = c.longValue();
+ if (factor < 0L) {
+ throw new IllegalArgumentException("valueOf(" + spec + ") bad factor (" + c + ") for " + v.name());
+ }
+
+ long added = v.getInterval() * factor;
+ total += added;
+ }
+
+ return total;
+ }
+}