2014-02-17 23:58:40 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Copyright the original author or authors
|
|
|
|
*
|
|
|
|
* 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.bytestreams.socks5.packet;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import org.jivesoftware.smack.packet.IQ;
|
2014-09-11 09:49:16 +02:00
|
|
|
import org.jivesoftware.smack.packet.NamedElement;
|
2016-02-10 12:37:45 +01:00
|
|
|
import org.jivesoftware.smack.util.Objects;
|
|
|
|
import org.jivesoftware.smack.util.StringUtils;
|
2014-07-05 11:58:13 +02:00
|
|
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
import org.jxmpp.jid.Jid;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/**
|
2015-03-21 09:36:28 +01:00
|
|
|
* A stanza(/packet) representing part of a SOCKS5 Bytestream negotiation.
|
2014-02-17 23:58:40 +01:00
|
|
|
*
|
|
|
|
* @author Alexander Wenckus
|
|
|
|
*/
|
|
|
|
public class Bytestream extends IQ {
|
2015-01-11 21:52:06 +01:00
|
|
|
|
|
|
|
public static final String ELEMENT = QUERY_ELEMENT;
|
|
|
|
|
2014-07-05 11:58:13 +02:00
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* The XMPP namespace of the SOCKS5 Bytestream.
|
2014-07-05 11:58:13 +02:00
|
|
|
*/
|
|
|
|
public static final String NAMESPACE = "http://jabber.org/protocol/bytestreams";
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
private String sessionID;
|
|
|
|
|
|
|
|
private Mode mode = Mode.tcp;
|
|
|
|
|
2017-12-13 23:10:11 +01:00
|
|
|
private final List<StreamHost> streamHosts = new ArrayList<>();
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
private StreamHostUsed usedHost;
|
|
|
|
|
|
|
|
private Activate toActivate;
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* The default constructor.
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
|
|
|
public Bytestream() {
|
2015-01-11 21:52:06 +01:00
|
|
|
super(ELEMENT, NAMESPACE);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A constructor where the session ID can be specified.
|
|
|
|
*
|
|
|
|
* @param SID The session ID related to the negotiation.
|
|
|
|
* @see #setSessionID(String)
|
|
|
|
*/
|
|
|
|
public Bytestream(final String SID) {
|
2014-11-07 21:12:01 +01:00
|
|
|
this();
|
2014-02-17 23:58:40 +01:00
|
|
|
setSessionID(SID);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the session ID related to the bytestream. The session ID is a unique identifier used to
|
|
|
|
* differentiate between stream negotiations.
|
|
|
|
*
|
|
|
|
* @param sessionID the unique session ID that identifies the transfer.
|
|
|
|
*/
|
|
|
|
public void setSessionID(final String sessionID) {
|
|
|
|
this.sessionID = sessionID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the session ID related to the bytestream negotiation.
|
|
|
|
*
|
|
|
|
* @return Returns the session ID related to the bytestream negotiation.
|
|
|
|
* @see #setSessionID(String)
|
|
|
|
*/
|
|
|
|
public String getSessionID() {
|
|
|
|
return sessionID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the transport mode. This should be put in the initiation of the interaction.
|
|
|
|
*
|
|
|
|
* @param mode the transport mode, either UDP or TCP
|
|
|
|
* @see Mode
|
|
|
|
*/
|
|
|
|
public void setMode(final Mode mode) {
|
|
|
|
this.mode = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the transport mode.
|
|
|
|
*
|
|
|
|
* @return Returns the transport mode.
|
|
|
|
* @see #setMode(Mode)
|
|
|
|
*/
|
|
|
|
public Mode getMode() {
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a potential stream host that the remote user can connect to to receive the file.
|
|
|
|
*
|
|
|
|
* @param JID The JID of the stream host.
|
|
|
|
* @param address The internet address of the stream host.
|
|
|
|
* @return The added stream host.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public StreamHost addStreamHost(final Jid JID, final String address) {
|
2014-02-17 23:58:40 +01:00
|
|
|
return addStreamHost(JID, address, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a potential stream host that the remote user can connect to to receive the file.
|
|
|
|
*
|
|
|
|
* @param JID The JID of the stream host.
|
|
|
|
* @param address The internet address of the stream host.
|
|
|
|
* @param port The port on which the remote host is seeking connections.
|
|
|
|
* @return The added stream host.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public StreamHost addStreamHost(final Jid JID, final String address, final int port) {
|
2014-08-19 19:22:08 +02:00
|
|
|
StreamHost host = new StreamHost(JID, address, port);
|
2014-02-17 23:58:40 +01:00
|
|
|
addStreamHost(host);
|
|
|
|
|
|
|
|
return host;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a potential stream host that the remote user can transfer the file through.
|
|
|
|
*
|
|
|
|
* @param host The potential stream host.
|
|
|
|
*/
|
|
|
|
public void addStreamHost(final StreamHost host) {
|
|
|
|
streamHosts.add(host);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the list of stream hosts contained in the packet.
|
|
|
|
*
|
|
|
|
* @return Returns the list of stream hosts contained in the packet.
|
|
|
|
*/
|
2014-08-19 18:21:07 +02:00
|
|
|
public List<StreamHost> getStreamHosts() {
|
|
|
|
return Collections.unmodifiableList(streamHosts);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the stream host related to the given JID, or null if there is none.
|
|
|
|
*
|
|
|
|
* @param JID The JID of the desired stream host.
|
|
|
|
* @return Returns the stream host related to the given JID, or null if there is none.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public StreamHost getStreamHost(final Jid JID) {
|
2014-02-17 23:58:40 +01:00
|
|
|
if (JID == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
for (StreamHost host : streamHosts) {
|
|
|
|
if (host.getJID().equals(JID)) {
|
|
|
|
return host;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the count of stream hosts contained in this packet.
|
|
|
|
*
|
|
|
|
* @return Returns the count of stream hosts contained in this packet.
|
|
|
|
*/
|
|
|
|
public int countStreamHosts() {
|
|
|
|
return streamHosts.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Upon connecting to the stream host the target of the stream replies to the initiator with the
|
|
|
|
* JID of the SOCKS5 host that they used.
|
|
|
|
*
|
|
|
|
* @param JID The JID of the used host.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public void setUsedHost(final Jid JID) {
|
2014-02-17 23:58:40 +01:00
|
|
|
this.usedHost = new StreamHostUsed(JID);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the SOCKS5 host connected to by the remote user.
|
|
|
|
*
|
|
|
|
* @return Returns the SOCKS5 host connected to by the remote user.
|
|
|
|
*/
|
|
|
|
public StreamHostUsed getUsedHost() {
|
|
|
|
return usedHost;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-21 09:36:28 +01:00
|
|
|
* Returns the activate element of the stanza(/packet) sent to the proxy host to verify the identity of
|
2014-02-17 23:58:40 +01:00
|
|
|
* the initiator and match them to the appropriate stream.
|
|
|
|
*
|
2015-03-21 09:36:28 +01:00
|
|
|
* @return Returns the activate element of the stanza(/packet) sent to the proxy host to verify the
|
2014-02-17 23:58:40 +01:00
|
|
|
* identity of the initiator and match them to the appropriate stream.
|
|
|
|
*/
|
|
|
|
public Activate getToActivate() {
|
|
|
|
return toActivate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-21 09:36:28 +01:00
|
|
|
* Upon the response from the target of the used host the activate stanza(/packet) is sent to the SOCKS5
|
2014-02-17 23:58:40 +01:00
|
|
|
* proxy. The proxy will activate the stream or return an error after verifying the identity of
|
|
|
|
* the initiator, using the activate packet.
|
|
|
|
*
|
|
|
|
* @param targetID The JID of the target of the file transfer.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public void setToActivate(final Jid targetID) {
|
2014-02-17 23:58:40 +01:00
|
|
|
this.toActivate = new Activate(targetID);
|
|
|
|
}
|
|
|
|
|
2014-07-05 11:58:13 +02:00
|
|
|
@Override
|
2014-11-07 21:12:01 +01:00
|
|
|
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
2017-05-23 16:45:04 +02:00
|
|
|
switch (getType()) {
|
2014-07-05 11:58:13 +02:00
|
|
|
case set:
|
2014-08-19 12:43:21 +02:00
|
|
|
xml.optAttribute("sid", getSessionID());
|
|
|
|
xml.optAttribute("mode", getMode());
|
2014-08-20 21:25:14 +02:00
|
|
|
xml.rightAngleBracket();
|
2014-02-17 23:58:40 +01:00
|
|
|
if (getToActivate() == null) {
|
|
|
|
for (StreamHost streamHost : getStreamHosts()) {
|
2014-07-05 11:58:13 +02:00
|
|
|
xml.append(streamHost.toXML());
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2014-07-05 11:58:13 +02:00
|
|
|
xml.append(getToActivate().toXML());
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
2014-07-05 11:58:13 +02:00
|
|
|
break;
|
|
|
|
case result:
|
2014-08-20 21:25:14 +02:00
|
|
|
xml.rightAngleBracket();
|
2014-11-07 21:12:01 +01:00
|
|
|
xml.optAppend(getUsedHost());
|
|
|
|
// TODO Bytestream can include either used host *or* streamHosts. Never both. This should be ensured by the
|
|
|
|
// constructions mechanisms of Bytestream
|
2014-02-17 23:58:40 +01:00
|
|
|
// A result from the server can also contain stream hosts
|
2014-11-07 21:12:01 +01:00
|
|
|
for (StreamHost host : streamHosts) {
|
|
|
|
xml.append(host.toXML());
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
2014-07-05 11:58:13 +02:00
|
|
|
break;
|
|
|
|
case get:
|
2014-11-07 21:12:01 +01:00
|
|
|
xml.setEmptyElement();
|
|
|
|
break;
|
2014-07-05 11:58:13 +02:00
|
|
|
default:
|
|
|
|
throw new IllegalStateException();
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
2014-07-05 11:58:13 +02:00
|
|
|
return xml;
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-21 09:36:28 +01:00
|
|
|
* Stanza(/Packet) extension that represents a potential SOCKS5 proxy for the file transfer. Stream hosts
|
2014-02-17 23:58:40 +01:00
|
|
|
* are forwarded to the target of the file transfer who then chooses and connects to one.
|
|
|
|
*
|
|
|
|
* @author Alexander Wenckus
|
|
|
|
*/
|
2014-09-11 09:49:16 +02:00
|
|
|
public static class StreamHost implements NamedElement {
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
public static String ELEMENTNAME = "streamhost";
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
private final Jid JID;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
private final String addy;
|
|
|
|
|
2014-08-19 19:22:08 +02:00
|
|
|
private final int port;
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
public StreamHost(Jid jid, String address) {
|
2014-08-19 19:22:08 +02:00
|
|
|
this(jid, address, 0);
|
|
|
|
}
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor.
|
|
|
|
*
|
|
|
|
* @param JID The JID of the stream host.
|
|
|
|
* @param address The internet address of the stream host.
|
2017-12-23 20:21:19 +01:00
|
|
|
* @param port port of the stream host.
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public StreamHost(final Jid JID, final String address, int port) {
|
2016-02-10 12:37:45 +01:00
|
|
|
this.JID = Objects.requireNonNull(JID, "StreamHost JID must not be null");
|
|
|
|
this.addy = StringUtils.requireNotNullOrEmpty(address, "StreamHost address must not be null");
|
2014-08-19 19:22:08 +02:00
|
|
|
this.port = port;
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the JID of the stream host.
|
|
|
|
*
|
|
|
|
* @return Returns the JID of the stream host.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public Jid getJID() {
|
2014-02-17 23:58:40 +01:00
|
|
|
return JID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the internet address of the stream host.
|
|
|
|
*
|
|
|
|
* @return Returns the internet address of the stream host.
|
|
|
|
*/
|
|
|
|
public String getAddress() {
|
|
|
|
return addy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the port on which the potential stream host would accept the connection.
|
|
|
|
*
|
|
|
|
* @return Returns the port on which the potential stream host would accept the connection.
|
|
|
|
*/
|
|
|
|
public int getPort() {
|
|
|
|
return port;
|
|
|
|
}
|
|
|
|
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2014-02-17 23:58:40 +01:00
|
|
|
public String getElementName() {
|
|
|
|
return ELEMENTNAME;
|
|
|
|
}
|
|
|
|
|
2014-07-05 11:58:13 +02:00
|
|
|
@Override
|
|
|
|
public XmlStringBuilder toXML() {
|
|
|
|
XmlStringBuilder xml = new XmlStringBuilder(this);
|
|
|
|
xml.attribute("jid", getJID());
|
|
|
|
xml.attribute("host", getAddress());
|
2014-02-17 23:58:40 +01:00
|
|
|
if (getPort() != 0) {
|
2014-07-05 11:58:13 +02:00
|
|
|
xml.attribute("port", Integer.toString(getPort()));
|
|
|
|
} else {
|
|
|
|
xml.attribute("zeroconf", "_jabber.bytestreams");
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
2014-07-05 11:58:13 +02:00
|
|
|
xml.closeEmptyElement();
|
|
|
|
return xml;
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* After selected a SOCKS5 stream host and successfully connecting, the target of the file
|
2015-03-21 09:36:28 +01:00
|
|
|
* transfer returns a byte stream stanza(/packet) with the stream host used extension.
|
2014-02-17 23:58:40 +01:00
|
|
|
*
|
|
|
|
* @author Alexander Wenckus
|
|
|
|
*/
|
2014-09-11 09:49:16 +02:00
|
|
|
public static class StreamHostUsed implements NamedElement {
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
public static String ELEMENTNAME = "streamhost-used";
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
private final Jid JID;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor.
|
|
|
|
*
|
|
|
|
* @param JID The JID of the selected stream host.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public StreamHostUsed(final Jid JID) {
|
2014-02-17 23:58:40 +01:00
|
|
|
this.JID = JID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the JID of the selected stream host.
|
|
|
|
*
|
|
|
|
* @return Returns the JID of the selected stream host.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public Jid getJID() {
|
2014-02-17 23:58:40 +01:00
|
|
|
return JID;
|
|
|
|
}
|
|
|
|
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2014-02-17 23:58:40 +01:00
|
|
|
public String getElementName() {
|
|
|
|
return ELEMENTNAME;
|
|
|
|
}
|
|
|
|
|
2014-07-05 11:58:13 +02:00
|
|
|
@Override
|
|
|
|
public XmlStringBuilder toXML() {
|
|
|
|
XmlStringBuilder xml = new XmlStringBuilder(this);
|
|
|
|
xml.attribute("jid", getJID());
|
|
|
|
xml.closeEmptyElement();
|
|
|
|
return xml;
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-21 09:36:28 +01:00
|
|
|
* The stanza(/packet) sent by the stream initiator to the stream proxy to activate the connection.
|
2014-02-17 23:58:40 +01:00
|
|
|
*
|
|
|
|
* @author Alexander Wenckus
|
|
|
|
*/
|
2014-09-11 09:49:16 +02:00
|
|
|
public static class Activate implements NamedElement {
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
public static String ELEMENTNAME = "activate";
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
private final Jid target;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor specifying the target of the stream.
|
|
|
|
*
|
|
|
|
* @param target The target of the stream.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public Activate(final Jid target) {
|
2014-02-17 23:58:40 +01:00
|
|
|
this.target = target;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the target of the activation.
|
|
|
|
*
|
|
|
|
* @return Returns the target of the activation.
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
public Jid getTarget() {
|
2014-02-17 23:58:40 +01:00
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2014-02-17 23:58:40 +01:00
|
|
|
public String getElementName() {
|
|
|
|
return ELEMENTNAME;
|
|
|
|
}
|
|
|
|
|
2014-07-05 11:58:13 +02:00
|
|
|
@Override
|
|
|
|
public XmlStringBuilder toXML() {
|
|
|
|
XmlStringBuilder xml = new XmlStringBuilder(this);
|
2014-08-20 21:25:14 +02:00
|
|
|
xml.rightAngleBracket();
|
2014-07-05 11:58:13 +02:00
|
|
|
xml.escape(getTarget());
|
|
|
|
xml.closeElement(this);
|
|
|
|
return xml;
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The stream can be either a TCP stream or a UDP stream.
|
|
|
|
*
|
|
|
|
* @author Alexander Wenckus
|
|
|
|
*/
|
|
|
|
public enum Mode {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A TCP based stream.
|
|
|
|
*/
|
|
|
|
tcp,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A UDP based stream.
|
|
|
|
*/
|
|
|
|
udp;
|
|
|
|
|
|
|
|
public static Mode fromName(String name) {
|
|
|
|
Mode mode;
|
|
|
|
try {
|
|
|
|
mode = Mode.valueOf(name);
|
|
|
|
}
|
|
|
|
catch (Exception ex) {
|
|
|
|
mode = tcp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|