Skip to content

Commit 6329970

Browse files
committed
Refactor transport manager to fix invalid socket state introduced in r47. Fix issue #16.
1 parent d64f4c6 commit 6329970

File tree

8 files changed

+1896
-2025
lines changed

8 files changed

+1896
-2025
lines changed

src/main/java/ch/ethz/ssh2/Connection.java

Lines changed: 1145 additions & 1195 deletions
Large diffs are not rendered by default.

src/main/java/ch/ethz/ssh2/ServerConnection.java

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
import ch.ethz.ssh2.server.ServerConnectionState;
1717
import ch.ethz.ssh2.signature.DSAPrivateKey;
1818
import ch.ethz.ssh2.signature.RSAPrivateKey;
19-
import ch.ethz.ssh2.transport.TransportManager;
19+
import ch.ethz.ssh2.transport.ServerTransportManager;
2020

2121
/**
2222
* A server-side SSH-2 connection.
23-
*
23+
*
2424
* @author Christian
2525
*
2626
*/
@@ -39,9 +39,9 @@ public class ServerConnection
3939
* <p>
4040
* Note: you need to call {@link #connect()} or {@link #connect(int)} to
4141
* perform the initial handshake and establish the encrypted communication.
42-
*
42+
*
4343
* @see #connect(int)
44-
*
44+
*
4545
* @param s The socket
4646
*/
4747
public ServerConnection(Socket s)
@@ -62,9 +62,9 @@ public ServerConnection(Socket s, String softwareversion) {
6262
* perform the initial handshake and establish the encrypted communication.
6363
* <p>
6464
* Please read the javadoc for the {@link #connect(int)} method.
65-
*
65+
*
6666
* @see #connect(int)
67-
*
67+
*
6868
* @param s The socket
6969
* @param dsa_key The DSA hostkey, may be <code>NULL</code>
7070
* @param rsa_key The RSA hostkey, may be <code>NULL</code>
@@ -84,9 +84,9 @@ public ServerConnection(Socket s, DSAPrivateKey dsa_key, RSAPrivateKey rsa_key)
8484
* Note: this is a wrapper that calls <code>connect(0)</code> (i.e., connect with no timeout).
8585
* <p>
8686
* Please read the javadoc for the {@link #connect(int)} method.
87-
*
87+
*
8888
* @see #connect(int)
89-
*
89+
*
9090
* @throws IOException
9191
*/
9292
public synchronized void connect() throws IOException
@@ -101,12 +101,12 @@ public synchronized void connect() throws IOException
101101
* <p>
102102
* Note 2: You must set the callbacks for authentication ({@link #setAuthenticationCallback(ServerAuthenticationCallback)})
103103
* and connection events ({@link #setServerConnectionCallback(ServerConnectionCallback)}).
104-
*
104+
*
105105
* @see #setPEMHostKey(char[], String)
106106
* @see #setPEMHostKey(File, String)
107107
* @see #setRsaHostKey(RSAPrivateKey)
108108
* @see #setDsaHostKey(DSAPrivateKey)
109-
*
109+
*
110110
* @param timeout_milliseconds Timeout in milliseconds, <code>0</code> means no timeout.
111111
* @throws IOException
112112
*/
@@ -126,14 +126,10 @@ public synchronized void connect(int timeout_milliseconds) throws IOException
126126
if ((state.next_dsa_key == null) && (state.next_rsa_key == null))
127127
throw new IllegalStateException("Neither a RSA nor a DSA host key has been specified!");
128128

129-
state.tm = new TransportManager();
129+
state.tm = new ServerTransportManager(state.s);
130130
}
131131

132-
//tm.setSoTimeout(connectTimeout);
133-
//tm.setConnectionMonitors(connectionMonitors);
134-
135-
state.tm.setTcpNoDelay(true);
136-
state.tm.serverInit(state);
132+
state.tm.connect(state);
137133

138134
/* Wait until first KEX has finished */
139135

@@ -142,7 +138,7 @@ public synchronized void connect(int timeout_milliseconds) throws IOException
142138

143139
/**
144140
* Retrieve the underlying socket.
145-
*
141+
*
146142
* @return the socket that has been passed to the constructor.
147143
*/
148144
public Socket getSocket()
@@ -160,7 +156,7 @@ public Socket getSocket()
160156
* <p>
161157
* Note: This implementation will never start automatically a key exchange (other than the initial one)
162158
* unless you or the connected SSH-2 client ask for it.
163-
*
159+
*
164160
* @throws IOException
165161
* In case of any failure behind the scenes.
166162
*/
@@ -179,10 +175,10 @@ public synchronized void forceKeyExchange() throws IOException
179175
/**
180176
* Returns a {@link ConnectionInfo} object containing the details of
181177
* the connection. May be called as soon as the first key exchange has been
182-
* started. The method blocks in case the first key exchange has not been completed.
178+
* started. The method blocks in case the first key exchange has not been completed.
183179
* <p>
184180
* Note: upon return of this method, authentication may still be pending.
185-
*
181+
*
186182
* @return A {@link ConnectionInfo} object.
187183
* @throws IOException
188184
* In case of any failure behind the scenes; e.g., first key exchange was aborted.
@@ -201,12 +197,12 @@ public synchronized ConnectionInfo getConnectionInfo() throws IOException
201197

202198
/**
203199
* Change the current DSA hostkey. Either a DSA or RSA private key must be set for a successful handshake with
204-
* the client.
200+
* the client.
205201
* <p>
206202
* Note: You can change an existing DSA hostkey after the initial kex exchange (the new value will
207203
* be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the
208204
* current DSA key, otherwise the next key exchange may fail in case the client supports only DSA hostkeys.
209-
*
205+
*
210206
* @param dsa_hostkey
211207
*/
212208
public synchronized void setDsaHostKey(DSAPrivateKey dsa_hostkey)
@@ -223,12 +219,12 @@ public synchronized void setDsaHostKey(DSAPrivateKey dsa_hostkey)
223219

224220
/**
225221
* Change the current RSA hostkey. Either a DSA or RSA private key must be set for a successful handshake with
226-
* the client.
222+
* the client.
227223
* <p>
228224
* Note: You can change an existing RSA hostkey after the initial kex exchange (the new value will
229225
* be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the
230226
* current RSA key, otherwise the next key exchange may fail in case the client supports only RSA hostkeys.
231-
*
227+
*
232228
* @param rsa_hostkey
233229
*/
234230
public synchronized void setRsaHostKey(RSAPrivateKey rsa_hostkey)
@@ -246,8 +242,8 @@ public synchronized void setRsaHostKey(RSAPrivateKey rsa_hostkey)
246242
/**
247243
* Utility method that loads a PEM based hostkey (either RSA or DSA based) and
248244
* calls either <code>setRsaHostKey()</code> or <code>setDsaHostKey()</code>.
249-
*
250-
* @param pemfile The PEM data
245+
*
246+
* @param pemdata The PEM data
251247
* @param password Password, may be null in case the PEM data is not password protected
252248
* @throws IOException In case of any error.
253249
*/
@@ -265,7 +261,7 @@ public void setPEMHostKey(char[] pemdata, String password) throws IOException
265261
/**
266262
* Utility method that loads a hostkey from a PEM file (either RSA or DSA based) and
267263
* calls either <code>setRsaHostKey()</code> or <code>setDsaHostKey()</code>.
268-
*
264+
*
269265
* @param pemFile The PEM file
270266
* @param password Password, may be null in case the PEM file is not password protected
271267
* @throws IOException
@@ -312,7 +308,7 @@ else if (next_rsa_key != null)
312308
* generated by the client (e.g., client opens a new Session which results in a <code>ServerSession</code>).
313309
* <p>
314310
* Note: This must be set before the first handshake.
315-
*
311+
*
316312
* @param cb The callback implementation
317313
*/
318314
public synchronized void setServerConnectionCallback(ServerConnectionCallback cb)
@@ -327,7 +323,7 @@ public synchronized void setServerConnectionCallback(ServerConnectionCallback cb
327323
* Callback interface with methods that will be called upon authentication events.
328324
* <p>
329325
* Note: This must be set before the first handshake.
330-
*
326+
*
331327
* @param cb The callback implementation
332328
*/
333329
public synchronized void setAuthenticationCallback(ServerAuthenticationCallback cb)

src/main/java/ch/ethz/ssh2/auth/AuthenticationManager.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@
3030
import ch.ethz.ssh2.signature.RSAPrivateKey;
3131
import ch.ethz.ssh2.signature.RSASHA1Verify;
3232
import ch.ethz.ssh2.signature.RSASignature;
33+
import ch.ethz.ssh2.transport.ClientTransportManager;
3334
import ch.ethz.ssh2.transport.MessageHandler;
34-
import ch.ethz.ssh2.transport.TransportManager;
3535

3636
/**
3737
* @author Christian Plattner
3838
*/
3939
public class AuthenticationManager implements MessageHandler
4040
{
41-
private TransportManager tm;
41+
private ClientTransportManager tm;
4242

4343
private final List<byte[]> packets = new ArrayList<byte[]>();
4444
private boolean connectionClosed = false;
@@ -51,7 +51,7 @@ public class AuthenticationManager implements MessageHandler
5151
private boolean authenticated = false;
5252
private boolean initDone = false;
5353

54-
public AuthenticationManager(TransportManager tm)
54+
public AuthenticationManager(ClientTransportManager tm)
5555
{
5656
this.tm = tm;
5757
}
@@ -327,7 +327,7 @@ else if (key instanceof RSAPrivateKey)
327327
}
328328
catch (IOException e)
329329
{
330-
tm.close(e, false);
330+
tm.close(e);
331331
throw new IOException("Publickey authentication failed.", e);
332332
}
333333
}
@@ -341,7 +341,7 @@ public boolean authenticateNone(String user) throws IOException
341341
}
342342
catch (IOException e)
343343
{
344-
tm.close(e, false);
344+
tm.close(e);
345345
throw new IOException("None authentication failed.", e);
346346
}
347347
}
@@ -382,7 +382,7 @@ public boolean authenticatePassword(String user, String pass) throws IOException
382382
}
383383
catch (IOException e)
384384
{
385-
tm.close(e, false);
385+
tm.close(e);
386386
throw new IOException("Password authentication failed.", e);
387387
}
388388
}
@@ -456,7 +456,7 @@ public boolean authenticateInteractive(String user, String[] submethods, Interac
456456
}
457457
catch (IOException e)
458458
{
459-
tm.close(e, false);
459+
tm.close(e);
460460
throw new IOException("Keyboard-interactive authentication failed.", e);
461461
}
462462
}

src/main/java/ch/ethz/ssh2/server/ServerConnectionState.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import ch.ethz.ssh2.signature.DSAPrivateKey;
1717
import ch.ethz.ssh2.signature.RSAPrivateKey;
1818
import ch.ethz.ssh2.transport.ClientServerHello;
19-
import ch.ethz.ssh2.transport.TransportManager;
19+
import ch.ethz.ssh2.transport.ServerTransportManager;
2020

2121
public class ServerConnectionState
2222
{
@@ -38,7 +38,7 @@ public class ServerConnectionState
3838
public Socket s;
3939

4040
public ClientServerHello csh;
41-
public TransportManager tm;
41+
public ServerTransportManager tm;
4242
public ServerAuthenticationManager am;
4343
public ChannelManager cm;
4444

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package ch.ethz.ssh2.transport;
2+
3+
import java.io.IOException;
4+
import java.net.InetAddress;
5+
import java.net.InetSocketAddress;
6+
import java.net.Socket;
7+
import java.net.UnknownHostException;
8+
import java.security.SecureRandom;
9+
10+
import ch.ethz.ssh2.DHGexParameters;
11+
import ch.ethz.ssh2.ServerHostKeyVerifier;
12+
import ch.ethz.ssh2.crypto.CryptoWishList;
13+
import ch.ethz.ssh2.util.Tokenizer;
14+
15+
/**
16+
* @version $Id:$
17+
*/
18+
public class ClientTransportManager extends TransportManager {
19+
20+
protected final Socket sock = new Socket();
21+
22+
public void setTcpNoDelay(boolean state) throws IOException {
23+
sock.setTcpNoDelay(state);
24+
}
25+
26+
public void setSoTimeout(int timeout) throws IOException {
27+
sock.setSoTimeout(timeout);
28+
}
29+
30+
public void connect(String hostname, int port, String softwareversion, CryptoWishList cwl,
31+
ServerHostKeyVerifier verifier, DHGexParameters dhgex, int connectTimeout, SecureRandom rnd)
32+
throws IOException {
33+
// Establish the TCP connection to the SSH-2 server
34+
this.connect(hostname, port, connectTimeout);
35+
36+
// Parse the server line and say hello - important: this information is later needed for the
37+
// key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
38+
// for later use.
39+
40+
ClientServerHello csh = ClientServerHello.clientHello(softwareversion, sock.getInputStream(),
41+
sock.getOutputStream());
42+
43+
TransportConnection tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
44+
45+
KexManager km = new ClientKexManager(this, csh, cwl, hostname, port, verifier, rnd);
46+
super.init(tc, km);
47+
48+
km.initiateKEX(cwl, dhgex, null, null);
49+
50+
this.startReceiver();
51+
}
52+
53+
@Override
54+
public void close(final Throwable cause, final boolean useDisconnectPacket) {
55+
this.close(sock, cause, useDisconnectPacket);
56+
}
57+
58+
protected void connect(String hostname, int port, int connectTimeout)
59+
throws IOException {
60+
61+
InetAddress addr = createInetAddress(hostname);
62+
sock.connect(new InetSocketAddress(addr, port), connectTimeout);
63+
}
64+
65+
/**
66+
* There were reports that there are JDKs which use
67+
* the resolver even though one supplies a dotted IP
68+
* address in the Socket constructor. That is why we
69+
* try to generate the InetAdress "by hand".
70+
*
71+
* @param host
72+
* @return the InetAddress
73+
* @throws java.net.UnknownHostException
74+
*/
75+
protected static InetAddress createInetAddress(String host) throws UnknownHostException {
76+
/* Check if it is a dotted IP4 address */
77+
78+
InetAddress addr = parseIPv4Address(host);
79+
80+
if(addr != null) {
81+
return addr;
82+
}
83+
84+
return InetAddress.getByName(host);
85+
}
86+
87+
private static InetAddress parseIPv4Address(String host) throws UnknownHostException {
88+
if(host == null) {
89+
return null;
90+
}
91+
92+
String[] quad = Tokenizer.parseTokens(host, '.');
93+
94+
if((quad == null) || (quad.length != 4)) {
95+
return null;
96+
}
97+
98+
byte[] addr = new byte[4];
99+
100+
for(int i = 0; i < 4; i++) {
101+
int part = 0;
102+
103+
if((quad[i].length() == 0) || (quad[i].length() > 3)) {
104+
return null;
105+
}
106+
107+
for(int k = 0; k < quad[i].length(); k++) {
108+
char c = quad[i].charAt(k);
109+
110+
/* No, Character.isDigit is not the same */
111+
if((c < '0') || (c > '9')) {
112+
return null;
113+
}
114+
115+
part = part * 10 + (c - '0');
116+
}
117+
118+
if(part > 255) /* 300.1.2.3 is invalid =) */ {
119+
return null;
120+
}
121+
122+
addr[i] = (byte) part;
123+
}
124+
125+
return InetAddress.getByAddress(host, addr);
126+
}
127+
}

0 commit comments

Comments
 (0)