Skip to content

Commit b9f7296

Browse files
ppb2020kruton
authored andcommitted
Add support for specifying IP version
1 parent dc4bc03 commit b9f7296

File tree

2 files changed

+126
-14
lines changed

2 files changed

+126
-14
lines changed

src/main/java/com/trilead/ssh2/Connection.java

+80-8
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@
4646

4747
public class Connection implements AutoCloseable
4848
{
49+
/**
50+
* Allow the caller to restrict the IP version of the connection to
51+
* be established.
52+
*/
53+
public enum IpVersion {
54+
IPV4_AND_IPV6, ///< Allow both IPV4 and IPv6, the default.
55+
IPV4_ONLY, ///< Require that the connection be over IPv4 only.
56+
IPV6_ONLY ///< Require that the connection be over IPv6 only.
57+
}
58+
4959
/**
5060
* The identifier presented to the SSH-2 server.
5161
*/
@@ -564,30 +574,74 @@ private void close(Throwable t, boolean hard)
564574

565575
/**
566576
* Same as
567-
* {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
577+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(null, 0, 0, IpVersion.IPV4_AND_IPV6)}.
568578
*
569579
* @return see comments for the
570-
* {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
580+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(ServerHostKeyVerifier, int, int, IpVersion)}
571581
* method.
572582
* @throws IOException
573583
*/
574584
public synchronized ConnectionInfo connect() throws IOException
575585
{
576-
return connect(null, 0, 0);
586+
return connect(null, 0, 0, IpVersion.IPV4_AND_IPV6);
587+
}
588+
589+
/**
590+
* Same as
591+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(null, 0, 0, ipVersion)}.
592+
*
593+
* @return see comments for the
594+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(ServerHostKeyVerifier, int, int, IpVersion)}
595+
* method.
596+
* @throws IOException
597+
*/
598+
public synchronized ConnectionInfo connect(IpVersion ipVersion) throws IOException
599+
{
600+
return connect(null, 0, 0, ipVersion);
577601
}
578602

603+
579604
/**
580605
* Same as
581-
* {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
606+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(verifier, 0, 0, IpVersion.IPV4_AND_IPV6)}.
582607
*
583608
* @return see comments for the
584-
* {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
609+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(ServerHostKeyVerifier, int, int, IpVersion)}
585610
* method.
586611
* @throws IOException
587612
*/
588613
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
589614
{
590-
return connect(verifier, 0, 0);
615+
return connect(verifier, 0, 0, IpVersion.IPV4_AND_IPV6);
616+
}
617+
618+
/**
619+
* Same as
620+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(verifier, 0, 0, ipVersion)}.
621+
*
622+
* @return see comments for the
623+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(ServerHostKeyVerifier, int, int, IpVersion)}
624+
* method.
625+
* @throws IOException
626+
*/
627+
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, IpVersion ipVersion) throws IOException
628+
{
629+
return connect(verifier, 0, 0, ipVersion);
630+
}
631+
632+
/**
633+
* Same as
634+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(verifier, connectTimeout, kexTimeout, IpVersion.IPV4_AND_IPV6)}.
635+
*
636+
* @return see comments for the
637+
* {@link #connect(ServerHostKeyVerifier, int, int, IpVersion) connect(ServerHostKeyVerifier, int, int, IpVersion)}
638+
* method.
639+
* @throws IOException
640+
*/
641+
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
642+
throws IOException
643+
{
644+
return connect(verifier, connectTimeout, kexTimeout, IpVersion.IPV4_AND_IPV6);
591645
}
592646

593647
/**
@@ -649,6 +703,11 @@ public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throw
649703
* but it will only have an effect after the
650704
* <code>verifier</code> returns.
651705
*
706+
* @param ipVersion
707+
* Specify whether the connection should be restricted to one of
708+
* IPv4 or IPv6, with a default of allowing both. See
709+
* {@link IpVersion}.
710+
*
652711
* @return A {@link ConnectionInfo} object containing the details of the
653712
* established connection.
654713
*
@@ -672,7 +731,7 @@ public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throw
672731
* proxy is buggy and does not return a proper HTTP response,
673732
* then a normal IOException is thrown instead.
674733
*/
675-
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
734+
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout, IpVersion ipVersion)
676735
throws IOException
677736
{
678737
final class TimeoutState
@@ -745,9 +804,22 @@ public void run()
745804
token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
746805
}
747806

807+
TransportManager.IpVersion tmIpVersion;
808+
if (ipVersion == IpVersion.IPV4_ONLY)
809+
{
810+
tmIpVersion = TransportManager.IpVersion.IPV4_ONLY;
811+
}
812+
else if (ipVersion == IpVersion.IPV6_ONLY) {
813+
tmIpVersion = TransportManager.IpVersion.IPV6_ONLY;
814+
}
815+
else // Assume (ipVersion == IpVersion.IPV4_AND_IPV6), the default.
816+
{
817+
tmIpVersion = TransportManager.IpVersion.IPV4_AND_IPV6;
818+
}
819+
748820
try
749821
{
750-
tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
822+
tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, tmIpVersion, getOrCreateSecureRND(), proxyData);
751823
}
752824
catch (SocketTimeoutException se)
753825
{

src/main/java/com/trilead/ssh2/transport/TransportManager.java

+46-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.trilead.ssh2.ExtensionInfo;
55
import com.trilead.ssh2.packets.PacketExtInfo;
66
import java.io.IOException;
7+
import java.net.Inet6Address;
78
import java.net.InetAddress;
89
import java.net.InetSocketAddress;
910
import java.net.Socket;
@@ -49,6 +50,16 @@
4950
*/
5051
public class TransportManager
5152
{
53+
/**
54+
* Allow the caller to restrict the IP version of the connection to
55+
* be established.
56+
*/
57+
public enum IpVersion {
58+
IPV4_AND_IPV6, ///< Allow both IPV4 and IPv6, the default.
59+
IPV4_ONLY, ///< Require that the connection be over IPv4 only.
60+
IPV6_ONLY ///< Require that the connection be over IPv6 only.
61+
}
62+
5263
private static final Logger log = Logger.getLogger(TransportManager.class);
5364

5465
class HandlerEntry
@@ -265,30 +276,59 @@ public void close(Throwable cause, boolean useDisconnectPacket)
265276
}
266277
}
267278

268-
private void establishConnection(ProxyData proxyData, int connectTimeout) throws IOException
279+
private static InetAddress getIPv4Address(InetAddress[] addresses) {
280+
for (InetAddress address : addresses) {
281+
if (! (address instanceof Inet6Address)) {
282+
return address;
283+
}
284+
}
285+
return null;
286+
}
287+
private static Inet6Address getIPv6Address(InetAddress[] addresses) {
288+
for (InetAddress address : addresses) {
289+
if (address instanceof Inet6Address) {
290+
return (Inet6Address) address;
291+
}
292+
}
293+
return null;
294+
}
295+
296+
private void establishConnection(ProxyData proxyData, int connectTimeout, IpVersion ipVersion) throws IOException
269297
{
270298
if (proxyData == null)
271-
sock = connectDirect(hostname, port, connectTimeout);
299+
sock = connectDirect(hostname, port, connectTimeout, ipVersion);
272300
else
273301
sock = proxyData.openConnection(hostname, port, connectTimeout);
274302
}
275303

276-
private static Socket connectDirect(String hostname, int port, int connectTimeout)
304+
private static Socket connectDirect(String hostname, int port, int connectTimeout, IpVersion ipVersion)
277305
throws IOException
278306
{
279307
Socket sock = new Socket();
280-
InetAddress addr = InetAddress.getByName(hostname);
308+
InetAddress addr;
309+
if (ipVersion == IpVersion.IPV4_ONLY)
310+
{
311+
addr = getIPv4Address(InetAddress.getAllByName(hostname));
312+
}
313+
else if (ipVersion == IpVersion.IPV6_ONLY)
314+
{
315+
addr = getIPv6Address(InetAddress.getAllByName(hostname));
316+
}
317+
else // Assume (ipVersion == IpVersion.IPV4_AND_IPV6), the default.
318+
{
319+
addr = InetAddress.getByName(hostname);
320+
}
281321
sock.connect(new InetSocketAddress(addr, port), connectTimeout);
282322
sock.setSoTimeout(0);
283323
return sock;
284324
}
285325

286326
public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
287-
int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
327+
int connectTimeout, IpVersion ipVersion, SecureRandom rnd, ProxyData proxyData) throws IOException
288328
{
289329
/* First, establish the TCP connection to the SSH-2 server */
290330

291-
establishConnection(proxyData, connectTimeout);
331+
establishConnection(proxyData, connectTimeout, ipVersion);
292332

293333
/* Parse the server line and say hello - important: this information is later needed for the
294334
* key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object

0 commit comments

Comments
 (0)