/* * This file is part of the OWASP Proxy, a free intercepting proxy library. * Copyright (C) 2008-2010 Rogan Dawes <rogan@dawes.za.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ package org.owasp.proxy.tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Socket; import org.owasp.proxy.daemon.TargetedConnectionHandler; /** * This {@link TargetedConnectionHandler} provides a framework for intercepting arbitrary TCP socket-based protocols. * * It establishes a connection to the indicated target, then instantiates a pair of threads that read from the accepted * socket and the target socket, and notify the {@link StreamInterceptor} provided when data has been read, or the * connection terminated. * * The basic sequence is as follows: * <ul> * <li>A pair of threads are created, one reading from the accepted socket, and the other reading from the socket * connected to the target.</li> * <li>The {@link StreamInterceptor#connected(StreamHandle, StreamHandle)} method is called, passing the * {@link StreamHandle}'s responsible for reading from the accepted socket, and from the target (in that order)</li> * <li>As each StreamHandle reads from its socket, it calls * {@link StreamInterceptor#received(StreamHandle, byte[], int, int)} with the data read. The StreamHandle serves as a * key to inform the StreamInterceptor which socket the data relates to.</li> * <li>If there is an error reading from the socket, the StreamHandle calls * {@link StreamInterceptor#readException(StreamHandle, IOException)}</li> * <li>Finally, {@link StreamInterceptor#inputClosed(StreamHandle)} is always called to allow the StreamInterceptor to * release any allocated resources.</li> * </ul> * * Note that it is still possible to write data to the StreamHandle even after the input has been closed, to allow for * modification of the last packet sent. * * @author rogan * */ public class InterceptingConnectionHandler implements TargetedConnectionHandler { private StreamInterceptor<InetSocketAddress, InetSocketAddress> interceptor; public InterceptingConnectionHandler( StreamInterceptor<InetSocketAddress, InetSocketAddress> interceptor) { if (interceptor == null) throw new IllegalArgumentException("Interceptor may not be null"); this.interceptor = interceptor; } /* * (non-Javadoc) * * @see org.owasp.proxy.daemon.TargetedConnectionHandler#handleConnection(java .net.Socket, * java.net.InetSocketAddress) */ public void handleConnection(final Socket client, InetSocketAddress target) throws IOException { InetSocketAddress clientLabel, serverLabel; InputStream ci, si; OutputStream so, co; StreamRelay<InetSocketAddress, InetSocketAddress> sr; final Socket server = new Socket(Proxy.NO_PROXY); server.connect(target); clientLabel = (InetSocketAddress) client.getRemoteSocketAddress(); ci = client.getInputStream(); co = client.getOutputStream(); serverLabel = (InetSocketAddress) server.getRemoteSocketAddress(); si = server.getInputStream(); so = server.getOutputStream(); sr = new StreamRelay<InetSocketAddress, InetSocketAddress>(interceptor, clientLabel, ci, co, serverLabel, si, so); Runnable cch = new Closer(server); Runnable sch = new Closer(client); sr.setCloseHandlers(cch, sch); sr.run(); } private static class Closer implements Runnable { private Socket socket; public Closer(Socket socket) { this.socket = socket; } public void run() { try { if (!socket.isOutputShutdown()) socket.shutdownOutput(); } catch (IOException e) { try { if (!socket.isOutputShutdown()) socket.close(); } catch (IOException ignore) { } } } } }