1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-23 20:42:06 +01:00

Rewrote S5BTransportSession

This commit is contained in:
vanitasvitae 2017-06-26 18:05:17 +02:00
parent 5eba48b7d8
commit 69b3f5837e
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
8 changed files with 298 additions and 331 deletions

View file

@ -70,7 +70,7 @@ public abstract class JingleContentTransport implements ExtensionElement {
XmlStringBuilder xml = new XmlStringBuilder(this); XmlStringBuilder xml = new XmlStringBuilder(this);
addExtraAttributes(xml); addExtraAttributes(xml);
if (candidates.isEmpty() && infos.isEmpty()) { if (candidates.isEmpty() && info == null) {
xml.closeEmptyElement(); xml.closeEmptyElement();
} else { } else {

View file

@ -1,3 +1,19 @@
/**
*
* Copyright 2017 Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.transports; package org.jivesoftware.smackx.jingle.transports;
/** /**

View file

@ -16,8 +16,6 @@
*/ */
package org.jivesoftware.smackx.jingle.transports; package org.jivesoftware.smackx.jingle.transports;
import java.lang.ref.WeakReference;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingle.JingleSession; import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.element.Jingle; import org.jivesoftware.smackx.jingle.element.Jingle;
@ -28,12 +26,11 @@ import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
* Created by vanitas on 20.06.17. * Created by vanitas on 20.06.17.
*/ */
public abstract class JingleTransportSession<T extends JingleContentTransport> { public abstract class JingleTransportSession<T extends JingleContentTransport> {
protected final WeakReference<JingleSession> jingleSession; protected final JingleSession jingleSession;
protected JingleContentTransport remoteTransport; protected T ourProposal, theirProposal;
protected JingleContentTransport localTransport;
public JingleTransportSession(JingleSession session) { public JingleTransportSession(JingleSession session) {
this.jingleSession = new WeakReference<>(session); this.jingleSession = session;
} }
public abstract T createTransport(); public abstract T createTransport();
@ -47,10 +44,12 @@ public abstract class JingleTransportSession<T extends JingleContentTransport> {
JingleContentTransport t = content.getJingleTransport(); JingleContentTransport t = content.getJingleTransport();
if (t != null && t.getNamespace().equals(getNamespace())) { if (t != null && t.getNamespace().equals(getNamespace())) {
remoteTransport = t; setTheirProposal(t);
} }
} }
public abstract void setTheirProposal(JingleContentTransport transport);
public abstract void initiateOutgoingSession(JingleTransportInitiationCallback callback); public abstract void initiateOutgoingSession(JingleTransportInitiationCallback callback);
public abstract void initiateIncomingSession(JingleTransportInitiationCallback callback); public abstract void initiateIncomingSession(JingleTransportInitiationCallback callback);

View file

@ -28,6 +28,7 @@ import org.jivesoftware.smackx.bytestreams.BytestreamSession;
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
import org.jivesoftware.smackx.jingle.JingleSession; import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.element.Jingle; import org.jivesoftware.smackx.jingle.element.Jingle;
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback; import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback;
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager; import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
import org.jivesoftware.smackx.jingle.transports.JingleTransportSession; import org.jivesoftware.smackx.jingle.transports.JingleTransportSession;
@ -46,29 +47,27 @@ public class JingleIBBTransportSession extends JingleTransportSession<JingleIBBT
@Override @Override
public JingleIBBTransport createTransport() { public JingleIBBTransport createTransport() {
if (remoteTransport == null) { if (theirProposal == null) {
return new JingleIBBTransport(); return new JingleIBBTransport();
} else { } else {
JingleIBBTransport existing = (JingleIBBTransport) remoteTransport; return new JingleIBBTransport(theirProposal.getBlockSize(), theirProposal.getSessionId());
return new JingleIBBTransport(existing.getBlockSize(), existing.getSessionId());
} }
} }
@Override
public void setTheirProposal(JingleContentTransport transport) {
theirProposal = (JingleIBBTransport) transport;
}
@Override @Override
public void initiateOutgoingSession(JingleTransportInitiationCallback callback) { public void initiateOutgoingSession(JingleTransportInitiationCallback callback) {
LOGGER.log(Level.INFO, "Initiate Jingle InBandBytestream session."); LOGGER.log(Level.INFO, "Initiate Jingle InBandBytestream session.");
if (jingleSession.get() == null) {
callback.onException(new NullPointerException("Lost reference to JingleSession."));
return;
}
JingleIBBTransport ibbTransport = (JingleIBBTransport) remoteTransport;
BytestreamSession session; BytestreamSession session;
try { try {
session = InBandBytestreamManager.getByteStreamManager(jingleSession.get().getConnection()) session = InBandBytestreamManager.getByteStreamManager(jingleSession.getConnection())
.establishSession(jingleSession.get().getRemote(), ibbTransport.getSessionId()); .establishSession(jingleSession.getRemote(), theirProposal.getSessionId());
callback.onSessionInitiated(session); callback.onSessionInitiated(session);
} catch (SmackException.NoResponseException | InterruptedException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) { } catch (SmackException.NoResponseException | InterruptedException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
callback.onException(e); callback.onException(e);
@ -78,17 +77,12 @@ public class JingleIBBTransportSession extends JingleTransportSession<JingleIBBT
@Override @Override
public void initiateIncomingSession(final JingleTransportInitiationCallback callback) { public void initiateIncomingSession(final JingleTransportInitiationCallback callback) {
LOGGER.log(Level.INFO, "Await Jingle InBandBytestream session."); LOGGER.log(Level.INFO, "Await Jingle InBandBytestream session.");
if (jingleSession.get() == null) {
callback.onException(new NullPointerException("Lost reference to JingleSession."));
}
final JingleIBBTransport ibbTransport = (JingleIBBTransport) remoteTransport; InBandBytestreamManager.getByteStreamManager(jingleSession.getConnection()).addIncomingBytestreamListener(new BytestreamListener() {
InBandBytestreamManager.getByteStreamManager(jingleSession.get().getConnection()).addIncomingBytestreamListener(new BytestreamListener() {
@Override @Override
public void incomingBytestreamRequest(BytestreamRequest request) { public void incomingBytestreamRequest(BytestreamRequest request) {
if (request.getFrom().asFullJidIfPossible().equals(jingleSession.get().getRemote()) if (request.getFrom().asFullJidIfPossible().equals(jingleSession.getRemote())
&& request.getSessionID().equals(ibbTransport.getSessionId())) { && request.getSessionID().equals(theirProposal.getSessionId())) {
BytestreamSession session; BytestreamSession session;
try { try {
@ -116,6 +110,6 @@ public class JingleIBBTransportSession extends JingleTransportSession<JingleIBBT
@Override @Override
public JingleTransportManager<JingleIBBTransport> transportManager() { public JingleTransportManager<JingleIBBTransport> transportManager() {
return JingleIBBTransportManager.getInstanceFor(jingleSession.get().getConnection()); return JingleIBBTransportManager.getInstanceFor(jingleSession.getConnection());
} }
} }

View file

@ -34,82 +34,70 @@ import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils;
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
import org.jivesoftware.smackx.jingle.JingleManager; import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.JingleSession; import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.JingleUtil;
import org.jivesoftware.smackx.jingle.element.Jingle; import org.jivesoftware.smackx.jingle.element.Jingle;
import org.jivesoftware.smackx.jingle.element.JingleContent; import org.jivesoftware.smackx.jingle.element.JingleContent;
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate; import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate;
import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback; import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback;
import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationException;
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
import org.jivesoftware.smackx.jingle.transports.JingleTransportSession; import org.jivesoftware.smackx.jingle.transports.JingleTransportSession;
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport; import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate; import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate;
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportInfo; import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportInfo;
/** /**
* LOL. * Created by vanitas on 26.06.17.
*/ */
public class JingleS5BTransportSession extends JingleTransportSession<JingleS5BTransport> { public class JingleS5BTransportSession extends JingleTransportSession<JingleS5BTransport> {
private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportSession.class.getName()); private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportSession.class.getName());
private final JingleS5BTransportManager transportManager;
private final JingleUtil jutil;
private Socket connectedSocket;
private JingleS5BTransportCandidate localUsedCandidate;
private JingleS5BTransportCandidate remoteUsedCandidate;
private JingleTransportInitiationCallback callback; private JingleTransportInitiationCallback callback;
private boolean remoteError = false;
private boolean localError = false;
public JingleS5BTransportSession(JingleSession jingleSession) { public JingleS5BTransportSession(JingleSession jingleSession) {
super(jingleSession); super(jingleSession);
transportManager = JingleS5BTransportManager.getInstanceFor(jingleSession.getConnection());
jutil = new JingleUtil(jingleSession.getConnection());
} }
private UsedCandidate ourChoice, theirChoice;
@Override @Override
public JingleS5BTransport createTransport() { public JingleS5BTransport createTransport() {
if (localTransport != null) { if (ourProposal == null) {
return (JingleS5BTransport) localTransport; ourProposal = createTransport(JingleManager.randomId(), Bytestream.Mode.tcp);
} }
return ourProposal;
return createTransport(JingleManager.randomId(), Bytestream.Mode.tcp);
} }
private JingleS5BTransport createTransport(String sid, Bytestream.Mode mode) { @Override
JingleSession jSession = jingleSession.get(); public void setTheirProposal(JingleContentTransport transport) {
if (jSession == null) { theirProposal = (JingleS5BTransport) transport;
throw new NullPointerException("Lost reference to JingleSession."); }
public JingleS5BTransport createTransport(String sid, Bytestream.Mode mode) {
JingleS5BTransport.Builder jb = JingleS5BTransport.getBuilder()
.setStreamId(sid).setMode(mode).setDestinationAddress(
Socks5Utils.createDigest(sid, jingleSession.getLocal(), jingleSession.getRemote()));
//Local host
for (Bytestream.StreamHost host : transportManager().getLocalStreamHosts()) {
jb.addTransportCandidate(new JingleS5BTransportCandidate(host, 100));
} }
JingleS5BTransport.Builder builder = JingleS5BTransport.getBuilder(); List<Bytestream.StreamHost> remoteHosts;
for (Bytestream.StreamHost host : transportManager.getLocalStreamHosts()) {
JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate(host, 100);
builder.addTransportCandidate(candidate);
}
List<Bytestream.StreamHost> availableStreamHosts = null;
try { try {
availableStreamHosts = transportManager.getAvailableStreamHosts(); remoteHosts = transportManager().getAvailableStreamHosts();
} catch (XMPPException.XMPPErrorException | SmackException.NoResponseException | InterruptedException | } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
SmackException.NotConnectedException e) { LOGGER.log(Level.WARNING, "Could not determine available StreamHosts.", e);
LOGGER.log(Level.WARNING, "Could not get available StreamHosts: " + e, e); remoteHosts = Collections.emptyList();
} }
for (Bytestream.StreamHost host : availableStreamHosts != null ? for (Bytestream.StreamHost host : remoteHosts) {
availableStreamHosts : Collections.<Bytestream.StreamHost>emptyList()) { jb.addTransportCandidate(new JingleS5BTransportCandidate(host, 0));
JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate(host, 0);
builder.addTransportCandidate(candidate);
} }
builder.setStreamId(sid); return jb.build();
builder.setMode(mode); }
builder.setDestinationAddress(Socks5Utils.createDigest(sid, jSession.getLocal(), jSession.getRemote()));
localTransport = builder.build();
return (JingleS5BTransport) localTransport; public void setTheirTransport(JingleContentTransport transport) {
theirProposal = (JingleS5BTransport) transport;
} }
@Override @Override
@ -125,277 +113,246 @@ public class JingleS5BTransportSession extends JingleTransportSession<JingleS5BT
} }
private void initiateSession() { private void initiateSession() {
JingleSession jSession = jingleSession.get(); JingleContent content = jingleSession.getContents().get(0);
if (jSession == null) { UsedCandidate usedCandidate = chooseFromProposedCandidates(theirProposal);
throw new NullPointerException("Lost reference to jingleSession."); if (usedCandidate == null) {
} ourChoice = CANDIDATE_FAILURE;
Jingle candidateError = transportManager().createCandidateError(
JingleS5BTransport receivedTransport = (JingleS5BTransport) remoteTransport; jingleSession.getRemote(), jingleSession.getInitiator(), jingleSession.getSessionId(),
content.getSenders(), content.getCreator(), content.getName(), theirProposal.getStreamId());
Socket socket = null;
JingleS5BTransportCandidate workedForUs = null;
for (JingleContentTransportCandidate c : receivedTransport.getCandidates()) {
JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) c;
Bytestream.StreamHost streamHost = candidate.getStreamHost();
String address = streamHost.getAddress();
try { try {
Socks5Client socks5Client = new Socks5Client(streamHost, receivedTransport.getDestinationAddress()); jingleSession.getConnection().sendStanza(candidateError);
socket = socks5Client.getSocket(10 * 1000); } catch (SmackException.NotConnectedException | InterruptedException e) {
workedForUs = candidate; LOGGER.log(Level.WARNING, "Could not send candidate-error.", e);
LOGGER.log(Level.INFO, "Connected to remote address " + address + " with dstAddr "
+ receivedTransport.getDestinationAddress());
break;
} catch (IOException | XMPPException | InterruptedException | TimeoutException | SmackException e) {
LOGGER.log(Level.WARNING, "Could not connect to remotes address " + address + " with dstAddr "
+ receivedTransport.getDestinationAddress());
} }
}
JingleContent content = jSession.getContents().get(0);
Jingle response;
if (socket != null) {
connectedSocket = socket;
localUsedCandidate = workedForUs;
response = transportManager.createCandidateUsed(jSession.getRemote(), jSession.getInitiator(),
jSession.getSessionId(), content.getSenders(), content.getCreator(),
content.getName(), receivedTransport.getStreamId(), localUsedCandidate.getCandidateId());
} else { } else {
localError = true; ourChoice = usedCandidate;
response = transportManager.createCandidateError(jSession.getRemote(), jSession.getInitiator(), Jingle jingle = transportManager().createCandidateUsed(jingleSession.getRemote(), jingleSession.getInitiator(), jingleSession.getSessionId(),
jSession.getSessionId(), content.getSenders(), content.getCreator(), content.getSenders(), content.getCreator(), content.getName(), theirProposal.getStreamId(), ourChoice.candidate.getCandidateId());
content.getName(), receivedTransport.getStreamId()); try {
} jingleSession.getConnection().createStanzaCollectorAndSend(jingle)
.nextResultOrThrow();
try { } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
jSession.getConnection().sendStanza(response); LOGGER.log(Level.WARNING, "Could not send candidate-used.", e);
} catch (SmackException.NotConnectedException | InterruptedException e) {
LOGGER.log(Level.WARNING, "Could not send candidate-used.", e);
}
closeIfBothSidesFailed();
}
private boolean closeIfBothSidesFailed() {
JingleSession jSession = jingleSession.get();
if (jSession != null) {
if (localError && remoteError) {
callback.onException(new JingleTransportInitiationException.CandidateError());
return true;
} }
} }
return false; connectIfReady();
} }
private JingleS5BTransportCandidate determineUsedCandidate() { private UsedCandidate chooseFromProposedCandidates(JingleS5BTransport proposal) {
if (localUsedCandidate == null && remoteUsedCandidate == null) { for (JingleContentTransportCandidate c : proposal.getCandidates()) {
return null;
}
if (remoteUsedCandidate == null) {
return localUsedCandidate;
}
if (localUsedCandidate == null) {
return remoteUsedCandidate;
}
if (localUsedCandidate.getPriority() > remoteUsedCandidate.getPriority()) {
return localUsedCandidate;
}
if (localUsedCandidate.getPriority() < remoteUsedCandidate.getPriority()) {
return remoteUsedCandidate;
}
return jingleSession.get().isInitiator() ? localUsedCandidate : remoteUsedCandidate;
}
public IQ handleCandidateUsed(Jingle candidateUsed) {
JingleS5BTransportInfo info = (JingleS5BTransportInfo) candidateUsed.getContents().get(0)
.getJingleTransport().getInfos().get(0);
String candidateId = ((JingleS5BTransportInfo.CandidateUsed) info).getCandidateId();
for (JingleContentTransportCandidate c : localTransport.getCandidates()) {
JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) c; JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) c;
if (candidate.getCandidateId().equals(candidateId)) {
remoteUsedCandidate = candidate;
break;
}
}
if (remoteUsedCandidate == null) {
callback.onException(new Exception("Unknown candidate"));
return jutil.createErrorMalformedRequest(candidateUsed);
}
if (localUsedCandidate != null || localError) {
try { try {
connect(determineUsedCandidate()); return connectToTheirCandidate(candidate);
} catch (SmackException.NotConnectedException | InterruptedException e) { } catch (InterruptedException | TimeoutException | XMPPException | SmackException | IOException e) {
callback.onException(e); LOGGER.log(Level.WARNING, "Could not connect to " + candidate.getHost(), e);
} }
} }
LOGGER.log(Level.WARNING, "Failed to connect to any candidate.");
return IQ.createResultIQ(candidateUsed); return null;
} }
private void connect(JingleS5BTransportCandidate candidate) throws SmackException.NotConnectedException, InterruptedException { private UsedCandidate connectToTheirCandidate(JingleS5BTransportCandidate candidate)
JingleSession jSession = jingleSession.get(); throws InterruptedException, TimeoutException, SmackException, XMPPException, IOException {
if (jSession == null) { Bytestream.StreamHost streamHost = candidate.getStreamHost();
throw new NullPointerException("Lost reference to JingleSession."); String address = streamHost.getAddress();
} Socks5Client socks5Client = new Socks5Client(streamHost, theirProposal.getDestinationAddress());
JingleContent content = jSession.getContents().get(0); Socket socket = socks5Client.getSocket(10 * 1000);
LOGGER.log(Level.INFO, "Connected to their StreamHost " + address + " using dstAddr "
// Used candidate belongs to remote. + theirProposal.getDestinationAddress());
if (candidate == localUsedCandidate) { return new UsedCandidate(theirProposal, candidate, socket);
if (connectedSocket != null) {
callback.onSessionInitiated(new Socks5BytestreamSession(connectedSocket,
candidate.getJid().asBareJid().equals(jSession.getRemote().asBareJid())));
}
else {
throw new AssertionError("Connected socket is null.");
}
}
// Used candidate belongs to us.
else {
if (candidate.getType() == JingleS5BTransportCandidate.Type.proxy) {
if (!candidate.getJid().asBareJid().equals(jSession.getLocal().asBareJid())) {
//activate proxy
Bytestream activateProxy = new Bytestream(((JingleS5BTransport) localTransport).getStreamId());
activateProxy.setToActivate(candidate.getJid());
activateProxy.setTo(candidate.getJid());
Bytestream result;
try {
result = jSession.getConnection().createStanzaCollectorAndSend(activateProxy).nextResultOrThrow();
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException |
SmackException.NotConnectedException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not activate proxy server: " + e, e);
//send proxy error
jSession.getConnection().sendStanza(transportManager.createProxyError(
jSession.getRemote(), jSession.getInitiator(), jSession.getSessionId(),
content.getSenders(), content.getCreator(), content.getName(),
((JingleS5BTransport) localTransport).getStreamId()));
return;
}
transportManager.createCandidateActivated(jSession.getRemote(), jSession.getInitiator(), jSession.getSessionId(),
content.getSenders(), content.getCreator(), content.getName(), ((JingleS5BTransport) localTransport).getStreamId(),
candidate.getCandidateId());
}
Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(candidate.getStreamHost(),
((JingleS5BTransport) localTransport).getDestinationAddress(),
jSession.getConnection(), ((JingleS5BTransport) localTransport).getStreamId(),
jSession.getLocal());
try {
connectedSocket = socks5Client.getSocket(10 * 1000);
} catch (IOException | XMPPException | SmackException | InterruptedException | TimeoutException e) {
callback.onException(e);
return;
}
callback.onSessionInitiated(new Socks5BytestreamSession(connectedSocket, true));
} else {
//TODO: Find out how to react.
}
}
} }
public IQ handleCandidateActivated(Jingle candidateActivated) { private UsedCandidate connectToOurCandidate(JingleS5BTransportCandidate candidate)
JingleContent content = candidateActivated.getContents().get(0); throws InterruptedException, TimeoutException, SmackException, XMPPException, IOException {
JingleS5BTransportInfo info = (JingleS5BTransportInfo) content.getJingleTransport().getInfos().get(0); Bytestream.StreamHost streamHost = candidate.getStreamHost();
if (!info.getElementName().equals(JingleS5BTransportInfo.CandidateActivated.ELEMENT)) { String address = streamHost.getAddress();
throw new AssertionError("Element mus be candidateActivated."); Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(
} streamHost, ourProposal.getDestinationAddress(), jingleSession.getConnection(),
jingleSession.getSessionId(), jingleSession.getRemote());
JingleS5BTransportInfo.CandidateActivated activated = (JingleS5BTransportInfo.CandidateActivated) info; Socket socket = socks5Client.getSocket(10 * 1000);
if (!localUsedCandidate.getCandidateId().equals(activated.getCandidateId())) { LOGGER.log(Level.INFO, "Connected to our StreamHost " + address + " using dstAddr "
throw new AssertionError("CandidateID must be equal."); + theirProposal.getDestinationAddress());
} return new UsedCandidate(ourProposal, candidate, socket);
if (connectedSocket == null) {
throw new AssertionError("connected Socket must not be null.");
}
callback.onSessionInitiated(new Socks5BytestreamSession(connectedSocket,
jingleSession.get().getRemote().asBareJid().equals(localUsedCandidate.getJid().asBareJid())));
return IQ.createResultIQ(candidateActivated);
}
public IQ handleCandidateError(Jingle candidateError) {
remoteError = true;
if (closeIfBothSidesFailed()) {
return IQ.createResultIQ(candidateError);
}
if (localUsedCandidate != null || localError) {
try {
connect(determineUsedCandidate());
} catch (SmackException.NotConnectedException | InterruptedException e) {
callback.onException(e);
}
}
return IQ.createResultIQ(candidateError);
}
public IQ handleProxyError(Jingle proxyError) {
return IQ.createResultIQ(proxyError);
} }
@Override @Override
public String getNamespace() { public String getNamespace() {
return transportManager.getNamespace(); return JingleS5BTransport.NAMESPACE_V1;
} }
@Override @Override
public IQ handleTransportInfo(Jingle transportInfo) { public IQ handleTransportInfo(Jingle transportInfo) {
JingleS5BTransport transport = (JingleS5BTransport) transportInfo.getContents().get(0).getJingleTransport(); JingleS5BTransportInfo info = (JingleS5BTransportInfo) transportInfo.getContents().get(0).getJingleTransport().getInfo();
JingleS5BTransportInfo info = (JingleS5BTransportInfo) transport.getInfos().get(0);
if (info != null) { switch (info.getElementName()) {
case JingleS5BTransportInfo.CandidateUsed.ELEMENT:
return handleCandidateUsed(transportInfo);
switch (info.getElementName()) { case JingleS5BTransportInfo.CandidateActivated.ELEMENT:
case JingleS5BTransportInfo.CandidateUsed.ELEMENT: return handleCandidateActivate(transportInfo);
return handleCandidateUsed(transportInfo);
case JingleS5BTransportInfo.CandidateActivated.ELEMENT: case JingleS5BTransportInfo.CandidateError.ELEMENT:
return handleCandidateActivated(transportInfo); return handleCandidateError(transportInfo);
case JingleS5BTransportInfo.CandidateError.ELEMENT: case JingleS5BTransportInfo.ProxyError.ELEMENT:
return handleCandidateError(transportInfo); return handleProxyError(transportInfo);
}
//We should never go here, but lets be gracious...
return IQ.createResultIQ(transportInfo);
}
case JingleS5BTransportInfo.ProxyError.ELEMENT: public IQ handleCandidateUsed(Jingle jingle) {
return handleProxyError(transportInfo); JingleS5BTransportInfo info = (JingleS5BTransportInfo) jingle.getContents().get(0).getJingleTransport().getInfo();
String candidateId = ((JingleS5BTransportInfo.CandidateUsed) info).getCandidateId();
theirChoice = new UsedCandidate(ourProposal, ourProposal.getCandidate(candidateId), null);
default: if (theirChoice.candidate == null) {
return IQ.createResultIQ(transportInfo); /*
} TODO: Booooooh illegal candidateId!! Go home!!!!11elf
*/
}
connectIfReady();
return IQ.createResultIQ(jingle);
}
public IQ handleCandidateActivate(Jingle jingle) {
Socks5BytestreamSession bs = new Socks5BytestreamSession(ourChoice.socket,
ourChoice.candidate.getJid().asBareJid().equals(jingleSession.getRemote().asBareJid()));
callback.onSessionInitiated(bs);
return IQ.createResultIQ(jingle);
}
public IQ handleCandidateError(Jingle jingle) {
theirChoice = CANDIDATE_FAILURE;
connectIfReady();
return IQ.createResultIQ(jingle);
}
public IQ handleProxyError(Jingle jingle) {
//TODO
return IQ.createResultIQ(jingle);
}
private void connectIfReady() {
if (ourChoice == null || theirChoice == null) {
// Not yet ready.
LOGGER.log(Level.INFO, "Not ready.");
return;
}
if (ourChoice == CANDIDATE_FAILURE && theirChoice == CANDIDATE_FAILURE) {
// TODO: Transport failed.
} else { } else {
return jutil.createErrorMalformedRequest(transportInfo); UsedCandidate nominated;
if (ourChoice != CANDIDATE_FAILURE && theirChoice != CANDIDATE_FAILURE) {
if (ourChoice.candidate.getPriority() > theirChoice.candidate.getPriority()) {
nominated = ourChoice;
} else if (ourChoice.candidate.getPriority() < theirChoice.candidate.getPriority()) {
nominated = theirChoice;
} else {
nominated = jingleSession.isInitiator() ? ourChoice : theirChoice;
}
} else if (ourChoice != CANDIDATE_FAILURE) {
nominated = ourChoice;
} else {
nominated = theirChoice;
}
// Proxy. Needs activation.
if (nominated.candidate.getType() == JingleS5BTransportCandidate.Type.proxy) {
//Our proxy. Activate it.
if (nominated == theirChoice) {
JingleContent content = jingleSession.getContents().get(0);
Bytestream activateProxy = new Bytestream(ourProposal.getStreamId());
activateProxy.setToActivate(nominated.candidate.getJid());
activateProxy.setTo(nominated.candidate.getJid());
//Send proxy activation.
try {
jingleSession.getConnection().createStanzaCollectorAndSend(activateProxy).nextResultOrThrow();
//Connect
try {
nominated = connectToOurCandidate(nominated.candidate);
Socks5BytestreamSession bs = new Socks5BytestreamSession(nominated.socket,
nominated.candidate.getJid().asBareJid().equals(jingleSession.getLocal().asBareJid()));
callback.onSessionInitiated(bs);
} catch (TimeoutException | SmackException | IOException | XMPPException e) {
LOGGER.log(Level.WARNING, "Could not connect to our own proxy after activation.", e);
//TODO: ???
}
}
//Could not activate proxy. Send proxy-error
catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
LOGGER.log(Level.WARNING, "Could not activate proxy at " + nominated.candidate.getJid(), e);
Jingle proxyError = transportManager().createProxyError(
jingleSession.getRemote(), jingleSession.getInitiator(),
jingleSession.getSessionId(), content.getSenders(),
content.getCreator(), content.getName(), nominated.transport.getStreamId());
try {
jingleSession.getConnection().sendStanza(proxyError);
}
//Could not send proxy-error. WTF?
catch (SmackException.NotConnectedException | InterruptedException e1) {
LOGGER.log(Level.WARNING, "Could not send proxy-error.", e1);
}
callback.onException(e);
}
//Send candidate-activate.
Jingle candidateActivate = transportManager().createCandidateActivated(
jingleSession.getRemote(), jingleSession.getInitiator(), jingleSession.getSessionId(),
content.getSenders(), content.getCreator(), content.getName(), nominated.transport.getStreamId(),
nominated.candidate.getCandidateId());
try {
jingleSession.getConnection().createStanzaCollectorAndSend(candidateActivate)
.nextResultOrThrow();
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
LOGGER.log(Level.WARNING, "Could not send candidate-activated", e);
}
}
//Else wait for activation.
}
// Direct connection. Go ahead.
else {
Socks5BytestreamSession bs;
if (nominated == ourChoice) {
bs = new Socks5BytestreamSession(nominated.socket,
nominated.candidate.getJid().asBareJid().equals(jingleSession.getRemote().asBareJid()));
} else {
try {
nominated = connectToOurCandidate(theirChoice.candidate);
} catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) {
LOGGER.log(Level.SEVERE, "Failed to connect to our own StreamHost!");
callback.onException(e);
return;
}
bs = new Socks5BytestreamSession(nominated.socket,
nominated.candidate.getJid().asBareJid().equals(jingleSession.getLocal().asBareJid()));
}
callback.onSessionInitiated(bs);
}
} }
} }
@Override @Override
public JingleTransportManager<JingleS5BTransport> transportManager() { public JingleS5BTransportManager transportManager() {
return JingleS5BTransportManager.getInstanceFor(jingleSession.get().getConnection()); return JingleS5BTransportManager.getInstanceFor(jingleSession.getConnection());
} }
private static class UsedCandidate {
private final Socket socket;
private final JingleS5BTransport transport;
private final JingleS5BTransportCandidate candidate;
public UsedCandidate(JingleS5BTransport transport, JingleS5BTransportCandidate candidate, Socket socket) {
this.socket = socket;
this.transport = transport;
this.candidate = candidate;
}
}
private static final UsedCandidate CANDIDATE_FAILURE = new UsedCandidate(null, null, null);
} }

View file

@ -39,8 +39,8 @@ public class JingleS5BTransport extends JingleContentTransport {
private final String dstAddr; private final String dstAddr;
private final Bytestream.Mode mode; private final Bytestream.Mode mode;
protected JingleS5BTransport(List<JingleContentTransportCandidate> candidates, List<JingleContentTransportInfo> infos, String streamId, String dstAddr, Bytestream.Mode mode) { protected JingleS5BTransport(List<JingleContentTransportCandidate> candidates, JingleContentTransportInfo info, String streamId, String dstAddr, Bytestream.Mode mode) {
super(candidates, infos); super(candidates, info);
StringUtils.requireNotNullOrEmpty(streamId, "sid MUST be neither null, nor empty."); StringUtils.requireNotNullOrEmpty(streamId, "sid MUST be neither null, nor empty.");
this.streamId = streamId; this.streamId = streamId;
this.dstAddr = dstAddr; this.dstAddr = dstAddr;
@ -94,7 +94,7 @@ public class JingleS5BTransport extends JingleContentTransport {
private String dstAddr; private String dstAddr;
private Bytestream.Mode mode; private Bytestream.Mode mode;
private ArrayList<JingleContentTransportCandidate> candidates = new ArrayList<>(); private ArrayList<JingleContentTransportCandidate> candidates = new ArrayList<>();
private ArrayList<JingleContentTransportInfo> infos = new ArrayList<>(); private JingleContentTransportInfo info = null;
public Builder setStreamId(String sid) { public Builder setStreamId(String sid) {
this.streamId = sid; this.streamId = sid;
@ -116,29 +116,29 @@ public class JingleS5BTransport extends JingleContentTransport {
return this; return this;
} }
public Builder addTransportInfo(JingleContentTransportInfo info) { public Builder setTransportInfo(JingleContentTransportInfo info) {
this.infos.add(info); this.info = info;
return this; return this;
} }
public Builder setCandidateUsed(String candidateId) { public Builder setCandidateUsed(String candidateId) {
return addTransportInfo(JingleS5BTransportInfo.CandidateUsed(candidateId)); return setTransportInfo(JingleS5BTransportInfo.CandidateUsed(candidateId));
} }
public Builder setCandidateActivated(String candidateId) { public Builder setCandidateActivated(String candidateId) {
return addTransportInfo(JingleS5BTransportInfo.CandidateActivated(candidateId)); return setTransportInfo(JingleS5BTransportInfo.CandidateActivated(candidateId));
} }
public Builder setCandidateError() { public Builder setCandidateError() {
return addTransportInfo(JingleS5BTransportInfo.CandidateError()); return setTransportInfo(JingleS5BTransportInfo.CandidateError());
} }
public Builder setProxyError() { public Builder setProxyError() {
return addTransportInfo(JingleS5BTransportInfo.ProxyError()); return setTransportInfo(JingleS5BTransportInfo.ProxyError());
} }
public JingleS5BTransport build() { public JingleS5BTransport build() {
return new JingleS5BTransport(candidates, infos, streamId, dstAddr, mode); return new JingleS5BTransport(candidates, info, streamId, dstAddr, mode);
} }
} }
} }

View file

@ -83,23 +83,23 @@ public class JingleS5BTransportProvider extends JingleContentTransportProvider<J
break; break;
case JingleS5BTransportInfo.CandidateActivated.ELEMENT: case JingleS5BTransportInfo.CandidateActivated.ELEMENT:
builder.addTransportInfo(JingleS5BTransportInfo.CandidateActivated( builder.setTransportInfo(JingleS5BTransportInfo.CandidateActivated(
parser.getAttributeValue(null, parser.getAttributeValue(null,
JingleS5BTransportInfo.CandidateActivated.ATTR_CID))); JingleS5BTransportInfo.CandidateActivated.ATTR_CID)));
break; break;
case JingleS5BTransportInfo.CandidateUsed.ELEMENT: case JingleS5BTransportInfo.CandidateUsed.ELEMENT:
builder.addTransportInfo(JingleS5BTransportInfo.CandidateUsed( builder.setTransportInfo(JingleS5BTransportInfo.CandidateUsed(
parser.getAttributeValue(null, parser.getAttributeValue(null,
JingleS5BTransportInfo.CandidateUsed.ATTR_CID))); JingleS5BTransportInfo.CandidateUsed.ATTR_CID)));
break; break;
case JingleS5BTransportInfo.CandidateError.ELEMENT: case JingleS5BTransportInfo.CandidateError.ELEMENT:
builder.addTransportInfo(JingleS5BTransportInfo.CandidateError()); builder.setTransportInfo(JingleS5BTransportInfo.CandidateError());
break; break;
case JingleS5BTransportInfo.ProxyError.ELEMENT: case JingleS5BTransportInfo.ProxyError.ELEMENT:
builder.addTransportInfo(JingleS5BTransportInfo.ProxyError()); builder.setTransportInfo(JingleS5BTransportInfo.ProxyError());
break; break;
} }
} }

View file

@ -16,6 +16,7 @@
*/ */
package org.jivesoftware.smackx.jingle.transports.jingle_s5b; package org.jivesoftware.smackx.jingle.transports.jingle_s5b;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertNull;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -114,10 +115,10 @@ public class JingleS5BTransportTest extends SmackTestSuite {
JingleS5BTransport candidateErrorTransport = new JingleS5BTransportProvider() JingleS5BTransport candidateErrorTransport = new JingleS5BTransportProvider()
.parse(TestUtils.getParser(candidateError)); .parse(TestUtils.getParser(candidateError));
assertNull(candidateErrorTransport.getDestinationAddress()); assertNull(candidateErrorTransport.getDestinationAddress());
assertEquals(1, candidateErrorTransport.getInfos().size()); assertNotNull(candidateErrorTransport.getInfo());
assertEquals("vj3hs98y", candidateErrorTransport.getStreamId()); assertEquals("vj3hs98y", candidateErrorTransport.getStreamId());
assertEquals(JingleS5BTransportInfo.CandidateError(), assertEquals(JingleS5BTransportInfo.CandidateError(),
candidateErrorTransport.getInfos().get(0)); candidateErrorTransport.getInfo());
assertEquals(candidateError, candidateErrorTransport.toXML().toString()); assertEquals(candidateError, candidateErrorTransport.toXML().toString());
String proxyError = String proxyError =
@ -127,10 +128,10 @@ public class JingleS5BTransportTest extends SmackTestSuite {
JingleS5BTransport proxyErrorTransport = new JingleS5BTransportProvider() JingleS5BTransport proxyErrorTransport = new JingleS5BTransportProvider()
.parse(TestUtils.getParser(proxyError)); .parse(TestUtils.getParser(proxyError));
assertNull(proxyErrorTransport.getDestinationAddress()); assertNull(proxyErrorTransport.getDestinationAddress());
assertEquals(1, proxyErrorTransport.getInfos().size()); assertNotNull(proxyErrorTransport.getInfo());
assertEquals("vj3hs98y", proxyErrorTransport.getStreamId()); assertEquals("vj3hs98y", proxyErrorTransport.getStreamId());
assertEquals(JingleS5BTransportInfo.ProxyError(), assertEquals(JingleS5BTransportInfo.ProxyError(),
proxyErrorTransport.getInfos().get(0)); proxyErrorTransport.getInfo());
assertEquals(proxyError, proxyErrorTransport.toXML().toString()); assertEquals(proxyError, proxyErrorTransport.toXML().toString());
String candidateUsed = String candidateUsed =
@ -139,12 +140,12 @@ public class JingleS5BTransportTest extends SmackTestSuite {
"</transport>"; "</transport>";
JingleS5BTransport candidateUsedTransport = new JingleS5BTransportProvider() JingleS5BTransport candidateUsedTransport = new JingleS5BTransportProvider()
.parse(TestUtils.getParser(candidateUsed)); .parse(TestUtils.getParser(candidateUsed));
assertEquals(1, candidateUsedTransport.getInfos().size()); assertNotNull(candidateUsedTransport.getInfo());
assertEquals(JingleS5BTransportInfo.CandidateUsed("hr65dqyd"), assertEquals(JingleS5BTransportInfo.CandidateUsed("hr65dqyd"),
candidateUsedTransport.getInfos().get(0)); candidateUsedTransport.getInfo());
assertEquals("hr65dqyd", assertEquals("hr65dqyd",
((JingleS5BTransportInfo.CandidateUsed) ((JingleS5BTransportInfo.CandidateUsed)
candidateUsedTransport.getInfos().get(0)).getCandidateId()); candidateUsedTransport.getInfo()).getCandidateId());
assertEquals(candidateUsed, candidateUsedTransport.toXML().toString()); assertEquals(candidateUsed, candidateUsedTransport.toXML().toString());
String candidateActivated = String candidateActivated =
@ -153,12 +154,12 @@ public class JingleS5BTransportTest extends SmackTestSuite {
"</transport>"; "</transport>";
JingleS5BTransport candidateActivatedTransport = new JingleS5BTransportProvider() JingleS5BTransport candidateActivatedTransport = new JingleS5BTransportProvider()
.parse(TestUtils.getParser(candidateActivated)); .parse(TestUtils.getParser(candidateActivated));
assertEquals(1, candidateActivatedTransport.getInfos().size()); assertNotNull(candidateActivatedTransport.getInfo());
assertEquals(JingleS5BTransportInfo.CandidateActivated("hr65dqyd"), assertEquals(JingleS5BTransportInfo.CandidateActivated("hr65dqyd"),
candidateActivatedTransport.getInfos().get(0)); candidateActivatedTransport.getInfo());
assertEquals("hr65dqyd", assertEquals("hr65dqyd",
((JingleS5BTransportInfo.CandidateActivated) ((JingleS5BTransportInfo.CandidateActivated)
candidateActivatedTransport.getInfos().get(0)).getCandidateId()); candidateActivatedTransport.getInfo()).getCandidateId());
assertEquals(candidateActivated, candidateActivatedTransport.toXML().toString()); assertEquals(candidateActivated, candidateActivatedTransport.toXML().toString());
} }
} }