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 2017/05/15 16:38:31 UTC
[2/3] mina-sshd git commit: [SSHD-747] Add support for non-standard
port specification in known_hosts file
[SSHD-747] Add support for non-standard port specification in known_hosts file
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/2119f798
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/2119f798
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/2119f798
Branch: refs/heads/master
Commit: 2119f7984ac0eceaca10c6ace8321ab506490657
Parents: f2798ec
Author: Goldstein Lyor <ly...@c-b4.com>
Authored: Sun May 14 12:17:02 2017 +0300
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Mon May 15 19:38:22 2017 +0300
----------------------------------------------------------------------
.../client/config/hosts/HostPatternValue.java | 97 ++++++++++++++++++++
.../client/config/hosts/HostPatternsHolder.java | 70 ++++++++++----
.../client/config/hosts/KnownHostEntry.java | 4 +-
.../KnownHostsServerKeyVerifier.java | 46 ++++++----
.../apache/sshd/common/util/GenericUtils.java | 24 +++++
.../sshd/common/util/net/SshdSocketAddress.java | 65 +++++++++++--
.../config/hosts/HostConfigEntryTest.java | 17 ++--
.../config/hosts/KnownHostHashValueTest.java | 3 +
.../KnownHostsServerKeyVerifierTest.java | 96 ++++++++++---------
.../apache/sshd/client/keyverifier/known_hosts | 5 +-
10 files changed, 329 insertions(+), 98 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
new file mode 100644
index 0000000..20d682f
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
@@ -0,0 +1,97 @@
+/*
+ * 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.client.config.hosts;
+
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Represents a pattern definition in the <U>known_hosts</U> file
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#About_the_Contents_of_the_known_hosts_Files">
+ * OpenSSH cookbook - About the Contents of the known hosts Files</A>
+ */
+public class HostPatternValue {
+ private Pattern pattern;
+ private int port;
+ private boolean negated;
+
+ public HostPatternValue() {
+ super();
+ }
+
+ public HostPatternValue(Pattern pattern, boolean negated) {
+ this(pattern, 0, negated);
+ }
+
+ public HostPatternValue(Pattern pattern, int port, boolean negated) {
+ this.pattern = pattern;
+ this.port = port;
+ this.negated = negated;
+ }
+
+ public Pattern getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(Pattern pattern) {
+ this.pattern = pattern;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public boolean isNegated() {
+ return negated;
+ }
+
+ public void setNegated(boolean negated) {
+ this.negated = negated;
+ }
+
+ @Override
+ public String toString() {
+ Pattern p = getPattern();
+ String purePattern = (p == null) ? null : p.pattern();
+ StringBuilder sb = new StringBuilder(GenericUtils.length(purePattern) + Short.SIZE);
+ if (isNegated()) {
+ sb.append(HostPatternsHolder.NEGATION_CHAR_PATTERN);
+ }
+
+ int portValue = getPort();
+ if (portValue > 0) {
+ sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
+ }
+ sb.append(purePattern);
+ if (portValue > 0) {
+ sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM);
+ sb.append(HostPatternsHolder.PORT_VALUE_DELIMITER);
+ sb.append(portValue);
+ }
+
+ return sb.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
index a3e20ab..eff34d9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
@@ -29,7 +29,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.Pair;
import org.apache.sshd.common.util.ValidateUtils;
/**
@@ -58,17 +57,26 @@ public abstract class HostPatternsHolder {
*/
public static final String PATTERN_CHARS = new String(new char[]{WILDCARD_PATTERN, SINGLE_CHAR_PATTERN, NEGATION_CHAR_PATTERN});
- private Collection<Pair<Pattern, Boolean>> patterns = new LinkedList<>();
+ /** Port value separator if non-standard port pattern used */
+ public static final char PORT_VALUE_DELIMITER = ':';
+
+ /** Non-standard port specification host pattern enclosure start delimiter */
+ public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM = '[';
+
+ /** Non-standard port specification host pattern enclosure end delimiter */
+ public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM = ']';
+
+ private Collection<HostPatternValue> patterns = new LinkedList<>();
protected HostPatternsHolder() {
super();
}
- public Collection<Pair<Pattern, Boolean>> getPatterns() {
+ public Collection<HostPatternValue> getPatterns() {
return patterns;
}
- public void setPatterns(Collection<Pair<Pattern, Boolean>> patterns) {
+ public void setPatterns(Collection<HostPatternValue> patterns) {
this.patterns = patterns;
}
@@ -76,11 +84,12 @@ public abstract class HostPatternsHolder {
* Checks if a given host name / address matches the entry's host pattern(s)
*
* @param host The host name / address - ignored if {@code null}/empty
+ * @param port The connection port
* @return {@code true} if the name / address matches the pattern(s)
* @see #isHostMatch(String, Pattern)
*/
- public boolean isHostMatch(String host) {
- return isHostMatch(host, getPatterns());
+ public boolean isHostMatch(String host, int port) {
+ return isHostMatch(host, port, getPatterns());
}
/**
@@ -138,7 +147,7 @@ public abstract class HostPatternsHolder {
List<HostConfigEntry> matches = null;
for (HostConfigEntry entry : entries) {
- if (!entry.isHostMatch(host)) {
+ if (!entry.isHostMatch(host, 0 /* any port */)) {
continue; // debug breakpoint
}
@@ -156,14 +165,14 @@ public abstract class HostPatternsHolder {
}
}
- public static boolean isHostMatch(String host, Collection<Pair<Pattern, Boolean>> patterns) {
+ public static boolean isHostMatch(String host, int port, Collection<HostPatternValue> patterns) {
if (GenericUtils.isEmpty(patterns)) {
return false;
}
boolean matchFound = false;
- for (Pair<Pattern, Boolean> pp : patterns) {
- Boolean negated = pp.getSecond();
+ for (HostPatternValue pv : patterns) {
+ boolean negated = pv.isNegated();
/*
* If already found a match we are interested only in negations
*/
@@ -171,7 +180,7 @@ public abstract class HostPatternsHolder {
continue;
}
- if (!isHostMatch(host, pp.getFirst())) {
+ if (!isHostMatch(host, pv.getPattern())) {
continue;
}
@@ -185,6 +194,11 @@ public abstract class HostPatternsHolder {
return false;
}
+ int pvPort = pv.getPort();
+ if ((pvPort != 0) && (port != 0) && (pvPort != port)) {
+ continue;
+ }
+
matchFound = true;
}
@@ -207,16 +221,16 @@ public abstract class HostPatternsHolder {
return m.matches();
}
- public static List<Pair<Pattern, Boolean>> parsePatterns(CharSequence... patterns) {
+ public static List<HostPatternValue> parsePatterns(CharSequence... patterns) {
return parsePatterns(GenericUtils.isEmpty(patterns) ? Collections.emptyList() : Arrays.asList(patterns));
}
- public static List<Pair<Pattern, Boolean>> parsePatterns(Collection<? extends CharSequence> patterns) {
+ public static List<HostPatternValue> parsePatterns(Collection<? extends CharSequence> patterns) {
if (GenericUtils.isEmpty(patterns)) {
return Collections.emptyList();
}
- List<Pair<Pattern, Boolean>> result = new ArrayList<>(patterns.size());
+ List<HostPatternValue> result = new ArrayList<>(patterns.size());
for (CharSequence p : patterns) {
result.add(ValidateUtils.checkNotNull(toPattern(p), "No pattern for %s", p));
}
@@ -231,18 +245,38 @@ public abstract class HostPatternsHolder {
* @param pattern The original pattern string - ignored if {@code null}/empty
* @return The regular expression matcher {@link Pattern} and the indication
* whether it is a negating pattern or not - {@code null} if no original string
+ * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM
+ * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM
* @see #WILDCARD_PATTERN
* @see #SINGLE_CHAR_PATTERN
* @see #NEGATION_CHAR_PATTERN
*/
- public static Pair<Pattern, Boolean> toPattern(CharSequence pattern) {
+ public static HostPatternValue toPattern(CharSequence pattern) {
if (GenericUtils.isEmpty(pattern)) {
return null;
}
- StringBuilder sb = new StringBuilder(pattern.length());
+ int patternLen = pattern.length();
+ int port = 0;
+ // Check if non-standard port value used
+ StringBuilder sb = new StringBuilder(patternLen);
+ if (pattern.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
+ int pos = GenericUtils.lastIndexOf(pattern, HostPatternsHolder.PORT_VALUE_DELIMITER);
+ ValidateUtils.checkTrue(pos > 0, "Missing non-standard port value delimiter in %s", pattern);
+ ValidateUtils.checkTrue(pos < (patternLen - 1), "Missing non-standard port value number in %s", pattern);
+ ValidateUtils.checkTrue(pattern.charAt(pos - 1) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM,
+ "Invalid non-standard port value host pattern enclosure delimiters in %s", pattern);
+
+ CharSequence csPort = pattern.subSequence(pos + 1, patternLen);
+ port = Integer.parseInt(csPort.toString());
+ ValidateUtils.checkTrue((port > 0) && (port <= 0xFFFF), "Invalid non-start port value (%d) in %s", port, pattern);
+
+ pattern = pattern.subSequence(1, pos - 1);
+ patternLen = pattern.length();
+ }
+
boolean negated = false;
- for (int curPos = 0; curPos < pattern.length(); curPos++) {
+ for (int curPos = 0; curPos < patternLen; curPos++) {
char ch = pattern.charAt(curPos);
ValidateUtils.checkTrue(isValidPatternChar(ch), "Invalid host pattern char in %s", pattern);
@@ -266,7 +300,7 @@ public abstract class HostPatternsHolder {
}
}
- return new Pair<>(Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE), negated);
+ return new HostPatternValue(Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE), port, negated);
}
/**
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
index da77bac..bf56ddf 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
@@ -118,8 +118,8 @@ public class KnownHostEntry extends HostPatternsHolder {
}
@Override
- public boolean isHostMatch(String host) {
- if (super.isHostMatch(host)) {
+ public boolean isHostMatch(String host, int port) {
+ if (super.isHostMatch(host, port)) {
return true;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
index 48f01bf..8dd6ca6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
@@ -39,6 +39,7 @@ import java.util.Objects;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.sshd.client.config.hosts.HostPatternsHolder;
import org.apache.sshd.client.config.hosts.KnownHostEntry;
import org.apache.sshd.client.config.hosts.KnownHostHashValue;
import org.apache.sshd.client.session.ClientSession;
@@ -490,7 +491,7 @@ public class KnownHostsServerKeyVerifier
return null;
}
- Collection<String> candidates = resolveHostNetworkIdentities(clientSession, remoteAddress);
+ Collection<SshdSocketAddress> candidates = resolveHostNetworkIdentities(clientSession, remoteAddress);
if (log.isDebugEnabled()) {
log.debug("findKnownHostEntry({})[{}] host network identities: {}",
clientSession, remoteAddress, candidates);
@@ -502,9 +503,9 @@ public class KnownHostsServerKeyVerifier
for (HostEntryPair match : knownHosts) {
KnownHostEntry entry = match.getHostEntry();
- for (String host : candidates) {
+ for (SshdSocketAddress host : candidates) {
try {
- if (entry.isHostMatch(host)) {
+ if (entry.isHostMatch(host.getHostName(), host.getPort())) {
if (log.isDebugEnabled()) {
log.debug("findKnownHostEntry({})[{}] matched host={} for entry={}",
clientSession, remoteAddress, host, entry);
@@ -620,8 +621,9 @@ public class KnownHostsServerKeyVerifier
* will be called in order to enable recovery of its data
* @see #resetReloadAttributes()
*/
- protected KnownHostEntry updateKnownHostsFile(ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey,
- Path file, Collection<HostEntryPair> knownHosts) throws Exception {
+ protected KnownHostEntry updateKnownHostsFile(
+ ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey, Path file, Collection<HostEntryPair> knownHosts)
+ throws Exception {
KnownHostEntry entry = prepareKnownHostEntry(clientSession, remoteAddress, serverKey);
if (entry == null) {
if (log.isDebugEnabled()) {
@@ -665,14 +667,14 @@ public class KnownHostsServerKeyVerifier
* @see KnownHostEntry#getConfigLine()
*/
protected KnownHostEntry prepareKnownHostEntry(ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey) throws Exception {
- Collection<String> patterns = resolveHostNetworkIdentities(clientSession, remoteAddress);
+ Collection<SshdSocketAddress> patterns = resolveHostNetworkIdentities(clientSession, remoteAddress);
if (GenericUtils.isEmpty(patterns)) {
return null;
}
StringBuilder sb = new StringBuilder(Byte.MAX_VALUE);
Random rnd = null;
- for (String hostIdentity : patterns) {
+ for (SshdSocketAddress hostIdentity : patterns) {
if (sb.length() > 0) {
sb.append(',');
}
@@ -692,10 +694,20 @@ public class KnownHostsServerKeyVerifier
byte[] salt = new byte[blockSize];
rnd.fill(salt);
- byte[] digestValue = KnownHostHashValue.calculateHashValue(hostIdentity, mac, salt);
+ byte[] digestValue = KnownHostHashValue.calculateHashValue(hostIdentity.getHostName(), mac, salt);
KnownHostHashValue.append(sb, digester, salt, digestValue);
} else {
- sb.append(hostIdentity);
+ int portValue = hostIdentity.getPort();
+ boolean nonDefaultPort = (portValue > 0) && (portValue != SshConfigFileReader.DEFAULT_PORT);
+ if (nonDefaultPort) {
+ sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
+ }
+ sb.append(hostIdentity.getHostName());
+ if (nonDefaultPort) {
+ sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM);
+ sb.append(HostPatternsHolder.PORT_VALUE_DELIMITER);
+ sb.append(portValue);
+ }
}
}
@@ -713,7 +725,7 @@ public class KnownHostsServerKeyVerifier
* @param hostIdentity The entry's host name/address
* @return The digester {@link NamedFactory} - {@code null} if no hashing is to be made
*/
- protected NamedFactory<Mac> getHostValueDigester(ClientSession clientSession, SocketAddress remoteAddress, String hostIdentity) {
+ protected NamedFactory<Mac> getHostValueDigester(ClientSession clientSession, SocketAddress remoteAddress, SshdSocketAddress hostIdentity) {
return null;
}
@@ -724,20 +736,20 @@ public class KnownHostsServerKeyVerifier
*
* @param clientSession The {@link ClientSession}
* @param remoteAddress The remote host address
- * @return A {@link Collection} of the names/addresses to use - if {@code null}/empty
- * then ignored (i.e., no matching is done or no entry is generated)
+ * @return A {@link Collection} of the {@code InetSocketAddress}-es to use - if
+ * {@code null}/empty then ignored (i.e., no matching is done or no entry is generated)
* @see ClientSession#getConnectAddress()
- * @see SshdSocketAddress#toAddressString(SocketAddress)
+ * @see SshdSocketAddress#toSshdSocketAddress(SocketAddress)
*/
- protected Collection<String> resolveHostNetworkIdentities(ClientSession clientSession, SocketAddress remoteAddress) {
+ protected Collection<SshdSocketAddress> resolveHostNetworkIdentities(ClientSession clientSession, SocketAddress remoteAddress) {
/*
* NOTE !!! we do not resolve the fully-qualified name to avoid long DNS timeouts.
* Instead we use the reported peer address and the original connection target host
*/
- Collection<String> candidates = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
- candidates.add(SshdSocketAddress.toAddressString(remoteAddress));
+ Collection<SshdSocketAddress> candidates = new TreeSet<>(SshdSocketAddress.BY_HOST_AND_PORT);
+ candidates.add(SshdSocketAddress.toSshdSocketAddress(remoteAddress));
SocketAddress connectAddress = clientSession.getConnectAddress();
- candidates.add(SshdSocketAddress.toAddressString(connectAddress));
+ candidates.add(SshdSocketAddress.toSshdSocketAddress(connectAddress));
return candidates;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index e2f8733..1f56fec 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -156,6 +156,30 @@ public final class GenericUtils {
return !isEmpty(cs);
}
+ public static int indexOf(CharSequence cs, char c) {
+ int len = length(cs);
+ for (int pos = 0; pos < len; pos++) {
+ char ch = cs.charAt(pos);
+ if (ch == c) {
+ return pos;
+ }
+ }
+
+ return -1;
+ }
+
+ public static int lastIndexOf(CharSequence cs, char c) {
+ int len = length(cs);
+ for (int pos = len - 1; pos >= 0; pos--) {
+ char ch = cs.charAt(pos);
+ if (ch == c) {
+ return pos;
+ }
+ }
+
+ return -1;
+ }
+
// a List would be better, but we want to be compatible with String.split(...)
public static String[] split(String s, char ch) {
if (isEmpty(s)) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java b/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
index c853e88..4919966 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
@@ -78,6 +78,8 @@ public class SshdSocketAddress extends SocketAddress {
/**
* Compares {@link InetAddress}-es according to their {@link InetAddress#getHostAddress()}
* value case <U>insensitive</U>
+ *
+ * @see #toAddressString(InetAddress)
*/
public static final Comparator<InetAddress> BY_HOST_ADDRESS = (a1, a2) -> {
String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
@@ -85,6 +87,31 @@ public class SshdSocketAddress extends SocketAddress {
return String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
};
+ /**
+ * Compares {@link SocketAddress}-es according to their host case <U>insensitive</U>
+ * and if equals, then according to their port value (if any)
+ *
+ * @see #toAddressString(SocketAddress)
+ * @see #toAddressPort(SocketAddress)
+ */
+ public static final Comparator<SocketAddress> BY_HOST_AND_PORT = (a1, a2) -> {
+ String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
+ String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
+ int nRes = String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
+ if (nRes != 0) {
+ return nRes;
+ }
+
+ int p1 = toAddressPort(a1);
+ int p2 = toAddressPort(a2);
+ nRes = Integer.compare(p1, p2);
+ if (nRes != 0) {
+ return nRes;
+ }
+
+ return 0;
+ };
+
private static final long serialVersionUID = 6461645947151952729L;
private final String hostName;
@@ -283,20 +310,46 @@ public class SshdSocketAddress extends SocketAddress {
return true;
}
- public static String toAddressString(SocketAddress addr) {
+ public static SshdSocketAddress toSshdSocketAddress(SocketAddress addr) {
if (addr == null) {
return null;
+ } else if (addr instanceof SshdSocketAddress) {
+ return (SshdSocketAddress) addr;
+ } else if (addr instanceof InetSocketAddress) {
+ InetSocketAddress isockAddress = (InetSocketAddress) addr;
+ return new SshdSocketAddress(isockAddress.getHostName(), isockAddress.getPort());
+ } else {
+ throw new UnsupportedOperationException("Cannot convert " + addr.getClass().getSimpleName()
+ + "=" + addr + " to " + SshdSocketAddress.class.getSimpleName());
}
+ }
- if (addr instanceof InetSocketAddress) {
+ public static String toAddressString(SocketAddress addr) {
+ if (addr == null) {
+ return null;
+ } else if (addr instanceof InetSocketAddress) {
return ((InetSocketAddress) addr).getHostString();
- }
-
- if (addr instanceof SshdSocketAddress) {
+ } else if (addr instanceof SshdSocketAddress) {
return ((SshdSocketAddress) addr).getHostName();
+ } else {
+ return addr.toString();
}
+ }
- return addr.toString();
+ /**
+ * Attempts to resolve the port value
+ *
+ * @param addr The {@link SocketAddress} to examine
+ * @return The associated port value - negative if failed to resolve
+ */
+ public static int toAddressPort(SocketAddress addr) {
+ if (addr instanceof InetSocketAddress) {
+ return ((InetSocketAddress) addr).getPort();
+ } else if (addr instanceof SshdSocketAddress) {
+ return ((SshdSocketAddress) addr).getPort();
+ } else {
+ return -1;
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
index 814deb6..3eeb6d4 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
@@ -28,7 +28,6 @@ import java.util.List;
import java.util.regex.Pattern;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.Pair;
import org.apache.sshd.util.test.BaseTestSupport;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -48,7 +47,7 @@ public class HostConfigEntryTest extends BaseTestSupport {
String testHost = "37.77.34.7";
String[] elements = GenericUtils.split(testHost, '.');
StringBuilder sb = new StringBuilder(testHost.length() + Byte.SIZE);
- List<Pair<Pattern, Boolean>> patterns = new ArrayList<>(elements.length + 1);
+ List<HostPatternValue> patterns = new ArrayList<>(elements.length + 1);
// all wildcard patterns are not negated - only the actual host
patterns.add(HostPatternsHolder.toPattern(String.valueOf(HostPatternsHolder.NEGATION_CHAR_PATTERN) + testHost));
@@ -70,7 +69,7 @@ public class HostConfigEntryTest extends BaseTestSupport {
}
for (int index = 0; index < patterns.size(); index++) {
- assertFalse("Unexpected match for " + patterns, HostPatternsHolder.isHostMatch(testHost, patterns));
+ assertFalse("Unexpected match for " + patterns, HostPatternsHolder.isHostMatch(testHost, 0, patterns));
Collections.shuffle(patterns);
}
}
@@ -85,8 +84,8 @@ public class HostConfigEntryTest extends BaseTestSupport {
}
String value = sb.toString();
- Pair<Pattern, Boolean> pp = HostPatternsHolder.toPattern(value);
- Pattern pattern = pp.getFirst();
+ HostPatternValue pp = HostPatternsHolder.toPattern(value);
+ Pattern pattern = pp.getPattern();
String domain = value.substring(1); // chomp the wildcard prefix
for (String host : new String[] {
getClass().getSimpleName(),
@@ -106,7 +105,7 @@ public class HostConfigEntryTest extends BaseTestSupport {
StringBuilder sb = new StringBuilder().append("10.0.0.");
int sbLen = sb.length();
- Pattern pattern = HostPatternsHolder.toPattern(sb.append(HostPatternsHolder.WILDCARD_PATTERN)).getFirst();
+ Pattern pattern = HostPatternsHolder.toPattern(sb.append(HostPatternsHolder.WILDCARD_PATTERN)).getPattern();
for (int v = 0; v <= 255; v++) {
sb.setLength(sbLen); // start from scratch
sb.append(v);
@@ -123,7 +122,7 @@ public class HostConfigEntryTest extends BaseTestSupport {
for (boolean restoreOriginal : new boolean[] {true, false}) {
for (int index = 0; index < value.length(); index++) {
sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
- testCaseInsensitivePatternMatching(value, HostPatternsHolder.toPattern(sb.toString()).getFirst(), true);
+ testCaseInsensitivePatternMatching(value, HostPatternsHolder.toPattern(sb.toString()).getPattern(), true);
if (restoreOriginal) {
sb.setCharAt(index, value.charAt(index));
}
@@ -147,8 +146,8 @@ public class HostConfigEntryTest extends BaseTestSupport {
}
String pattern = sb.toString();
- Pair<Pattern, Boolean> pp = HostPatternsHolder.toPattern(pattern);
- assertTrue("No match for " + address + " on pattern=" + pattern, HostPatternsHolder.isHostMatch(address, Collections.singletonList(pp)));
+ HostPatternValue pp = HostPatternsHolder.toPattern(pattern);
+ assertTrue("No match for " + address + " on pattern=" + pattern, HostPatternsHolder.isHostMatch(address, 0, Collections.singletonList(pp)));
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
index 4fa4943..9deed93 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
@@ -23,18 +23,21 @@ import java.util.Arrays;
import java.util.Collection;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
public class KnownHostHashValueTest extends BaseTestSupport {
private final String hostName;
private final String hashValue;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java b/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
index fff5c4b..8d11f93 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
@@ -35,6 +35,7 @@ import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.config.hosts.HostPatternsHolder;
import org.apache.sshd.client.config.hosts.KnownHostEntry;
import org.apache.sshd.client.config.hosts.KnownHostHashValue;
import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair;
@@ -63,8 +64,8 @@ import org.mockito.Mockito;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
private static final String HASHED_HOST = "192.168.1.61";
- private static final Map<String, PublicKey> HOST_KEYS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- private static Map<String, KnownHostEntry> hostsEntries;
+ private static final Map<SshdSocketAddress, PublicKey> HOST_KEYS = new TreeMap<>(SshdSocketAddress.BY_HOST_AND_PORT);
+ private static Map<SshdSocketAddress, KnownHostEntry> hostsEntries;
private static Path entriesFile;
public KnownHostsServerKeyVerifierTest() {
@@ -80,12 +81,12 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
hostsEntries = loadEntries(entriesFile);
// Cannot use forEach because of the potential IOException/GeneralSecurityException being thrown
- for (Map.Entry<String, KnownHostEntry> ke : hostsEntries.entrySet()) {
- String host = ke.getKey();
+ for (Map.Entry<SshdSocketAddress, KnownHostEntry> ke : hostsEntries.entrySet()) {
+ SshdSocketAddress hostIdentity = ke.getKey();
KnownHostEntry entry = ke.getValue();
AuthorizedKeyEntry authEntry = ValidateUtils.checkNotNull(entry.getKeyEntry(), "No key extracted from %s", entry);
PublicKey key = authEntry.resolvePublicKey(PublicKeyEntryResolver.FAILING);
- assertNull("Multiple keys for host=" + host, HOST_KEYS.put(host, key));
+ assertNull("Multiple keys for host=" + hostIdentity, HOST_KEYS.put(hostIdentity, key));
}
}
@@ -112,10 +113,10 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
};
- HOST_KEYS.forEach((host, serverKey) -> {
- KnownHostEntry entry = hostsEntries.get(host);
+ HOST_KEYS.forEach((hostIdentity, serverKey) -> {
+ KnownHostEntry entry = hostsEntries.get(hostIdentity);
outputDebugMessage("Verify host=%s", entry);
- assertTrue("Failed to verify server=" + entry, invokeVerifier(verifier, host, serverKey));
+ assertTrue("Failed to verify server=" + entry, invokeVerifier(verifier, hostIdentity, serverKey));
assertEquals("Unexpected delegate invocation for host=" + entry, 0, delegateCount.get());
assertEquals("Unexpected update invocation for host=" + entry, 0, updateCount.get());
});
@@ -146,28 +147,28 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
int verificationCount = 0;
// Cannot use forEach because the verification count variable is not effectively final
- for (Map.Entry<String, PublicKey> ke : HOST_KEYS.entrySet()) {
- String host = ke.getKey();
+ for (Map.Entry<SshdSocketAddress, PublicKey> ke : HOST_KEYS.entrySet()) {
+ SshdSocketAddress hostIdentity = ke.getKey();
PublicKey serverKey = ke.getValue();
- KnownHostEntry entry = hostsEntries.get(host);
+ KnownHostEntry entry = hostsEntries.get(hostIdentity);
outputDebugMessage("Verify host=%s", entry);
- assertTrue("Failed to verify server=" + entry, invokeVerifier(verifier, host, serverKey));
+ assertTrue("Failed to verify server=" + entry, invokeVerifier(verifier, hostIdentity, serverKey));
verificationCount++;
assertEquals("Mismatched number of delegate counts for server=" + entry, verificationCount, delegateCount.get());
assertEquals("Mismatched number of update counts for server=" + entry, verificationCount, updateCount.get());
}
// make sure we have all the original entries and ONLY them
- Map<String, KnownHostEntry> updatedEntries = loadEntries(path);
- hostsEntries.forEach((host, expected) -> {
- KnownHostEntry actual = updatedEntries.remove(host);
- assertNotNull("No updated entry for host=" + host, actual);
+ Map<SshdSocketAddress, KnownHostEntry> updatedEntries = loadEntries(path);
+ hostsEntries.forEach((hostIdentity, expected) -> {
+ KnownHostEntry actual = updatedEntries.remove(hostIdentity);
+ assertNotNull("No updated entry for host=" + hostIdentity, actual);
String expLine = expected.getConfigLine();
// if original is a list or hashed then replace them with the expected host
if ((expLine.indexOf(',') > 0) || (expLine.indexOf(KnownHostHashValue.HASHED_HOST_DELIMITER) >= 0)) {
int pos = expLine.indexOf(' ');
- expLine = host + expLine.substring(pos);
+ expLine = hostIdentity.getHostName() + expLine.substring(pos);
}
int pos = expLine.indexOf("comment-");
@@ -175,7 +176,7 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
expLine = expLine.substring(0, pos).trim();
}
- assertEquals("Mismatched entry data for host=" + host, expLine, actual.getConfigLine());
+ assertEquals("Mismatched entry data for host=" + hostIdentity, expLine, actual.getConfigLine());
});
assertTrue("Unexpected extra updated hosts: " + updatedEntries, updatedEntries.isEmpty());
@@ -188,7 +189,7 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
KnownHostsServerKeyVerifier verifier = new KnownHostsServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE, path) {
@Override
- protected NamedFactory<Mac> getHostValueDigester(ClientSession clientSession, SocketAddress remoteAddress, String hostIdentity) {
+ protected NamedFactory<Mac> getHostValueDigester(ClientSession clientSession, SocketAddress remoteAddress, SshdSocketAddress hostIdentity) {
return KnownHostHashValue.DEFAULT_DIGEST;
}
};
@@ -198,13 +199,12 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
ClientSession session = Mockito.mock(ClientSession.class);
Mockito.when(session.getFactoryManager()).thenReturn(manager);
- HOST_KEYS.forEach((host, serverKey) -> {
- KnownHostEntry entry = hostsEntries.get(host);
+ HOST_KEYS.forEach((hostIdentity, serverKey) -> {
+ KnownHostEntry entry = hostsEntries.get(hostIdentity);
outputDebugMessage("Write host=%s", entry);
- SocketAddress address = new SshdSocketAddress(host, 7365);
- Mockito.when(session.getConnectAddress()).thenReturn(address);
- assertTrue("Failed to validate server=" + entry, verifier.verifyServerKey(session, address, serverKey));
+ Mockito.when(session.getConnectAddress()).thenReturn(hostIdentity);
+ assertTrue("Failed to validate server=" + entry, verifier.verifyServerKey(session, hostIdentity, serverKey));
});
// force re-read to ensure all values are hashed
@@ -216,13 +216,12 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
verifier.setLoadedHostsEntries(keys);
// make sure can still validate the original hosts
- HOST_KEYS.forEach((host, serverKey) -> {
- KnownHostEntry entry = hostsEntries.get(host);
+ HOST_KEYS.forEach((hostIdentity, serverKey) -> {
+ KnownHostEntry entry = hostsEntries.get(hostIdentity);
outputDebugMessage("Re-validate host=%s", entry);
- SocketAddress address = new SshdSocketAddress(host, 7365);
- Mockito.when(session.getConnectAddress()).thenReturn(address);
- assertTrue("Failed to re-validate server=" + entry, verifier.verifyServerKey(session, address, serverKey));
+ Mockito.when(session.getConnectAddress()).thenReturn(hostIdentity);
+ assertTrue("Failed to re-validate server=" + entry, verifier.verifyServerKey(session, hostIdentity, serverKey));
});
}
@@ -244,11 +243,11 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
int validationCount = 0;
// Cannot use forEach because the validation count variable is not effectively final
- for (Map.Entry<String, KnownHostEntry> ke : hostsEntries.entrySet()) {
- String host = ke.getKey();
+ for (Map.Entry<SshdSocketAddress, KnownHostEntry> ke : hostsEntries.entrySet()) {
+ SshdSocketAddress hostIdentity = ke.getKey();
KnownHostEntry entry = ke.getValue();
outputDebugMessage("Verify host=%s", entry);
- assertFalse("Unexpected to verification success for " + entry, invokeVerifier(verifier, host, modifiedKey));
+ assertFalse("Unexpected to verification success for " + entry, invokeVerifier(verifier, hostIdentity, modifiedKey));
validationCount++;
assertEquals("Mismatched invocation count for host=" + entry, validationCount, acceptCount.get());
}
@@ -275,9 +274,9 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
});
String expected = PublicKeyEntry.toString(modifiedKey);
- Map<String, KnownHostEntry> updatedKeys = loadEntries(path);
- hostsEntries.forEach((host, original) -> {
- KnownHostEntry updated = updatedKeys.remove(host);
+ Map<SshdSocketAddress, KnownHostEntry> updatedKeys = loadEntries(path);
+ hostsEntries.forEach((hostIdentity, original) -> {
+ KnownHostEntry updated = updatedKeys.remove(hostIdentity);
assertNotNull("No updated entry for " + original, updated);
String actual = updated.getConfigLine();
@@ -292,7 +291,7 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
}
actual = GenericUtils.trimToEmpty(actual.substring(pos + 1));
- assertEquals("Mismatched updated value for host=" + host, expected, actual);
+ assertEquals("Mismatched updated value for host=" + hostIdentity, expected, actual);
});
assertTrue("Unexpected extra updated entries: " + updatedKeys, updatedKeys.isEmpty());
@@ -310,21 +309,20 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
return file;
}
- private boolean invokeVerifier(ServerKeyVerifier verifier, String host, PublicKey serverKey) {
- SocketAddress address = new SshdSocketAddress(host, 7365);
+ private boolean invokeVerifier(ServerKeyVerifier verifier, SshdSocketAddress hostIdentity, PublicKey serverKey) {
ClientSession session = Mockito.mock(ClientSession.class);
- Mockito.when(session.getConnectAddress()).thenReturn(address);
- Mockito.when(session.toString()).thenReturn(getCurrentTestName() + "[" + host + "]");
- return verifier.verifyServerKey(session, address, serverKey);
+ Mockito.when(session.getConnectAddress()).thenReturn(hostIdentity);
+ Mockito.when(session.toString()).thenReturn(getCurrentTestName() + "[" + hostIdentity + "]");
+ return verifier.verifyServerKey(session, hostIdentity, serverKey);
}
- private static Map<String, KnownHostEntry> loadEntries(Path file) throws IOException {
+ private static Map<SshdSocketAddress, KnownHostEntry> loadEntries(Path file) throws IOException {
Collection<KnownHostEntry> entries = KnownHostEntry.readKnownHostEntries(file);
if (GenericUtils.isEmpty(entries)) {
return Collections.emptyMap();
}
- Map<String, KnownHostEntry> hostsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ Map<SshdSocketAddress, KnownHostEntry> hostsMap = new TreeMap<>(SshdSocketAddress.BY_HOST_AND_PORT);
for (KnownHostEntry entry : entries) {
String line = entry.getConfigLine();
outputDebugMessage("loadTestLines(%s) processing %s", file, line);
@@ -332,11 +330,19 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
int pos = line.indexOf(' ');
String patterns = line.substring(0, pos);
if (entry.getHashedEntry() != null) {
- assertNull("Multiple hashed entries in file", hostsMap.put(HASHED_HOST, entry));
+ assertNull("Multiple hashed entries in file", hostsMap.put(new SshdSocketAddress(HASHED_HOST, 0), entry));
} else {
String[] addrs = GenericUtils.split(patterns, ',');
for (String a : addrs) {
- assertNull("Multiple entries for address=" + a, hostsMap.put(a, entry));
+ int port = 0;
+ if (a.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
+ pos = a.indexOf(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM, 1);
+ assertTrue("Missing non-standard port host pattern enclosure: " + a, pos > 0);
+
+ port = Integer.parseInt(a.substring(pos + 2));
+ a = a.substring(1, pos);
+ }
+ assertNull("Multiple entries for address=" + a, hostsMap.put(new SshdSocketAddress(a, port), entry));
}
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2119f798/sshd-core/src/test/resources/org/apache/sshd/client/keyverifier/known_hosts
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/keyverifier/known_hosts b/sshd-core/src/test/resources/org/apache/sshd/client/keyverifier/known_hosts
index 2f8034b..f842391 100644
--- a/sshd-core/src/test/resources/org/apache/sshd/client/keyverifier/known_hosts
+++ b/sshd-core/src/test/resources/org/apache/sshd/client/keyverifier/known_hosts
@@ -9,4 +9,7 @@ server.sshd.apache.org,10.23.222.240 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbml
192.168.174.129 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJvwXQ0Wc7TdhmjaTdkrHJvatWrw/6t6W1Hgh2nTauN+rhsMKYbmQeFTnzsP1NctWzQXwsqIOGcXIMNVhT92jgQ=
# hash of 192.168.1.61 - DO NOT CHANGE IT !!!
-|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2KFr3GqL/3yXY2bAwRGGDxl/qLuE9qdx20+DMh5oAZPpwprlUnlxLm+ikimwn65Z0KeUyfofYKt+vc3rl1k2mDqyG8DqHeH0C+uFBbom0fthX7PRiQr2T9SOzSodjowZuBHlWIfgtcZI0bygX+GlKaAq00l4yCoe1xUTCRd2ZVyNuB1nozcFI+sUzdeKfaxvuyvbccG4tOx06HDryNdxW2e99bsAhLAg7d8xciOeb4PCAI1USg83dt0wVZE9VJbnRnoZ2y/DaQCJtBJ8t8uNLVdggakydDzQuglyd4dYRxeU7t4TEw6wsfXPB0kqdecd0Llspjx0ciEY/BbycdiApw== comment-hashed-host
\ No newline at end of file
+|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2KFr3GqL/3yXY2bAwRGGDxl/qLuE9qdx20+DMh5oAZPpwprlUnlxLm+ikimwn65Z0KeUyfofYKt+vc3rl1k2mDqyG8DqHeH0C+uFBbom0fthX7PRiQr2T9SOzSodjowZuBHlWIfgtcZI0bygX+GlKaAq00l4yCoe1xUTCRd2ZVyNuB1nozcFI+sUzdeKfaxvuyvbccG4tOx06HDryNdxW2e99bsAhLAg7d8xciOeb4PCAI1USg83dt0wVZE9VJbnRnoZ2y/DaQCJtBJ8t8uNLVdggakydDzQuglyd4dYRxeU7t4TEw6wsfXPB0kqdecd0Llspjx0ciEY/BbycdiApw== comment-hashed-host
+
+# non-standard port overrides
+[issues.apache.org]:5637 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCWDHD00Ltb5fmmL9cFLTqWqxgJHwsxbiZgL632CXqbDmf69wA+8GSP96rtIix2d5aGXyh/kXMbSMjPgIx+n7p0=
\ No newline at end of file