Merged branch 'm0mus-issue_4540' into 'jetty-10.0.x'.

This commit is contained in:
Simone Bordet 2020-02-21 09:38:23 +01:00
commit 7ce14a419f
5 changed files with 133 additions and 74 deletions

View File

@ -45,6 +45,7 @@ import org.junit.jupiter.api.Test;
import static org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V1; import static org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V1;
import static org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V2; import static org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V2;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@ -165,6 +166,7 @@ public class HttpClientProxyProtocolTest
@Test @Test
public void testClientProxyProtocolV2WithVectors() throws Exception public void testClientProxyProtocolV2WithVectors() throws Exception
{ {
int typeTLS = 0x20;
String tlsVersion = "TLSv1.3"; String tlsVersion = "TLSv1.3";
byte[] tlsVersionBytes = tlsVersion.getBytes(StandardCharsets.US_ASCII); byte[] tlsVersionBytes = tlsVersion.getBytes(StandardCharsets.US_ASCII);
startServer(new EmptyServerHandler() startServer(new EmptyServerHandler()
@ -176,7 +178,10 @@ public class HttpClientProxyProtocolTest
assertTrue(endPoint instanceof ProxyConnectionFactory.ProxyEndPoint); assertTrue(endPoint instanceof ProxyConnectionFactory.ProxyEndPoint);
ProxyConnectionFactory.ProxyEndPoint proxyEndPoint = (ProxyConnectionFactory.ProxyEndPoint)endPoint; ProxyConnectionFactory.ProxyEndPoint proxyEndPoint = (ProxyConnectionFactory.ProxyEndPoint)endPoint;
if (target.equals("/tls_version")) if (target.equals("/tls_version"))
{
assertNotNull(proxyEndPoint.getTLV(typeTLS));
assertEquals(tlsVersion, proxyEndPoint.getAttribute(ProxyConnectionFactory.TLS_VERSION)); assertEquals(tlsVersion, proxyEndPoint.getAttribute(ProxyConnectionFactory.TLS_VERSION));
}
response.setContentType(MimeTypes.Type.TEXT_PLAIN.asString()); response.setContentType(MimeTypes.Type.TEXT_PLAIN.asString());
response.getOutputStream().print(request.getRemotePort()); response.getOutputStream().print(request.getRemotePort());
} }
@ -186,7 +191,6 @@ public class HttpClientProxyProtocolTest
int serverPort = connector.getLocalPort(); int serverPort = connector.getLocalPort();
int clientPort = ThreadLocalRandom.current().nextInt(1024, 65536); int clientPort = ThreadLocalRandom.current().nextInt(1024, 65536);
int typeTLS = 0x20;
byte[] dataTLS = new byte[1 + 4 + (1 + 2 + tlsVersionBytes.length)]; byte[] dataTLS = new byte[1 + 4 + (1 + 2 + tlsVersionBytes.length)];
dataTLS[0] = 0x01; // CLIENT_SSL dataTLS[0] = 0x01; // CLIENT_SSL
dataTLS[5] = 0x21; // SUBTYPE_SSL_VERSION dataTLS[5] = 0x21; // SUBTYPE_SSL_VERSION

View File

@ -95,7 +95,17 @@ import org.eclipse.jetty.util.thread.Invocable;
*/ */
public interface EndPoint extends Closeable public interface EndPoint extends Closeable
{ {
/**
* Marks an <code>EndPoint</code> that wraps another <code>EndPoint</code>.
*/
public interface Wrapper
{
/**
* @return The wrapped <code>EndPoint</code>
*/
EndPoint unwrap();
}
/** /**
* @return The local Inet address to which this <code>EndPoint</code> is bound, or <code>null</code> * @return The local Inet address to which this <code>EndPoint</code> is bound, or <code>null</code>
* if this <code>EndPoint</code> does not represent a network connection. * if this <code>EndPoint</code> does not represent a network connection.

View File

@ -457,7 +457,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
return getEndPoint().flush(output); return getEndPoint().flush(output);
} }
public class DecryptedEndPoint extends AbstractEndPoint public class DecryptedEndPoint extends AbstractEndPoint implements EndPoint.Wrapper
{ {
private final Callback _incompleteWriteCallback = new IncompleteWriteCallback(); private final Callback _incompleteWriteCallback = new IncompleteWriteCallback();
private Throwable _failure; private Throwable _failure;
@ -469,6 +469,12 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
super.setIdleTimeout(-1); super.setIdleTimeout(-1);
} }
@Override
public EndPoint unwrap()
{
return getEndPoint();
}
@Override @Override
public long getIdleTimeout() public long getIdleTimeout()
{ {
@ -682,7 +688,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
try try
{ {
_underflown = false; _underflown = false;
unwrapResult = unwrap(_sslEngine, _encryptedInput, appIn); unwrapResult = SslConnection.this.unwrap(_sslEngine, _encryptedInput, appIn);
} }
finally finally
{ {
@ -1554,5 +1560,6 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
return String.format("SSL@%h.DEP.writeCallback", SslConnection.this); return String.format("SSL@%h.DEP.writeCallback", SslConnection.this);
} }
} }
} }
} }

View File

@ -27,6 +27,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.ReadPendingException; import java.nio.channels.ReadPendingException;
import java.nio.channels.WritePendingException; import java.nio.channels.WritePendingException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.Connection;
@ -366,6 +368,7 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
{ {
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
}; };
private final String _nextProtocol; private final String _nextProtocol;
private int _maxProxyHeader = 1024; private int _maxProxyHeader = 1024;
@ -545,7 +548,6 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
private void parseBodyAndUpgrade() throws IOException private void parseBodyAndUpgrade() throws IOException
{ {
// stop reading when bufferRemainingReserve bytes are remaining in the buffer
int nonProxyRemaining = _buffer.remaining() - _length; int nonProxyRemaining = _buffer.remaining() - _length;
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Proxy v2 parsing body, length = {}, buffer = {}", _length, BufferUtil.toHexSummary(_buffer)); LOG.debug("Proxy v2 parsing body, length = {}, buffer = {}", _length, BufferUtil.toHexSummary(_buffer));
@ -609,53 +611,30 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug(String.format("Proxy v2 T=%x L=%d V=%s for %s", type, length, TypeUtil.toHexString(value), this)); LOG.debug(String.format("Proxy v2 T=%x L=%d V=%s for %s", type, length, TypeUtil.toHexString(value), this));
switch (type) // PP2_TYPE_NOOP is only used for byte alignment, skip them.
if (type != ProxyEndPoint.PP2_TYPE_NOOP)
proxyEndPoint.putTLV(type, value);
if (type == ProxyEndPoint.PP2_TYPE_SSL)
{ {
case 0x20: // PP2_TYPE_SSL int client = value[0] & 0xFF;
if (client == ProxyEndPoint.PP2_TYPE_SSL_PP2_CLIENT_SSL)
{ {
int client = value[0] & 0xFF; int i = 5; // Index of the first sub_tlv, after verify.
switch (client) while (i < length)
{ {
case 0x01: // PP2_CLIENT_SSL int subType = value[i++] & 0xFF;
int subLength = (value[i++] & 0xFF) * 256 + (value[i++] & 0xFF);
byte[] subValue = new byte[subLength];
System.arraycopy(value, i, subValue, 0, subLength);
i += subLength;
if (subType == ProxyEndPoint.PP2_SUBTYPE_SSL_VERSION)
{ {
int i = 5; // Index of the first sub_tlv, after verify. String tlsVersion = new String(subValue, StandardCharsets.US_ASCII);
while (i < length) proxyEndPoint.setAttribute(TLS_VERSION, tlsVersion);
{
int subType = value[i++] & 0xFF;
int subLength = (value[i++] & 0xFF) * 256 + (value[i++] & 0xFF);
byte[] subValue = new byte[subLength];
System.arraycopy(value, i, subValue, 0, subLength);
i += subLength;
switch (subType)
{
case 0x21: // PP2_SUBTYPE_SSL_VERSION
String tlsVersion = new String(subValue, StandardCharsets.US_ASCII);
proxyEndPoint.setAttribute(TLS_VERSION, tlsVersion);
break;
case 0x22: // PP2_SUBTYPE_SSL_CN
case 0x23: // PP2_SUBTYPE_SSL_CIPHER
case 0x24: // PP2_SUBTYPE_SSL_SIG_ALG
case 0x25: // PP2_SUBTYPE_SSL_KEY_ALG
default:
break;
}
}
break;
} }
case 0x02: // PP2_CLIENT_CERT_CONN
case 0x04: // PP2_CLIENT_CERT_SESS
default:
break;
} }
break;
} }
case 0x01: // PP2_TYPE_ALPN
case 0x02: // PP2_TYPE_AUTHORITY
case 0x03: // PP2_TYPE_CRC32C
case 0x04: // PP2_TYPE_NOOP
case 0x30: // PP2_TYPE_NETNS
default:
break;
} }
} }
@ -751,71 +730,106 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
} }
} }
public static class ProxyEndPoint extends AttributesMap implements EndPoint public static class ProxyEndPoint extends AttributesMap implements EndPoint, EndPoint.Wrapper
{ {
private final EndPoint _endp; private static final int PP2_TYPE_NOOP = 0x04;
private static final int PP2_TYPE_SSL = 0x20;
private static final int PP2_TYPE_SSL_PP2_CLIENT_SSL = 0x01;
private static final int PP2_SUBTYPE_SSL_VERSION = 0x21;
private final EndPoint _endPoint;
private final InetSocketAddress _remote; private final InetSocketAddress _remote;
private final InetSocketAddress _local; private final InetSocketAddress _local;
private Map<Integer, byte[]> _tlvs;
public ProxyEndPoint(EndPoint endp, InetSocketAddress remote, InetSocketAddress local) public ProxyEndPoint(EndPoint endPoint, InetSocketAddress remote, InetSocketAddress local)
{ {
_endp = endp; _endPoint = endPoint;
_remote = remote; _remote = remote;
_local = local; _local = local;
} }
public EndPoint unwrap()
{
return _endPoint;
}
/**
* <p>Sets a TLV vector, see section 2.2.7 of the PROXY protocol specification.</p>
*
* @param type the TLV type
* @param value the TLV value
*/
private void putTLV(int type, byte[] value)
{
if (_tlvs == null)
_tlvs = new HashMap<>();
_tlvs.put(type, value);
}
/**
* <p>Gets a TLV vector, see section 2.2.7 of the PROXY protocol specification.</p>
*
* @param type the TLV type
* @return the TLV value or null if not present.
*/
public byte[] getTLV(int type)
{
return _tlvs != null ? _tlvs.get(type) : null;
}
@Override @Override
public void close(Throwable cause) public void close(Throwable cause)
{ {
_endp.close(cause); _endPoint.close(cause);
} }
@Override @Override
public int fill(ByteBuffer buffer) throws IOException public int fill(ByteBuffer buffer) throws IOException
{ {
return _endp.fill(buffer); return _endPoint.fill(buffer);
} }
@Override @Override
public void fillInterested(Callback callback) throws ReadPendingException public void fillInterested(Callback callback) throws ReadPendingException
{ {
_endp.fillInterested(callback); _endPoint.fillInterested(callback);
} }
@Override @Override
public boolean flush(ByteBuffer... buffer) throws IOException public boolean flush(ByteBuffer... buffer) throws IOException
{ {
return _endp.flush(buffer); return _endPoint.flush(buffer);
} }
@Override @Override
public Connection getConnection() public Connection getConnection()
{ {
return _endp.getConnection(); return _endPoint.getConnection();
} }
@Override @Override
public void setConnection(Connection connection) public void setConnection(Connection connection)
{ {
_endp.setConnection(connection); _endPoint.setConnection(connection);
} }
@Override @Override
public long getCreatedTimeStamp() public long getCreatedTimeStamp()
{ {
return _endp.getCreatedTimeStamp(); return _endPoint.getCreatedTimeStamp();
} }
@Override @Override
public long getIdleTimeout() public long getIdleTimeout()
{ {
return _endp.getIdleTimeout(); return _endPoint.getIdleTimeout();
} }
@Override @Override
public void setIdleTimeout(long idleTimeout) public void setIdleTimeout(long idleTimeout)
{ {
_endp.setIdleTimeout(idleTimeout); _endPoint.setIdleTimeout(idleTimeout);
} }
@Override @Override
@ -833,49 +847,49 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
@Override @Override
public Object getTransport() public Object getTransport()
{ {
return _endp.getTransport(); return _endPoint.getTransport();
} }
@Override @Override
public boolean isFillInterested() public boolean isFillInterested()
{ {
return _endp.isFillInterested(); return _endPoint.isFillInterested();
} }
@Override @Override
public boolean isInputShutdown() public boolean isInputShutdown()
{ {
return _endp.isInputShutdown(); return _endPoint.isInputShutdown();
} }
@Override @Override
public boolean isOpen() public boolean isOpen()
{ {
return _endp.isOpen(); return _endPoint.isOpen();
} }
@Override @Override
public boolean isOutputShutdown() public boolean isOutputShutdown()
{ {
return _endp.isOutputShutdown(); return _endPoint.isOutputShutdown();
} }
@Override @Override
public void onClose(Throwable cause) public void onClose(Throwable cause)
{ {
_endp.onClose(cause); _endPoint.onClose(cause);
} }
@Override @Override
public void onOpen() public void onOpen()
{ {
_endp.onOpen(); _endPoint.onOpen();
} }
@Override @Override
public void shutdownOutput() public void shutdownOutput()
{ {
_endp.shutdownOutput(); _endPoint.shutdownOutput();
} }
@Override @Override
@ -886,25 +900,25 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
hashCode(), hashCode(),
_remote, _remote,
_local, _local,
_endp); _endPoint);
} }
@Override @Override
public boolean tryFillInterested(Callback callback) public boolean tryFillInterested(Callback callback)
{ {
return _endp.tryFillInterested(callback); return _endPoint.tryFillInterested(callback);
} }
@Override @Override
public void upgrade(Connection newConnection) public void upgrade(Connection newConnection)
{ {
_endp.upgrade(newConnection); _endPoint.upgrade(newConnection);
} }
@Override @Override
public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
{ {
_endp.write(callback, buffers); _endPoint.write(callback, buffers);
} }
} }
} }

View File

@ -25,10 +25,13 @@ import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.ProxyConnectionFactory.ProxyEndPoint;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -118,15 +121,29 @@ public class ProxyProtocolTest
{ {
final String remoteAddr = "192.168.0.1"; final String remoteAddr = "192.168.0.1";
final int remotePort = 12345; final int remotePort = 12345;
final byte[] customE0 = new byte[] {1, 2};
final byte[] customE1 = new byte[] {-1, -1, -1};
start(new AbstractHandler() start(new AbstractHandler()
{ {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{ {
if (remoteAddr.equals(request.getRemoteAddr()) && if (validateEndPoint(baseRequest) &&
remoteAddr.equals(request.getRemoteAddr()) &&
remotePort == request.getRemotePort()) remotePort == request.getRemotePort())
baseRequest.setHandled(true); baseRequest.setHandled(true);
} }
private boolean validateEndPoint(Request request)
{
HttpConnection con = (HttpConnection)request.getAttribute(HttpConnection.class.getName());
EndPoint endPoint = con.getEndPoint();
ProxyEndPoint proxyEndPoint = (ProxyEndPoint)endPoint;
return Arrays.equals(customE0, proxyEndPoint.getTLV(0xE0)) &&
Arrays.equals(customE1, proxyEndPoint.getTLV(0xE1)) &&
proxyEndPoint.getTLV(0xE2) == null;
}
}); });
try (Socket socket = new Socket("localhost", connector.getLocalPort())) try (Socket socket = new Socket("localhost", connector.getLocalPort()))
@ -141,8 +158,8 @@ public class ProxyProtocolTest
// 0x1 : AF_INET 0x1 : STREAM. Address length is 2*4 + 2*2 = 12 bytes. // 0x1 : AF_INET 0x1 : STREAM. Address length is 2*4 + 2*2 = 12 bytes.
"11" + "11" +
// length of remaining header (4+4+2+2+6+3 = 21) // length of remaining header (4+4+2+2+3+6+5+6 = 32)
"0015" + "0020" +
// uint32_t src_addr; uint32_t dst_addr; uint16_t src_port; uint16_t dst_port; // uint32_t src_addr; uint32_t dst_addr; uint16_t src_port; uint16_t dst_port;
"C0A80001" + "C0A80001" +
@ -154,7 +171,13 @@ public class ProxyProtocolTest
"040000" + "040000" +
// NOOP value ABCDEF // NOOP value ABCDEF
"040003ABCDEF"; "040003ABCDEF" +
// Custom 0xEO {0x01,0x02}
"E000020102" +
// Custom 0xE1 {0xFF,0xFF,0xFF}
"E10003FFFFFF";
String request1 = String request1 =
"GET /1 HTTP/1.1\r\n" + "GET /1 HTTP/1.1\r\n" +
@ -193,4 +216,5 @@ public class ProxyProtocolTest
} }
} }
} }
} }