This is an automated email from the ASF dual-hosted git repository.

twolf pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git


The following commit(s) were added to refs/heads/master by this push:
     new 8925c4f7a Add support for Proxy Protocol V2.
     new 340312c06 Merge pull request #404 from 
fherbreteau/sshd-contrib_ProxyProtocol_V2
8925c4f7a is described below

commit 8925c4f7ac8652814592539499f4929654b3deb1
Author: f.herbreteau <f.herbret...@oodrive.com>
AuthorDate: Fri Aug 11 00:40:21 2023 +0200

    Add support for Proxy Protocol V2.
    
    HAProxy protocol V2 has several improvements over the V1 protocol. Add an
    acceptor and parsing support for the protocol as described at [1].
    
    Also add a Buffer.getUShort() method to read an unsigned 16bit value from
    a Buffer. The proxy protocol V2 uses such values.
    
    [1] https://www.haproxy.org/download/2.7/doc/proxy-protocol.txt
    
    Signed-off-by: f.herbreteau <f.herbret...@oodrive.com>
---
 docs/extensions.md                                 |   4 +
 .../org/apache/sshd/common/util/buffer/Buffer.java |   4 +
 sshd-contrib/pom.xml                               |  10 +
 .../proxyprotocolv2/ProxyProtocolV2Acceptor.java   | 122 +++++++
 .../session/proxyprotocolv2/data/AddressData.java  | 139 ++++++++
 .../proxyprotocolv2/data/FamilyAndTransport.java   | 132 ++++++++
 .../proxyprotocolv2/data/VersionAndCommand.java    |  66 ++++
 .../exception/ProxyProtocolException.java          |  77 +++++
 .../session/proxyprotocolv2/utils/ProxyUtils.java  |  50 +++
 .../ProxyProtocolV2AcceptorTest.java               | 350 +++++++++++++++++++++
 10 files changed, 954 insertions(+)

diff --git a/docs/extensions.md b/docs/extensions.md
index fa2c61a6e..35cc1d089 100644
--- a/docs/extensions.md
+++ b/docs/extensions.md
@@ -57,6 +57,10 @@ methods that provide SFTP file information - including 
reading data - and those
 * `ProxyProtocolAcceptor` - A working prototype to support the PROXY protocol 
as described in
 [HAProxy 
Documentation](http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)
 
+* `ProxyProtocolV2Acceptor` - A working prototype to support the PROXY 
protocol V1 and V2 as described in
+[HAProxy 
Documentation](http://www.haproxy.org/download/2.7/doc/proxy-protocol.txt). 
This acceptor extends
+the `ProxyProtocolAcceptor` for V1 Protocol.
+
 * `ThrottlingPacketWriter` - An example of a way to overcome big window sizes 
when sending data - as
 described in [SSHD-754](https://issues.apache.org/jira/browse/SSHD-754) and 
[SSHD-768](https://issues.apache.org/jira/browse/SSHD-768)
 
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java 
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
index c9d73b269..5466aee49 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
@@ -292,6 +292,10 @@ public abstract class Buffer implements Readable {
         return v;
     }
 
+    public int getUShort() {
+        return getShort() & 0xFFFF;
+    }
+
     public int getInt() {
         return (int) getUInt();
     }
diff --git a/sshd-contrib/pom.xml b/sshd-contrib/pom.xml
index 835ca88c1..d26e4e73b 100644
--- a/sshd-contrib/pom.xml
+++ b/sshd-contrib/pom.xml
@@ -80,6 +80,16 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>3.24.2</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2Acceptor.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2Acceptor.java
new file mode 100644
index 000000000..05d5e9330
--- /dev/null
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2Acceptor.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.contrib.server.session.proxyprotocolv2;
+
+import java.util.Arrays;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import 
org.apache.sshd.contrib.server.session.proxyprotocol.ProxyProtocolAcceptor;
+import org.apache.sshd.contrib.server.session.proxyprotocolv2.data.AddressData;
+import 
org.apache.sshd.contrib.server.session.proxyprotocolv2.data.FamilyAndTransport;
+import 
org.apache.sshd.contrib.server.session.proxyprotocolv2.data.VersionAndCommand;
+import org.apache.sshd.contrib.server.session.proxyprotocolv2.utils.ProxyUtils;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * A working prototype to support PROXY protocol v2 as described in
+ * <A 
HREF="https://www.haproxy.org/download/2.7/doc/proxy-protocol.txt";>HAProxy 
Documentation</A>.
+ * <p>
+ * This <code>ServerProxyAcceptor</code> can process PROXY protocol v1 and v2.
+ * </p>
+ *
+ * @author Oodrive - François HERBRETEAU (f.herbret...@oodrive.com)
+ */
+public class ProxyProtocolV2Acceptor extends ProxyProtocolAcceptor {
+
+    private static final byte[] PROXY_V2_HEADER
+            = new byte[] { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 
0x55, 0x49, 0x54, 0x0A };
+
+    private static final char FIELD_SEPARATOR = ' ';
+
+    public ProxyProtocolV2Acceptor() {
+        super();
+    }
+
+    @Override
+    public boolean acceptServerProxyMetadata(ServerSession session, Buffer 
buffer) throws Exception {
+        int mark = buffer.rpos();
+        int dataLen = buffer.available();
+        if (dataLen < PROXY_V2_HEADER.length) {
+            if (log.isDebugEnabled()) {
+                log.debug("acceptServerProxyMetadata(session={}) incomplete 
data - {}/{}", session, dataLen,
+                        PROXY_V2_HEADER.length);
+            }
+            return false;
+        }
+
+        byte[] proxyV2Header = new byte[PROXY_V2_HEADER.length];
+        buffer.getRawBytes(proxyV2Header);
+
+        if (!Arrays.equals(PROXY_V2_HEADER, proxyV2Header)) {
+            buffer.rpos(mark); // Rewind the buffer to allow further reading
+            return super.acceptServerProxyMetadata(session, buffer);
+        }
+        return readProxyV2Header(session, mark, buffer);
+    }
+
+    protected boolean readProxyV2Header(ServerSession session, int 
markPosition, Buffer buffer) throws Exception {
+        if (log.isDebugEnabled()) {
+            int mark = buffer.rpos();
+            buffer.rpos(markPosition);
+            log.debug("readProxyV2Header(session={}) processing Proxy Protocol 
V2 buffer : [{}]", session,
+                    ProxyUtils.toHexString(buffer, mark));
+        }
+        StringBuilder proxyPayload = new StringBuilder();
+        // Read the version and command information
+        VersionAndCommand versionAndCommand = 
VersionAndCommand.extractValue(log, session, buffer);
+        proxyPayload.append(versionAndCommand.name());
+        // Read the family and transport.
+        FamilyAndTransport familyAndTransport = 
FamilyAndTransport.extractValue(log, session, buffer);
+        proxyPayload.append(FIELD_SEPARATOR).append(familyAndTransport.name());
+        // Read the data length
+        int dataLength = buffer.getUShort();
+        // Unix Socket are not supported by SSHD
+        if (familyAndTransport.hasSockAddress()) {
+            log.warn("parseProxyHeader(session={}) unsupported sub-protocol - 
{} - continue as usual", session,
+                    familyAndTransport);
+            // Skip socket address data
+            AddressData.skipUnprocessedData(log, session, buffer, 
FamilyAndTransport.UNSPEC, dataLength);
+            return true;
+        }
+        // Read the address Data (Host and Port for source and dest)
+        AddressData data = AddressData.extractAddressData(log, session, 
buffer, familyAndTransport, dataLength);
+        proxyPayload.append(FIELD_SEPARATOR).append(data);
+        // Parse the converted proxy header
+        return parseProxyHeader(session, proxyPayload.toString(), 
markPosition, buffer);
+    }
+
+    @Override
+    protected boolean parseProxyHeader(ServerSession session, String 
proxyHeader, int markPosition, Buffer buffer)
+            throws Exception {
+        String[] proxyFields = GenericUtils.split(proxyHeader, 
FIELD_SEPARATOR);
+        // Trim all fields just in case more than one space used
+        for (int index = 0; index < proxyFields.length; index++) {
+            String f = proxyFields[index];
+            proxyFields[index] = GenericUtils.trimToEmpty(f);
+        }
+        // Nothing to do for local proxy protocol
+        if ("LOCAL".equals(proxyFields[0])) {
+            log.debug("parseProxyHeader(session={}) local proxy check", 
session);
+            return true;
+        }
+        return super.parseProxyHeader(session, proxyHeader, markPosition, 
buffer);
+    }
+}
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/AddressData.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/AddressData.java
new file mode 100644
index 000000000..db9b6651e
--- /dev/null
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/AddressData.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.contrib.server.session.proxyprotocolv2.data;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.contrib.server.session.proxyprotocolv2.utils.ProxyUtils;
+import org.apache.sshd.server.session.ServerSession;
+import org.slf4j.Logger;
+
+
+/**
+ * Address data structure.
+ * <p>
+ * Starting from the 17th byte, addresses are presented in network byte order.
+ * </p>
+ * <p>
+ * The address order is always the same : - source layer 3 address in network 
byte order - destination layer 3 address
+ * in network byte order - source layer 4 address if any, in network byte 
order (port) - destination layer 4 address if
+ * any, in network byte order (port)
+ * </p>
+ * <p>
+ * The address block may directly be sent from or received into the following 
union which makes it easy to cast from/to
+ * the relevant socket native structs depending on the address type :
+ * </p>
+ *
+ * <pre>
+ *     union proxy_addr {
+ *         struct {        // for TCP/UDP over IPv4, len = 12
+ *             uint32_t src_addr;
+ *             uint32_t dst_addr;
+ *             uint16_t src_port;
+ *             uint16_t dst_port;
+ *         }ipv4_addr;
+ *         struct{        // for TCP/UDP over IPv6, len = 36
+ *             uint8_t src_addr[16];
+ *             uint8_t dst_addr[16];
+ *             uint16_t src_port;
+ *             uint16_t dst_port;
+ *         }ipv6_addr;
+ *         struct{        // for AF_UNIX sockets, len = 216
+ *             uint8_t src_addr[108];
+ *             uint8_t dst_addr[108];
+ *         }unix_addr;
+ *     };
+ * </pre>
+ *
+ * @author Oodrive - François HERBRETEAU (f.herbret...@oodrive.com)
+ */
+public final class AddressData {
+
+    private final String srcAddress;
+    private final String dstAddress;
+
+    private final int srcPort;
+    private final int dstPort;
+
+    private AddressData(String srcAddress, String dstAddress, int srcPort, int 
dstPort) {
+        this.srcAddress = srcAddress;
+        this.dstAddress = dstAddress;
+        this.srcPort = srcPort;
+        this.dstPort = dstPort;
+    }
+
+    public static AddressData extractAddressData(Logger logger,
+            ServerSession session,
+            Buffer buffer,
+            FamilyAndTransport familyAndTransport,
+            int dataLength)
+            throws IOException {
+        String srcAddress = extractAddresses(buffer, familyAndTransport);
+        String dstAddress = extractAddresses(buffer, familyAndTransport);
+        int srcPort = extractPort(buffer, familyAndTransport);
+        int dstPort = extractPort(buffer, familyAndTransport);
+        skipUnprocessedData(logger, session, buffer, familyAndTransport, 
dataLength);
+        return new AddressData(srcAddress, dstAddress, srcPort, dstPort);
+    }
+
+    public static void skipUnprocessedData(
+            Logger logger,
+            ServerSession session,
+            Buffer buffer,
+            FamilyAndTransport familyAndTransport,
+            int dataLength) {
+        int remaining = dataLength - familyAndTransport.getDataLength();
+        if (remaining > 0) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("extractAddressData({}) skipping additional datas 
[{}]",
+                        session,
+                        ProxyUtils.toHexString(buffer, buffer.rpos()));
+            }
+            // Insure the remaining bytes are available
+            buffer.ensureAvailable(remaining);
+            // Skip all extra datas
+            buffer.rpos(buffer.rpos() + remaining);
+        }
+    }
+
+    private static String extractAddresses(Buffer buffer, FamilyAndTransport 
familyAndTransport)
+            throws IOException {
+        byte[] datas = new byte[familyAndTransport.getAddressLength()];
+        buffer.getRawBytes(datas);
+        if (familyAndTransport.hasInetAddress()) {
+            return InetAddress.getByAddress(datas).getHostAddress();
+        }
+        return "";
+    }
+
+    private static int extractPort(Buffer buffer, FamilyAndTransport 
familyAndTransport) {
+        if (familyAndTransport.hasPort()) {
+            return buffer.getUShort();
+        }
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return String.join(" ", srcAddress, dstAddress, 
Integer.toString(srcPort), Integer.toString(dstPort));
+    }
+}
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/FamilyAndTransport.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/FamilyAndTransport.java
new file mode 100644
index 000000000..dd36c88be
--- /dev/null
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/FamilyAndTransport.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.contrib.server.session.proxyprotocolv2.data;
+
+import java.util.stream.Stream;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+import 
org.apache.sshd.contrib.server.session.proxyprotocolv2.exception.ProxyProtocolException;
+import org.apache.sshd.server.session.ServerSession;
+import org.slf4j.Logger;
+
+/**
+ * Family and Transport Enumeration.
+ * <p>
+ * The 14th byte contains the transport protocol and address family. The 
highest 4 bits contain the address family, the
+ * lowest 4 bits contain the protocol.
+ * </p>
+ * <p>
+ * The address family maps to the original socket family without necessarily 
matching the values internally used by the
+ * system. It may be one of : - 0x0 : AF_UNSPEC : the connection is forwarded 
for an unknown, unspecified or unsupported
+ * protocol. The sender should use this family when sending LOCAL commands or 
when dealing with unsupported protocol
+ * families. The receiver is free to accept the connection anyway and use the 
real endpoint addresses or to reject it.
+ * The receiver should ignore address information. - 0x1 : AF_INET : the 
forwarded connection uses the AF_INET address
+ * family (IPv4). The addresses are exactly 4 bytes each in network byte 
order, followed by transport protocol
+ * information (typically ports). - 0x2 : AF_INET6 : the forwarded connection 
uses the AF_INET6 address family (IPv6).
+ * The addresses are exactly 16 bytes each in network byte order, followed by 
transport protocol information (typically
+ * ports). - 0x3 : AF_UNIX : the forwarded connection uses the AF_UNIX address 
family (UNIX). The addresses are exactly
+ * 108 bytes each. - other values are unspecified and must not be emitted in 
version 2 of this protocol and must be
+ * rejected as invalid by receivers.
+ * </p>
+ * <p>
+ * The transport protocol is specified in the lowest 4 bits of the 14th byte : 
- 0x0 : UNSPEC : the connection is
+ * forwarded for an unknown, unspecified or unsupported protocol. The sender 
should use this family when sending LOCAL
+ * commands or when dealing with unsupported protocol families. The receiver 
is free to accept the connection anyway and
+ * use the real endpoint addresses or to reject it. The receiver should ignore 
address information. - 0x1 : STREAM : the
+ * forwarded connection uses a SOCK_STREAM protocol (eg: TCP or UNIX_STREAM). 
When used with AF_INET/AF_INET6 (TCP), the
+ * addresses are followed by the source and destination ports represented on 2 
bytes each in network byte order. - 0x2 :
+ * DGRAM : the forwarded connection uses a SOCK_DGRAM protocol (eg: UDP or 
UNIX_DGRAM). When used with AF_INET/AF_INET6
+ * (UDP), the addresses are followed by the source and destination ports 
represented on 2 bytes each in network byte
+ * order. - other values are unspecified and must not be emitted in version 2 
of this protocol and must be rejected as
+ * invalid by receivers.
+ * </p>
+ * <p>
+ * In practice, the following protocol bytes are expected : - \x00 : UNSPEC : 
the connection is forwarded for an
+ * unknown, unspecified or unsupported protocol. The sender should use this 
family when sending LOCAL commands or when
+ * dealing with unsupported protocol families. When used with a LOCAL command, 
the receiver must accept the connection
+ * and ignore any address information. For other commands, the receiver is 
free to accept the connection anyway and use
+ * the real endpoints addresses or to reject the connection. The receiver 
should ignore address information. - \x11 :
+ * TCP over IPv4 : the forwarded connection uses TCP over the AF_INET protocol 
family. Address length is 2*4 + 2*2 = 12
+ * bytes. - \x12 : UDP over IPv4 : the forwarded connection uses UDP over the 
AF_INET protocol family. Address length is
+ * 2*4 + 2*2 = 12 bytes. - \x21 : TCP over IPv6 : the forwarded connection 
uses TCP over the AF_INET6 protocol family.
+ * Address length is 2*16 + 2*2 = 36 bytes. - \x22 : UDP over IPv6 : the 
forwarded connection uses UDP over the AF_INET6
+ * protocol family. Address length is 2*16 + 2*2 = 36 bytes. - \x31 : UNIX 
stream : the forwarded connection uses
+ * SOCK_STREAM over the AF_UNIX protocol family. Address length is 2*108 = 216 
bytes. - \x32 : UNIX datagram : the
+ * forwarded connection uses SOCK_DGRAM over the AF_UNIX protocol family. 
Address length is 2*108 = 216 bytes.
+ * </p>
+ * <p>
+ * Only the UNSPEC protocol byte (\x00) is mandatory to implement on the 
receiver. A receiver is not required to
+ * implement other ones, provided that it automatically falls back to the 
UNSPEC mode for the valid combinations above
+ * that it does not support.
+ * </p>
+ *
+ * @author Oodrive - François HERBRETEAU (f.herbret...@oodrive.com)
+ */
+public enum FamilyAndTransport {
+
+    UNSPEC((byte) 0x00, 0, 0),
+    TCP4((byte) 0x11, 4, 2),
+    UDP4((byte) 0x12, 4, 2),
+    TCP6((byte) 0x21, 16, 2),
+    UDP6((byte) 0x22, 16, 2),
+    SOCK_STREAM((byte) 0x31, 108, 0),
+    SOCK_DGRAM((byte) 0x32, 108, 0);
+
+    private final byte value;
+
+    private final int addressLength;
+
+    private final int portLength;
+
+    FamilyAndTransport(byte value, int addressLength, int portLength) {
+        this.value = value;
+        this.addressLength = addressLength;
+        this.portLength = portLength;
+    }
+
+    public static FamilyAndTransport extractValue(Logger logger, ServerSession 
session, Buffer buffer)
+            throws ProxyProtocolException {
+        byte value = buffer.getByte();
+        return Stream.of(values())
+                .filter(val -> val.value == value)
+                .findFirst()
+                .orElseThrow(() -> 
ProxyProtocolException.buildFamilyAndTransport(logger, session, value));
+    }
+
+    public int getAddressLength() {
+        return addressLength;
+    }
+
+    public int getDataLength() {
+        return addressLength * 2 + portLength * 2;
+    }
+
+    public boolean hasInetAddress() {
+        return addressLength > 0 && portLength > 0;
+    }
+
+    public boolean hasPort() {
+        return portLength > 0;
+    }
+
+    public boolean hasSockAddress() {
+        return addressLength > 0 && portLength == 0;
+    }
+}
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/VersionAndCommand.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/VersionAndCommand.java
new file mode 100644
index 000000000..79c8ac615
--- /dev/null
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/data/VersionAndCommand.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.contrib.server.session.proxyprotocolv2.data;
+
+import java.util.stream.Stream;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+import 
org.apache.sshd.contrib.server.session.proxyprotocolv2.exception.ProxyProtocolException;
+import org.apache.sshd.server.session.ServerSession;
+import org.slf4j.Logger;
+
+/**
+ * Version and command enumeration.
+ * <p>
+ * The 13th byte is the protocol version and command. The highest four bits 
contains the version. As of this
+ * specification, it must always be sent as \x2 and the receiver must only 
accept this value.
+ * </p>
+ * <p>
+ * The lowest four bits represents the command : - \x0 : LOCAL : the 
connection was established on purpose by the proxy
+ * without being relayed. The connection endpoints are the sender and the 
receiver. Such connections exist when the
+ * proxy sends health-checks to the server. The receiver must accept this 
connection as valid and must use the real
+ * connection endpoints and discard the protocol block including the family 
which is ignored. - \x1 : PROXY : the
+ * connection was established on behalf of another node, and reflects the 
original connection endpoints. The receiver
+ * must then use the information provided in the protocol block to get 
original the address. - other values are
+ * unassigned and must not be emitted by senders. Receivers must drop 
connections presenting unexpected values here.
+ * </p>
+ *
+ * @author Oodrive - François HERBRETEAU (f.herbret...@oodrive.com)
+ */
+public enum VersionAndCommand {
+
+    LOCAL((byte) 0x20),
+    PROXY((byte) 0x21);
+
+    private final byte value;
+
+    VersionAndCommand(byte value) {
+        this.value = value;
+    }
+
+    public static VersionAndCommand extractValue(Logger logger, ServerSession 
session, Buffer buffer)
+            throws ProxyProtocolException {
+        byte value = buffer.getByte();
+        return Stream.of(values())
+                .filter(val -> val.value == value)
+                .findFirst()
+                .orElseThrow(() -> 
ProxyProtocolException.buildVersionOrCommand(logger, session, value));
+    }
+}
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/exception/ProxyProtocolException.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/exception/ProxyProtocolException.java
new file mode 100644
index 000000000..2cb64ed57
--- /dev/null
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/exception/ProxyProtocolException.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.contrib.server.session.proxyprotocolv2.exception;
+
+import org.apache.sshd.server.session.ServerSession;
+import org.slf4j.Logger;
+
+/**
+ * Blocking Exception that must block the connection.
+ *
+ * @author Oodrive - François HERBRETEAU (f.herbret...@oodrive.com)
+ */
+public final class ProxyProtocolException extends Exception {
+
+    public static final int PROXY_PROTOCOL_VERSION_2 = 2;
+
+    private static final int MAX_FAMILY_CODE = 3;
+
+    private static final long serialVersionUID = -7349477687125144605L;
+
+    private ProxyProtocolException(String message) {
+        super(message);
+    }
+
+    public static ProxyProtocolException buildVersionOrCommand(Logger log, 
ServerSession session, byte value) {
+        byte valueLow = (byte) (value & 0x0F);
+        byte valueHeight = (byte) (value >> 4);
+        if (valueHeight != PROXY_PROTOCOL_VERSION_2) {
+            if (log.isDebugEnabled()) {
+                log.debug("readProxyV2Header(session={}) mismatched version in 
proxy header: expected={}, actual={}",
+                        session,
+                        Integer.toHexString(PROXY_PROTOCOL_VERSION_2),
+                        Integer.toHexString(valueHeight));
+            }
+            return new ProxyProtocolException("Invalid version " + 
valueHeight);
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("readProxyV2Header(session={}) unassigned command in 
proxy header: actual={}",
+                    session, Integer.toHexString(valueLow));
+        }
+        return new ProxyProtocolException("Unassigned command " + valueLow);
+    }
+
+    public static ProxyProtocolException buildFamilyAndTransport(Logger log, 
ServerSession session, byte value) {
+        byte valueLow = (byte) (value & 0x0F);
+        byte valueHeight = (byte) (value >> 4);
+        if (valueHeight > MAX_FAMILY_CODE) {
+            if (log.isDebugEnabled()) {
+                log.debug("readProxyV2Header(session={}) unspecified family in 
proxy header: actual={}",
+                        session, Integer.toHexString(valueHeight));
+            }
+            return new ProxyProtocolException("Unspecified family " + 
valueHeight);
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("readProxyV2Header(session={}) unspecified transport in 
proxy header: actual={}",
+                    session, Integer.toHexString(valueLow));
+        }
+        return new ProxyProtocolException("Unspecified transport " + valueLow);
+    }
+}
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/utils/ProxyUtils.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/utils/ProxyUtils.java
new file mode 100644
index 000000000..7cb0a1c49
--- /dev/null
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/utils/ProxyUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.contrib.server.session.proxyprotocolv2.utils;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * Proxy Utilities class
+ *
+ * @author Oodrive - François HERBRETEAU (f.herbret...@oodrive.com)
+ */
+public final class ProxyUtils {
+
+    private ProxyUtils() {
+        // Utility Class
+    }
+
+    /**
+     * Create an hexadecimal string representation of the remaining content of 
a buffer and reset the buffer after
+     * reading.
+     *
+     * @param  buffer       a buffer to read from
+     * @param  markPosition the position from which to start.
+     * @return              a hexadecimal string representation.
+     */
+    public static String toHexString(Buffer buffer, int markPosition) {
+        byte[] datas = new byte[buffer.available()];
+        buffer.getRawBytes(datas);
+        buffer.rpos(markPosition);
+        return BufferUtils.toHex(datas, 0, datas.length, ',');
+    }
+}
diff --git 
a/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2AcceptorTest.java
 
b/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2AcceptorTest.java
new file mode 100644
index 000000000..19329e46a
--- /dev/null
+++ 
b/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2AcceptorTest.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.contrib.server.session.proxyprotocolv2;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import 
org.apache.sshd.contrib.server.session.proxyprotocol.ProxyProtocolAcceptor;
+import 
org.apache.sshd.contrib.server.session.proxyprotocolv2.exception.ProxyProtocolException;
+import org.apache.sshd.server.session.AbstractServerSession;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.assertj.core.api.InstanceOfAssertFactories.type;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+
+/**
+ * Test Suite for Proxy Protocol V2 handling.
+ *
+ * @author Oodrive - François HERBRETEAU (f.herbret...@oodrive.com)
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ProxyProtocolV2AcceptorTest {
+
+    private final ProxyProtocolAcceptor acceptor = new 
ProxyProtocolV2Acceptor();
+
+    @Mock
+    private AbstractServerSession session;
+
+    @Captor
+    private ArgumentCaptor<InetSocketAddress> socketAddressArgumentCaptor;
+
+    public ProxyProtocolV2AcceptorTest() {
+        // Nothing to do.
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV1Tcp4() throws Exception {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer("PROXY TCP4 172.19.0.1 
172.19.0.3 42272 80\r\n".getBytes());
+
+        // When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        // Then
+        
verify(session).setClientAddress(socketAddressArgumentCaptor.capture());
+        assertThat(socketAddressArgumentCaptor.getValue())
+                .isNotNull()
+                .isInstanceOf(InetSocketAddress.class)
+                .asInstanceOf(type(InetSocketAddress.class))
+                .extracting(InetSocketAddress::getAddress)
+                .asInstanceOf(type(InetAddress.class))
+                .extracting(InetAddress::getHostAddress)
+                .asString()
+                .isEqualTo("172.19.0.1");
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(43);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV1Tpc6() throws Exception {
+        // Given
+        ByteArrayBuffer buffer
+                = new ByteArrayBuffer("PROXY TCP6 fe80::a00:27ff:fe9f:4016 
fe80::a089:a3ff:fe15:e992 42272 80\r\n".getBytes());
+
+        // When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        // Then
+        
verify(session).setClientAddress(socketAddressArgumentCaptor.capture());
+        assertThat(socketAddressArgumentCaptor.getValue())
+                .isNotNull()
+                .isInstanceOf(InetSocketAddress.class)
+                .asInstanceOf(type(InetSocketAddress.class))
+                .extracting(InetSocketAddress::getAddress)
+                .asInstanceOf(type(InetAddress.class))
+                .extracting(InetAddress::getHostAddress)
+                .asString()
+                .isEqualTo("fe80:0:0:0:a00:27ff:fe9f:4016");
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(72);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2Tcp4() throws Exception {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x21, 0x11, 0x00, 0x0c, (byte) 0xac, 0x13, 0x00, 0x01, (byte) 
0xac, 0x13, 0x00, 0x03,
+                (byte) 0xa5, 0x20, 0x00, (byte) 0x50 });
+        //When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        
verify(session).setClientAddress(socketAddressArgumentCaptor.capture());
+        assertThat(socketAddressArgumentCaptor.getValue())
+                .isNotNull()
+                .isInstanceOf(InetSocketAddress.class)
+                .asInstanceOf(type(InetSocketAddress.class))
+                .extracting(InetSocketAddress::getAddress)
+                .asInstanceOf(type(InetAddress.class))
+                .extracting(InetAddress::getHostAddress)
+                .asString()
+                .isEqualTo("172.19.0.1");
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(28);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2Tcp6() throws Exception {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x21, 0x21, 0x00, 0x24, (byte) 0xfe, (byte) 0x80, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x0a,
+                0x00, 0x27, (byte) 0xff, (byte) 0xfe, (byte) 0x9f, 0x40, 0x16, 
(byte) 0xfe, (byte) 0x80, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xa0, (byte) 0x89, (byte) 
0xa3, (byte) 0xff, (byte) 0xfe,
+                0x15, (byte) 0xe9, (byte) 0x92, (byte) 0xa5, 0x20, 0x00, 
(byte) 0x50 });
+
+        // When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        // Then
+        
verify(session).setClientAddress(socketAddressArgumentCaptor.capture());
+        assertThat(socketAddressArgumentCaptor.getValue())
+                .isNotNull()
+                .isInstanceOf(InetSocketAddress.class)
+                .asInstanceOf(type(InetSocketAddress.class))
+                .extracting(InetSocketAddress::getAddress)
+                .asInstanceOf(type(InetAddress.class))
+                .extracting(InetAddress::getHostAddress)
+                .asString()
+                .isEqualTo("fe80:0:0:0:a00:27ff:fe9f:4016");
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(52);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2UnixSocket() throws Exception {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x21, 0x31, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 });
+
+        //When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(20);
+
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2Udp4() throws Exception {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x21, 0x12, 0x00, 0x0c, (byte) 0xac, 0x13, 0x00, 0x01, (byte) 
0xac, 0x13, 0x00, 0x03,
+                (byte) 0xa5, 0x20, 0x00, (byte) 0x50 });
+
+        //When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(28);
+
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2Udp6() throws Exception {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x21, 0x22, 0x00, 0x24, (byte) 0xfe, (byte) 0x80, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x0a,
+                0x00, 0x27, (byte) 0xff, (byte) 0xfe, (byte) 0x9f, 0x40, 0x16, 
(byte) 0xfe, (byte) 0x80, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xa0, (byte) 0x89, (byte) 
0xa3, (byte) 0xff, (byte) 0xfe,
+                0x15, (byte) 0xe9, (byte) 0x92, (byte) 0xa5, 0x20, 0x00, 
(byte) 0x50 });
+
+        //When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(52);
+
+    }
+
+    @Test
+    public void testHandlingOtherProtocolHeader() throws Exception {
+        // Given
+        ByteArrayBuffer buffer = new 
ByteArrayBuffer("SSH-2.0-OpenSSH_9.3".getBytes());
+
+        //When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        verify(session, never()).setClientAddress(any());
+        assertThat(buffer.available()).isEqualTo(19);
+        assertThat(buffer.rpos()).isZero();
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2WithLocalCommand() throws Exception 
{
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x20, 0x00, 0x00, 0x00 });
+
+        //When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        verify(session, never()).setClientAddress(any());
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(16);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2WithExtendedData() throws Exception 
{
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x20, 0x00, 0x00, 0x07, 0x03, 0x00, 0x04, (byte) 0xa9, (byte) 
0xb8, 0x7e, (byte) 0x8f });
+
+        //When
+        assertTrue(acceptor.acceptServerProxyMetadata(session, buffer));
+        //Then
+        verify(session, never()).setClientAddress(any());
+        assertThat(buffer.available()).isZero();
+        assertThat(buffer.rpos()).isEqualTo(23);
+
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2WithInvalidVersion() {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x31, 0x11, 0x00, 0x0c, (byte) 0xac, 0x13, 0x00, 0x01, (byte) 
0xac, 0x13, 0x00, 0x03,
+                (byte) 0xa5, 0x20, 0x00, (byte) 0x50 });
+
+        //When
+        ProxyProtocolException exception
+                = assertThrows(ProxyProtocolException.class, () -> 
acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        assertThat(exception).hasMessage("Invalid version 3");
+        assertThat(buffer.available()).isEqualTo(15);
+        assertThat(buffer.rpos()).isEqualTo(13);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2WithUnassignedCommand() {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x23, 0x00, 0x00, 0x00 });
+
+        //When
+        ProxyProtocolException exception
+                = assertThrows(ProxyProtocolException.class, () -> 
acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        assertThat(exception).hasMessage("Unassigned command 3");
+        verify(session, never()).setClientAddress(any());
+        assertThat(buffer.available()).isEqualTo(3);
+        assertThat(buffer.rpos()).isEqualTo(13);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2WithUnexpectedFamily() {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x21, 0x40, 0x00, 0x00 });
+
+        //When
+        ProxyProtocolException exception
+                = assertThrows(ProxyProtocolException.class, () -> 
acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        assertThat(exception).hasMessage("Unspecified family 4");
+        verify(session, never()).setClientAddress(any());
+        assertThat(buffer.available()).isEqualTo(2);
+        assertThat(buffer.rpos()).isEqualTo(14);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2WithUnexpectedTransport() {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] {
+                0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 
0x54, 0x0a,
+                0x21, 0x14, 0x00, 0x00 });
+
+        //When
+        ProxyProtocolException exception
+                = assertThrows(ProxyProtocolException.class, () -> 
acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        assertThat(exception).hasMessage("Unspecified transport 4");
+        verify(session, never()).setClientAddress(any());
+        assertThat(buffer.available()).isEqualTo(2);
+        assertThat(buffer.rpos()).isEqualTo(14);
+    }
+
+    @Test
+    public void testHandlingProxyProtocolV2WithInvalidSize() throws Exception {
+        // Given
+        ByteArrayBuffer buffer = new ByteArrayBuffer(new byte[] { 0x00, 0x00, 
0x00, 0x00 });
+
+        //When
+        assertFalse(acceptor.acceptServerProxyMetadata(session, buffer));
+
+        //Then
+        verify(session, never()).setClientAddress(any());
+        assertThat(buffer.available()).isEqualTo(4);
+        assertThat(buffer.rpos()).isZero();
+
+    }
+}


Reply via email to