• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
Bytestream()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      */
Bytestream(final String SID)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      */
setSessionID(final String sessionID)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      */
getSessionID()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      */
setMode(final Mode mode)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      */
getMode()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      */
addStreamHost(final String JID, final String address)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      */
addStreamHost(final String JID, final String address, final int port)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      */
addStreamHost(final StreamHost host)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      */
getStreamHosts()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      */
getStreamHost(final String JID)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      */
countStreamHosts()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      */
setUsedHost(final String JID)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      */
getUsedHost()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      */
getToActivate()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      */
setToActivate(final String targetID)209     public void setToActivate(final String targetID) {
210         this.toActivate = new Activate(targetID);
211     }
212 
getChildElementXML()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          */
StreamHost(final String JID, final String address)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          */
getJID()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          */
getAddress()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          */
setPort(final int port)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          */
getPort()318         public int getPort() {
319             return port;
320         }
321 
getNamespace()322         public String getNamespace() {
323             return NAMESPACE;
324         }
325 
getElementName()326         public String getElementName() {
327             return ELEMENTNAME;
328         }
329 
toXML()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          */
StreamHostUsed(final String JID)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          */
getJID()376         public String getJID() {
377             return JID;
378         }
379 
getNamespace()380         public String getNamespace() {
381             return NAMESPACE;
382         }
383 
getElementName()384         public String getElementName() {
385             return ELEMENTNAME;
386         }
387 
toXML()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          */
Activate(final String target)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          */
getTarget()424         public String getTarget() {
425             return target;
426         }
427 
getNamespace()428         public String getNamespace() {
429             return NAMESPACE;
430         }
431 
getElementName()432         public String getElementName() {
433             return ELEMENTNAME;
434         }
435 
toXML()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 
fromName(String name)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