Does Http Proxy work?

Hi All,

I’m using Smack 3.2.0

I have this code:

ProxyInfo proxyInfo = new ProxyInfo(ProxyInfo.ProxyType.HTTP, proxyHost,

proxyPort, proxyUser, proxyPassword);

config = new ConnectionConfiguration(“jabberclust.somedomain.com”, 443, proxyInfo);

config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);

config.setCompressionEnabled(true);

config.setSASLAuthenticationEnabled(true);

config.connect()

I run wireshark and see that all my traffic is being made directly to the OpenFire server and not through the proxy (when I use wireshark for http connections through a proxy, I see traffic only going to the proxy)

I did some digging and found some conflicting posts, some of the posts suggest that this functionality is not supported.

So what is the latest? Does this actually work in 3.2.0? Should I upgrade?

thanks

You can’t proxy pure XMPP through a HTTP proxy. You need either

  • XMPP BOSH which exists as extra experimental branch in Smack
  • A Socks proxy. But I am not sure if Smack supports Socks proxies.

thanks Flow.

I had a feeling this was the case. Why does XMPPConnection accept a ProxyInfo object, and this object can be of HTTP type?

I just had a lock at the source and found out that I was wrong. If you use ProxyInfo.ProxyType.HTTP, Smack should be able to create an outgoing XMPP connection with help of the Proxy CONNECT SSL trick.

So to clarify my previous answer:

You can’t proxy pure XMPP through a HTTP-only proxy, you need an proxy that allows HTTPS connections to a c2s port of the XMPP service you want to use. Also Smack supports Socks proxies.

Now, what makes me wonder is that you say that you see Smack doing direct connections instead of using the supplied proxy. I am not familar with Smacks proxy code, but from looking at it, it seems that it should be possible to use proxies with a XMPPConnection instance.

Try debugging the problem. Find out what’s happening with ConnectionConfiguration.socketFactory and where things go wrong.

So the direct connection is actually happening when I specify the SocketFactory (new SSLSocketFactory()), I used a similar implementation to what the Spark messenger is doing.

what is the “Proxy CONNECT SSL trick” ?

is the spark messenger using bosh?

You can tunnel XMPP SSL traffic through a proxy server which supports HTTP CONNECT. I did this some days ago:

Does Smack support for HTTP-Proxy Connection ???

It did work with the old SSL port 5223, I had no fun with port 5222. This seemed to be a Java limitation.

what if I’m terminating SSL at our load blancer, and I have SSL disabled within openfire?

Does your load balancer converts SSL (port 5223) to TLS (port 5222)? Your load balancer should be able to terminate the SSL session and route the traffic to Openfire, but I wonder whether Openfire can do anything with with. Your load balancer may create a new SSL connection to Openfire:5223 - this could work.

I think this might work, we terminate SSL at the load balancer, then route the traffic to port 5222. I’ll try it and let you know. Thanks LG!

channa wrote:

So the direct connection is actually happening when I specify the SocketFactory (new SSLSocketFactory()), I used a similar implementation to what the Spark messenger is doing.

what is the “Proxy CONNECT SSL trick” ?

That is what Smack tries to do when you are trying to connect to an XMPP server with a HTTP Proxy configured.

Hi LG,

I just have a few questions,

in the below code I have to change the smack lib ConnectionConfiguration object, and the XMPPConnection object

//ConnectionConfiguration.java - new method:

public Socket setProxy(String proxyHost, String proxyPort, String host, int port)

{

System.setProperty(“java.protocol.handler.pkgs”, “com.sun.net.ssl.internal.www.protocol”);

System.setProperty(“https.proxyHost”, proxyHost);

System.setProperty(“https.proxyPort”, proxyPort);

SSLTunnelSocketFactory SSLTSF = new SSLTunnelSocketFactory(proxyHost,proxyPort);

Socket s = null;

try {

} catch (Exception e) {

e.printStackTrace();

}

return s;

}

But does it only intializes the SSLTSF factory class and returns null, why?

//XMPPConnection.java - connectUsingConfiguration()

//change

if (config.getSocketFactory() == null) {

this.socket = new Socket(host, port);

} else

{

this.socket = config.getSocketFactory().createSocket(host, port);

}

//to

this.socket = config.setProxy(“10.1.1.1”, “8080”, host, port); // Is this just NULL???

wouldn’t “this.socket” just be null? Given the setProxy method? And given your SSLTunnelSocketFactory could we do this:

config = new ConnectionConfiguration(xmppParams.getDomain(), xmppParams.getPort());

config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);

config.setCompressionEnabled(true);

config.setSocketFactory(new SSLTunnelSocketFactory (proxyHost, proxyPort));

config.setSASLAuthenticationEnabled(true);

is that correct? Sorry im not a Java programmer by trade. Maybe Im missing something?

So here is what I did with the Smack 3.2 libs, wanted to put this up so that folks can critque and it hopefully helps someone out.

HttpProxyTest.java

package com.httpTunnel.text;

import java.net.SocketException;

import org.jivesoftware.smack.Chat;

import org.jivesoftware.smack.ConnectionConfiguration;

import org.jivesoftware.smack.MessageListener;

import org.jivesoftware.smack.XMPPConnection;

import org.jivesoftware.smack.XMPPException;

import org.jivesoftware.smack.packet.Message;

import org.jivesoftware.smack.proxy.ProxyInfo;

public class HttpTest {

public static void main(String[] args) {

// TODO Auto-generated method stub

try {

ConnectionConfiguration config = getConfiguration();

config.setReconnectionAllowed(false);

XMPPConnection conn = TrytoConnect(config);

conn.login(“channa”, “1234567”);

} catch (SocketException | XMPPException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

private static XMPPConnection TrytoConnect(ConnectionConfiguration config){

XMPPConnection connection = new XMPPConnection(config);

try {

connection.connect();

} catch (XMPPException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return connection;

}

private static ConnectionConfiguration getConfiguration() throws SocketException {

final ConnectionConfiguration config;

config = new ConnectionConfiguration(“jabclust.dev.xxxxx.local”, 443);

config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);

config.setCompressionEnabled(true);

config.setSocketFactory(new SSLTunnelSocketFactory(“xx.xx.xx.xx”,3128));

config.setSASLAuthenticationEnabled(true);

return config;

}

}

SSLTunnelSocketFactory.java

package com.httpTunnel.text;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.UnsupportedEncodingException;

import java.net.InetAddress;

import java.net.Socket;

import java.net.UnknownHostException;

import javax.net.ssl.HandshakeCompletedEvent;

import javax.net.ssl.HandshakeCompletedListener;

import javax.net.ssl.SSLContext;

import javax.net.ssl.SSLSocket;

import javax.net.ssl.SSLSocketFactory;

import javax.net.ssl.TrustManager;

import javax.net.ssl.X509TrustManager;

public class SSLTunnelSocketFactory extends SSLSocketFactory

{

private SSLSocketFactory dfactory;

private String tunnelHost;

private int tunnelPort;

public SSLTunnelSocketFactory(String proxyhost, int proxyport){

tunnelHost = proxyhost;

tunnelPort = proxyport;

}

public Socket createSocket(String host, int port)

throws IOException, UnknownHostException

{

return createSocket(null,host,port,true);

}

public Socket createSocket(String host,int port,InetAddress clientHost,

int clientPort)

throws IOException,UnknownHostException

{

return createSocket(null,host,port,true);

}

public Socket createSocket(InetAddress host,int port)

throws IOException

{

return createSocket(null,host.getHostName(),port,true);

}

public Socket createSocket(InetAddress address,int port,

InetAddress clientAddress,int clientPort)

throws IOException

{

return createSocket(null,address.getHostName(),port,true);

}

public Socket createSocket(Socket s, String host, int port,

boolean autoClose)

throws IOException,UnknownHostException

{

Socket tunnel = new Socket(tunnelHost,tunnelPort);

doTunnelHandshake(tunnel,host,port);

SSLContext sslContext = null;

try {

sslContext = SSLContext.getInstance(“SSL”);

sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

} catch (Exception e) {

}

dfactory = sslContext.getSocketFactory();

SSLSocket result = (SSLSocket)dfactory.createSocket(

tunnel,host,port,autoClose);

result.addHandshakeCompletedListener(

new HandshakeCompletedListener() {

public void handshakeCompleted(HandshakeCompletedEvent event) {

System.out.println(“Handshake finished!”);

System.out.println(

“\t CipherSuite:” + event.getCipherSuite());

System.out.println(

"\t SessionId " + event.getSession());

System.out.println(

"\t PeerHost " + event.getSession().getPeerHost());

}

}

);

result.startHandshake();

return result;

}

private void doTunnelHandshake(Socket tunnel, String host, int port)

throws IOException

{

OutputStream out = tunnel.getOutputStream();

String msg = “CONNECT " + host + “:” + port + " HTTP/1.0\n”

  • "User-Agent: "

  • “DesktopAlertingTest”

  • “\r\n\r\n”;

byte b[];

try {

b = msg.getBytes(“ASCII7”);

} catch (UnsupportedEncodingException ignored) {

b = msg.getBytes();

}

out.write(b);

out.flush();

byte reply[] = new byte[200];

int replyLen = 0;

int newlinesSeen = 0;

boolean headerDone = false; /* Done on first newline */

InputStream in = tunnel.getInputStream();

while (newlinesSeen < 2) {

int i = in.read();

if (i < 0) {

throw new IOException(“Unexpected EOF from proxy”);

}

if (i == ‘\n’) {

headerDone = true;

++newlinesSeen;

} else if (i != ‘\r’) {

newlinesSeen = 0;

if (!headerDone && replyLen < reply.length) {

reply[replyLen++] = (byte) i;

}

}

}

String replyStr;

try {

replyStr = new String(reply, 0, replyLen, “ASCII7”);

} catch (UnsupportedEncodingException ignored) {

replyStr = new String(reply, 0, replyLen);

}

if(replyStr.toLowerCase().indexOf(“200 connection established”) == -1){

throw new IOException("Unable to tunnel through "

  • tunnelHost + “:” + tunnelPort

  • “. Proxy returns “” + replyStr + “””);

}

}

public String[] getDefaultCipherSuites(){

return dfactory.getDefaultCipherSuites();

}

public String[] getSupportedCipherSuites(){

return dfactory.getSupportedCipherSuites();

}

TrustManager[] trustAllCerts = new TrustManager[]{

new X509TrustManager() {

public java.security.cert.X509Certificate[] getAcceptedIssuers() {

return null;

}

public void checkClientTrusted(

java.security.cert.X509Certificate[] certs, String authType) {

}

public void checkServerTrusted(

java.security.cert.X509Certificate[] certs, String authType) {

}

}

};

}

I’m going to try an actual tunneling lib to replace the “doHandshake” method. Thanks again LG you are a wizard.

Socket s = null;

try {

// it seems that some code is missing here, something like: s = SSLTSF.createSocket(host, port);

} catch (Exception e) {

e.printStackTrace();

}

return s;

so LG I was able to do the following:

config.setSocketFactory(new SSLTunnelSocketFactory(http_proxy_ip,http_proxy_port));

and not have to make any mods to the smack lib. I ran a little test program seemed to work, I am able to successfully connect and login.

Do you see any long term problems with that approach?

This looks good and seems to be the right way to set the proxy. Anyhow flow or rcollier know the Smack code much better than I do, so I hope they comment and maybe add this code also to Spark.

Do you use the server port 5222 or 5223? Within Java 7 the proxy support is at least a little bit better than it was with Java 5.

We have a different implementation, I’ll explain, orginally I thought I would be able to terminate SSL at the OpenFire server, I had a hard time with this, I had purchased a comodo cert and it never imported correctly. I found some issues with intermediate cert support in OpenFire, so I decided to pass the SSL termination to our load balancer (aka Virtual IP or VIP) so the VIP has 443 open, terminates SSL there then routes the traffic unencrypted to OpenFire over 80, the openfire server is listening on 80,

So Im using the unencrypted port, but my VIP is terminating the ssl, I do still have to set the socket as an SSL socket because the TLS handshake happens at the VIP level.

HTH