Added support for HTTP CONNECT proxies by implementing custom Socket (Fixes #170)

This commit is contained in:
Jeroen van Erp
2015-03-30 21:53:04 +02:00
parent 8398b6e3c3
commit ace09fa8c8
3 changed files with 100 additions and 29 deletions

View File

@@ -0,0 +1,13 @@
package com.hierynomus.sshj.backport;
import java.math.BigDecimal;
public class JavaVersion {
public static boolean isJava7OrEarlier() {
String property = System.getProperty("java.specification.version");
float diff = Float.parseFloat(property) - 1.7f;
return diff < 0.01;
}
}

View File

@@ -0,0 +1,62 @@
package com.hierynomus.sshj.backport;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.nio.charset.Charset;
public class Jdk7HttpProxySocket extends Socket {
private Proxy httpProxy = null;
public Jdk7HttpProxySocket(Proxy proxy) {
super(proxy.type() == Proxy.Type.HTTP ? Proxy.NO_PROXY : proxy);
if (proxy.type() == Proxy.Type.HTTP) {
this.httpProxy = proxy;
}
}
@Override
public void connect(SocketAddress endpoint, int timeout) throws IOException {
if (httpProxy != null) {
connectHttpProxy(endpoint, timeout);
} else {
super.connect(endpoint, timeout);
}
}
private void connectHttpProxy(SocketAddress endpoint, int timeout) throws IOException {
super.connect(httpProxy.address(), timeout);
if (!(endpoint instanceof InetSocketAddress)) {
throw new SocketException("Expected an InetSocketAddress to connect to, got: " + endpoint);
}
InetSocketAddress isa = (InetSocketAddress) endpoint;
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
getOutputStream().write(httpConnect.getBytes(Charset.forName("UTF-8")));
checkAndFlushProxyResponse();
}
private void checkAndFlushProxyResponse()throws IOException {
InputStream socketInput = getInputStream();
byte[] tmpBuffer = new byte[512];
int len = socketInput.read(tmpBuffer, 0, tmpBuffer.length);
if (len == 0) {
throw new SocketException("Empty response from proxy");
}
String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");
// Expecting HTTP/1.x 200 OK
if (proxyResponse.contains("200")) {
// Flush any outstanding message in buffer
if (socketInput.available() > 0) {
socketInput.skip(socketInput.available());
}
// Proxy Connect Successful
} else {
throw new SocketException("Fail to create Socket\nResponse was:" + proxyResponse);
}
}
}

View File

@@ -15,6 +15,9 @@
*/ */
package net.schmizz.sshj; package net.schmizz.sshj;
import com.hierynomus.sshj.backport.JavaVersion;
import com.hierynomus.sshj.backport.Jdk7HttpProxySocket;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -45,34 +48,35 @@ public abstract class SocketClient {
this.defaultPort = defaultPort; this.defaultPort = defaultPort;
} }
public void connect(InetAddress host, int port) public void connect(InetAddress host, int port) throws IOException {
throws IOException {
socket = socketFactory.createSocket(); socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(host, port), connectTimeout); socket.connect(new InetSocketAddress(host, port), connectTimeout);
onConnect(); onConnect();
} }
public void connect(InetAddress host, int port, Proxy proxy)
throws IOException { public void connect(InetAddress host, int port, Proxy proxy) throws IOException {
socket = new Socket(proxy); if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
socket = new Jdk7HttpProxySocket(proxy);
} else {
socket = new Socket(proxy);
}
socket.connect(new InetSocketAddress(host, port), connectTimeout); socket.connect(new InetSocketAddress(host, port), connectTimeout);
onConnect(); onConnect();
} }
public void connect(String hostname, int port) public void connect(String hostname, int port) throws IOException {
throws IOException {
this.hostname = hostname; this.hostname = hostname;
connect(InetAddress.getByName(hostname), port); connect(InetAddress.getByName(hostname), port);
} }
public void connect(String hostname, int port, Proxy proxy) public void connect(String hostname, int port, Proxy proxy) throws IOException {
throws IOException {
this.hostname = hostname; this.hostname = hostname;
connect(InetAddress.getByName(hostname), port, proxy); connect(InetAddress.getByName(hostname), port, proxy);
} }
public void connect(InetAddress host, int port, public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
InetAddress localAddr, int localPort)
throws IOException { throws IOException {
socket = socketFactory.createSocket(); socket = socketFactory.createSocket();
socket.bind(new InetSocketAddress(localAddr, localPort)); socket.bind(new InetSocketAddress(localAddr, localPort));
@@ -80,35 +84,28 @@ public abstract class SocketClient {
onConnect(); onConnect();
} }
public void connect(String hostname, int port, public void connect(String hostname, int port, InetAddress localAddr, int localPort) throws IOException {
InetAddress localAddr, int localPort)
throws IOException {
this.hostname = hostname; this.hostname = hostname;
connect(InetAddress.getByName(hostname), port, localAddr, localPort); connect(InetAddress.getByName(hostname), port, localAddr, localPort);
} }
public void connect(InetAddress host) public void connect(InetAddress host) throws IOException {
throws IOException {
connect(host, defaultPort); connect(host, defaultPort);
} }
public void connect(String hostname) public void connect(String hostname) throws IOException {
throws IOException {
connect(hostname, defaultPort); connect(hostname, defaultPort);
} }
public void connect(InetAddress host, Proxy proxy) public void connect(InetAddress host, Proxy proxy) throws IOException {
throws IOException {
connect(host, defaultPort, proxy); connect(host, defaultPort, proxy);
} }
public void connect(String hostname, Proxy proxy) public void connect(String hostname, Proxy proxy) throws IOException {
throws IOException {
connect(hostname, defaultPort, proxy); connect(hostname, defaultPort, proxy);
} }
public void disconnect() public void disconnect() throws IOException {
throws IOException {
if (socket != null) { if (socket != null) {
socket.close(); socket.close();
socket = null; socket = null;
@@ -131,7 +128,6 @@ public abstract class SocketClient {
return socket.getLocalPort(); return socket.getLocalPort();
} }
public InetAddress getLocalAddress() { public InetAddress getLocalAddress() {
return socket.getLocalAddress(); return socket.getLocalAddress();
} }
@@ -149,10 +145,11 @@ public abstract class SocketClient {
} }
public void setSocketFactory(SocketFactory factory) { public void setSocketFactory(SocketFactory factory) {
if (factory == null) if (factory == null) {
socketFactory = SocketFactory.getDefault(); socketFactory = SocketFactory.getDefault();
else } else {
socketFactory = factory; socketFactory = factory;
}
} }
public SocketFactory getSocketFactory() { public SocketFactory getSocketFactory() {
@@ -187,8 +184,7 @@ public abstract class SocketClient {
return output; return output;
} }
void onConnect() void onConnect() throws IOException {
throws IOException {
socket.setSoTimeout(timeout); socket.setSoTimeout(timeout);
input = socket.getInputStream(); input = socket.getInputStream();
output = socket.getOutputStream(); output = socket.getOutputStream();