RFC6066 defines server name indication in extension of type server_name
. The extension_data
field of this extension SHALL contain ServerNameList
where:
struct {
NameType name_type;
select (name_type) {
case host_name: HostName;
} name;
} ServerName;
enum {
host_name(0), (255)
} NameType;
opaque HostName<1..2^16-1>;
struct {
ServerName server_name_list<1..2^16-1>
} ServerNameList;
Would be nice to have step by step explanation of this data structure. Also, here is the sample code, could be found here, how to read the extension data:
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: // 0x00
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()));
}
Byte reader:
private static int getInt16(ByteBuffer input) {
return ((input.get() & 0xFF) << 8) | (input.get() & 0xFF);
}
Here is good example how data should be read. For example, extension type is defined by reading 2 bytes - so another question is - which RFC defines it?
If you already have the source code implementing it, what is more needed to know?
The format used for the abstract schema is derived from XDR but is defined specifically in each TLS specification, like for the last one in 3. Presentation Language
So if we go piece by piece:
struct {
NameType name_type;
select (name_type) {
case host_name: HostName;
} name;
} ServerName;
See https://www.rfc-editor.org/rfc/rfc8446#section-3.6, this defines a structure:
name_type
and is of type called NameType
(defined later)name
and is a variant (https://www.rfc-editor.org/rfc/rfc8446#section-3.8): its value depends
of the previous name_type
content. If name_type
have the value of
host_name
, then the value of this second component is of type
HostName
(defined later)Next:
enum {
host_name(0), (255)
} NameType;
See https://www.rfc-editor.org/rfc/rfc8446#section-3.5, this defines an enumeration with only one possible value (0
), whose alias is host_name
The (255)
is solely used to force the width (so 0 to 255 as values fits in one byte, this structure uses one byte of space), as explained in the specification:
One may optionally specify a value without its associated tag to force the width definition without defining a superfluous element.
So it means on the wire you use 0, but if you have 0 it is to encode host_name
in other parts of the specification.
opaque HostName<1..2^16-1>;
In https://www.rfc-editor.org/rfc/rfc8446#section-3.2 we have:
Single-byte entities containing uninterpreted data are of type opaque.
And in https://www.rfc-editor.org/rfc/rfc8446#section-3.4, <>
is used to define a variable length vector (or single dimensional array, or list).
So HostName
is a vector containing between 1 to 216-1 bytes (not elements), each element being of type "opaque", that is a single byte.
Note that there is further explanation as text in the RFC about SNI:
"HostName" contains the fully qualified DNS hostname of the server, as understood by the client. The hostname is represented as a byte string using ASCII encoding without a trailing dot.
struct {
ServerName server_name_list<1..2^16-1>
} ServerNameList;
Same case as first one, but using a variable length array like above
ServerNameList
is a structureServerName
, as defined previouslySaid differently:
ServerNameList
structure is a list of elements, each one being of type ServerName
ServerName
encodes a name_type
(that can be only "host_name", aka value 0) and a name that is of type HostName
, which is a non empty list of up to 216-1 bytes encoding an hostname as explained in https://www.rfc-editor.org/rfc/rfc6066#section-3