connectbot/sshlib

View on GitHub
src/main/java/com/trilead/ssh2/channel/DynamicAcceptThread.java

Summary

Maintainability
A
1 hr
Test Coverage
F
0%
/*
 * Copyright 2007 Kenny Root, Jeffrey Sharkey
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * a.) Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * b.) Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * c.) Neither the name of Trilead nor the names of its contributors may
 *     be used to endorse or promote products derived from this software
 *     without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.trilead.ssh2.channel;

import org.connectbot.simplesocks.Socks5Server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * DynamicAcceptThread.
 *
 * @author Kenny Root
 * @version $Id$
 */
public class DynamicAcceptThread extends Thread implements IChannelWorkerThread {
    private ChannelManager cm;
    private ServerSocket ss;

    public DynamicAcceptThread(ChannelManager cm, int local_port)
            throws IOException {
        this.cm = cm;

        setName("DynamicAcceptThread");

        ss = new ServerSocket(local_port);
    }

    public DynamicAcceptThread(ChannelManager cm, InetSocketAddress localAddress)
            throws IOException {
        this.cm = cm;

        ss = new ServerSocket();
        ss.bind(localAddress);
    }

    @Override
    public void run() {
        try {
            cm.registerThread(this);
        } catch (IOException e) {
            stopWorking();
            return;
        }

        while (true) {
            final Socket sock;
            try {
                sock = ss.accept();
            } catch (IOException e) {
                stopWorking();
                return;
            }

            DynamicAcceptRunnable dar = new DynamicAcceptRunnable(sock);
            Thread t = new Thread(dar);
            t.setDaemon(true);
            t.start();
        }
    }

    @Override
    public void stopWorking() {
        try {
            /* This will lead to an IOException in the ss.accept() call */
            ss.close();
        } catch (IOException ignore) {
        }
    }

    class DynamicAcceptRunnable implements Runnable {
        private static final int idleTimeout = 180000; //3 minutes

        private Socket sock;
        private InputStream in;
        private OutputStream out;

        public DynamicAcceptRunnable(Socket sock) {
            this.sock = sock;

            setName("DynamicAcceptRunnable");
        }

        public void run() {
            try {
                startSession();
            } catch (IOException ioe) {
                try {
                    sock.close();
                } catch (IOException ignore) {
                }
            }
        }

        private void startSession() throws IOException {
            sock.setSoTimeout(idleTimeout);

            in = sock.getInputStream();
            out = sock.getOutputStream();
            Socks5Server server = new Socks5Server(in, out);
            try {
                if (!server.acceptAuthentication() || !server.readRequest()) {
                    System.out.println("Could not start SOCKS session");
                    return;
                }
            } catch (IOException ioe) {
                server.sendReply(Socks5Server.ResponseCode.GENERAL_FAILURE);
                return;
            }

            if (server.getCommand() == Socks5Server.Command.CONNECT) {
                onConnect(server);
            } else {
                server.sendReply(Socks5Server.ResponseCode.COMMAND_NOT_SUPPORTED);
            }
        }

        private void onConnect(Socks5Server server) throws IOException {
            final Channel cn;

            String destHost = server.getHostName();
            if (destHost == null) {
                destHost = server.getAddress().getHostAddress();
            }

            try {
                /*
                 * This may fail, e.g., if the remote port is closed (in
                 * optimistic terms: not open yet)
                 */

                cn = cm.openDirectTCPIPChannel(destHost, server.getPort(),
                        "127.0.0.1", 0);

            } catch (IOException e) {
                /*
                 * Try to send a notification back to the client and then close the socket.
                 */
                try {
                    server.sendReply(Socks5Server.ResponseCode.GENERAL_FAILURE);
                } catch (IOException ignore) {
                }

                try {
                    sock.close();
                } catch (IOException ignore) {
                }

                return;
            }

            server.sendReply(Socks5Server.ResponseCode.SUCCESS);

            final StreamForwarder r2l = new StreamForwarder(cn, null, sock, cn.stdoutStream, out, "RemoteToLocal");
            final StreamForwarder l2r = new StreamForwarder(cn, r2l, sock, in, cn.stdinStream, "LocalToRemote");

            r2l.setDaemon(true);
            l2r.setDaemon(true);
            r2l.start();
            l2r.start();
        }
    }
}