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.XMPPConnection; 028import org.jivesoftware.smack.XMPPException; 029import org.jivesoftware.smack.XMPPException.XMPPErrorException; 030import org.jivesoftware.smack.packet.IQ; 031 032import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; 033import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; 034 035import org.jxmpp.jid.Jid; 036 037/** 038 * Implementation of a SOCKS5 client used on the initiators side. This is needed because connecting 039 * to the local SOCKS5 proxy differs form the regular way to connect to a SOCKS5 proxy. Additionally 040 * a remote SOCKS5 proxy has to be activated by the initiator before data can be transferred between 041 * the peers. 042 * 043 * @author Henning Staib 044 */ 045public class Socks5ClientForInitiator extends Socks5Client { 046 047 /* the XMPP connection used to communicate with the SOCKS5 proxy */ 048 private WeakReference<XMPPConnection> connection; 049 050 /* the session ID used to activate SOCKS5 stream */ 051 private String sessionID; 052 053 /* the target JID used to activate SOCKS5 stream */ 054 // TODO fullJid? 055 private final Jid target; 056 057 /** 058 * Creates a new SOCKS5 client for the initiators side. 059 * 060 * @param streamHost containing network settings of the SOCKS5 proxy 061 * @param digest identifying the SOCKS5 Bytestream 062 * @param connection the XMPP connection 063 * @param sessionID the session ID of the SOCKS5 Bytestream 064 * @param target the target JID of the SOCKS5 Bytestream 065 */ 066 public Socks5ClientForInitiator(StreamHost streamHost, String digest, XMPPConnection connection, 067 String sessionID, Jid target) { 068 super(streamHost, digest); 069 this.connection = new WeakReference<>(connection); 070 this.sessionID = sessionID; 071 this.target = target; 072 } 073 074 @Override 075 public Socket getSocket(int timeout) throws IOException, InterruptedException, 076 TimeoutException, XMPPException, SmackException { 077 Socket socket; 078 079 // check if stream host is the local SOCKS5 proxy 080 if (this.streamHost.getJID().equals(this.connection.get().getUser())) { 081 Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy(); 082 socket = socks5Server.getSocket(this.digest); 083 if (socket == null) { 084 throw new SmackException("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 111 * @throws NoResponseException 112 * @throws NotConnectedException 113 * @throws InterruptedException 114 * @throws SmackException if there was no response from the server. 115 */ 116 private void activate() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 117 Bytestream activate = createStreamHostActivation(); 118 // if activation fails #nextResultOrThrow() throws an exception 119 connection.get().createStanzaCollectorAndSend(activate).nextResultOrThrow(); 120 } 121 122 /** 123 * Returns a SOCKS5 Bytestream activation packet. 124 * 125 * @return SOCKS5 Bytestream activation packet 126 */ 127 private Bytestream createStreamHostActivation() { 128 Bytestream activate = new Bytestream(this.sessionID); 129 activate.setMode(null); 130 activate.setType(IQ.Type.set); 131 activate.setTo(this.streamHost.getJID()); 132 133 activate.setToActivate(this.target); 134 135 return activate; 136 } 137 138}