2017-07-21 23:05:46 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* 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.transport.jingle_s5b;
|
2017-07-19 23:15:17 +02:00
|
|
|
|
2017-07-21 18:29:27 +02:00
|
|
|
import java.io.IOException;
|
2017-07-19 23:15:17 +02:00
|
|
|
import java.util.Collections;
|
2017-07-21 18:29:27 +02:00
|
|
|
import java.util.Iterator;
|
2017-07-19 23:15:17 +02:00
|
|
|
import java.util.List;
|
2017-07-21 18:29:27 +02:00
|
|
|
import java.util.concurrent.TimeoutException;
|
2017-07-19 23:15:17 +02:00
|
|
|
|
2017-07-21 18:29:27 +02:00
|
|
|
import org.jivesoftware.smack.SmackException;
|
2017-07-19 23:15:17 +02:00
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
2017-07-21 18:29:27 +02:00
|
|
|
import org.jivesoftware.smack.XMPPException;
|
2017-07-21 17:58:57 +02:00
|
|
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
|
2017-07-21 18:29:27 +02:00
|
|
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils;
|
2017-07-19 23:15:17 +02:00
|
|
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
2017-07-21 23:05:46 +02:00
|
|
|
import org.jivesoftware.smackx.jingle.transport.BytestreamSessionEstablishedListener;
|
|
|
|
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.JingleS5BTransportInfoElement;
|
|
|
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfoElement;
|
2017-07-21 23:51:06 +02:00
|
|
|
import org.jivesoftware.smackx.jingle.internal.JingleContent;
|
|
|
|
import org.jivesoftware.smackx.jingle.internal.JingleTransport;
|
|
|
|
import org.jivesoftware.smackx.jingle.internal.JingleTransportCandidate;
|
2017-07-19 23:15:17 +02:00
|
|
|
|
|
|
|
import org.jxmpp.jid.FullJid;
|
|
|
|
|
|
|
|
/**
|
2017-07-21 23:05:46 +02:00
|
|
|
* Jingle SOCKS5Bytestream transport component.
|
2017-07-19 23:15:17 +02:00
|
|
|
*/
|
2017-07-21 23:51:06 +02:00
|
|
|
public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElement> {
|
2017-07-19 23:15:17 +02:00
|
|
|
|
|
|
|
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:s5b:1";
|
|
|
|
public static final String NAMESPACE = NAMESPACE_V1;
|
|
|
|
|
2017-07-21 23:05:46 +02:00
|
|
|
public static final int MAX_TIMEOUT = 10 * 1000;
|
|
|
|
|
2017-07-19 23:15:17 +02:00
|
|
|
private final String sid;
|
|
|
|
|
|
|
|
private String dstAddr;
|
|
|
|
private Bytestream.Mode mode;
|
|
|
|
|
2017-07-21 18:29:27 +02:00
|
|
|
// PEERS candidate of OUR choice.
|
|
|
|
private JingleS5BTransportCandidate selectedCandidate;
|
2017-07-19 23:15:17 +02:00
|
|
|
|
2017-07-21 18:29:27 +02:00
|
|
|
/**
|
|
|
|
* Create fresh JingleS5BTransport.
|
2017-07-21 23:05:46 +02:00
|
|
|
* @param initiator initiator.
|
|
|
|
* @param responder responder.
|
2017-07-21 18:29:27 +02:00
|
|
|
*/
|
2017-07-21 23:51:06 +02:00
|
|
|
public JingleS5BTransport(FullJid initiator, FullJid responder, String sid, List<JingleTransportCandidate<?>> candidates) {
|
2017-07-21 23:05:46 +02:00
|
|
|
this(sid, Socks5Utils.createDigest(sid, initiator, responder), Bytestream.Mode.tcp, candidates);
|
|
|
|
}
|
|
|
|
|
2017-07-21 23:51:06 +02:00
|
|
|
public JingleS5BTransport(JingleContent content, JingleS5BTransport other, List<JingleTransportCandidate<?>> candidates) {
|
2017-07-21 23:05:46 +02:00
|
|
|
this(other.getSid(),
|
|
|
|
Socks5Utils.createDigest(other.getSid(), content.getParent().getInitiator(), content.getParent().getResponder()),
|
|
|
|
other.mode, candidates);
|
2017-07-21 18:29:27 +02:00
|
|
|
}
|
2017-07-19 23:15:17 +02:00
|
|
|
|
2017-07-21 23:51:06 +02:00
|
|
|
public JingleS5BTransport(String sid, String dstAddr, Bytestream.Mode mode, List<JingleTransportCandidate<?>> candidates) {
|
2017-07-19 23:15:17 +02:00
|
|
|
this.sid = sid;
|
|
|
|
this.dstAddr = dstAddr;
|
|
|
|
this.mode = mode;
|
|
|
|
|
2017-07-21 23:51:06 +02:00
|
|
|
for (JingleTransportCandidate<?> c : (candidates != null ?
|
2017-07-19 23:15:17 +02:00
|
|
|
candidates : Collections.<JingleS5BTransportCandidate>emptySet())) {
|
2017-07-21 17:58:57 +02:00
|
|
|
addCandidate(c);
|
2017-07-19 23:15:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public JingleS5BTransportElement getElement() {
|
|
|
|
JingleS5BTransportElement.Builder builder = JingleS5BTransportElement.getBuilder()
|
|
|
|
.setStreamId(sid)
|
|
|
|
.setDestinationAddress(dstAddr)
|
|
|
|
.setMode(mode);
|
|
|
|
|
2017-07-21 23:51:06 +02:00
|
|
|
for (JingleTransportCandidate candidate : getCandidates()) {
|
2017-07-21 17:58:57 +02:00
|
|
|
builder.addTransportCandidate((JingleS5BTransportCandidateElement) candidate.getElement());
|
2017-07-19 23:15:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return builder.build();
|
|
|
|
}
|
|
|
|
|
2017-07-21 23:05:46 +02:00
|
|
|
public String getSid() {
|
|
|
|
return sid;
|
|
|
|
}
|
|
|
|
|
2017-07-21 17:58:57 +02:00
|
|
|
public String getDstAddr() {
|
|
|
|
return dstAddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Bytestream.Mode getMode() {
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
2017-07-19 23:15:17 +02:00
|
|
|
@Override
|
|
|
|
public String getNamespace() {
|
|
|
|
return NAMESPACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-21 23:05:46 +02:00
|
|
|
public void establishIncomingBytestreamSession(BytestreamSessionEstablishedListener listener, XMPPConnection connection)
|
|
|
|
throws SmackException.NotConnectedException, InterruptedException {
|
|
|
|
establishBytestreamSession(listener, connection);
|
2017-07-19 23:15:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-21 23:05:46 +02:00
|
|
|
public void establishOutgoingBytestreamSession(BytestreamSessionEstablishedListener listener, XMPPConnection connection)
|
|
|
|
throws SmackException.NotConnectedException, InterruptedException {
|
|
|
|
establishBytestreamSession(listener, connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
void establishBytestreamSession(BytestreamSessionEstablishedListener listener, XMPPConnection connection)
|
|
|
|
throws SmackException.NotConnectedException, InterruptedException {
|
|
|
|
Socks5Proxy.getSocks5Proxy().addTransfer(dstAddr);
|
|
|
|
JingleS5BTransportManager transportManager = JingleS5BTransportManager.getInstanceFor(connection);
|
|
|
|
this.selectedCandidate = connectToCandidates(MAX_TIMEOUT);
|
|
|
|
|
2017-07-21 23:51:06 +02:00
|
|
|
if (selectedCandidate == CANDIDATE_FAILURE) {
|
2017-07-21 23:05:46 +02:00
|
|
|
connection.createStanzaCollectorAndSend(transportManager.createCandidateError(this));
|
|
|
|
}
|
|
|
|
|
2017-07-19 23:15:17 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:31:42 +02:00
|
|
|
public JingleS5BTransportCandidate connectToCandidates(int timeout) {
|
2017-07-21 23:51:06 +02:00
|
|
|
for (JingleTransportCandidate c : getCandidates()) {
|
2017-07-21 18:31:42 +02:00
|
|
|
int _timeout = timeout / getCandidates().size(); //TODO: Wise?
|
2017-07-21 18:29:27 +02:00
|
|
|
try {
|
2017-07-21 18:31:42 +02:00
|
|
|
return ((JingleS5BTransportCandidate) c).connect(_timeout);
|
2017-07-21 18:29:27 +02:00
|
|
|
} catch (IOException | TimeoutException | InterruptedException | SmackException | XMPPException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 18:31:42 +02:00
|
|
|
|
|
|
|
// Failed to connect to any candidate.
|
2017-07-21 23:51:06 +02:00
|
|
|
return CANDIDATE_FAILURE;
|
2017-07-21 18:29:27 +02:00
|
|
|
}
|
|
|
|
|
2017-07-19 23:15:17 +02:00
|
|
|
@Override
|
|
|
|
public void handleTransportInfo(JingleContentTransportInfoElement info) {
|
|
|
|
switch (info.getElementName()) {
|
|
|
|
|
|
|
|
case JingleS5BTransportInfoElement.CandidateUsed.ELEMENT:
|
|
|
|
handleCandidateUsed((JingleS5BTransportInfoElement) info);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case JingleS5BTransportInfoElement.CandidateActivated.ELEMENT:
|
|
|
|
handleCandidateActivate((JingleS5BTransportInfoElement) info);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case JingleS5BTransportInfoElement.CandidateError.ELEMENT:
|
|
|
|
handleCandidateError((JingleS5BTransportInfoElement) info);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case JingleS5BTransportInfoElement.ProxyError.ELEMENT:
|
|
|
|
handleProxyError((JingleS5BTransportInfoElement) info);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new AssertionError("Unknown transport-info element: " + info.getElementName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleCandidateUsed(JingleS5BTransportInfoElement info) {
|
|
|
|
String candidateId = ((JingleS5BTransportInfoElement.CandidateUsed) info).getCandidateId();
|
|
|
|
|
2017-07-21 18:29:27 +02:00
|
|
|
JingleS5BTransport peers = (JingleS5BTransport) getPeersProposal();
|
|
|
|
|
|
|
|
if (peers.getSelectedCandidate() != null) {
|
|
|
|
//TODO: Alert! We already received one candidateUsed previously!
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-21 23:51:06 +02:00
|
|
|
Iterator<JingleTransportCandidate<?>> ourCandidates = getCandidates().iterator();
|
2017-07-21 18:29:27 +02:00
|
|
|
while (ourCandidates.hasNext()) {
|
|
|
|
JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) ourCandidates.next();
|
|
|
|
if (candidate.getCandidateId().equals(candidateId)) {
|
|
|
|
peers.setSelectedCandidate(candidate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (peers.getSelectedCandidate() == null) {
|
|
|
|
//TODO: Alert! Illegal candidateId!
|
2017-07-19 23:15:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//connectIfReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleCandidateActivate(JingleS5BTransportInfoElement info) {
|
|
|
|
//Socks5BytestreamSession bs = new Socks5BytestreamSession(ourChoice.socket,
|
|
|
|
// ourChoice.candidate.getJid().asBareJid().equals(jingleSession.getRemote().asBareJid()));
|
|
|
|
//callback.onSessionInitiated(bs);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleCandidateError(JingleS5BTransportInfoElement info) {
|
2017-07-21 18:29:27 +02:00
|
|
|
((JingleS5BTransport) getPeersProposal()).setSelectedCandidate(CANDIDATE_FAILURE);
|
2017-07-19 23:15:17 +02:00
|
|
|
//connectIfReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void handleProxyError(JingleS5BTransportInfoElement info) {
|
|
|
|
//TODO
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:29:27 +02:00
|
|
|
public void setSelectedCandidate(JingleS5BTransportCandidate candidate) {
|
|
|
|
selectedCandidate = candidate;
|
|
|
|
}
|
|
|
|
|
|
|
|
public JingleS5BTransportCandidate getSelectedCandidate() {
|
|
|
|
return selectedCandidate;
|
|
|
|
}
|
|
|
|
|
2017-07-19 23:15:17 +02:00
|
|
|
/**
|
|
|
|
* Internal dummy candidate used to represent failure.
|
|
|
|
* Kinda depressing, isn't it?
|
|
|
|
*/
|
|
|
|
private final static JingleS5BTransportCandidate CANDIDATE_FAILURE = new JingleS5BTransportCandidate(null, null, -1, null);
|
|
|
|
}
|