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 */
14package org.jivesoftware.smackx.bytestreams.socks5;
15
16import java.io.IOException;
17import java.net.Socket;
18import java.util.concurrent.TimeoutException;
19
20import org.jivesoftware.smack.Connection;
21import org.jivesoftware.smack.XMPPException;
22import org.jivesoftware.smack.packet.IQ;
23import org.jivesoftware.smack.util.SyncPacketSend;
24import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
25import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
26
27/**
28 * Implementation of a SOCKS5 client used on the initiators side. This is needed because connecting
29 * to the local SOCKS5 proxy differs form the regular way to connect to a SOCKS5 proxy. Additionally
30 * a remote SOCKS5 proxy has to be activated by the initiator before data can be transferred between
31 * the peers.
32 *
33 * @author Henning Staib
34 */
35class Socks5ClientForInitiator extends Socks5Client {
36
37    /* the XMPP connection used to communicate with the SOCKS5 proxy */
38    private Connection connection;
39
40    /* the session ID used to activate SOCKS5 stream */
41    private String sessionID;
42
43    /* the target JID used to activate SOCKS5 stream */
44    private String target;
45
46    /**
47     * Creates a new SOCKS5 client for the initiators side.
48     *
49     * @param streamHost containing network settings of the SOCKS5 proxy
50     * @param digest identifying the SOCKS5 Bytestream
51     * @param connection the XMPP connection
52     * @param sessionID the session ID of the SOCKS5 Bytestream
53     * @param target the target JID of the SOCKS5 Bytestream
54     */
55    public Socks5ClientForInitiator(StreamHost streamHost, String digest, Connection connection,
56                    String sessionID, String target) {
57        super(streamHost, digest);
58        this.connection = connection;
59        this.sessionID = sessionID;
60        this.target = target;
61    }
62
63    public Socket getSocket(int timeout) throws IOException, XMPPException, InterruptedException,
64                    TimeoutException {
65        Socket socket = null;
66
67        // check if stream host is the local SOCKS5 proxy
68        if (this.streamHost.getJID().equals(this.connection.getUser())) {
69            Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
70            socket = socks5Server.getSocket(this.digest);
71            if (socket == null) {
72                throw new XMPPException("target is not connected to SOCKS5 proxy");
73            }
74        }
75        else {
76            socket = super.getSocket(timeout);
77
78            try {
79                activate();
80            }
81            catch (XMPPException e) {
82                socket.close();
83                throw new XMPPException("activating SOCKS5 Bytestream failed", e);
84            }
85
86        }
87
88        return socket;
89    }
90
91    /**
92     * Activates the SOCKS5 Bytestream by sending a XMPP SOCKS5 Bytestream activation packet to the
93     * SOCKS5 proxy.
94     */
95    private void activate() throws XMPPException {
96        Bytestream activate = createStreamHostActivation();
97        // if activation fails #getReply throws an exception
98        SyncPacketSend.getReply(this.connection, activate);
99    }
100
101    /**
102     * Returns a SOCKS5 Bytestream activation packet.
103     *
104     * @return SOCKS5 Bytestream activation packet
105     */
106    private Bytestream createStreamHostActivation() {
107        Bytestream activate = new Bytestream(this.sessionID);
108        activate.setMode(null);
109        activate.setType(IQ.Type.SET);
110        activate.setTo(this.streamHost.getJID());
111
112        activate.setToActivate(this.target);
113
114        return activate;
115    }
116
117}
118