Quic Toy中默认使用的是QuicCrypto握手,现在尝试将其配置为TLS1.3握手。
首先应当让version支持TLS,从quic的流程看,version的设置在QUICToyClient::SendRequestAndPrintResponse中完成。
quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions();
std::string quic_version_string = GetQuicFlag(FLAGS_quic_version);
const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft);
if (quic_ietf_draft > 0) {
quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft);
if (quic_version_string.length() == 0) {
quic_version_string = "T099";
}
}
if (quic_version_string.length() > 0) {
if (quic_version_string[0] == 'T') {
// ParseQuicVersionString checks quic_supports_tls_handshake.
SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
}
quic::ParsedQuicVersion parsed_quic_version =
quic::ParseQuicVersionString(quic_version_string);
if (parsed_quic_version.transport_version ==
quic::QUIC_VERSION_UNSUPPORTED) {
return 1;
}
versions.clear();
versions.push_back(parsed_quic_version);
quic::QuicEnableVersion(parsed_quic_version);
}
if (GetQuicFlag(FLAGS_force_version_negotiation)) {
versions.insert(versions.begin(),
quic::QuicVersionReservedForNegotiation());
}
Version包含两个部分,Transport_version与Handshake_protocol。
从GDB信息看,versions只保存了QUICCrypto部分,TLS全部被过滤掉了。
过滤的TLS算法的逻辑在:
quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions();
......
......
ParsedQuicVersionVector CurrentSupportedVersions() {
return FilterSupportedVersions(AllSupportedVersions());
}
ParsedQuicVersionVector FilterSupportedVersions(
ParsedQuicVersionVector versions) {
ParsedQuicVersionVector filtered_versions;
filtered_versions.reserve(versions.size());
for (ParsedQuicVersion version : versions) {
if (version.handshake_protocol == PROTOCOL_TLS1_3 &&
!GetQuicFlag(FLAGS_quic_supports_tls_handshake)) {
continue;
}
if (version.transport_version == QUIC_VERSION_99) {
if (GetQuicReloadableFlag(quic_enable_version_99)) {
filtered_versions.push_back(version);
}
} else if (version.transport_version == QUIC_VERSION_48) {
if (GetQuicReloadableFlag(quic_enable_version_48_2)) {
filtered_versions.push_back(version);
}
} else if (version.transport_version == QUIC_VERSION_47) {
if (GetQuicReloadableFlag(quic_enable_version_47)) {
filtered_versions.push_back(version);
}
} else if (version.transport_version == QUIC_VERSION_39) {
if (!GetQuicReloadableFlag(quic_disable_version_39)) {
filtered_versions.push_back(version);
}
} else {
filtered_versions.push_back(version);
}
}
return filtered_versions;
}
可见如果要enable TLS1_3,需要使能 FLAGS_quic_supports_tls_handshake。
从上面的QUICToyClient::SendRequestAndPrintResponse代码片段,可以看到:
if (quic_version_string.length() > 0) {
if (quic_version_string[0] == 'T') {
// ParseQuicVersionString checks quic_supports_tls_handshake.
SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
}
quic::ParsedQuicVersion parsed_quic_version =
quic::ParseQuicVersionString(quic_version_string);
if (parsed_quic_version.transport_version ==
quic::QUIC_VERSION_UNSUPPORTED) {
return 1;
}
versions.clear();
versions.push_back(parsed_quic_version);
quic::QuicEnableVersion(parsed_quic_version);
}
如果quic_version设置为了'T'开头,那会设置TLS FLAG,并且清除掉先前所有的version(QUIC_CRYPTO)。
而quic_version由FLAGS_quic_version决定:
std::string quic_version_string = GetQuicFlag(FLAGS_quic_version);
在quic_toy_client.cc文件中有定义FLAGS_quic_version:
DEFINE_QUIC_COMMAND_LINE_FLAG(
std::string,
quic_version,
"",
"QUIC version to speak, e.g. 21. If not set, then all available "
"versions are offered in the handshake. Also supports wire versions "
"such as Q043 or T099.");
是否设置这个值为T099就可以了?我们还缺少服务端的支持,先暂停客户端,去看看服务端的配置。
在quic_simple_server_bin.cc中,对应用程序参数的解析如下:
if (line->HasSwitch("quic_ietf_draft")) {
if (!base::StringToInt(line->GetSwitchValueASCII("quic_ietf_draft"),
&FLAGS_quic_ietf_draft)) {
LOG(ERROR) << "--quic_ietf_draft must be an integer\n";
return 1;
}
if (FLAGS_quic_ietf_draft > 0) {
quic::QuicVersionInitializeSupportForIetfDraft(FLAGS_quic_ietf_draft);
quic::QuicEnableVersion(quic::ParsedQuicVersion(quic::PROTOCOL_TLS1_3,
quic::QUIC_VERSION_99));
}
}
嗯,看来只要指定--quic_ietf_draft就可以打开TLS握手,但是这样的话就必须要用IETF QUIC。
先用IETF QUIC试试,在server侧配置IEFT选项
./quic_server --quic_response_cache_dir=/tmp/quic-data/www.example.org --certificate_file=./leaf_cert.pem --key_file=./leaf_cert.pkcs8 --quic_ietf_draft=18
在Client侧,通过配置
DEFINE_QUIC_COMMAND_LINE_FLAG(
int32_t,
quic_ietf_draft,
0,
"QUIC IETF draft number to use over the wire, e.g. 18. "
"By default this sets quic_version to T099. "
"This also enables required internal QUIC flags.");
我错误的将这里的0修改为了1,结果出现错误:
Server talks QUIC, but none of the versions supported by this client: ff000001
奇怪的为什么版本号变成了ff000001。我调试了代码,发现version里面的值是正确的,但是ParsedQuicVersionVectorToString(versions)函数解析成为ff000001。
而在服务端,我调试看到,在QuicDispatcher::ProcessPacket函数中,对packet_info的解析变成了0, 0。
抓包,发现version字段里面也是ff00001,说明客户端发送出来的时候就是该值。
那为什么会编程ff00001呢,答案在ParsedQuicVersionVectorToString函数,该函数实现如下:
switch (parsed_version.transport_version) {
case QUIC_VERSION_39:
return MakeVersionLabel(proto, '0', '3', '9');
case QUIC_VERSION_43:
return MakeVersionLabel(proto, '0', '4', '3');
case QUIC_VERSION_46:
return MakeVersionLabel(proto, '0', '4', '6');
case QUIC_VERSION_47:
return MakeVersionLabel(proto, '0', '4', '7');
case QUIC_VERSION_48:
return MakeVersionLabel(proto, '0', '4', '8');
case QUIC_VERSION_99:
if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 &&
GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) {
return MakeVersionLabel(0xff, 0x00, 0x00,
GetQuicFlag(FLAGS_quic_ietf_draft_version));
}
return MakeVersionLabel(proto, '0', '9', '9');
case QUIC_VERSION_RESERVED_FOR_NEGOTIATION:
return CreateRandomVersionLabelForNegotiation();
default:
// This is a bug because we should never attempt to convert an invalid
// QuicTransportVersion to be written to the wire.
QUIC_BUG << "Unsupported QuicTransportVersion: "
<< parsed_version.transport_version;
return 0;
}
可见如果是TLS,且开启了IETF,那么就会设置为ff00000x格式,在quic_toy_client.c中我设置为了1,所以出现ff000001。把这个值修改为18,握手成功!
抓包如下:
第二种尝试:
在quic_toy_client.cc中,将quic_ietf_draft选项配置为0。服务端去掉--quic_ietf_draft=18启动参数。
通过打印,发现client已经使用了T099,服务端也打印支持T099。
握手,出现Error: QUIC_NETWORK_IDLE_TIMEOUT错误。
抓包发现还是IETF的报文,报文里面的version被wireshark解析为Unknown。在服务端查看是在QuicDispatcher::MaybeDispatchPacket函数中被丢弃了,因为server_connection_id.length()检查不通过。
将版本号修改为T048,出现版本不支持问题,原因是supported_version检查不合格。
bool QuicDispatcher::IsSupportedVersion(const ParsedQuicVersion version) {
for (const ParsedQuicVersion& supported_version :
version_manager_->GetSupportedVersions()) {
if (version == supported_version) {
return true;
}
}
return false;
}
从打印看,支持的版本有:46,43,39版本的QuicCrypto握手,而我们发出的是48版本的TLS握手。
version_manager_的构造函数会过滤掉一些Flag没有enable的版本,其中48版本的Flag为quic_enable_version_48,在server启动前设置此Flag。
重新运行,结果没变还是版本不支持。从打印看,虽然48版本支持了,但是只支持QUICCRYPTO握手。因为如果使用TLS,需要enable FLAGS_quic_supports_tls_handshake,再设置一下server的flag,成功!
抓包看:版本号wireshark还是解析为未知。
另外,从打印的版本号看,48之前的版本,只支持QUIC CRYPTO,48支持TLS,99也支持TLS,且使用48版本是用的IETF格式。
为什么只有48版本以上才支持TLS呢,答案在quic::AllsupportedVersions()中
ParsedQuicVersionVector AllSupportedVersions() {
ParsedQuicVersionVector supported_versions;
for (HandshakeProtocol protocol : kSupportedHandshakeProtocols) {
for (QuicTransportVersion version : kSupportedTransportVersions) {
if (protocol == PROTOCOL_TLS1_3 &&
!QuicVersionUsesCryptoFrames(version)) {
// The TLS handshake is only deployable if CRYPTO frames are also used.
continue;
}
supported_versions.push_back(ParsedQuicVersion(protocol, version));
}
}
这里支持的版本为kSupportedHandshakeProtocols与kSupportedTransportVersions的组合。
但是有一个条件,就是如果使用TLS1.3,那必须要满足QuicVersionUsesCryptoFrames(version)
// Returns whether |transport_version| uses CRYPTO frames for the handshake
// instead of stream 1.
QUIC_EXPORT_PRIVATE inline bool QuicVersionUsesCryptoFrames(
QuicTransportVersion transport_version) {
return transport_version >= QUIC_VERSION_48;
}
所以,只有48版本及以上才使用TLS1.3。
遗留问题:为什么TLS1.3必然使用的是IETF Quic格式,这个还没明白。