/** * If the target is not connected to the local SOCKS5 proxy an exception should be thrown. * * @throws Exception should not happen */ @Test public void shouldFailIfTargetIsNotConnectedToLocalSocks5Proxy() throws Exception { // start a local SOCKS5 proxy Socks5Proxy.setLocalSocks5ProxyPort(proxyPort); Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); socks5Proxy.start(); // build stream host information for local SOCKS5 proxy StreamHost streamHost = new StreamHost(connection.getUser(), socks5Proxy.getLocalAddresses().get(0)); streamHost.setPort(socks5Proxy.getPort()); // create digest to get the socket opened by target String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, connection, sessionID, targetJID); try { socks5Client.getSocket(10000); fail("exception should be thrown"); } catch (SmackException e) { assertTrue(e.getMessage().contains("target is not connected to SOCKS5 proxy")); protocol.verifyAll(); // assert no XMPP messages were sent } socks5Proxy.stop(); }
/** * Target and initiator should successfully connect to a "remote" SOCKS5 proxy and the initiator * activates the bytestream. * * @throws Exception should not happen */ @Test public void shouldSuccessfullyEstablishConnectionAndActivateSocks5Proxy() throws Exception { // build activation confirmation response IQ activationResponse = new IQ() { @Override public String getChildElementXML() { return null; } }; activationResponse.setFrom(proxyJID); activationResponse.setTo(initiatorJID); activationResponse.setType(IQ.Type.RESULT); protocol.addResponse( activationResponse, Verification.correspondingSenderReceiver, Verification.requestTypeSET, new Verification<Bytestream, IQ>() { public void verify(Bytestream request, IQ response) { // verify that the correct stream should be activated assertNotNull(request.getToActivate()); assertEquals(targetJID, request.getToActivate().getTarget()); } }); // start a local SOCKS5 proxy Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort); socks5Proxy.start(); StreamHost streamHost = new StreamHost(proxyJID, socks5Proxy.getAddress()); streamHost.setPort(socks5Proxy.getPort()); // create digest to get the socket opened by target String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, connection, sessionID, targetJID); Socket initiatorSocket = socks5Client.getSocket(10000); InputStream in = initiatorSocket.getInputStream(); Socket targetSocket = socks5Proxy.getSocket(digest); OutputStream out = targetSocket.getOutputStream(); // verify test data for (int i = 0; i < 10; i++) { out.write(i); assertEquals(i, in.read()); } protocol.verifyAll(); initiatorSocket.close(); targetSocket.close(); socks5Proxy.stop(); }
/** * Initiator and target should successfully connect to the local SOCKS5 proxy. * * @throws Exception should not happen */ @Test public void shouldSuccessfullyConnectThroughLocalSocks5Proxy() throws Exception { // start a local SOCKS5 proxy Socks5Proxy.setLocalSocks5ProxyPort(proxyPort); Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); socks5Proxy.start(); // test data final byte[] data = new byte[] {1, 2, 3}; // create digest final String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); // allow connection of target with this digest socks5Proxy.addTransfer(digest); // build stream host information final StreamHost streamHost = new StreamHost(connection.getUser(), socks5Proxy.getLocalAddresses().get(0)); streamHost.setPort(socks5Proxy.getPort()); // target connects to local SOCKS5 proxy Thread targetThread = new Thread() { @Override public void run() { try { Socks5Client targetClient = new Socks5Client(streamHost, digest); Socket socket = targetClient.getSocket(10000); socket.getOutputStream().write(data); } catch (Exception e) { fail(e.getMessage()); } } }; targetThread.start(); Thread.sleep(200); // initiator connects Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, connection, sessionID, targetJID); Socket socket = socks5Client.getSocket(10000); // verify test data InputStream in = socket.getInputStream(); for (int i = 0; i < data.length; i++) { assertEquals(data[i], in.read()); } targetThread.join(); protocol.verifyAll(); // assert no XMPP messages were sent socks5Proxy.removeTransfer(digest); socks5Proxy.stop(); }
/** * Returns the response to the SOCKS5 Bytestream request containing the SOCKS5 proxy used. * * @param selectedHost the used SOCKS5 proxy * @return the response to the SOCKS5 Bytestream request */ private Bytestream createUsedHostResponse(StreamHost selectedHost) { Bytestream response = new Bytestream(this.bytestreamRequest.getSessionID()); response.setTo(this.bytestreamRequest.getFrom()); response.setType(IQ.Type.RESULT); response.setPacketID(this.bytestreamRequest.getPacketID()); response.setUsedHost(selectedHost.getJID()); return response; }
/** * If the initiator can connect to a SOCKS5 proxy but activating the stream fails an exception * should be thrown. * * @throws Exception should not happen */ @Test public void shouldFailIfActivateSocks5ProxyFails() throws Exception { // build error response as reply to the stream activation XMPPError xmppError = new XMPPError(XMPPError.Condition.internal_server_error); IQ error = new IQ() { public String getChildElementXML() { return null; } }; error.setType(Type.ERROR); error.setFrom(proxyJID); error.setTo(initiatorJID); error.setError(xmppError); protocol.addResponse( error, Verification.correspondingSenderReceiver, Verification.requestTypeSET); // start a local SOCKS5 proxy Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort); socks5Proxy.start(); StreamHost streamHost = new StreamHost(proxyJID, socks5Proxy.getAddress()); streamHost.setPort(socks5Proxy.getPort()); // create digest to get the socket opened by target String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, connection, sessionID, targetJID); try { socks5Client.getSocket(10000); fail("exception should be thrown"); } catch (XMPPErrorException e) { assertTrue(XMPPError.Condition.internal_server_error.equals(e.getXMPPError().getCondition())); protocol.verifyAll(); } socks5Proxy.stop(); }
/** * Accepts the SOCKS5 Bytestream initialization request and returns the socket to send/receive * data. * * <p>Before accepting the SOCKS5 Bytestream request you can set timeouts by invoking {@link * #setTotalConnectTimeout(int)} and {@link #setMinimumConnectTimeout(int)}. * * @return the socket to send/receive data * @throws XMPPException if connection to all SOCKS5 proxies failed or if stream is invalid. * @throws InterruptedException if the current thread was interrupted while waiting */ public Socks5BytestreamSession accept() throws XMPPException, InterruptedException { Collection<StreamHost> streamHosts = this.bytestreamRequest.getStreamHosts(); // throw exceptions if request contains no stream hosts if (streamHosts.size() == 0) { cancelRequest(); } StreamHost selectedHost = null; Socket socket = null; String digest = Socks5Utils.createDigest( this.bytestreamRequest.getSessionID(), this.bytestreamRequest.getFrom(), this.manager.getConnection().getUser()); /* * determine timeout for each connection attempt; each SOCKS5 proxy has the same amount of * time so that the first does not consume the whole timeout */ int timeout = Math.max(getTotalConnectTimeout() / streamHosts.size(), getMinimumConnectTimeout()); for (StreamHost streamHost : streamHosts) { String address = streamHost.getAddress() + ":" + streamHost.getPort(); // check to see if this address has been blacklisted int failures = getConnectionFailures(address); if (CONNECTION_FAILURE_THRESHOLD > 0 && failures >= CONNECTION_FAILURE_THRESHOLD) { continue; } // establish socket try { // build SOCKS5 client final Socks5Client socks5Client = new Socks5Client(streamHost, digest); // connect to SOCKS5 proxy with a timeout socket = socks5Client.getSocket(timeout); // set selected host selectedHost = streamHost; break; } catch (TimeoutException e) { incrementConnectionFailures(address); } catch (IOException e) { incrementConnectionFailures(address); } catch (XMPPException e) { incrementConnectionFailures(address); } } // throw exception if connecting to all SOCKS5 proxies failed if (selectedHost == null || socket == null) { cancelRequest(); } // send used-host confirmation Bytestream response = createUsedHostResponse(selectedHost); this.manager.getConnection().sendPacket(response); return new Socks5BytestreamSession( socket, selectedHost.getJID().equals(this.bytestreamRequest.getFrom())); }