diff options
Diffstat (limited to 'src')
6 files changed, 174 insertions, 6 deletions
diff --git a/src/main/java/de/pixart/messenger/persistance/FileBackend.java b/src/main/java/de/pixart/messenger/persistance/FileBackend.java index 063ff50a0..962496dcb 100644 --- a/src/main/java/de/pixart/messenger/persistance/FileBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/FileBackend.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.net.ServerSocket; import java.net.Socket; import java.net.URL; import java.security.DigestOutputStream; @@ -1369,6 +1370,14 @@ public class FileBackend { } } + public static void close(final ServerSocket socket) { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + } + } + } public static boolean weOwnFile(Context context, Uri uri) { if (uri == null || !ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { diff --git a/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java b/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java index e3465fb9b..0bc27749a 100644 --- a/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java +++ b/src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java @@ -18,6 +18,9 @@ public class SocksSocketFactory { proxyOs.write(new byte[]{0x05, 0x01, 0x00}); byte[] response = new byte[2]; proxyIs.read(response); + if (response[0] != 0x05 || response[1] != 0x00) { + throw new SocksConnectionException(); + } byte[] dest = destination.getBytes(); ByteBuffer request = ByteBuffer.allocate(7 + dest.length); request.put(new byte[]{0x05, 0x01, 0x00, 0x03}); @@ -32,6 +35,15 @@ public class SocksSocketFactory { } } + public static boolean contains(byte needle, byte[] haystack) { + for(byte hay : haystack) { + if (hay == needle) { + return true; + } + } + return false; + } + public static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException { Socket socket = new Socket(); try { diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/DirectConnectionUtils.java b/src/main/java/de/pixart/messenger/xmpp/jingle/DirectConnectionUtils.java new file mode 100644 index 000000000..3292e65e4 --- /dev/null +++ b/src/main/java/de/pixart/messenger/xmpp/jingle/DirectConnectionUtils.java @@ -0,0 +1,51 @@ +package de.pixart.messenger.xmpp.jingle; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.UUID; + +import rocks.xmpp.addr.Jid; + +public class DirectConnectionUtils { + + private static List<InetAddress> getLocalAddresses() { + final List<InetAddress> addresses = new ArrayList<>(); + final Enumeration<NetworkInterface> interfaces; + try { + interfaces = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + return addresses; + } + while (interfaces.hasMoreElements()) { + NetworkInterface networkInterface = interfaces.nextElement(); + final Enumeration<InetAddress> inetAddressEnumeration = networkInterface.getInetAddresses(); + while (inetAddressEnumeration.hasMoreElements()) { + final InetAddress inetAddress = inetAddressEnumeration.nextElement(); + if (!inetAddress.isLoopbackAddress()) { + addresses.add(inetAddress); + } + } + } + return addresses; + } + + public static List<JingleCandidate> getLocalCandidates(Jid jid) { + SecureRandom random = new SecureRandom(); + ArrayList<JingleCandidate> candidates = new ArrayList<>(); + for (InetAddress inetAddress : getLocalAddresses()) { + final JingleCandidate candidate = new JingleCandidate(UUID.randomUUID().toString(), true); + candidate.setHost(inetAddress.getHostAddress()); + candidate.setPort(random.nextInt(60000) + 1024); + candidate.setType(JingleCandidate.TYPE_DIRECT); + candidate.setJid(jid); + candidate.setPriority(8257536 + candidates.size()); + candidates.add(candidate); + } + return candidates; + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java index 8a5c1cfe3..40d6f077b 100644 --- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java +++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java @@ -127,7 +127,9 @@ public class JingleCandidate { element.setAttribute("cid", this.getCid()); element.setAttribute("host", this.getHost()); element.setAttribute("port", Integer.toString(this.getPort())); - element.setAttribute("jid", this.getJid().toString()); + if (jid != null) { + element.setAttribute("jid", jid.toEscapedString()); + } element.setAttribute("priority", Integer.toString(this.getPriority())); if (this.getType() == TYPE_DIRECT) { element.setAttribute("type", "direct"); diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java index 7050874fb..00acb6ca3 100644 --- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java +++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java @@ -308,9 +308,13 @@ public class JingleConnection implements Transferable { this.transportId = this.mJingleConnectionManager.nextRandomId(); if (this.initialTransport == Transport.IBB) { this.sendInitRequest(); - } else if (this.candidates.size() > 0) { - this.sendInitRequest(); //TODO we will never get here? Can probably be removed } else { + final List<JingleCandidate> directCandidates = DirectConnectionUtils.getLocalCandidates(account.getJid()); + for (JingleCandidate directCandidate : directCandidates) { + final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, directCandidate); + connections.put(directCandidate.getCid(), socksConnection); + candidates.add(directCandidate); + } this.mJingleConnectionManager.getPrimaryCandidate(account, (success, candidate) -> { if (success) { final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); @@ -699,7 +703,7 @@ public class JingleConnection implements Transferable { onProxyActivated.failed(); return true; } else if (content.socks5transport().hasChild("candidate-error")) { - Log.d(Config.LOGTAG, "received candidate error"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received candidate error"); this.receivedCandidate = true; if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) { this.connect(); @@ -737,7 +741,7 @@ public class JingleConnection implements Transferable { final JingleSocks5Transport connection = chooseConnection(); this.transport = connection; if (connection == null) { - Log.d(Config.LOGTAG, "could not find suitable candidate"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not find suitable candidate"); this.disconnectSocks5Connections(); if (initiating()) { this.sendFallbackToIbb(); @@ -764,6 +768,7 @@ public class JingleConnection implements Transferable { .setContent(this.getCounterPart().toString()); mXmppConnectionService.sendIqPacket(account, activation, (account, response) -> { if (response.getType() != IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + response.toString()); onProxyActivated.failed(); } else { onProxyActivated.success(); @@ -1061,7 +1066,7 @@ public class JingleConnection implements Transferable { } private void sendCandidateError() { - Log.d(Config.LOGTAG, "sending candidate error"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending candidate error"); JinglePacket packet = bootstrapPacket("transport-info"); Content content = new Content(this.contentCreator, this.contentName); content.setTransportId(this.transportId); diff --git a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java index 7538ad980..f1b527b99 100644 --- a/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java @@ -6,9 +6,12 @@ import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -30,6 +33,7 @@ public class JingleSocks5Transport extends JingleTransport { private InputStream inputStream; private boolean isEstablished = false; private boolean activated = false; + private ServerSocket serverSocket; private Socket socket; JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) { @@ -57,6 +61,88 @@ public class JingleSocks5Transport extends JingleTransport { } messageDigest.reset(); this.destination = CryptoHelper.bytesToHex(messageDigest.digest(destBuilder.toString().getBytes())); + if (candidate.isOurs() && candidate.getType() == JingleCandidate.TYPE_DIRECT) { + createServerSocket(); + } + } + + private void createServerSocket() { + try { + serverSocket = new ServerSocket(); + serverSocket.bind(new InetSocketAddress(InetAddress.getByName(candidate.getHost()), candidate.getPort())); + new Thread(() -> { + try { + final Socket socket = serverSocket.accept(); + new Thread(() -> { + try { + acceptIncomingSocketConnection(socket); + } catch (IOException e) { + Log.d(Config.LOGTAG, "unable to read from socket", e); + + } + }).start(); + } catch (IOException e) { + if (!serverSocket.isClosed()) { + Log.d(Config.LOGTAG, "unable to accept socket", e); + } + } + }).start(); + } catch (IOException e) { + Log.d(Config.LOGTAG, "unable to bind server socket ", e); + } + } + + private void acceptIncomingSocketConnection(Socket socket) throws IOException { + Log.d(Config.LOGTAG, "accepted connection from " + socket.getInetAddress().getHostAddress()); + byte[] authBegin = new byte[2]; + InputStream inputStream = socket.getInputStream(); + OutputStream outputStream = socket.getOutputStream(); + inputStream.read(authBegin); + if (authBegin[0] != 0x5) { + socket.close(); + } + short methodCount = authBegin[1]; + byte[] methods = new byte[methodCount]; + inputStream.read(methods); + if (SocksSocketFactory.contains((byte) 0x00, methods)) { + outputStream.write(new byte[]{0x05, 0x00}); + } else { + outputStream.write(new byte[]{0x05, (byte) 0xff}); + } + byte[] connectCommand = new byte[4]; + inputStream.read(connectCommand); + if (connectCommand[0] == 0x05 && connectCommand[1] == 0x01 && connectCommand[3] == 0x03) { + int destinationCount = inputStream.read(); + byte[] destination = new byte[destinationCount]; + inputStream.read(destination); + int port = inputStream.read(); + final String receivedDestination = new String(destination); + Log.d(Config.LOGTAG, "received destination " + receivedDestination + ":" + port + " - expected " + this.destination); + final ByteBuffer response = ByteBuffer.allocate(7 + destination.length); + final byte[] responseHeader; + final boolean success; + if (receivedDestination.equals(this.destination)) { + responseHeader = new byte[]{0x05, 0x00, 0x00, 0x03}; + success = true; + } else { + responseHeader = new byte[]{0x05, 0x04, 0x00, 0x03}; + success = false; + } + response.put(responseHeader); + response.put((byte) destination.length); + response.put(destination); + response.putShort((short) port); + outputStream.write(response.array()); + outputStream.flush(); + if (success) { + this.socket = socket; + this.inputStream = inputStream; + this.outputStream = outputStream; + this.isEstablished = true; + } + } else { + socket.close(); + } } public void connect(final OnTransportConnected callback) { @@ -72,7 +158,9 @@ public class JingleSocks5Transport extends JingleTransport { } inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); + socket.setSoTimeout(5000); SocksSocketFactory.createSocksConnection(socket, destination, 0); + socket.setSoTimeout(0); isEstablished = true; callback.established(); } catch (IOException e) { @@ -183,6 +271,7 @@ public class JingleSocks5Transport extends JingleTransport { FileBackend.close(inputStream); FileBackend.close(outputStream); FileBackend.close(socket); + FileBackend.close(serverSocket); } public boolean isEstablished() { |