在PeerConnection中无论是SetLocalDescription还是SetRemoteDescription,里面的第一个都是对SDP进行认证检测即调用ValidateSessionDescription,在ValidateSessionDescription中主要对如下三个方面进行了认证:
1 加密信息认证
2 ice信息认证
3 m-section中track个数进行认证
1 加密信息认证,即VerifyCrypto函数
RTCError VerifyCrypto(const SessionDescription* desc, bool dtls_enabled)
{
const cricket::ContentGroup* bundle = desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
for (const cricket::ContentInfo& content_info : desc->contents())
{
1 被拒绝的直接忽略
if (content_info.rejected)
{
continue;
}
// Note what media is used with each crypto protocol, for all sections.
NoteKeyProtocolAndMedia(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls
: webrtc::kEnumCounterKeyProtocolSdes,
content_info.media_description()->type());
const std::string& mid = content_info.name;
if (bundle && bundle->HasContentName(mid) && mid != *(bundle->FirstContentName()))
{
// This isn't the first media section in the BUNDLE group, so it's not
// required to have crypto attributes, since only the crypto attributes
// from the first section actually get used.
continue;
}
2 如果开启BUNDLE,则看看第一个是否包含fingerprint,没有则返回false
// If the content isn't rejected or bundled into another m= section, crypto
// must be present.
const MediaContentDescription* media = content_info.media_description();
const TransportInfo* tinfo = desc->GetTransportInfoByName(mid);
if (!media || !tinfo)
{
// Something is not right.
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp);
}
if (dtls_enabled)
{
if (!tinfo->description.identity_fingerprint)
{
RTC_LOG(LS_WARNING)
<< "Session description must have DTLS fingerprint if "
"DTLS enabled.";
return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutDtlsFingerprint);
}
}
else
{
if (media->cryptos().empty())
{
RTC_LOG(LS_WARNING)
<< "Session description must have SDES when DTLS disabled.";
return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutSdesCrypto);
}
}
}
return RTCError::OK();
}
代码中已做标注
2 ice信息认证,即VerifyIceUfragPwdPresent
bool VerifyIceUfragPwdPresent(const SessionDescription* desc)
{
0 判断是否有BUNDLE,并获取BUNDLE列表即mid列表
const cricket::ContentGroup* bundle = desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
for (const cricket::ContentInfo& content_info : desc->contents())
{
1 被拒绝的m-section直接continue
if (content_info.rejected)
{
continue;
}
2 如果开启了BUNDLE,并且当前m-section在BUNDLE中,但不是第一个m-section则直接continue
const std::string& mid = content_info.name;
if (bundle && bundle->HasContentName(mid) && mid != *(bundle->FirstContentName()))
{
// This isn't the first media section in the BUNDLE group, so it's not
// required to have ufrag/password, since only the ufrag/password from
// the first section actually get used.
continue;
}
3 如果第一个m-section中获取不到ice信息
// If the content isn't rejected or bundled into another m= section,
// ice-ufrag and ice-pwd must be present.
const TransportInfo* tinfo = desc->GetTransportInfoByName(mid);
if (!tinfo)
{
// Something is not right.
RTC_LOG(LS_ERROR) << kInvalidSdp;
return false;
}
4 如果ice_ufrag或ice_pwd任意一个为空 则返回false
if (tinfo->description.ice_ufrag.empty() || tinfo->description.ice_pwd.empty())
{
RTC_LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
return false;
}
}
return true;
}
3 m-section中track个数进行认证
if (IsUnifiedPlan())
{
// Ensure that each audio and video media section has at most one
// "StreamParams". This will return an error if receiving a session
// description from a "Plan B" endpoint which adds multiple tracks of the
// same type. With Unified Plan, there can only be at most one track per
// media section.
for (const ContentInfo& content : sdesc->description()->contents())
{
const MediaContentDescription& desc = *content.description;
if ((desc.type() == cricket::MEDIA_TYPE_AUDIO ||
desc.type() == cricket::MEDIA_TYPE_VIDEO) &&
desc.streams().size() > 1u) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
"Media section has more than one track specified "
"with a=ssrc lines which is not supported with "
"Unified Plan.");
}
}
}
这里只列出了公共认证部分,针对answer还有一个m-section个数和顺序的认证,详见如下两个函数:
MediaSectionsHaveSameCount()和MediaSectionsInSameOrder()
从上可以得出,UnifiedPlan下开启BUNDLE时其认证的过程可以总结为:
1 加密信息在第一个m-section中必须有,其他m-section随意
2 同上,ice信息在第一个m-section中必须有,其他m-section随意
3 如果开启BUNDLE,每个m-section中必须有a=rtcp-mux属性
4 UnifiedPlan中,每个m-section中只能有一个track