xref: /JGit/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/OpenSshConfig.java (revision 8d2d683655e2de17cf465fa46af10e0e56b3aaed)
1*8d2d6836SMatthias Sohn /*
2*8d2d6836SMatthias Sohn  * Copyright (C) 2008, 2018, Google Inc. and others
3*8d2d6836SMatthias Sohn  *
4*8d2d6836SMatthias Sohn  * This program and the accompanying materials are made available under the
5*8d2d6836SMatthias Sohn  * terms of the Eclipse Distribution License v. 1.0 which is available at
6*8d2d6836SMatthias Sohn  * https://www.eclipse.org/org/documents/edl-v10.php.
7*8d2d6836SMatthias Sohn  *
8*8d2d6836SMatthias Sohn  * SPDX-License-Identifier: BSD-3-Clause
9*8d2d6836SMatthias Sohn  */
10*8d2d6836SMatthias Sohn 
11*8d2d6836SMatthias Sohn //TODO(ms): move to org.eclipse.jgit.ssh.jsch in 6.0
12*8d2d6836SMatthias Sohn package org.eclipse.jgit.transport;
13*8d2d6836SMatthias Sohn 
14*8d2d6836SMatthias Sohn import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive;
15*8d2d6836SMatthias Sohn 
16*8d2d6836SMatthias Sohn import java.io.File;
17*8d2d6836SMatthias Sohn import java.util.List;
18*8d2d6836SMatthias Sohn import java.util.Map;
19*8d2d6836SMatthias Sohn import java.util.TreeMap;
20*8d2d6836SMatthias Sohn 
21*8d2d6836SMatthias Sohn import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
22*8d2d6836SMatthias Sohn import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.HostEntry;
23*8d2d6836SMatthias Sohn import org.eclipse.jgit.util.FS;
24*8d2d6836SMatthias Sohn 
25*8d2d6836SMatthias Sohn import com.jcraft.jsch.ConfigRepository;
26*8d2d6836SMatthias Sohn 
27*8d2d6836SMatthias Sohn /**
28*8d2d6836SMatthias Sohn  * Fairly complete configuration parser for the OpenSSH ~/.ssh/config file.
29*8d2d6836SMatthias Sohn  * <p>
30*8d2d6836SMatthias Sohn  * JSch does have its own config file parser
31*8d2d6836SMatthias Sohn  * {@link com.jcraft.jsch.OpenSSHConfig} since version 0.1.50, but it has a
32*8d2d6836SMatthias Sohn  * number of problems:
33*8d2d6836SMatthias Sohn  * <ul>
34*8d2d6836SMatthias Sohn  * <li>it splits lines of the format "keyword = value" wrongly: you'd end up
35*8d2d6836SMatthias Sohn  * with the value "= value".
36*8d2d6836SMatthias Sohn  * <li>its "Host" keyword is not case insensitive.
37*8d2d6836SMatthias Sohn  * <li>it doesn't handle quoted values.
38*8d2d6836SMatthias Sohn  * <li>JSch's OpenSSHConfig doesn't monitor for config file changes.
39*8d2d6836SMatthias Sohn  * </ul>
40*8d2d6836SMatthias Sohn  * <p>
41*8d2d6836SMatthias Sohn  * This parser makes the critical options available to
42*8d2d6836SMatthias Sohn  * {@link org.eclipse.jgit.transport.SshSessionFactory} via
43*8d2d6836SMatthias Sohn  * {@link org.eclipse.jgit.transport.OpenSshConfig.Host} objects returned
44*8d2d6836SMatthias Sohn  * by {@link #lookup(String)}, and implements a fully conforming
45*8d2d6836SMatthias Sohn  * {@link com.jcraft.jsch.ConfigRepository} providing
46*8d2d6836SMatthias Sohn  * {@link com.jcraft.jsch.ConfigRepository.Config}s via
47*8d2d6836SMatthias Sohn  * {@link #getConfig(String)}.
48*8d2d6836SMatthias Sohn  * </p>
49*8d2d6836SMatthias Sohn  *
50*8d2d6836SMatthias Sohn  * @see OpenSshConfigFile
51*8d2d6836SMatthias Sohn  */
52*8d2d6836SMatthias Sohn public class OpenSshConfig implements ConfigRepository {
53*8d2d6836SMatthias Sohn 
54*8d2d6836SMatthias Sohn 	/**
55*8d2d6836SMatthias Sohn 	 * Obtain the user's configuration data.
56*8d2d6836SMatthias Sohn 	 * <p>
57*8d2d6836SMatthias Sohn 	 * The configuration file is always returned to the caller, even if no file
58*8d2d6836SMatthias Sohn 	 * exists in the user's home directory at the time the call was made. Lookup
59*8d2d6836SMatthias Sohn 	 * requests are cached and are automatically updated if the user modifies
60*8d2d6836SMatthias Sohn 	 * the configuration file since the last time it was cached.
61*8d2d6836SMatthias Sohn 	 *
62*8d2d6836SMatthias Sohn 	 * @param fs
63*8d2d6836SMatthias Sohn 	 *            the file system abstraction which will be necessary to
64*8d2d6836SMatthias Sohn 	 *            perform certain file system operations.
65*8d2d6836SMatthias Sohn 	 * @return a caching reader of the user's configuration file.
66*8d2d6836SMatthias Sohn 	 */
get(FS fs)67*8d2d6836SMatthias Sohn 	public static OpenSshConfig get(FS fs) {
68*8d2d6836SMatthias Sohn 		File home = fs.userHome();
69*8d2d6836SMatthias Sohn 		if (home == null)
70*8d2d6836SMatthias Sohn 			home = new File(".").getAbsoluteFile(); //$NON-NLS-1$
71*8d2d6836SMatthias Sohn 
72*8d2d6836SMatthias Sohn 		final File config = new File(new File(home, SshConstants.SSH_DIR),
73*8d2d6836SMatthias Sohn 				SshConstants.CONFIG);
74*8d2d6836SMatthias Sohn 		return new OpenSshConfig(home, config);
75*8d2d6836SMatthias Sohn 	}
76*8d2d6836SMatthias Sohn 
77*8d2d6836SMatthias Sohn 	/** The base file. */
78*8d2d6836SMatthias Sohn 	private OpenSshConfigFile configFile;
79*8d2d6836SMatthias Sohn 
OpenSshConfig(File h, File cfg)80*8d2d6836SMatthias Sohn 	OpenSshConfig(File h, File cfg) {
81*8d2d6836SMatthias Sohn 		configFile = new OpenSshConfigFile(h, cfg,
82*8d2d6836SMatthias Sohn 				SshSessionFactory.getLocalUserName());
83*8d2d6836SMatthias Sohn 	}
84*8d2d6836SMatthias Sohn 
85*8d2d6836SMatthias Sohn 	/**
86*8d2d6836SMatthias Sohn 	 * Locate the configuration for a specific host request.
87*8d2d6836SMatthias Sohn 	 *
88*8d2d6836SMatthias Sohn 	 * @param hostName
89*8d2d6836SMatthias Sohn 	 *            the name the user has supplied to the SSH tool. This may be a
90*8d2d6836SMatthias Sohn 	 *            real host name, or it may just be a "Host" block in the
91*8d2d6836SMatthias Sohn 	 *            configuration file.
92*8d2d6836SMatthias Sohn 	 * @return r configuration for the requested name. Never null.
93*8d2d6836SMatthias Sohn 	 */
lookup(String hostName)94*8d2d6836SMatthias Sohn 	public Host lookup(String hostName) {
95*8d2d6836SMatthias Sohn 		HostEntry entry = configFile.lookup(hostName, -1, null);
96*8d2d6836SMatthias Sohn 		return new Host(entry, hostName, configFile.getLocalUserName());
97*8d2d6836SMatthias Sohn 	}
98*8d2d6836SMatthias Sohn 
99*8d2d6836SMatthias Sohn 	/**
100*8d2d6836SMatthias Sohn 	 * Configuration of one "Host" block in the configuration file.
101*8d2d6836SMatthias Sohn 	 * <p>
102*8d2d6836SMatthias Sohn 	 * If returned from {@link OpenSshConfig#lookup(String)} some or all of the
103*8d2d6836SMatthias Sohn 	 * properties may not be populated. The properties which are not populated
104*8d2d6836SMatthias Sohn 	 * should be defaulted by the caller.
105*8d2d6836SMatthias Sohn 	 * <p>
106*8d2d6836SMatthias Sohn 	 * When returned from {@link OpenSshConfig#lookup(String)} any wildcard
107*8d2d6836SMatthias Sohn 	 * entries which appear later in the configuration file will have been
108*8d2d6836SMatthias Sohn 	 * already merged into this block.
109*8d2d6836SMatthias Sohn 	 */
110*8d2d6836SMatthias Sohn 	public static class Host {
111*8d2d6836SMatthias Sohn 		String hostName;
112*8d2d6836SMatthias Sohn 
113*8d2d6836SMatthias Sohn 		int port;
114*8d2d6836SMatthias Sohn 
115*8d2d6836SMatthias Sohn 		File identityFile;
116*8d2d6836SMatthias Sohn 
117*8d2d6836SMatthias Sohn 		String user;
118*8d2d6836SMatthias Sohn 
119*8d2d6836SMatthias Sohn 		String preferredAuthentications;
120*8d2d6836SMatthias Sohn 
121*8d2d6836SMatthias Sohn 		Boolean batchMode;
122*8d2d6836SMatthias Sohn 
123*8d2d6836SMatthias Sohn 		String strictHostKeyChecking;
124*8d2d6836SMatthias Sohn 
125*8d2d6836SMatthias Sohn 		int connectionAttempts;
126*8d2d6836SMatthias Sohn 
127*8d2d6836SMatthias Sohn 		private HostEntry entry;
128*8d2d6836SMatthias Sohn 
129*8d2d6836SMatthias Sohn 		private Config config;
130*8d2d6836SMatthias Sohn 
131*8d2d6836SMatthias Sohn 		// See com.jcraft.jsch.OpenSSHConfig. Translates some command-line keys
132*8d2d6836SMatthias Sohn 		// to ssh-config keys.
133*8d2d6836SMatthias Sohn 		private static final Map<String, String> KEY_MAP = new TreeMap<>(
134*8d2d6836SMatthias Sohn 				String.CASE_INSENSITIVE_ORDER);
135*8d2d6836SMatthias Sohn 
136*8d2d6836SMatthias Sohn 		static {
137*8d2d6836SMatthias Sohn 			KEY_MAP.put("kex", SshConstants.KEX_ALGORITHMS); //$NON-NLS-1$
138*8d2d6836SMatthias Sohn 			KEY_MAP.put("server_host_key", SshConstants.HOST_KEY_ALGORITHMS); //$NON-NLS-1$
139*8d2d6836SMatthias Sohn 			KEY_MAP.put("cipher.c2s", SshConstants.CIPHERS); //$NON-NLS-1$
140*8d2d6836SMatthias Sohn 			KEY_MAP.put("cipher.s2c", SshConstants.CIPHERS); //$NON-NLS-1$
141*8d2d6836SMatthias Sohn 			KEY_MAP.put("mac.c2s", SshConstants.MACS); //$NON-NLS-1$
142*8d2d6836SMatthias Sohn 			KEY_MAP.put("mac.s2c", SshConstants.MACS); //$NON-NLS-1$
143*8d2d6836SMatthias Sohn 			KEY_MAP.put("compression.s2c", SshConstants.COMPRESSION); //$NON-NLS-1$
144*8d2d6836SMatthias Sohn 			KEY_MAP.put("compression.c2s", SshConstants.COMPRESSION); //$NON-NLS-1$
145*8d2d6836SMatthias Sohn 			KEY_MAP.put("compression_level", "CompressionLevel"); //$NON-NLS-1$ //$NON-NLS-2$
146*8d2d6836SMatthias Sohn 			KEY_MAP.put("MaxAuthTries", //$NON-NLS-1$
147*8d2d6836SMatthias Sohn 					SshConstants.NUMBER_OF_PASSWORD_PROMPTS);
148*8d2d6836SMatthias Sohn 		}
149*8d2d6836SMatthias Sohn 
mapKey(String key)150*8d2d6836SMatthias Sohn 		private static String mapKey(String key) {
151*8d2d6836SMatthias Sohn 			String k = KEY_MAP.get(key);
152*8d2d6836SMatthias Sohn 			return k != null ? k : key;
153*8d2d6836SMatthias Sohn 		}
154*8d2d6836SMatthias Sohn 
155*8d2d6836SMatthias Sohn 		/**
156*8d2d6836SMatthias Sohn 		 * Creates a new uninitialized {@link Host}.
157*8d2d6836SMatthias Sohn 		 */
Host()158*8d2d6836SMatthias Sohn 		public Host() {
159*8d2d6836SMatthias Sohn 			// For API backwards compatibility with pre-4.9 JGit
160*8d2d6836SMatthias Sohn 		}
161*8d2d6836SMatthias Sohn 
Host(HostEntry entry, String hostName, String localUserName)162*8d2d6836SMatthias Sohn 		Host(HostEntry entry, String hostName, String localUserName) {
163*8d2d6836SMatthias Sohn 			this.entry = entry;
164*8d2d6836SMatthias Sohn 			complete(hostName, localUserName);
165*8d2d6836SMatthias Sohn 		}
166*8d2d6836SMatthias Sohn 
167*8d2d6836SMatthias Sohn 		/**
168*8d2d6836SMatthias Sohn 		 * @return the value StrictHostKeyChecking property, the valid values
169*8d2d6836SMatthias Sohn 		 *         are "yes" (unknown hosts are not accepted), "no" (unknown
170*8d2d6836SMatthias Sohn 		 *         hosts are always accepted), and "ask" (user should be asked
171*8d2d6836SMatthias Sohn 		 *         before accepting the host)
172*8d2d6836SMatthias Sohn 		 */
getStrictHostKeyChecking()173*8d2d6836SMatthias Sohn 		public String getStrictHostKeyChecking() {
174*8d2d6836SMatthias Sohn 			return strictHostKeyChecking;
175*8d2d6836SMatthias Sohn 		}
176*8d2d6836SMatthias Sohn 
177*8d2d6836SMatthias Sohn 		/**
178*8d2d6836SMatthias Sohn 		 * @return the real IP address or host name to connect to; never null.
179*8d2d6836SMatthias Sohn 		 */
getHostName()180*8d2d6836SMatthias Sohn 		public String getHostName() {
181*8d2d6836SMatthias Sohn 			return hostName;
182*8d2d6836SMatthias Sohn 		}
183*8d2d6836SMatthias Sohn 
184*8d2d6836SMatthias Sohn 		/**
185*8d2d6836SMatthias Sohn 		 * @return the real port number to connect to; never 0.
186*8d2d6836SMatthias Sohn 		 */
getPort()187*8d2d6836SMatthias Sohn 		public int getPort() {
188*8d2d6836SMatthias Sohn 			return port;
189*8d2d6836SMatthias Sohn 		}
190*8d2d6836SMatthias Sohn 
191*8d2d6836SMatthias Sohn 		/**
192*8d2d6836SMatthias Sohn 		 * @return path of the private key file to use for authentication; null
193*8d2d6836SMatthias Sohn 		 *         if the caller should use default authentication strategies.
194*8d2d6836SMatthias Sohn 		 */
getIdentityFile()195*8d2d6836SMatthias Sohn 		public File getIdentityFile() {
196*8d2d6836SMatthias Sohn 			return identityFile;
197*8d2d6836SMatthias Sohn 		}
198*8d2d6836SMatthias Sohn 
199*8d2d6836SMatthias Sohn 		/**
200*8d2d6836SMatthias Sohn 		 * @return the real user name to connect as; never null.
201*8d2d6836SMatthias Sohn 		 */
getUser()202*8d2d6836SMatthias Sohn 		public String getUser() {
203*8d2d6836SMatthias Sohn 			return user;
204*8d2d6836SMatthias Sohn 		}
205*8d2d6836SMatthias Sohn 
206*8d2d6836SMatthias Sohn 		/**
207*8d2d6836SMatthias Sohn 		 * @return the preferred authentication methods, separated by commas if
208*8d2d6836SMatthias Sohn 		 *         more than one authentication method is preferred.
209*8d2d6836SMatthias Sohn 		 */
getPreferredAuthentications()210*8d2d6836SMatthias Sohn 		public String getPreferredAuthentications() {
211*8d2d6836SMatthias Sohn 			return preferredAuthentications;
212*8d2d6836SMatthias Sohn 		}
213*8d2d6836SMatthias Sohn 
214*8d2d6836SMatthias Sohn 		/**
215*8d2d6836SMatthias Sohn 		 * @return true if batch (non-interactive) mode is preferred for this
216*8d2d6836SMatthias Sohn 		 *         host connection.
217*8d2d6836SMatthias Sohn 		 */
isBatchMode()218*8d2d6836SMatthias Sohn 		public boolean isBatchMode() {
219*8d2d6836SMatthias Sohn 			return batchMode != null && batchMode.booleanValue();
220*8d2d6836SMatthias Sohn 		}
221*8d2d6836SMatthias Sohn 
222*8d2d6836SMatthias Sohn 		/**
223*8d2d6836SMatthias Sohn 		 * @return the number of tries (one per second) to connect before
224*8d2d6836SMatthias Sohn 		 *         exiting. The argument must be an integer. This may be useful
225*8d2d6836SMatthias Sohn 		 *         in scripts if the connection sometimes fails. The default is
226*8d2d6836SMatthias Sohn 		 *         1.
227*8d2d6836SMatthias Sohn 		 * @since 3.4
228*8d2d6836SMatthias Sohn 		 */
getConnectionAttempts()229*8d2d6836SMatthias Sohn 		public int getConnectionAttempts() {
230*8d2d6836SMatthias Sohn 			return connectionAttempts;
231*8d2d6836SMatthias Sohn 		}
232*8d2d6836SMatthias Sohn 
233*8d2d6836SMatthias Sohn 
complete(String initialHostName, String localUserName)234*8d2d6836SMatthias Sohn 		private void complete(String initialHostName, String localUserName) {
235*8d2d6836SMatthias Sohn 			// Try to set values from the options.
236*8d2d6836SMatthias Sohn 			hostName = entry.getValue(SshConstants.HOST_NAME);
237*8d2d6836SMatthias Sohn 			user = entry.getValue(SshConstants.USER);
238*8d2d6836SMatthias Sohn 			port = positive(entry.getValue(SshConstants.PORT));
239*8d2d6836SMatthias Sohn 			connectionAttempts = positive(
240*8d2d6836SMatthias Sohn 					entry.getValue(SshConstants.CONNECTION_ATTEMPTS));
241*8d2d6836SMatthias Sohn 			strictHostKeyChecking = entry
242*8d2d6836SMatthias Sohn 					.getValue(SshConstants.STRICT_HOST_KEY_CHECKING);
243*8d2d6836SMatthias Sohn 			batchMode = Boolean.valueOf(OpenSshConfigFile
244*8d2d6836SMatthias Sohn 					.flag(entry.getValue(SshConstants.BATCH_MODE)));
245*8d2d6836SMatthias Sohn 			preferredAuthentications = entry
246*8d2d6836SMatthias Sohn 					.getValue(SshConstants.PREFERRED_AUTHENTICATIONS);
247*8d2d6836SMatthias Sohn 			// Fill in defaults if still not set
248*8d2d6836SMatthias Sohn 			if (hostName == null || hostName.isEmpty()) {
249*8d2d6836SMatthias Sohn 				hostName = initialHostName;
250*8d2d6836SMatthias Sohn 			}
251*8d2d6836SMatthias Sohn 			if (user == null || user.isEmpty()) {
252*8d2d6836SMatthias Sohn 				user = localUserName;
253*8d2d6836SMatthias Sohn 			}
254*8d2d6836SMatthias Sohn 			if (port <= 0) {
255*8d2d6836SMatthias Sohn 				port = SshConstants.SSH_DEFAULT_PORT;
256*8d2d6836SMatthias Sohn 			}
257*8d2d6836SMatthias Sohn 			if (connectionAttempts <= 0) {
258*8d2d6836SMatthias Sohn 				connectionAttempts = 1;
259*8d2d6836SMatthias Sohn 			}
260*8d2d6836SMatthias Sohn 			List<String> identityFiles = entry
261*8d2d6836SMatthias Sohn 					.getValues(SshConstants.IDENTITY_FILE);
262*8d2d6836SMatthias Sohn 			if (identityFiles != null && !identityFiles.isEmpty()) {
263*8d2d6836SMatthias Sohn 				identityFile = new File(identityFiles.get(0));
264*8d2d6836SMatthias Sohn 			}
265*8d2d6836SMatthias Sohn 		}
266*8d2d6836SMatthias Sohn 
getConfig()267*8d2d6836SMatthias Sohn 		Config getConfig() {
268*8d2d6836SMatthias Sohn 			if (config == null) {
269*8d2d6836SMatthias Sohn 				config = new Config() {
270*8d2d6836SMatthias Sohn 
271*8d2d6836SMatthias Sohn 					@Override
272*8d2d6836SMatthias Sohn 					public String getHostname() {
273*8d2d6836SMatthias Sohn 						return Host.this.getHostName();
274*8d2d6836SMatthias Sohn 					}
275*8d2d6836SMatthias Sohn 
276*8d2d6836SMatthias Sohn 					@Override
277*8d2d6836SMatthias Sohn 					public String getUser() {
278*8d2d6836SMatthias Sohn 						return Host.this.getUser();
279*8d2d6836SMatthias Sohn 					}
280*8d2d6836SMatthias Sohn 
281*8d2d6836SMatthias Sohn 					@Override
282*8d2d6836SMatthias Sohn 					public int getPort() {
283*8d2d6836SMatthias Sohn 						return Host.this.getPort();
284*8d2d6836SMatthias Sohn 					}
285*8d2d6836SMatthias Sohn 
286*8d2d6836SMatthias Sohn 					@Override
287*8d2d6836SMatthias Sohn 					public String getValue(String key) {
288*8d2d6836SMatthias Sohn 						// See com.jcraft.jsch.OpenSSHConfig.MyConfig.getValue()
289*8d2d6836SMatthias Sohn 						// for this special case.
290*8d2d6836SMatthias Sohn 						if (key.equals("compression.s2c") //$NON-NLS-1$
291*8d2d6836SMatthias Sohn 								|| key.equals("compression.c2s")) { //$NON-NLS-1$
292*8d2d6836SMatthias Sohn 							if (!OpenSshConfigFile.flag(
293*8d2d6836SMatthias Sohn 									Host.this.entry.getValue(mapKey(key)))) {
294*8d2d6836SMatthias Sohn 								return "none,zlib@openssh.com,zlib"; //$NON-NLS-1$
295*8d2d6836SMatthias Sohn 							}
296*8d2d6836SMatthias Sohn 							return "zlib@openssh.com,zlib,none"; //$NON-NLS-1$
297*8d2d6836SMatthias Sohn 						}
298*8d2d6836SMatthias Sohn 						return Host.this.entry.getValue(mapKey(key));
299*8d2d6836SMatthias Sohn 					}
300*8d2d6836SMatthias Sohn 
301*8d2d6836SMatthias Sohn 					@Override
302*8d2d6836SMatthias Sohn 					public String[] getValues(String key) {
303*8d2d6836SMatthias Sohn 						List<String> values = Host.this.entry
304*8d2d6836SMatthias Sohn 								.getValues(mapKey(key));
305*8d2d6836SMatthias Sohn 						if (values == null) {
306*8d2d6836SMatthias Sohn 							return new String[0];
307*8d2d6836SMatthias Sohn 						}
308*8d2d6836SMatthias Sohn 						return values.toArray(new String[0]);
309*8d2d6836SMatthias Sohn 					}
310*8d2d6836SMatthias Sohn 				};
311*8d2d6836SMatthias Sohn 			}
312*8d2d6836SMatthias Sohn 			return config;
313*8d2d6836SMatthias Sohn 		}
314*8d2d6836SMatthias Sohn 
315*8d2d6836SMatthias Sohn 		@Override
316*8d2d6836SMatthias Sohn 		@SuppressWarnings("nls")
toString()317*8d2d6836SMatthias Sohn 		public String toString() {
318*8d2d6836SMatthias Sohn 			return "Host [hostName=" + hostName + ", port=" + port
319*8d2d6836SMatthias Sohn 					+ ", identityFile=" + identityFile + ", user=" + user
320*8d2d6836SMatthias Sohn 					+ ", preferredAuthentications=" + preferredAuthentications
321*8d2d6836SMatthias Sohn 					+ ", batchMode=" + batchMode + ", strictHostKeyChecking="
322*8d2d6836SMatthias Sohn 					+ strictHostKeyChecking + ", connectionAttempts="
323*8d2d6836SMatthias Sohn 					+ connectionAttempts + ", entry=" + entry + "]";
324*8d2d6836SMatthias Sohn 		}
325*8d2d6836SMatthias Sohn 	}
326*8d2d6836SMatthias Sohn 
327*8d2d6836SMatthias Sohn 	/**
328*8d2d6836SMatthias Sohn 	 * {@inheritDoc}
329*8d2d6836SMatthias Sohn 	 * <p>
330*8d2d6836SMatthias Sohn 	 * Retrieves the full {@link com.jcraft.jsch.ConfigRepository.Config Config}
331*8d2d6836SMatthias Sohn 	 * for the given host name. Should be called only by Jsch and tests.
332*8d2d6836SMatthias Sohn 	 *
333*8d2d6836SMatthias Sohn 	 * @since 4.9
334*8d2d6836SMatthias Sohn 	 */
335*8d2d6836SMatthias Sohn 	@Override
getConfig(String hostName)336*8d2d6836SMatthias Sohn 	public Config getConfig(String hostName) {
337*8d2d6836SMatthias Sohn 		Host host = lookup(hostName);
338*8d2d6836SMatthias Sohn 		return host.getConfig();
339*8d2d6836SMatthias Sohn 	}
340*8d2d6836SMatthias Sohn 
341*8d2d6836SMatthias Sohn 	/** {@inheritDoc} */
342*8d2d6836SMatthias Sohn 	@Override
toString()343*8d2d6836SMatthias Sohn 	public String toString() {
344*8d2d6836SMatthias Sohn 		return "OpenSshConfig [configFile=" + configFile + ']'; //$NON-NLS-1$
345*8d2d6836SMatthias Sohn 	}
346*8d2d6836SMatthias Sohn }
347