Fixes #4540 - ProxyConnectionFactory should not ignore TLVs.

Generified the API to expose TLVs, so that they are all exposed,
not only the custom TLV types in the 0xE0-0xEF range.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2020-02-21 09:37:12 +01:00
parent 411624c419
commit b0975aaba0
3 changed files with 73 additions and 102 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.V2;
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.assertTrue;
@ -165,6 +166,7 @@ public class HttpClientProxyProtocolTest
@Test
public void testClientProxyProtocolV2WithVectors() throws Exception
{
int typeTLS = 0x20;
String tlsVersion = "TLSv1.3";
byte[] tlsVersionBytes = tlsVersion.getBytes(StandardCharsets.US_ASCII);
startServer(new EmptyServerHandler()
@ -176,7 +178,10 @@ public class HttpClientProxyProtocolTest
assertTrue(endPoint instanceof ProxyConnectionFactory.ProxyEndPoint);
ProxyConnectionFactory.ProxyEndPoint proxyEndPoint = (ProxyConnectionFactory.ProxyEndPoint)endPoint;
if (target.equals("/tls_version"))
{
assertNotNull(proxyEndPoint.getTLV(typeTLS));
assertEquals(tlsVersion, proxyEndPoint.getAttribute(ProxyConnectionFactory.TLS_VERSION));
}
response.setContentType(MimeTypes.Type.TEXT_PLAIN.asString());
response.getOutputStream().print(request.getRemotePort());
}
@ -186,7 +191,6 @@ public class HttpClientProxyProtocolTest
int serverPort = connector.getLocalPort();
int clientPort = ThreadLocalRandom.current().nextInt(1024, 65536);
int typeTLS = 0x20;
byte[] dataTLS = new byte[1 + 4 + (1 + 2 + tlsVersionBytes.length)];
dataTLS[0] = 0x01; // CLIENT_SSL
dataTLS[5] = 0x21; // SUBTYPE_SSL_VERSION

View File

@ -27,6 +27,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.ReadPendingException;
import java.nio.channels.WritePendingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection;
@ -546,7 +548,6 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
private void parseBodyAndUpgrade() throws IOException
{
// stop reading when bufferRemainingReserve bytes are remaining in the buffer
int nonProxyRemaining = _buffer.remaining() - _length;
if (LOG.isDebugEnabled())
LOG.debug("Proxy v2 parsing body, length = {}, buffer = {}", _length, BufferUtil.toHexSummary(_buffer));
@ -610,14 +611,14 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
if (LOG.isDebugEnabled())
LOG.debug(String.format("Proxy v2 T=%x L=%d V=%s for %s", type, length, TypeUtil.toHexString(value), this));
switch (type)
{
case 0x20: // PP2_TYPE_SSL
// 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)
{
int client = value[0] & 0xFF;
switch (client)
{
case 0x01: // PP2_CLIENT_SSL
if (client == ProxyEndPoint.PP2_TYPE_SSL_PP2_CLIENT_SSL)
{
int i = 5; // Index of the first sub_tlv, after verify.
while (i < length)
@ -627,43 +628,13 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
byte[] subValue = new byte[subLength];
System.arraycopy(value, i, subValue, 0, subLength);
i += subLength;
switch (subType)
if (subType == ProxyEndPoint.PP2_SUBTYPE_SSL_VERSION)
{
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;
}
// PP2_TYPE_MIN_CUSTOM .. PP2_TYPE_MAX_CUSTOM
case 0xE0: case 0xE1: case 0xE2: case 0xE3:
case 0xE4: case 0xE5: case 0xE6: case 0xE7:
case 0xE8: case 0xE9: case 0xEA: case 0xEB:
case 0xEC: case 0xED: case 0xEE: case 0xEF:
proxyEndPoint.setCustomValue(type, value);
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;
}
}
@ -761,107 +732,104 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
public static class ProxyEndPoint extends AttributesMap implements EndPoint, EndPoint.Wrapper
{
private static final int PP2_TYPE_MIN_CUSTOM = 0xE0;
private static final int PP2_TYPE_MAX_CUSTOM = 0xEF;
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 _endp;
private final EndPoint _endPoint;
private final InetSocketAddress _remote;
private final InetSocketAddress _local;
private byte[][] customValues;
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;
_local = local;
}
public EndPoint unwrap()
{
return _endp;
return _endPoint;
}
/**
* Sets a custom TLV vector. See section 2.2.7 of the PROXY
* protocol specification.
* <p>Sets a TLV vector, see section 2.2.7 of the PROXY protocol specification.</p>
*
* @param type must be between 0xE0..0xEF inclusive.
* @param value the custom value
* @param type the TLV type
* @param value the TLV value
*/
public synchronized void setCustomValue(int type, byte[] value)
private void putTLV(int type, byte[] value)
{
int index = type - PP2_TYPE_MIN_CUSTOM;
if (customValues == null)
{
customValues = new byte[PP2_TYPE_MAX_CUSTOM - PP2_TYPE_MIN_CUSTOM + 1][];
}
customValues[index] = value;
if (_tlvs == null)
_tlvs = new HashMap<>();
_tlvs.put(type, value);
}
/**
* Gets a custom TLV vector.
* <p>Gets a TLV vector, see section 2.2.7 of the PROXY protocol specification.</p>
*
* @param type must be between 0xE0..0xEF inclusive.
* @return the custom value or null if not set
* @param type the TLV type
* @return the TLV value or null if not present.
*/
public synchronized byte[] getCustomValue(int type)
public byte[] getTLV(int type)
{
return customValues == null ? null
: customValues[type - PP2_TYPE_MIN_CUSTOM];
return _tlvs != null ? _tlvs.get(type) : null;
}
@Override
public void close(Throwable cause)
{
_endp.close(cause);
_endPoint.close(cause);
}
@Override
public int fill(ByteBuffer buffer) throws IOException
{
return _endp.fill(buffer);
return _endPoint.fill(buffer);
}
@Override
public void fillInterested(Callback callback) throws ReadPendingException
{
_endp.fillInterested(callback);
_endPoint.fillInterested(callback);
}
@Override
public boolean flush(ByteBuffer... buffer) throws IOException
{
return _endp.flush(buffer);
return _endPoint.flush(buffer);
}
@Override
public Connection getConnection()
{
return _endp.getConnection();
return _endPoint.getConnection();
}
@Override
public void setConnection(Connection connection)
{
_endp.setConnection(connection);
_endPoint.setConnection(connection);
}
@Override
public long getCreatedTimeStamp()
{
return _endp.getCreatedTimeStamp();
return _endPoint.getCreatedTimeStamp();
}
@Override
public long getIdleTimeout()
{
return _endp.getIdleTimeout();
return _endPoint.getIdleTimeout();
}
@Override
public void setIdleTimeout(long idleTimeout)
{
_endp.setIdleTimeout(idleTimeout);
_endPoint.setIdleTimeout(idleTimeout);
}
@Override
@ -879,49 +847,49 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
@Override
public Object getTransport()
{
return _endp.getTransport();
return _endPoint.getTransport();
}
@Override
public boolean isFillInterested()
{
return _endp.isFillInterested();
return _endPoint.isFillInterested();
}
@Override
public boolean isInputShutdown()
{
return _endp.isInputShutdown();
return _endPoint.isInputShutdown();
}
@Override
public boolean isOpen()
{
return _endp.isOpen();
return _endPoint.isOpen();
}
@Override
public boolean isOutputShutdown()
{
return _endp.isOutputShutdown();
return _endPoint.isOutputShutdown();
}
@Override
public void onClose(Throwable cause)
{
_endp.onClose(cause);
_endPoint.onClose(cause);
}
@Override
public void onOpen()
{
_endp.onOpen();
_endPoint.onOpen();
}
@Override
public void shutdownOutput()
{
_endp.shutdownOutput();
_endPoint.shutdownOutput();
}
@Override
@ -932,25 +900,25 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
hashCode(),
_remote,
_local,
_endp);
_endPoint);
}
@Override
public boolean tryFillInterested(Callback callback)
{
return _endp.tryFillInterested(callback);
return _endPoint.tryFillInterested(callback);
}
@Override
public void upgrade(Connection newConnection)
{
_endp.upgrade(newConnection);
_endPoint.upgrade(newConnection);
}
@Override
public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
{
_endp.write(callback, buffers);
_endPoint.write(callback, buffers);
}
}
}

View File

@ -26,7 +26,6 @@ import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -141,9 +140,9 @@ public class ProxyProtocolTest
HttpConnection con = (HttpConnection)request.getAttribute(HttpConnection.class.getName());
EndPoint endPoint = con.getEndPoint();
ProxyEndPoint proxyEndPoint = (ProxyEndPoint)endPoint;
return Arrays.equals(customE0, proxyEndPoint.getCustomValue(0xE0)) &&
Arrays.equals(customE1, proxyEndPoint.getCustomValue(0xE1)) &&
proxyEndPoint.getCustomValue(0xE2) == null;
return Arrays.equals(customE0, proxyEndPoint.getTLV(0xE0)) &&
Arrays.equals(customE1, proxyEndPoint.getTLV(0xE1)) &&
proxyEndPoint.getTLV(0xE2) == null;
}
});