private static List<SNIServerName> exploreSNIExt(ByteBuffer input,
            int extLen) throws IOException {

        Map<Integer, SNIServerName> sniMap = new LinkedHashMap<>();

        int remains = extLen;
        if (extLen >= 2) {     // "server_name" extension in ClientHello
            int listLen = getInt16(input);     // length of server_name_list
            if (listLen == 0 || listLen + 2 != extLen) {
                throw new SSLProtocolException(
                    "Invalid server name indication extension");
            }

            remains -= 2;     // 0x02: the length field of server_name_list
            while (remains > 0) {
                int code = getInt8(input);      // name_type
                int snLen = getInt16(input);    // length field of server name
                if (snLen > remains) {
                    throw new SSLProtocolException(
                        "Not enough data to fill declared vector size");
                }
                byte[] encoded = new byte[snLen];
                input.get(encoded);

                SNIServerName serverName;
                switch (code) {
                    case StandardConstants.SNI_HOST_NAME:
                        if (encoded.length == 0) {
                            throw new SSLProtocolException(
                                "Empty HostName in server name indication");
                        }
                        serverName = new SNIHostName(encoded);
                        break;
                    default:
                        serverName = new UnknownServerName(code, encoded);
                }
                // check for duplicated server name type
                if (sniMap.put(serverName.getType(), serverName) != null) {
                    throw new SSLProtocolException(
                            "Duplicated server name of type " +
                            serverName.getType());
                }

                remains -= encoded.length + 3;  // NameType: 1 byte
                                                // HostName length: 2 bytes
            }
        } else if (extLen == 0) {     // "server_name" extension in ServerHello
            throw new SSLProtocolException(
                        "Not server name indication extension in client");
        }

        if (remains != 0) {
            throw new SSLProtocolException(
                        "Invalid server name indication extension");
        }

        return Collections.<SNIServerName>unmodifiableList(
                                            new ArrayList<>(sniMap.values()));
    }
Launch and explore the security capabilities from byte array.
<P>
Please NOTE that this method must be called before any handshaking
occurs.  The behavior of this method is not defined in this release
if the handshake has begun, or has completed.  Once handshake has
begun, or has completed, the security capabilities can not and
should not be launched with this method.

@param  source
        a byte array containing inbound or outbound network data for
        an SSL/TLS connection.
@param  offset
        the start offset in array {@code source} at which the
        network data is read from.
@param  length
        the maximum number of bytes to read.

@throws IOException on network data error
@throws BufferUnderflowException if not enough source bytes available
        to make a complete exploration.
@return the explored {@link SSLCapabilities} of the SSL/TLS
        connection

@see #explore(ByteBuffer)
src/main/java/core/packetproxy/common/SSLExplorer.java