hierynomus / sshj

ssh, scp and sftp for java
Apache License 2.0
2.46k stars 595 forks source link

HTTP Proxy is not supported #170

Closed missedone closed 9 years ago

missedone commented 9 years ago

Hi Folks

as we work behind the firewall, the SSH connection need go over HTTP proxy, but I found SSHj only support SOCKS proxy. here is the code snappet with HTTP proxy

try (SSHClient ssh = new SSHClient()) {
            ssh.setConnectTimeout(60 * 1000);
            ssh.setTimeout(60 * 60 * 1000);
            ssh.getConnection().getKeepAlive().setKeepAliveInterval(30);
            ssh.loadKnownHosts();
            ssh.addHostKeyVerifier(new PromiscuousVerifier());

            ssh.connect(host, new Proxy(Proxy.Type.HTTP, new InetSocketAddress("16.85.88.10", 8080)));
            ssh.authPublickey(user, ssh.loadKeys(privateKey, null, null));

            try (Session session = ssh.startSession()) {
                final Session.Command cmd = session.exec("echo sleeping && date && sleep 3000 && date && echo awake");
                System.out.println(IOUtils.readFully(cmd.getInputStream()).toString());
                cmd.join(90, TimeUnit.MINUTES);
                System.out.println("\n** exit status: " + cmd.getExitStatus());
            }
        }

i got exception:

java.lang.IllegalArgumentException: Invalid Proxy
    at java.net.Socket.<init>(Socket.java:147)
    at net.schmizz.sshj.SocketClient.connect(SocketClient.java:57)
    at net.schmizz.sshj.SocketClient.connect(SocketClient.java:71)
    at net.schmizz.sshj.SocketClient.connect(SocketClient.java:107)
    at com.hp.es.cto.sp.remote.SshjTest.testKeepAlive(SshjTest.java:32)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)

by dig into the source code of java.net.Socket.Socket(Proxy), we can see it only support SOCKS and NO_PROXY proxy.

I saw both jsch and ganymed support HTTP proxy, so would you consider to add HTTP proxy support? thanks

ghost commented 9 years ago

You could use custom socket factory

hierynomus commented 9 years ago

I don't get it, if I look at my JDK's source code, I see the following in the constructor:

    public Socket(Proxy proxy) {
        // Create a copy of Proxy as a security measure
        if (proxy == null) {
            throw new IllegalArgumentException("Invalid Proxy");
        }
        Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY
                                          : sun.net.ApplicationProxy.create(proxy);
        Proxy.Type type = p.type();
        if (type == Proxy.Type.SOCKS || type == Proxy.Type.HTTP) {

So it actually supports HTTP Proxies. Which JDK are you using?

missedone commented 9 years ago

right, i'm using Oracle JDK 1.7.x which is the version we used on our production when I switch to JDK 1.8, it does works. do you have any idea to make it back ported though i will check it via google search as well. thanks

hierynomus commented 9 years ago

I might have found something... will try to implement this next week. In the meantime though I'm going to release 0.11.0 without this.

Regards, Jeroen

2015-01-23 5:35 GMT+01:00 Nick Tan notifications@github.com:

right, i'm using Oracle JDK 1.7.x which is the version we used on our production when I switch to JDK 1.8, it does works. do you have any idea to make it back ported though i will check it via google search as well. thanks

— Reply to this email directly or view it on GitHub https://github.com/hierynomus/sshj/issues/170#issuecomment-71146525.

missedone commented 9 years ago

Thanks Jeroen

dkocher commented 9 years ago

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. I tend to say providing an proxy implementation should be out of scope for sshj. The change in fc535a5e76c6a729a191ef7dec5c4bd9aa0006aa breaks API compatibility for users that want to provide a custom socket factory implementation.

hierynomus commented 9 years ago

Hi David, I also thought that would work, unfortunately for JDK6 and 7, sockets with HTTP proxies are not implemented. See the code here:

    public Socket(Proxy proxy) {
        // Create a copy of Proxy as a security measure
        if (proxy == null) {
            throw new IllegalArgumentException("Invalid Proxy");
        }
        Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy);
        if (p.type() == Proxy.Type.SOCKS) {
            SecurityManager security = System.getSecurityManager();
            InetSocketAddress epoint = (InetSocketAddress) p.address();
            if (epoint.getAddress() != null) {
                checkAddress (epoint.getAddress(), "Socket");
            }
            if (security != null) {
                if (epoint.isUnresolved())
                    epoint = new InetSocketAddress(epoint.getHostName(), epoint.getPort());
                if (epoint.isUnresolved())
                    security.checkConnect(epoint.getHostName(), epoint.getPort());
                else
                    security.checkConnect(epoint.getAddress().getHostAddress(),
                                  epoint.getPort());
            }
            impl = new SocksSocketImpl(p);
            impl.setSocket(this);
        } else {
            if (p == Proxy.NO_PROXY) {
                if (factory == null) {
                    impl = new PlainSocketImpl();
                    impl.setSocket(this);
                } else
                    setImpl();
            } else
                throw new IllegalArgumentException("Invalid Proxy");
        }
hierynomus commented 9 years ago

I've reopened the issue to actually correct the SocketFactory to correctly implement the SocketFactory interface so as not to break the external API as David mentions.

dkocher commented 9 years ago

Yes, support was introduced with HttpConnectSocketImpl in 1.8. I still think it is acceptable for users that require this to run Java 1.8 or later that need this and default to the provided implementation in the JDK. Or they can write their own socket factory implementation and provide HTTP Connect proxy support.

hierynomus commented 9 years ago

Ok, changed the implementation for this, no custom SocketFactory anymore. The entire proxy support actually already bypassed the proxy factory. That should have been implemented differently from the start. I'll mark those methods deprecated and suggest that you should use a custom SocketFactory for that. For now, it will work with different versions of Java.

hierynomus commented 9 years ago

@dkocher Let me know whether you like this one better ;)

dkocher commented 9 years ago

Thanks for taking my input into account. +1 for marking the constructors with proxy parameter as deprecated.

hierynomus commented 9 years ago

Thanks for contributing and noticing I broke the API :wink:. Deprecations are marked in 4250c61e457ab8c8f38fef22253bf3e806ded7a7