From f0b1761ec3826b72fe3b20032b532dc5b1adfc1c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 28 Nov 2015 20:11:38 +0100 Subject: initial tor support --- .../eu/siacs/conversations/xmpp/XmppConnection.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java') diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 16b3f78b4..dec3cad6d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -27,6 +27,7 @@ import java.net.ConnectException; import java.net.IDN; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.Socket; import java.net.UnknownHostException; import java.net.URL; @@ -233,16 +234,23 @@ public class XmppConnection implements Runnable { tagReader = new XmlReader(wakeLock); tagWriter = new TagWriter(); this.changeStatus(Account.State.CONNECTING); + final boolean useTor = mXmppConnectionService.useTorToConnect(); + final Proxy TOR_PROXY = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(InetAddress.getLocalHost(), 9050)); if (DNSHelper.isIp(account.getServer().toString())) { - socket = new Socket(); + socket = useTor ? new Socket(TOR_PROXY) : new Socket(); try { socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000); } catch (IOException e) { throw new UnknownHostException(); } } else { - final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService); - final ArrayList values = result.getParcelableArrayList("values"); + final ArrayList values; + if (useTor) { + values = account.getHostnamePortBundles(); + } else { + final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService); + values = result.getParcelableArrayList("values"); + } int i = 0; boolean socketError = true; while (socketError && values.size() > i) { @@ -269,11 +277,11 @@ public class XmppConnection implements Runnable { + ": using values from dns " + srvRecordServer + ":" + srvRecordPort); } - socket = new Socket(); + socket = useTor ? new Socket(TOR_PROXY) : new Socket(); socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); socketError = false; } catch (final Throwable e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() +"("+e.getClass().getName()+")"); i++; } } -- cgit v1.2.3 From ebccb67a7208042ca8362ae326b6b75369d61d12 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 29 Nov 2015 15:44:45 +0100 Subject: do socks5 connect manually --- .../siacs/conversations/xmpp/XmppConnection.java | 48 +++++++++++++++------- 1 file changed, 34 insertions(+), 14 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java') diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index dec3cad6d..f5f3860c2 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -13,7 +13,6 @@ import android.util.Log; import android.util.Pair; import android.util.SparseArray; -import org.apache.http.conn.ssl.StrictHostnameVerifier; import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParserException; @@ -27,10 +26,10 @@ import java.net.ConnectException; import java.net.IDN; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.Proxy; import java.net.Socket; import java.net.UnknownHostException; import java.net.URL; +import java.nio.ByteBuffer; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.Principal; @@ -234,23 +233,44 @@ public class XmppConnection implements Runnable { tagReader = new XmlReader(wakeLock); tagWriter = new TagWriter(); this.changeStatus(Account.State.CONNECTING); - final boolean useTor = mXmppConnectionService.useTorToConnect(); - final Proxy TOR_PROXY = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(InetAddress.getLocalHost(), 9050)); - if (DNSHelper.isIp(account.getServer().toString())) { - socket = useTor ? new Socket(TOR_PROXY) : new Socket(); + final boolean useTor = mXmppConnectionService.useTorToConnect() || account.isOnion(); + if (useTor) { + InetSocketAddress proxyAddress = new InetSocketAddress(InetAddress.getLocalHost(), 9050); + byte[] destination; + if (account.getHostname() == null || account.getHostname().isEmpty()) { + destination = account.getServer().toString().getBytes(); + } else { + destination = account.getHostname().getBytes(); + } + Log.d(Config.LOGTAG,"connecting via tor to "+new String(destination)); + socket = new Socket(); + socket.connect(proxyAddress, Config.CONNECT_TIMEOUT * 1000); + InputStream proxyIs = socket.getInputStream(); + OutputStream proxyOs = socket.getOutputStream(); + proxyOs.write(new byte[]{0x05, 0x01, 0x00}); + byte[] response = new byte[2]; + proxyIs.read(response); + ByteBuffer request = ByteBuffer.allocate(7 + destination.length); + request.put(new byte[]{0x05, 0x01, 0x00, 0x03}); + request.put((byte) destination.length); + request.put(destination); + request.putShort((short) account.getPort()); + proxyOs.write(request.array()); + response = new byte[7 + destination.length]; + proxyIs.read(response); + if (response[1] != 0x00) { + throw new UnknownHostException(); + } + } else if (DNSHelper.isIp(account.getServer().toString())) { + socket = new Socket(); try { socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000); } catch (IOException e) { throw new UnknownHostException(); } } else { - final ArrayList values; - if (useTor) { - values = account.getHostnamePortBundles(); - } else { - final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService); - values = result.getParcelableArrayList("values"); - } + final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService); + final ArrayListvalues = result.getParcelableArrayList("values"); int i = 0; boolean socketError = true; while (socketError && values.size() > i) { @@ -277,7 +297,7 @@ public class XmppConnection implements Runnable { + ": using values from dns " + srvRecordServer + ":" + srvRecordPort); } - socket = useTor ? new Socket(TOR_PROXY) : new Socket(); + socket = new Socket(); socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); socketError = false; } catch (final Throwable e) { -- cgit v1.2.3 From 8ffcc11f278994b5bd2cebe2ee50b78a9d14a985 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 29 Nov 2015 16:31:51 +0100 Subject: refactored socks5 connection code. make jingle transport use that new code --- .../siacs/conversations/xmpp/XmppConnection.java | 29 +++++----------------- 1 file changed, 6 insertions(+), 23 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java') diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index f5f3860c2..388f65722 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -67,6 +67,7 @@ import eu.siacs.conversations.generator.IqGenerator; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.DNSHelper; +import eu.siacs.conversations.utils.SocksSocketFactory; import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Tag; @@ -235,32 +236,14 @@ public class XmppConnection implements Runnable { this.changeStatus(Account.State.CONNECTING); final boolean useTor = mXmppConnectionService.useTorToConnect() || account.isOnion(); if (useTor) { - InetSocketAddress proxyAddress = new InetSocketAddress(InetAddress.getLocalHost(), 9050); - byte[] destination; + String destination; if (account.getHostname() == null || account.getHostname().isEmpty()) { - destination = account.getServer().toString().getBytes(); + destination = account.getServer().toString(); } else { - destination = account.getHostname().getBytes(); - } - Log.d(Config.LOGTAG,"connecting via tor to "+new String(destination)); - socket = new Socket(); - socket.connect(proxyAddress, Config.CONNECT_TIMEOUT * 1000); - InputStream proxyIs = socket.getInputStream(); - OutputStream proxyOs = socket.getOutputStream(); - proxyOs.write(new byte[]{0x05, 0x01, 0x00}); - byte[] response = new byte[2]; - proxyIs.read(response); - ByteBuffer request = ByteBuffer.allocate(7 + destination.length); - request.put(new byte[]{0x05, 0x01, 0x00, 0x03}); - request.put((byte) destination.length); - request.put(destination); - request.putShort((short) account.getPort()); - proxyOs.write(request.array()); - response = new byte[7 + destination.length]; - proxyIs.read(response); - if (response[1] != 0x00) { - throw new UnknownHostException(); + destination = account.getHostname(); } + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": connect to "+destination+" via TOR"); + socket = SocksSocketFactory.createSocketOverTor(destination,account.getPort()); } else if (DNSHelper.isIp(account.getServer().toString())) { socket = new Socket(); try { -- cgit v1.2.3 From 2225b0b6d5586d608b15d4e1ac13faa99b8971bf Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 30 Nov 2015 16:01:48 +0100 Subject: add error state for unavailable tor network --- src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java') diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 388f65722..9ec4d9bbb 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -29,7 +29,6 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.net.URL; -import java.nio.ByteBuffer; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.Principal; @@ -318,8 +317,8 @@ public class XmppConnection implements Runnable { this.changeStatus(Account.State.UNAUTHORIZED); } catch (final UnknownHostException | ConnectException e) { this.changeStatus(Account.State.SERVER_NOT_FOUND); - } catch (final DnsTimeoutException e) { - this.changeStatus(Account.State.DNS_TIMEOUT); + } catch (final SocksSocketFactory.SocksProxyNotFoundException e) { + this.changeStatus(Account.State.TOR_NOT_AVAILABLE); } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) { Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); this.changeStatus(Account.State.OFFLINE); @@ -1327,9 +1326,6 @@ public class XmppConnection implements Runnable { } - private class DnsTimeoutException extends IOException { - - } public enum Identity { FACEBOOK, SLACK, -- cgit v1.2.3 From 5e151c7311bed98ce7eafa041735484c32bd7dda Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 9 Dec 2015 11:16:03 +0100 Subject: wait with status change to online after all disco queries have been made --- .../eu/siacs/conversations/xmpp/XmppConnection.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java') diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 9ec4d9bbb..7455ff8d3 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -116,6 +116,7 @@ public class XmppConnection implements Runnable { private long lastPingSent = 0; private long lastConnect = 0; private long lastSessionStarted = 0; + private int mPendingServiceDiscoveries = 0; private boolean mInteractive = false; private int attempt = 0; private final Hashtable> packetCallbacks = new Hashtable<>(); @@ -926,18 +927,16 @@ public class XmppConnection implements Runnable { synchronized (this.disco) { this.disco.clear(); } + mPendingServiceDiscoveries = 0; + sendServiceDiscoveryItems(account.getServer()); sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryInfo(account.getJid().toBareJid()); - sendServiceDiscoveryItems(account.getServer()); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource()); this.lastSessionStarted = SystemClock.elapsedRealtime(); - changeStatus(Account.State.ONLINE); - if (bindListener != null) { - bindListener.onBind(account); - } } private void sendServiceDiscoveryInfo(final Jid jid) { + mPendingServiceDiscoveries++; final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); iq.setTo(jid); iq.query("http://jabber.org/protocol/disco#info"); @@ -988,6 +987,16 @@ public class XmppConnection implements Runnable { } else { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString()); } + if (packet.getType() != IqPacket.TYPE.TIMEOUT) { + mPendingServiceDiscoveries--; + if (mPendingServiceDiscoveries <= 0) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": done with service discovery"); + changeStatus(Account.State.ONLINE); + if (bindListener != null) { + bindListener.onBind(account); + } + } + } } }); } -- cgit v1.2.3 From 43dd681239ea5a287f5761597de5dcdc4daed3de Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 15 Dec 2015 19:14:38 +0100 Subject: timeout service discovery after 20s --- .../siacs/conversations/xmpp/XmppConnection.java | 49 ++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java') diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 7455ff8d3..efb670726 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -116,7 +116,9 @@ public class XmppConnection implements Runnable { private long lastPingSent = 0; private long lastConnect = 0; private long lastSessionStarted = 0; + private long lastDiscoStarted = 0; private int mPendingServiceDiscoveries = 0; + private final ArrayList mPendingServiceDiscoveriesIds = new ArrayList<>(); private boolean mInteractive = false; private int attempt = 0; private final Hashtable> packetCallbacks = new Hashtable<>(); @@ -225,6 +227,7 @@ public class XmppConnection implements Runnable { features.encryptionEnabled = false; lastConnect = SystemClock.elapsedRealtime(); lastPingSent = SystemClock.elapsedRealtime(); + lastDiscoStarted = Long.MAX_VALUE; this.attempt++; if (account.getJid().getDomainpart().equals("chat.facebook.com")) { mServerIdentity = Identity.FACEBOOK; @@ -893,6 +896,29 @@ public class XmppConnection implements Runnable { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": done clearing iq callbacks. " + this.packetCallbacks.size() + " left"); } + public void sendDiscoTimeout() { + final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.ERROR); //don't use timeout + final ArrayList callbacks = new ArrayList<>(); + synchronized (this.mPendingServiceDiscoveriesIds) { + for(String id : mPendingServiceDiscoveriesIds) { + synchronized (this.packetCallbacks) { + Pair pair = this.packetCallbacks.remove(id); + if (pair != null) { + callbacks.add(pair.second); + } + } + } + this.mPendingServiceDiscoveriesIds.clear(); + } + if (callbacks.size() > 0) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": sending disco timeout"); + resetStreamId(); //we don't want to live with this for ever + } + for(OnIqPacketReceived callback : callbacks) { + callback.onIqPacketReceived(account,failurePacket); + } + } + private void sendStartSession() { final IqPacket startSession = new IqPacket(IqPacket.TYPE.SET); startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session"); @@ -928,10 +954,12 @@ public class XmppConnection implements Runnable { this.disco.clear(); } mPendingServiceDiscoveries = 0; + lastDiscoStarted = SystemClock.elapsedRealtime(); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": starting service discovery"); + mXmppConnectionService.scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode()); sendServiceDiscoveryItems(account.getServer()); sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryInfo(account.getJid().toBareJid()); - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource()); this.lastSessionStarted = SystemClock.elapsedRealtime(); } @@ -940,7 +968,7 @@ public class XmppConnection implements Runnable { final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); iq.setTo(jid); iq.query("http://jabber.org/protocol/disco#info"); - this.sendIqPacket(iq, new OnIqPacketReceived() { + String id = this.sendIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { @@ -991,6 +1019,7 @@ public class XmppConnection implements Runnable { mPendingServiceDiscoveries--; if (mPendingServiceDiscoveries <= 0) { Log.d(Config.LOGTAG,account.getJid().toBareJid()+": done with service discovery"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource()); changeStatus(Account.State.ONLINE); if (bindListener != null) { bindListener.onBind(account); @@ -999,6 +1028,9 @@ public class XmppConnection implements Runnable { } } }); + synchronized (this.mPendingServiceDiscoveriesIds) { + this.mPendingServiceDiscoveriesIds.add(id); + } } private void enableAdvancedStreamFeatures() { @@ -1033,7 +1065,7 @@ public class XmppConnection implements Runnable { } } } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not query disco items of "+server); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco items of " + server); } } }); @@ -1086,13 +1118,12 @@ public class XmppConnection implements Runnable { return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32); } - public void sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) { + public String sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) { packet.setFrom(account.getJid()); - this.sendUnmodifiedIqPacket(packet, callback); - + return this.sendUnmodifiedIqPacket(packet, callback); } - private synchronized void sendUnmodifiedIqPacket(final IqPacket packet, final OnIqPacketReceived callback) { + private synchronized String sendUnmodifiedIqPacket(final IqPacket packet, final OnIqPacketReceived callback) { if (packet.getId() == null) { final String id = nextRandomId(); packet.setAttribute("id", id); @@ -1103,6 +1134,7 @@ public class XmppConnection implements Runnable { } } this.sendPacket(packet); + return packet.getId(); } public void sendMessagePacket(final MessagePacket packet) { @@ -1293,6 +1325,9 @@ public class XmppConnection implements Runnable { return this.lastPingSent; } + public long getLastDiscoStarted() { + return this.lastDiscoStarted; + } public long getLastPacketReceived() { return this.lastPacketReceived; } -- cgit v1.2.3