Skip to content

Commit

Permalink
Added support for (unauthenticated) HTTP proxies (fixes #170)
Browse files Browse the repository at this point in the history
  • Loading branch information
hierynomus committed Mar 27, 2015
1 parent c7373f0 commit fc535a5
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 14 deletions.
92 changes: 92 additions & 0 deletions src/main/java/com/hierynomus/sshj/socket/SocketFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.hierynomus.sshj.socket;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketException;

import static java.lang.String.format;

/**
* https://code.google.com/p/java-socket-over-http-proxy-connect/source/browse/trunk/src/sg/com/en/SocketFactory.java
*/
public class SocketFactory {

private static final int DEFAULT_CONNECT_TIMEOUT = 0;
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;

private final javax.net.SocketFactory delegateSocketFactory = javax.net.SocketFactory.getDefault();

public static SocketFactory getDefault() {
return new SocketFactory();
}

public Socket createSocket(String address, int port) throws IOException {
return createSocket(new InetSocketAddress(address, port));
}

public Socket createSocket(InetSocketAddress inetSocketAddress) throws IOException {
Socket socket = delegateSocketFactory.createSocket();
socket.connect(inetSocketAddress, connectTimeout);
return socket;
}

public Socket createSocket(InetSocketAddress inetSocketAddress, Proxy proxy) throws IOException {
if (proxy.type() == Proxy.Type.HTTP) {
return createHttpProxySocket(inetSocketAddress, proxy);
}
Socket socket = new Socket(proxy);
socket.connect(inetSocketAddress, connectTimeout);
return socket;
}

private Socket createHttpProxySocket(InetSocketAddress inetSocketAddress, Proxy proxy) throws IOException {
Socket socket = delegateSocketFactory.createSocket();
socket.connect(proxy.address());

String connect = format("CONNECT %s:%d\n\n", inetSocketAddress.getHostName(), inetSocketAddress.getPort());
socket.getOutputStream().write(connect.getBytes());
checkAndFlushProxyResponse(socket);
return socket;
}

private void checkAndFlushProxyResponse(Socket socket)throws IOException {
InputStream socketInput = socket.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);
}
}

public Socket createSocket(InetSocketAddress bindpoint, InetSocketAddress endpoint) throws IOException {
Socket socket = delegateSocketFactory.createSocket();
socket.bind(bindpoint);
socket.connect(endpoint, connectTimeout);
return socket;
}

public int getConnectTimeout() {
return connectTimeout;
}

public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}

}
24 changes: 10 additions & 14 deletions src/main/java/net/schmizz/sshj/SocketClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
*/
package net.schmizz.sshj;

import javax.net.SocketFactory;
import com.hierynomus.sshj.socket.SocketFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
Expand All @@ -32,10 +33,7 @@ public abstract class SocketClient {
private InputStream input;
private OutputStream output;

private SocketFactory socketFactory = SocketFactory.getDefault();

private static final int DEFAULT_CONNECT_TIMEOUT = 0;
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
private SocketFactory socketFactory = new SocketFactory();

private int timeout = 0;

Expand All @@ -47,15 +45,13 @@ public abstract class SocketClient {

public void connect(InetAddress host, int port)
throws IOException {
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(host, port), connectTimeout);
socket = socketFactory.createSocket(new InetSocketAddress(host, port));
onConnect();
}

public void connect(InetAddress host, int port, Proxy proxy)
throws IOException {
socket = new Socket(proxy);
socket.connect(new InetSocketAddress(host, port), connectTimeout);
socket = socketFactory.createSocket(new InetSocketAddress(host, port), proxy);
onConnect();
}

Expand All @@ -74,9 +70,9 @@ public void connect(String hostname, int port, Proxy proxy)
public void connect(InetAddress host, int port,
InetAddress localAddr, int localPort)
throws IOException {
socket = socketFactory.createSocket();
socket.bind(new InetSocketAddress(localAddr, localPort));
socket.connect(new InetSocketAddress(host, port), connectTimeout);
InetSocketAddress bindpoint = new InetSocketAddress(localAddr, localPort);
InetSocketAddress endpoint = new InetSocketAddress(host, port);
socket = socketFactory.createSocket(bindpoint, endpoint);
onConnect();
}

Expand Down Expand Up @@ -160,11 +156,11 @@ public SocketFactory getSocketFactory() {
}

public int getConnectTimeout() {
return connectTimeout;
return socketFactory.getConnectTimeout();
}

public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
socketFactory.setConnectTimeout(connectTimeout);
}

public int getTimeout() {
Expand Down

5 comments on commit fc535a5

@dkocher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change breaks API compatibility because the new socket factory does not implement javax.net.SocketFactory.

@dkocher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this needs its own implementation and doesn't rely on using the JDK internal HTTP tunneling support using the Socket(Proxy.Type.HTTP)constructor which then delegates to the java.net.HttpConnectSocketImpl implementation.

@dkocher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to say providing an proxy implementation should be out of scope for sshj.

@dkocher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a comment to #170 to follow up the discussion that has already taken place there.

@hierynomus
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reopened #170 to continue there.

Please sign in to comment.