1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-24 04:52:05 +01:00

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

View file

@ -62,7 +62,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
private JingleTransport<?> transport; private JingleTransport<?> transport;
private JingleSecurity<?> security; private JingleSecurity<?> security;
private JingleTransport<?> replaceTransport = null; private JingleTransport<?> pendingReplacingTransport = null;
private final List<Callback> callbacks = Collections.synchronizedList(new ArrayList<Callback>()); private final List<Callback> callbacks = Collections.synchronizedList(new ArrayList<Callback>());
private final Set<String> transportBlacklist = Collections.synchronizedSet(new HashSet<String>()); 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()) { for (JingleContentElement c : request.getContents()) {
if (c.getName().equals(getName())) { if (c.getName().equals(getName())) {
contentElement = c; contentElement = c;
break;
} }
} }
@ -198,11 +199,12 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
} }
public IQ handleTransportAccept(JingleElement request, XMPPConnection connection) { 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."); LOGGER.log(Level.WARNING, "Received transport-accept, but apparently we did not try to replace the transport.");
return JingleElement.createJingleErrorOutOfOrder(request); return JingleElement.createJingleErrorOutOfOrder(request);
} }
transport = replaceTransport; transport = pendingReplacingTransport;
pendingReplacingTransport = null;
onAccept(connection); onAccept(connection);
@ -221,6 +223,54 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
} }
public IQ handleTransportReplace(JingleElement request, XMPPConnection connection) { 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); return IQ.createResultIQ(request);
} }
@ -403,10 +453,10 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
return; return;
} }
JingleTransport<?> rTransport = rManager.createTransport(this); pendingReplacingTransport = rManager.createTransport(this);
JingleElement transportReplace = JingleElement.createTransportReplace(session.getInitiator(), session.getPeer(), JingleElement transportReplace = JingleElement.createTransportReplace(session.getInitiator(), session.getPeer(),
session.getSessionId(), getCreator(), getName(), rTransport.getElement()); session.getSessionId(), getCreator(), getName(), pendingReplacingTransport.getElement());
connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow(); 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.JingleTransportManager;
import org.jivesoftware.smackx.jingle.components.JingleContent; import org.jivesoftware.smackx.jingle.components.JingleContent;
import org.jivesoftware.smackx.jingle.components.JingleTransport; import org.jivesoftware.smackx.jingle.components.JingleTransport;
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.provider.JingleIBBTransportProvider; 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)); 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 @Override
public int getPriority() { public int getPriority() {
return -1; return -1;

View file

@ -29,6 +29,7 @@ import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Stanza; 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.Socks5BytestreamSession;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; 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_V1 = "urn:xmpp:jingle:transports:s5b:1";
public static final String NAMESPACE = NAMESPACE_V1; 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; private final String sid;
@ -75,26 +76,15 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
private JingleTransportCallback callback; 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. * Create transport as initiator.
* @param initiator * @param initiator initiator of the Jingle session.
* @param responder * @param responder responder.
* @param sid * @param sid sessionId of the Jingle session.
* @param mode * @param mode TCP/UDP.
* @param ourCandidates * @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.sid = sid;
this.ourMode = mode; this.ourMode = mode;
this.ourDstAddr = Socks5Utils.createDigest(sid, initiator, responder); this.ourDstAddr = Socks5Utils.createDigest(sid, initiator, responder);
@ -107,12 +97,12 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
/** /**
* Create simple transport as responder. * Create simple transport as responder.
* @param initiator * @param initiator initiator of the Jingle session.
* @param responder * @param responder responder.
* @param ourCandidates * @param ourCandidates our proxy candidates.
* @param other * @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.sid = other.sid;
this.ourMode = other.theirMode; this.ourMode = other.theirMode;
this.ourDstAddr = Socks5Utils.createDigest(other.sid, initiator, responder); this.ourDstAddr = Socks5Utils.createDigest(other.sid, initiator, responder);
@ -131,15 +121,15 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
/** /**
* Create custom transport as responder. * Create custom transport as responder.
* @param sid * @param sid sessionId of the Jingle session.
* @param ourMode * @param ourMode UPD/TCP.
* @param theirMode * @param theirMode UPD/TCP.
* @param ourDstAddr * @param ourDstAddr SOCKS5 destination address (digest)
* @param theirDstAddr * @param theirDstAddr SOCKS5 destination address (digest)
* @param ourCandidates * @param ourCandidates our proxy candidates.
* @param theirCandidates * @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.sid = sid;
this.ourMode = ourMode; this.ourMode = ourMode;
this.theirMode = theirMode; this.theirMode = theirMode;
@ -160,7 +150,7 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
/** /**
* Copy constructor. * Copy constructor.
* @param original * @param original which will be copied.
*/ */
public JingleS5BTransport(JingleS5BTransport original) { public JingleS5BTransport(JingleS5BTransport original) {
this.sid = original.sid; this.sid = original.sid;
@ -287,8 +277,8 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
} }
void connectIfReady() { void connectIfReady() {
JingleS5BTransportManager jingleS5BTransportManager = JingleS5BTransportManager.getInstanceFor(getParent().getParent().getJingleManager().getConnection()); final JingleS5BTransportManager jingleS5BTransportManager = JingleS5BTransportManager.getInstanceFor(getParent().getParent().getJingleManager().getConnection());
JingleSession session = getParent().getParent(); final JingleSession session = getParent().getParent();
if (ourSelectedCandidate == null || theirSelectedCandidate == null) { if (ourSelectedCandidate == null || theirSelectedCandidate == null) {
// Not yet ready if we or peer did not yet decide on a candidate. // 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; nominated = theirSelectedCandidate;
} }
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
if (nominated == theirSelectedCandidate) { if (nominated == theirSelectedCandidate) {
LOGGER.log(Level.INFO, "Their choice, so our proposed candidate is used."); 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); nominated = nominated.connect(MAX_TIMEOUT, false);
} catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) { } catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) {
LOGGER.log(Level.INFO, "Could not connect to our candidate.", 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)); callback.onTransportFailed(new S5BTransportException.CandidateError(e));
return; return;
} }
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
if (isProxy) { if (isProxy) {
LOGGER.log(Level.INFO, "Send candidate-activate."); LOGGER.log(Level.INFO, "Send candidate-activate.");
JingleElement candidateActivate = jingleS5BTransportManager.createCandidateActivated((JingleS5BTransport) nominated.getParent(), nominated); JingleElement candidateActivate = jingleS5BTransportManager.createCandidateActivated((JingleS5BTransport) nominated.getParent(), nominated);
@ -358,7 +360,6 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
//Our choice //Our choice
else { else {
LOGGER.log(Level.INFO, "Our choice, so their candidate was used."); LOGGER.log(Level.INFO, "Our choice, so their candidate was used.");
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
if (!isProxy) { if (!isProxy) {
LOGGER.log(Level.INFO, "Start transmission on " + nominated.getCandidateId()); LOGGER.log(Level.INFO, "Start transmission on " + nominated.getCandidateId());
this.bytestreamSession = new Socks5BytestreamSession(nominated.getSocket(), true); 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 { 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()); Bytestream activate = new Bytestream(getSid());
activate.setMode(null); activate.setMode(null);
activate.setType(IQ.Type.set); activate.setType(IQ.Type.set);
@ -380,6 +381,17 @@ public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElemen
getParent().getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(activate).nextResultOrThrow(); 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 @Override
public IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) { public IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
switch (info.getElementName()) { 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.components.JingleTransportCandidate;
import org.jivesoftware.smackx.jingle.element.JingleAction; import org.jivesoftware.smackx.jingle.element.JingleAction;
import org.jivesoftware.smackx.jingle.element.JingleContentElement; 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.element.JingleElement;
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement; import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement;
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportElement; 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); 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 @Override
public int getPriority() { public int getPriority() {
return 10000; return 10000;