bare minimum direct connections
This commit is contained in:
parent
783ed53d3a
commit
1c413edf06
|
@ -39,6 +39,7 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.security.DigestOutputStream;
|
||||
|
@ -359,6 +360,15 @@ 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())) {
|
||||
return false;
|
||||
|
|
|
@ -20,6 +20,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});
|
||||
|
@ -34,6 +37,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 {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package eu.siacs.conversations.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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -11,7 +11,6 @@ import java.io.OutputStream;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -303,9 +302,15 @@ 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);
|
||||
|
@ -690,7 +695,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();
|
||||
|
@ -728,7 +733,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();
|
||||
|
@ -755,6 +760,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();
|
||||
|
@ -1052,7 +1058,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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -29,6 +32,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) {
|
||||
|
@ -56,6 +60,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) {
|
||||
|
@ -71,7 +157,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) {
|
||||
|
@ -182,6 +270,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||
FileBackend.close(inputStream);
|
||||
FileBackend.close(outputStream);
|
||||
FileBackend.close(socket);
|
||||
FileBackend.close(serverSocket);
|
||||
}
|
||||
|
||||
public boolean isEstablished() {
|
||||
|
|
Loading…
Reference in New Issue