Home | History | Annotate | Download | only in packet
      1 /**
      2  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      3  * you may not use this file except in compliance with the License.
      4  * You may obtain a copy of the License at
      5  *
      6  *     http://www.apache.org/licenses/LICENSE-2.0
      7  *
      8  * Unless required by applicable law or agreed to in writing, software
      9  * distributed under the License is distributed on an "AS IS" BASIS,
     10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     11  * See the License for the specific language governing permissions and
     12  * limitations under the License.
     13  */
     14 package org.jivesoftware.smackx.bytestreams.socks5.packet;
     15 
     16 import java.util.ArrayList;
     17 import java.util.Collection;
     18 import java.util.Collections;
     19 import java.util.List;
     20 
     21 import org.jivesoftware.smack.packet.IQ;
     22 import org.jivesoftware.smack.packet.PacketExtension;
     23 
     24 /**
     25  * A packet representing part of a SOCKS5 Bytestream negotiation.
     26  *
     27  * @author Alexander Wenckus
     28  */
     29 public class Bytestream extends IQ {
     30 
     31     private String sessionID;
     32 
     33     private Mode mode = Mode.tcp;
     34 
     35     private final List<StreamHost> streamHosts = new ArrayList<StreamHost>();
     36 
     37     private StreamHostUsed usedHost;
     38 
     39     private Activate toActivate;
     40 
     41     /**
     42      * The default constructor
     43      */
     44     public Bytestream() {
     45         super();
     46     }
     47 
     48     /**
     49      * A constructor where the session ID can be specified.
     50      *
     51      * @param SID The session ID related to the negotiation.
     52      * @see #setSessionID(String)
     53      */
     54     public Bytestream(final String SID) {
     55         super();
     56         setSessionID(SID);
     57     }
     58 
     59     /**
     60      * Set the session ID related to the bytestream. The session ID is a unique identifier used to
     61      * differentiate between stream negotiations.
     62      *
     63      * @param sessionID the unique session ID that identifies the transfer.
     64      */
     65     public void setSessionID(final String sessionID) {
     66         this.sessionID = sessionID;
     67     }
     68 
     69     /**
     70      * Returns the session ID related to the bytestream negotiation.
     71      *
     72      * @return Returns the session ID related to the bytestream negotiation.
     73      * @see #setSessionID(String)
     74      */
     75     public String getSessionID() {
     76         return sessionID;
     77     }
     78 
     79     /**
     80      * Set the transport mode. This should be put in the initiation of the interaction.
     81      *
     82      * @param mode the transport mode, either UDP or TCP
     83      * @see Mode
     84      */
     85     public void setMode(final Mode mode) {
     86         this.mode = mode;
     87     }
     88 
     89     /**
     90      * Returns the transport mode.
     91      *
     92      * @return Returns the transport mode.
     93      * @see #setMode(Mode)
     94      */
     95     public Mode getMode() {
     96         return mode;
     97     }
     98 
     99     /**
    100      * Adds a potential stream host that the remote user can connect to to receive the file.
    101      *
    102      * @param JID The JID of the stream host.
    103      * @param address The internet address of the stream host.
    104      * @return The added stream host.
    105      */
    106     public StreamHost addStreamHost(final String JID, final String address) {
    107         return addStreamHost(JID, address, 0);
    108     }
    109 
    110     /**
    111      * Adds a potential stream host that the remote user can connect to to receive the file.
    112      *
    113      * @param JID The JID of the stream host.
    114      * @param address The internet address of the stream host.
    115      * @param port The port on which the remote host is seeking connections.
    116      * @return The added stream host.
    117      */
    118     public StreamHost addStreamHost(final String JID, final String address, final int port) {
    119         StreamHost host = new StreamHost(JID, address);
    120         host.setPort(port);
    121         addStreamHost(host);
    122 
    123         return host;
    124     }
    125 
    126     /**
    127      * Adds a potential stream host that the remote user can transfer the file through.
    128      *
    129      * @param host The potential stream host.
    130      */
    131     public void addStreamHost(final StreamHost host) {
    132         streamHosts.add(host);
    133     }
    134 
    135     /**
    136      * Returns the list of stream hosts contained in the packet.
    137      *
    138      * @return Returns the list of stream hosts contained in the packet.
    139      */
    140     public Collection<StreamHost> getStreamHosts() {
    141         return Collections.unmodifiableCollection(streamHosts);
    142     }
    143 
    144     /**
    145      * Returns the stream host related to the given JID, or null if there is none.
    146      *
    147      * @param JID The JID of the desired stream host.
    148      * @return Returns the stream host related to the given JID, or null if there is none.
    149      */
    150     public StreamHost getStreamHost(final String JID) {
    151         if (JID == null) {
    152             return null;
    153         }
    154         for (StreamHost host : streamHosts) {
    155             if (host.getJID().equals(JID)) {
    156                 return host;
    157             }
    158         }
    159 
    160         return null;
    161     }
    162 
    163     /**
    164      * Returns the count of stream hosts contained in this packet.
    165      *
    166      * @return Returns the count of stream hosts contained in this packet.
    167      */
    168     public int countStreamHosts() {
    169         return streamHosts.size();
    170     }
    171 
    172     /**
    173      * Upon connecting to the stream host the target of the stream replies to the initiator with the
    174      * JID of the SOCKS5 host that they used.
    175      *
    176      * @param JID The JID of the used host.
    177      */
    178     public void setUsedHost(final String JID) {
    179         this.usedHost = new StreamHostUsed(JID);
    180     }
    181 
    182     /**
    183      * Returns the SOCKS5 host connected to by the remote user.
    184      *
    185      * @return Returns the SOCKS5 host connected to by the remote user.
    186      */
    187     public StreamHostUsed getUsedHost() {
    188         return usedHost;
    189     }
    190 
    191     /**
    192      * Returns the activate element of the packet sent to the proxy host to verify the identity of
    193      * the initiator and match them to the appropriate stream.
    194      *
    195      * @return Returns the activate element of the packet sent to the proxy host to verify the
    196      *         identity of the initiator and match them to the appropriate stream.
    197      */
    198     public Activate getToActivate() {
    199         return toActivate;
    200     }
    201 
    202     /**
    203      * Upon the response from the target of the used host the activate packet is sent to the SOCKS5
    204      * proxy. The proxy will activate the stream or return an error after verifying the identity of
    205      * the initiator, using the activate packet.
    206      *
    207      * @param targetID The JID of the target of the file transfer.
    208      */
    209     public void setToActivate(final String targetID) {
    210         this.toActivate = new Activate(targetID);
    211     }
    212 
    213     public String getChildElementXML() {
    214         StringBuilder buf = new StringBuilder();
    215 
    216         buf.append("<query xmlns=\"http://jabber.org/protocol/bytestreams\"");
    217         if (this.getType().equals(IQ.Type.SET)) {
    218             if (getSessionID() != null) {
    219                 buf.append(" sid=\"").append(getSessionID()).append("\"");
    220             }
    221             if (getMode() != null) {
    222                 buf.append(" mode = \"").append(getMode()).append("\"");
    223             }
    224             buf.append(">");
    225             if (getToActivate() == null) {
    226                 for (StreamHost streamHost : getStreamHosts()) {
    227                     buf.append(streamHost.toXML());
    228                 }
    229             }
    230             else {
    231                 buf.append(getToActivate().toXML());
    232             }
    233         }
    234         else if (this.getType().equals(IQ.Type.RESULT)) {
    235             buf.append(">");
    236             if (getUsedHost() != null) {
    237                 buf.append(getUsedHost().toXML());
    238             }
    239             // A result from the server can also contain stream hosts
    240             else if (countStreamHosts() > 0) {
    241                 for (StreamHost host : streamHosts) {
    242                     buf.append(host.toXML());
    243                 }
    244             }
    245         }
    246         else if (this.getType().equals(IQ.Type.GET)) {
    247             return buf.append("/>").toString();
    248         }
    249         else {
    250             return null;
    251         }
    252         buf.append("</query>");
    253 
    254         return buf.toString();
    255     }
    256 
    257     /**
    258      * Packet extension that represents a potential SOCKS5 proxy for the file transfer. Stream hosts
    259      * are forwarded to the target of the file transfer who then chooses and connects to one.
    260      *
    261      * @author Alexander Wenckus
    262      */
    263     public static class StreamHost implements PacketExtension {
    264 
    265         public static String NAMESPACE = "";
    266 
    267         public static String ELEMENTNAME = "streamhost";
    268 
    269         private final String JID;
    270 
    271         private final String addy;
    272 
    273         private int port = 0;
    274 
    275         /**
    276          * Default constructor.
    277          *
    278          * @param JID The JID of the stream host.
    279          * @param address The internet address of the stream host.
    280          */
    281         public StreamHost(final String JID, final String address) {
    282             this.JID = JID;
    283             this.addy = address;
    284         }
    285 
    286         /**
    287          * Returns the JID of the stream host.
    288          *
    289          * @return Returns the JID of the stream host.
    290          */
    291         public String getJID() {
    292             return JID;
    293         }
    294 
    295         /**
    296          * Returns the internet address of the stream host.
    297          *
    298          * @return Returns the internet address of the stream host.
    299          */
    300         public String getAddress() {
    301             return addy;
    302         }
    303 
    304         /**
    305          * Sets the port of the stream host.
    306          *
    307          * @param port The port on which the potential stream host would accept the connection.
    308          */
    309         public void setPort(final int port) {
    310             this.port = port;
    311         }
    312 
    313         /**
    314          * Returns the port on which the potential stream host would accept the connection.
    315          *
    316          * @return Returns the port on which the potential stream host would accept the connection.
    317          */
    318         public int getPort() {
    319             return port;
    320         }
    321 
    322         public String getNamespace() {
    323             return NAMESPACE;
    324         }
    325 
    326         public String getElementName() {
    327             return ELEMENTNAME;
    328         }
    329 
    330         public String toXML() {
    331             StringBuilder buf = new StringBuilder();
    332 
    333             buf.append("<").append(getElementName()).append(" ");
    334             buf.append("jid=\"").append(getJID()).append("\" ");
    335             buf.append("host=\"").append(getAddress()).append("\" ");
    336             if (getPort() != 0) {
    337                 buf.append("port=\"").append(getPort()).append("\"");
    338             }
    339             else {
    340                 buf.append("zeroconf=\"_jabber.bytestreams\"");
    341             }
    342             buf.append("/>");
    343 
    344             return buf.toString();
    345         }
    346     }
    347 
    348     /**
    349      * After selected a SOCKS5 stream host and successfully connecting, the target of the file
    350      * transfer returns a byte stream packet with the stream host used extension.
    351      *
    352      * @author Alexander Wenckus
    353      */
    354     public static class StreamHostUsed implements PacketExtension {
    355 
    356         public String NAMESPACE = "";
    357 
    358         public static String ELEMENTNAME = "streamhost-used";
    359 
    360         private final String JID;
    361 
    362         /**
    363          * Default constructor.
    364          *
    365          * @param JID The JID of the selected stream host.
    366          */
    367         public StreamHostUsed(final String JID) {
    368             this.JID = JID;
    369         }
    370 
    371         /**
    372          * Returns the JID of the selected stream host.
    373          *
    374          * @return Returns the JID of the selected stream host.
    375          */
    376         public String getJID() {
    377             return JID;
    378         }
    379 
    380         public String getNamespace() {
    381             return NAMESPACE;
    382         }
    383 
    384         public String getElementName() {
    385             return ELEMENTNAME;
    386         }
    387 
    388         public String toXML() {
    389             StringBuilder buf = new StringBuilder();
    390             buf.append("<").append(getElementName()).append(" ");
    391             buf.append("jid=\"").append(getJID()).append("\" ");
    392             buf.append("/>");
    393             return buf.toString();
    394         }
    395     }
    396 
    397     /**
    398      * The packet sent by the stream initiator to the stream proxy to activate the connection.
    399      *
    400      * @author Alexander Wenckus
    401      */
    402     public static class Activate implements PacketExtension {
    403 
    404         public String NAMESPACE = "";
    405 
    406         public static String ELEMENTNAME = "activate";
    407 
    408         private final String target;
    409 
    410         /**
    411          * Default constructor specifying the target of the stream.
    412          *
    413          * @param target The target of the stream.
    414          */
    415         public Activate(final String target) {
    416             this.target = target;
    417         }
    418 
    419         /**
    420          * Returns the target of the activation.
    421          *
    422          * @return Returns the target of the activation.
    423          */
    424         public String getTarget() {
    425             return target;
    426         }
    427 
    428         public String getNamespace() {
    429             return NAMESPACE;
    430         }
    431 
    432         public String getElementName() {
    433             return ELEMENTNAME;
    434         }
    435 
    436         public String toXML() {
    437             StringBuilder buf = new StringBuilder();
    438             buf.append("<").append(getElementName()).append(">");
    439             buf.append(getTarget());
    440             buf.append("</").append(getElementName()).append(">");
    441             return buf.toString();
    442         }
    443     }
    444 
    445     /**
    446      * The stream can be either a TCP stream or a UDP stream.
    447      *
    448      * @author Alexander Wenckus
    449      */
    450     public enum Mode {
    451 
    452         /**
    453          * A TCP based stream.
    454          */
    455         tcp,
    456 
    457         /**
    458          * A UDP based stream.
    459          */
    460         udp;
    461 
    462         public static Mode fromName(String name) {
    463             Mode mode;
    464             try {
    465                 mode = Mode.valueOf(name);
    466             }
    467             catch (Exception ex) {
    468                 mode = tcp;
    469             }
    470 
    471             return mode;
    472         }
    473     }
    474 }
    475