001/** 002 * 003 * Copyright the original author or authors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.bytestreams.socks5; 018 019import java.io.IOException; 020import java.lang.ref.WeakReference; 021import java.net.Socket; 022import java.util.concurrent.TimeoutException; 023 024import org.jivesoftware.smack.SmackException; 025import org.jivesoftware.smack.SmackException.NoResponseException; 026import org.jivesoftware.smack.SmackException.NotConnectedException; 027import org.jivesoftware.smack.SmackException.SmackMessageException; 028import org.jivesoftware.smack.XMPPConnection; 029import org.jivesoftware.smack.XMPPException; 030import org.jivesoftware.smack.XMPPException.XMPPErrorException; 031import org.jivesoftware.smack.packet.IQ; 032 033import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; 034import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; 035 036import org.jxmpp.jid.Jid; 037 038/** 039 * Implementation of a SOCKS5 client used on the initiators side. This is needed because connecting 040 * to the local SOCKS5 proxy differs form the regular way to connect to a SOCKS5 proxy. Additionally 041 * a remote SOCKS5 proxy has to be activated by the initiator before data can be transferred between 042 * the peers. 043 * 044 * @author Henning Staib 045 */ 046public class Socks5ClientForInitiator extends Socks5Client { 047 048 /* the XMPP connection used to communicate with the SOCKS5 proxy */ 049 private WeakReference<XMPPConnection> connection; 050 051 /* the session ID used to activate SOCKS5 stream */ 052 private String sessionID; 053 054 /* the target JID used to activate SOCKS5 stream */ 055 // TODO fullJid? 056 private final Jid target; 057 058 /** 059 * Creates a new SOCKS5 client for the initiators side. 060 * 061 * @param streamHost containing network settings of the SOCKS5 proxy 062 * @param digest identifying the SOCKS5 Bytestream 063 * @param connection the XMPP connection 064 * @param sessionID the session ID of the SOCKS5 Bytestream 065 * @param target the target JID of the SOCKS5 Bytestream 066 */ 067 public Socks5ClientForInitiator(StreamHost streamHost, String digest, XMPPConnection connection, 068 String sessionID, Jid target) { 069 super(streamHost, digest); 070 this.connection = new WeakReference<>(connection); 071 this.sessionID = sessionID; 072 this.target = target; 073 } 074 075 @Override 076 public Socket getSocket(int timeout) throws IOException, InterruptedException, 077 TimeoutException, XMPPException, SmackMessageException, NotConnectedException, NoResponseException { 078 Socket socket; 079 080 // check if stream host is the local SOCKS5 proxy 081 if (this.streamHost.getJID().equals(this.connection.get().getUser())) { 082 socket = Socks5Proxy.getSocketForDigest(this.digest); 083 if (socket == null) { 084 throw new SmackException.SmackMessageException("target is not connected to SOCKS5 proxy"); 085 } 086 } 087 else { 088 socket = super.getSocket(timeout); 089 090 try { 091 activate(); 092 } 093 catch (XMPPException e1) { 094 socket.close(); 095 throw e1; 096 } 097 catch (NoResponseException e2) { 098 socket.close(); 099 throw e2; 100 } 101 102 } 103 104 return socket; 105 } 106 107 /** 108 * Activates the SOCKS5 Bytestream by sending an XMPP SOCKS5 Bytestream activation stanza to the 109 * SOCKS5 proxy. 110 * @throws XMPPErrorException if there was an XMPP error returned. 111 * @throws NoResponseException if there was no response from the remote entity. 112 * @throws NotConnectedException if the XMPP connection is not connected. 113 * @throws InterruptedException if the calling thread was interrupted. 114 */ 115 private void activate() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 116 Bytestream activate = createStreamHostActivation(); 117 // if activation fails #nextResultOrThrow() throws an exception 118 connection.get().createStanzaCollectorAndSend(activate).nextResultOrThrow(); 119 } 120 121 /** 122 * Returns a SOCKS5 Bytestream activation packet. 123 * 124 * @return SOCKS5 Bytestream activation packet 125 */ 126 private Bytestream createStreamHostActivation() { 127 Bytestream activate = new Bytestream(this.sessionID); 128 activate.setMode(null); 129 activate.setType(IQ.Type.set); 130 activate.setTo(this.streamHost.getJID()); 131 132 activate.setToActivate(this.target); 133 134 return activate; 135 } 136 137}