Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/main/java/org/littleshoot/proxy/MitmManager.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.littleshoot.proxy;

import io.netty.handler.codec.http.HttpRequest;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;

Expand All @@ -16,7 +15,7 @@ public interface MitmManager {
*
* @param peerHost to start a client connection to the server.
* @param peerPort to start a client connection to the server.
*
*
* @return an SSLEngine used to connect to an upstream server
*/
SSLEngine serverSslEngine(String peerHost, int peerPort);
Expand All @@ -33,13 +32,13 @@ public interface MitmManager {
* Creates an {@link SSLEngine} for encrypting the client connection based
* on the given serverSslSession.
* </p>
*
*
* <p>
* The serverSslSession is provided in case this method needs to inspect the
* server's certificates or something else about the encryption on the way
* to the server.
* </p>
*
*
* <p>
* This is the place where one would implement impersonation of the server
* by issuing replacement certificates signed by the proxy's own
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/org/littleshoot/proxy/SelectiveMitmManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.littleshoot.proxy;

/**
* An extension to the {@link MitmManager} interface to allow MITM to be
* selectively applied based on the peer. Added as a new interface to not
* break existing implementations.
*/
public interface SelectiveMitmManager extends MitmManager {
/**
* Checks if MITM should be applied for a given peer.
*
* @param peerHost The peer host
* @param peerPort The peer port
*
* @return true to continue with MITM, false to act as if MITM was not enabled for this peer and tunnel raw content.
*/
boolean shouldMITMPeer(String peerHost, int peerPort);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.littleshoot.proxy;

import io.netty.handler.codec.http.HttpRequest;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;

/**
* Convenience base class for adapting a non-selective-{@link MitmManager} into a {@link SelectiveMitmManager}.
*/
public abstract class SelectiveMitmManagerAdapter implements SelectiveMitmManager {
private final MitmManager childMitmManager;

public SelectiveMitmManagerAdapter(MitmManager childMitmManager) {
this.childMitmManager = childMitmManager;
}

@Override
public SSLEngine serverSslEngine(String peerHost, int peerPort) {
return this.childMitmManager.serverSslEngine(peerHost, peerPort);
}

@Override
public SSLEngine serverSslEngine() {
return this.childMitmManager.serverSslEngine();
}

@Override
public SSLEngine clientSslEngineFor(HttpRequest httpRequest, SSLSession serverSslSession) {
return this.childMitmManager.clientSslEngineFor(httpRequest, serverSslSession);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.littleshoot.proxy.FullFlowContext;
import org.littleshoot.proxy.HttpFilters;
import org.littleshoot.proxy.MitmManager;
import org.littleshoot.proxy.SelectiveMitmManager;
import org.littleshoot.proxy.TransportProtocol;
import org.littleshoot.proxy.UnknownTransportProtocolException;

Expand All @@ -56,15 +57,14 @@
import static org.littleshoot.proxy.impl.ConnectionState.AWAITING_INITIAL;
import static org.littleshoot.proxy.impl.ConnectionState.CONNECTING;
import static org.littleshoot.proxy.impl.ConnectionState.DISCONNECTED;
import static org.littleshoot.proxy.impl.ConnectionState.HANDSHAKING;

import static org.littleshoot.proxy.impl.ConnectionState.HANDSHAKING;;
/**
* <p>
* Represents a connection from our proxy to a server on the web.
* ProxyConnections are reused fairly liberally, and can go from disconnected to
* connected, back to disconnected and so on.
* </p>
*
*
* <p>
* Connecting a {@link ProxyToServerConnection} can involve more than just
* connecting the underlying {@link Channel}. In particular, the connection may
Expand Down Expand Up @@ -134,13 +134,13 @@ public class ProxyToServerConnection extends ProxyConnection<HttpResponse> {
private volatile GlobalTrafficShapingHandler trafficHandler;

/**
* Minimum size of the adaptive recv buffer when throttling is enabled.
* Minimum size of the adaptive recv buffer when throttling is enabled.
*/
private static final int MINIMUM_RECV_BUFFER_SIZE_BYTES = 64;

/**
* Create a new ProxyToServerConnection.
*
*
* @param proxyServer
* @param clientConnection
* @param serverHostAndPort
Expand Down Expand Up @@ -262,12 +262,12 @@ protected void readRaw(ByteBuf buf) {
* doesn't know that any given response is to a HEAD request, so it needs to
* be told that there's no content so that it doesn't hang waiting for it.
* </p>
*
*
* <p>
* See the documentation for {@link HttpResponseDecoder} for information
* about why HEAD requests need special handling.
* </p>
*
*
* <p>
* Thanks to <a href="https://github.com/nataliakoval">nataliakoval</a> for
* pointing out that with connections being reused as they are, this needs
Expand Down Expand Up @@ -302,7 +302,7 @@ protected boolean isContentAlwaysEmpty(HttpMessage httpMessage) {
/**
* Like {@link #write(Object)} and also sets the current filters to the
* given value.
*
*
* @param msg
* @param filters
*/
Expand Down Expand Up @@ -497,7 +497,7 @@ protected HttpFilters getHttpFiltersFromProxyServer(HttpRequest httpRequest) {
/**
* Keeps track of the current HttpResponse so that we can associate its
* headers with future related chunks for this same transfer.
*
*
* @param response
*/
private void rememberCurrentResponse(HttpResponse response) {
Expand All @@ -512,7 +512,7 @@ private void rememberCurrentResponse(HttpResponse response) {

/**
* Respond to the client with the given {@link HttpObject}.
*
*
* @param httpObject
*/
private void respondWith(HttpObject httpObject) {
Expand Down Expand Up @@ -554,31 +554,39 @@ private void initializeConnectionFlow() {
if (hasUpstreamChainedProxy()) {
connectionFlow.then(
serverConnection.HTTPCONNECTWithChainedProxy);
}
}

MitmManager mitmManager = proxyServer.getMitmManager();
boolean isMitmEnabled = mitmManager != null;
boolean shouldMITM = mitmManager != null;

if (isMitmEnabled) {
if (shouldMITM) {
// When MITM is enabled and when chained proxy is set up, remoteAddress
// will be the chained proxy's address. So we use serverHostAndPort
// which is the end server's address.
HostAndPort parsedHostAndPort = HostAndPort.fromString(serverHostAndPort);

// SNI may be disabled for this request due to a previous failed attempt to connect to the server
// with SNI enabled.
if (disableSni) {
connectionFlow.then(serverConnection.EncryptChannel(proxyServer.getMitmManager()
.serverSslEngine()));
} else {
connectionFlow.then(serverConnection.EncryptChannel(proxyServer.getMitmManager()
.serverSslEngine(parsedHostAndPort.getHost(), parsedHostAndPort.getPort())));
// Potentially skip MITM
if (mitmManager instanceof SelectiveMitmManager) {
shouldMITM = ((SelectiveMitmManager)mitmManager).shouldMITMPeer(parsedHostAndPort.getHost(), parsedHostAndPort.getPort());
}

connectionFlow
.then(clientConnection.RespondCONNECTSuccessful)
.then(serverConnection.MitmEncryptClientChannel);
} else {
if (shouldMITM) {
// SNI may be disabled for this request due to a previous failed attempt to connect to the server
// with SNI enabled.
if (disableSni) {
connectionFlow.then(serverConnection.EncryptChannel(mitmManager
.serverSslEngine()));
} else {
connectionFlow.then(serverConnection.EncryptChannel(mitmManager
.serverSslEngine(parsedHostAndPort.getHost(), parsedHostAndPort.getPort())));
}

connectionFlow
.then(clientConnection.RespondCONNECTSuccessful)
.then(serverConnection.MitmEncryptClientChannel);
}
}
if (!shouldMITM) {
connectionFlow.then(serverConnection.StartTunneling)
.then(clientConnection.RespondCONNECTSuccessful)
.then(clientConnection.StartTunneling);
Expand Down Expand Up @@ -699,7 +707,7 @@ void read(ConnectionFlow flow, Object msg) {
* <p>
* Encrypts the client channel based on our server {@link SSLSession}.
* </p>
*
*
* <p>
* This does not wait for the handshake to finish so that we can go on and
* respond to the CONNECT request.
Expand Down Expand Up @@ -809,7 +817,7 @@ private void resetConnectionForRetry() throws UnknownHostException {
/**
* Set up our connection parameters based on server address and chained
* proxies.
*
*
* @throws UnknownHostException when unable to resolve the hostname to an IP address
*/
private void setupConnectionParameters() throws UnknownHostException {
Expand Down Expand Up @@ -854,15 +862,15 @@ private void setupConnectionParameters() throws UnknownHostException {
/**
* Initialize our {@link ChannelPipeline} to connect the upstream server.
* LittleProxy acts as a client here.
*
*
* A {@link ChannelPipeline} invokes the read (Inbound) handlers in
* ascending ordering of the list and then the write (Outbound) handlers in
* descending ordering.
*
*
* Regarding the Javadoc of {@link HttpObjectAggregator} it's needed to have
* the {@link HttpResponseEncoder} or {@link HttpRequestEncoder} before the
* {@link HttpObjectAggregator} in the {@link ChannelPipeline}.
*
*
* @param pipeline
* @param httpRequest
*/
Expand Down Expand Up @@ -906,7 +914,7 @@ private void initChannelPipeline(ChannelPipeline pipeline,
* Do all the stuff that needs to be done after our {@link ConnectionFlow}
* has succeeded.
* </p>
*
*
* @param shouldForwardInitialRequest
* whether or not we should forward the initial HttpRequest to
* the server after the connection has been established.
Expand Down Expand Up @@ -941,7 +949,7 @@ void connectionSucceeded(boolean shouldForwardInitialRequest) {

/**
* Build an {@link InetSocketAddress} for the given hostAndPort.
*
*
* @param hostAndPort String representation of the host and port
* @param proxyServer the current {@link DefaultHttpProxyServer}
* @return a resolved InetSocketAddress for the specified hostAndPort
Expand All @@ -966,7 +974,7 @@ public static InetSocketAddress addressFor(String hostAndPort, DefaultHttpProxyS

/***************************************************************************
* Activity Tracking/Statistics
*
*
* We track statistics on bytes, requests and responses by adding handlers
* at the appropriate parts of the pipeline (see initChannelPipeline()).
**************************************************************************/
Expand Down
Loading