/** 
 * This program is an example from the book "Internet 
 * programming with Java" by Svetlin Nakov. It is freeware. 
 * For more information: http://www.nakov.com/books/inetjava/ 
 */ 
import java.io.*; 
import java.net.*; 
 
/** 
 * TCPForwardServer is a simple TCP bridging software that 
 * allows a TCP port on some host to be transparently forwarded 
 * to some other TCP port on some other host. TCPForwardServer 
 * continuously accepts client connections on the listening TCP 
 * port (source port) and starts a thread (ClientThread) that 
 * connects to the destination host and starts forwarding the 
 * data between the client socket and destination socket. 
 */ 
public class TCPForwardServer { 
    public static final int SOURCE_PORT = 2525; 
    public static final String DESTINATION_HOST = "mail.abv.bg"; 
    public static final int DESTINATION_PORT = 25; 
 
    public static void main(String[] args) throws IOException { 
        ServerSocket serverSocket = 
            new ServerSocket(SOURCE_PORT); 
        while (true) { 
            Socket clientSocket = serverSocket.accept(); 
            ClientThread clientThread = 
                new ClientThread(clientSocket); 
            clientThread.start(); 
        } 
    } 
} 
 
/** 
 * ClientThread is responsible for starting forwarding between 
 * the client and the server. It keeps track of the client and 
 * servers sockets that are both closed on input/output error 
 * durinf the forwarding. The forwarding is bidirectional and 
 * is performed by two ForwardThread instances. 
 */ 
class ClientThread extends Thread { 
    private Socket mClientSocket; 
    private Socket mServerSocket; 
    private boolean mForwardingActive = false; 
 
    public ClientThread(Socket aClientSocket) { 
        mClientSocket = aClientSocket; 
    } 
 
    /** 
     * Establishes connection to the destination server and 
     * starts bidirectional forwarding ot data between the 
     * client and the server. 
     */ 
    public void run() { 
        InputStream clientIn; 
        OutputStream clientOut; 
        InputStream serverIn; 
        OutputStream serverOut; 
        try { 
            // Connect to the destination server 
            mServerSocket = new Socket( 
                TCPForwardServer.DESTINATION_HOST, 
                TCPForwardServer.DESTINATION_PORT); 
 
            // Turn on keep-alive for both the sockets 
            mServerSocket.setKeepAlive(true); 
            mClientSocket.setKeepAlive(true); 
 
            // Obtain client & server input & output streams 
            clientIn = mClientSocket.getInputStream(); 
            clientOut = mClientSocket.getOutputStream(); 
            serverIn = mServerSocket.getInputStream(); 
            serverOut = mServerSocket.getOutputStream(); 
        } catch (IOException ioe) { 
            System.err.println("Can not connect to " + 
                TCPForwardServer.DESTINATION_HOST + ":" + 
                TCPForwardServer.DESTINATION_PORT); 
            connectionBroken(); 
            return; 
        } 
 
        // Start forwarding data between server and client 
        mForwardingActive = true; 
        ForwardThread clientForward = 
            new ForwardThread(this, clientIn, serverOut); 
        clientForward.start(); 
        ForwardThread serverForward = 
            new ForwardThread(this, serverIn, clientOut); 
        serverForward.start(); 
 
        System.out.println("TCP Forwarding " + 
            mClientSocket.getInetAddress().getHostAddress() + 
            ":" + mClientSocket.getPort() + " <--> " + 
            mServerSocket.getInetAddress().getHostAddress() + 
            ":" + mServerSocket.getPort() + " started."); 
    } 
 
    /** 
     * Called by some of the forwarding threads to indicate 
     * that its socket connection is brokean and both client 
     * and server sockets should be closed. Closing the client 
     * and server sockets causes all threads blocked on reading 
     * or writing to these sockets to get an exception and to 
     * finish their execution. 
     */ 
    public synchronized void connectionBroken() { 
        try { 
            mServerSocket.close(); 
        } catch (Exception e) {} 
        try { 
            mClientSocket.close(); } 
        catch (Exception e) {} 
 
        if (mForwardingActive) { 
            System.out.println("TCP Forwarding " + 
                mClientSocket.getInetAddress().getHostAddress() 
                + ":" + mClientSocket.getPort() + " <--> " + 
                mServerSocket.getInetAddress().getHostAddress() 
                + ":" + mServerSocket.getPort() + " stopped."); 
            mForwardingActive = false; 
        } 
    } 
} 
 
/** 
 * ForwardThread handles the TCP forwarding between a socket 
 * input stream (source) and a socket output stream (dest). 
 * It reads the input stream and forwards everything to the 
 * output stream. If some of the streams fails, the forwarding 
 * stops and the parent is notified to close all its sockets. 
 */ 
class ForwardThread extends Thread { 
    private static final int BUFFER_SIZE = 8192; 
 
    InputStream mInputStream; 
    OutputStream mOutputStream; 
    ClientThread mParent; 
 
    /** 
     * Creates a new traffic redirection thread specifying 
     * its parent, input stream and output stream. 
     */ 
    public ForwardThread(ClientThread aParent, InputStream 
            aInputStream, OutputStream aOutputStream) { 
        mParent = aParent; 
        mInputStream = aInputStream; 
        mOutputStream = aOutputStream; 
    } 
 
    /** 
     * Runs the thread. Continuously reads the input stream and 
     * writes the read data to the output stream. If reading or 
     * writing fail, exits the thread and notifies the parent 
     * about the failure. 
     */ 
    public void run() { 
        byte[] buffer = new byte[BUFFER_SIZE]; 
        try { 
            while (true) { 
                int bytesRead = mInputStream.read(buffer); 
                if (bytesRead == -1) 
                    break; // End of stream is reached --> exit 
                mOutputStream.write(buffer, 0, bytesRead); 
                mOutputStream.flush(); 
            } 
        } catch (IOException e) { 
            // Read/write failed --> connection is broken 
        } 
 
        // Notify parent thread that the connection is broken 
        mParent.connectionBroken(); 
    } 
}
Back to Internet Programming with Java books's web site