Implement transport fallback

This commit is contained in:
vanitasvitae 2017-08-04 14:06:12 +02:00
parent 23eb34932f
commit a07a1cdc61
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
5 changed files with 122 additions and 43 deletions

View File

@ -18,6 +18,7 @@ package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smackx.jingle.components.JingleContent;
import org.jivesoftware.smackx.jingle.components.JingleTransport;
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
/**
* Manager for JingleTransport components.
@ -30,6 +31,8 @@ public interface JingleTransportManager extends Comparable<JingleTransportManage
JingleTransport<?> createTransport(JingleContent content, JingleTransport<?> peersTransport);
JingleTransport<?> createTransport(JingleContent content, JingleContentTransportElement peersTransportElement);
/**
* Return a (usually) positive integer, which is used to define a strict order over the set of available transport
* managers.

View File

@ -62,7 +62,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
private JingleTransport<?> transport;
private JingleSecurity<?> security;
private JingleTransport<?> replaceTransport = null;
private JingleTransport<?> pendingReplacingTransport = null;
private final List<Callback> callbacks = Collections.synchronizedList(new ArrayList<Callback>());
private final Set<String> transportBlacklist = Collections.synchronizedSet(new HashSet<String>());
@ -165,6 +165,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
for (JingleContentElement c : request.getContents()) {
if (c.getName().equals(getName())) {
contentElement = c;
break;
}
}
@ -198,11 +199,12 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
}
public IQ handleTransportAccept(JingleElement request, XMPPConnection connection) {
if (replaceTransport == null) {
if (pendingReplacingTransport == null) {
LOGGER.log(Level.WARNING, "Received transport-accept, but apparently we did not try to replace the transport.");
return JingleElement.createJingleErrorOutOfOrder(request);
}
transport = replaceTransport;
transport = pendingReplacingTransport;
pendingReplacingTransport = null;
onAccept(connection);
@ -221,6 +223,54 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
}
public IQ handleTransportReplace(JingleElement request, XMPPConnection connection) {
JingleContentElement contentElement = null;
for (JingleContentElement c : request.getContents()) {
if (c.getName().equals(getName())) {
contentElement = c;
break;
}
}
if (contentElement == null) {
throw new AssertionError("Unknown content");
}
final JingleSession session = getParent();
final JingleContentTransportElement transportElement = contentElement.getTransport();
JingleTransportManager tm = session.getJingleManager().getTransportManager(transportElement.getNamespace());
// Unsupported/Blacklisted transport -> reject.
if (tm == null || getTransportBlacklist().contains(transportElement.getNamespace())) {
Async.go(new Runnable() {
@Override
public void run() {
try {
getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(JingleElement.createTransportReject(session.getOurJid(), session.getPeer(), session.getSessionId(), getCreator(), getName(), transportElement));
} catch (SmackException.NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not send transport-reject: " + e, e);
}
}
});
} else {
//Blacklist current transport
this.getTransportBlacklist().add(this.transport.getNamespace());
this.transport = tm.createTransport(this, transportElement);
Async.go(new Runnable() {
@Override
public void run() {
try {
getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(JingleElement.createTransportAccept(session.getOurJid(), session.getPeer(), session.getSessionId(), getCreator(), getName(), transport.getElement()));
} catch (SmackException.NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not send transport-accept: " + e, e);
}
}
});
onAccept(connection);
}
return IQ.createResultIQ(request);
}
@ -403,10 +453,10 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
return;
}
JingleTransport<?> rTransport = rManager.createTransport(this);
pendingReplacingTransport = rManager.createTransport(this);
JingleElement transportReplace = JingleElement.createTransportReplace(session.getInitiator(), session.getPeer(),
session.getSessionId(), getCreator(), getName(), rTransport.getElement());
session.getSessionId(), getCreator(), getName(), pendingReplacingTransport.getElement());
connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow();
}

View File

@ -25,6 +25,7 @@ import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.JingleTransportManager;
import org.jivesoftware.smackx.jingle.components.JingleContent;
import org.jivesoftware.smackx.jingle.components.JingleTransport;
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.provider.JingleIBBTransportProvider;
/**
@ -75,6 +76,12 @@ public final class JingleIBBTransportManager extends Manager implements JingleTr
return new JingleIBBTransport(other.getSid(), (short) Math.min(other.getBlockSize(), MAX_BLOCKSIZE));
}
@Override
public JingleTransport<?> createTransport(JingleContent content, JingleContentTransportElement peersTransportElement) {
JingleIBBTransport other = new JingleIBBTransportAdapter().transportFromElement(peersTransportElement);
return createTransport(content, other);
}
@Override
public int getPriority() {
return -1;

View File

@ -29,6 +29,7 @@ import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils;
@ -59,7 +60,7 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:s5b:1";
public static final String NAMESPACE = NAMESPACE_V1;
public static final int MAX_TIMEOUT = 10 * 1000;
private static final int MAX_TIMEOUT = 10 * 1000;
private final String sid;
@ -75,26 +76,15 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
private JingleTransportCallback callback;
@Override
public void handleSessionAccept(JingleContentTransportElement transportElement, XMPPConnection connection) {
JingleS5BTransportElement transport = (JingleS5BTransportElement) transportElement;
theirDstAddr = transport.getDestinationAddress();
theirMode = transport.getMode();
for (JingleContentTransportCandidateElement c : transport.getCandidates()) {
JingleS5BTransportCandidateElement candidate = (JingleS5BTransportCandidateElement) c;
addTheirCandidate(new JingleS5BTransportCandidate(candidate));
}
}
/**
* Create transport as initiator.
* @param initiator
* @param responder
* @param sid
* @param mode
* @param ourCandidates
* @param initiator initiator of the Jingle session.
* @param responder responder.
* @param sid sessionId of the Jingle session.
* @param mode TCP/UDP.
* @param ourCandidates our proxy candidates.
*/
public JingleS5BTransport(FullJid initiator, FullJid responder, String sid, Bytestream.Mode mode, List<JingleTransportCandidate<?>> ourCandidates) {
JingleS5BTransport(FullJid initiator, FullJid responder, String sid, Bytestream.Mode mode, List<JingleTransportCandidate<?>> ourCandidates) {
this.sid = sid;
this.ourMode = mode;
this.ourDstAddr = Socks5Utils.createDigest(sid, initiator, responder);
@ -107,12 +97,12 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
/**
* Create simple transport as responder.
* @param initiator
* @param responder
* @param ourCandidates
* @param other
* @param initiator initiator of the Jingle session.
* @param responder responder.
* @param ourCandidates our proxy candidates.
* @param other transport of the other party.
*/
public JingleS5BTransport(FullJid initiator, FullJid responder, List<JingleTransportCandidate<?>> ourCandidates, JingleS5BTransport other) {
JingleS5BTransport(FullJid initiator, FullJid responder, List<JingleTransportCandidate<?>> ourCandidates, JingleS5BTransport other) {
this.sid = other.sid;
this.ourMode = other.theirMode;
this.ourDstAddr = Socks5Utils.createDigest(other.sid, initiator, responder);
@ -131,15 +121,15 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
/**
* Create custom transport as responder.
* @param sid
* @param ourMode
* @param theirMode
* @param ourDstAddr
* @param theirDstAddr
* @param ourCandidates
* @param theirCandidates
* @param sid sessionId of the Jingle session.
* @param ourMode UPD/TCP.
* @param theirMode UPD/TCP.
* @param ourDstAddr SOCKS5 destination address (digest)
* @param theirDstAddr SOCKS5 destination address (digest)
* @param ourCandidates our proxy candidates.
* @param theirCandidates their proxy candidates.
*/
public JingleS5BTransport(String sid, Bytestream.Mode ourMode, Bytestream.Mode theirMode, String ourDstAddr, String theirDstAddr, List<JingleTransportCandidate<?>> ourCandidates, List<JingleTransportCandidate<?>> theirCandidates) {
JingleS5BTransport(String sid, Bytestream.Mode ourMode, Bytestream.Mode theirMode, String ourDstAddr, String theirDstAddr, List<JingleTransportCandidate<?>> ourCandidates, List<JingleTransportCandidate<?>> theirCandidates) {
this.sid = sid;
this.ourMode = ourMode;
this.theirMode = theirMode;
@ -160,7 +150,7 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
/**
* Copy constructor.
* @param original
* @param original which will be copied.
*/
public JingleS5BTransport(JingleS5BTransport original) {
this.sid = original.sid;
@ -287,8 +277,8 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
}
void connectIfReady() {
JingleS5BTransportManager jingleS5BTransportManager = JingleS5BTransportManager.getInstanceFor(getParent().getParent().getJingleManager().getConnection());
JingleSession session = getParent().getParent();
final JingleS5BTransportManager jingleS5BTransportManager = JingleS5BTransportManager.getInstanceFor(getParent().getParent().getJingleManager().getConnection());
final JingleSession session = getParent().getParent();
if (ourSelectedCandidate == null || theirSelectedCandidate == null) {
// Not yet ready if we or peer did not yet decide on a candidate.
@ -322,6 +312,8 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
nominated = theirSelectedCandidate;
}
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
if (nominated == theirSelectedCandidate) {
LOGGER.log(Level.INFO, "Their choice, so our proposed candidate is used.");
@ -330,12 +322,22 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
nominated = nominated.connect(MAX_TIMEOUT, false);
} catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) {
LOGGER.log(Level.INFO, "Could not connect to our candidate.", e);
Async.go(new Runnable() {
@Override
public void run() {
try {
session.getJingleManager().getConnection().createStanzaCollectorAndSend(jingleS5BTransportManager.createProxyError(JingleS5BTransport.this));
} catch (SmackException.NotConnectedException | InterruptedException e1) {
LOGGER.log(Level.SEVERE, "Could not send proxy error: " + e, e);
}
}
});
callback.onTransportFailed(new S5BTransportException.CandidateError(e));
return;
}
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
if (isProxy) {
LOGGER.log(Level.INFO, "Send candidate-activate.");
JingleElement candidateActivate = jingleS5BTransportManager.createCandidateActivated((JingleS5BTransport) nominated.getParent(), nominated);
@ -358,7 +360,6 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
//Our choice
else {
LOGGER.log(Level.INFO, "Our choice, so their candidate was used.");
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
if (!isProxy) {
LOGGER.log(Level.INFO, "Start transmission on " + nominated.getCandidateId());
this.bytestreamSession = new Socks5BytestreamSession(nominated.getSocket(), true);
@ -370,7 +371,7 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
}
private void activateProxy(JingleS5BTransportCandidate candidate) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException {
LOGGER.log(Level.INFO, "Activate proxy: " + candidate.getCandidateId() + " " + candidate.getStreamHost().getAddress() + ":" + candidate.getStreamHost().getPort() + " " + candidate.getStreamHost().getJID());
LOGGER.log(Level.INFO, "Activate proxy: " + candidate.getCandidateId() + " " + candidate.getStreamHost().getAddress() + ":" + candidate.getStreamHost().getPort() + " " + candidate.getStreamHost().getJID() + " for " + getParent().getParent().getPeer());
Bytestream activate = new Bytestream(getSid());
activate.setMode(null);
activate.setType(IQ.Type.set);
@ -380,6 +381,17 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
getParent().getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(activate).nextResultOrThrow();
}
@Override
public void handleSessionAccept(JingleContentTransportElement transportElement, XMPPConnection connection) {
JingleS5BTransportElement transport = (JingleS5BTransportElement) transportElement;
theirDstAddr = transport.getDestinationAddress();
theirMode = transport.getMode();
for (JingleContentTransportCandidateElement c : transport.getCandidates()) {
JingleS5BTransportCandidateElement candidate = (JingleS5BTransportCandidateElement) c;
addTheirCandidate(new JingleS5BTransportCandidate(candidate));
}
}
@Override
public IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
switch (info.getElementName()) {

View File

@ -43,6 +43,7 @@ import org.jivesoftware.smackx.jingle.components.JingleTransport;
import org.jivesoftware.smackx.jingle.components.JingleTransportCandidate;
import org.jivesoftware.smackx.jingle.element.JingleAction;
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
import org.jivesoftware.smackx.jingle.element.JingleElement;
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement;
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportElement;
@ -108,6 +109,12 @@ public final class JingleS5BTransportManager extends Manager implements JingleTr
return new JingleS5BTransport(session.getInitiator(), session.getResponder(), collectCandidates(), (JingleS5BTransport) peersTransport);
}
@Override
public JingleTransport<?> createTransport(JingleContent content, JingleContentTransportElement peersTransportElement) {
JingleS5BTransport other = new JingleS5BTransportAdapter().transportFromElement(peersTransportElement);
return createTransport(content, other);
}
@Override
public int getPriority() {
return 10000;