aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Schneppe <christian@pix-art.de>2019-09-03 19:50:42 +0200
committerChristian Schneppe <christian@pix-art.de>2019-09-03 19:50:42 +0200
commit71e4bcf9cb807e2f3954a5e968a44a409aaeebd7 (patch)
treee1f92e73467a3c5cad8da7943fbc8dbf0eb1f6be
parentedcfdb974dc544c0e79e457f50c12e5942619251 (diff)
bare minimum direct connections
-rw-r--r--src/main/java/de/pixart/messenger/persistance/FileBackend.java9
-rw-r--r--src/main/java/de/pixart/messenger/utils/SocksSocketFactory.java12
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/DirectConnectionUtils.java51
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleCandidate.java4
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleConnection.java15
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/jingle/JingleSocks5Transport.java89
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() {