Skip to content

Commit 2fd3640

Browse files
nils-ohlmeierSean-Der
authored andcommitted
Only collect single fingerprints/ICE credentials
The way currently DTLS fingerprints and ICE credentials are picked is causing interop issues as described in pion#2621 Peers which don't use Bundle can use different fingerprints and credentials in each media section. Even though is not (yet) supported by Pion, receiving an SDP offer from such a peer is valid. Additionally if Bundle is being used the group attribute determines which media section is the master bundle section, which establishes the transport. Currently Pion always just uses the first credentials/fingerprint it can find in the SDP, which results in not spec compliant behavior. This PR attempts to fix the above issues and make Pion more spec compliant and interoperable. Fixes pion#2621
1 parent 363e017 commit 2fd3640

File tree

6 files changed

+247
-71
lines changed

6 files changed

+247
-71
lines changed

mediaengine_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ v=0
645645
o=- 8448668841136641781 4 IN IP4 127.0.0.1
646646
s=-
647647
t=0 0
648-
a=group:BUNDLE 0 1 2
648+
a=group:BUNDLE 1
649649
a=extmap-allow-mixed
650650
a=msid-semantic: WMS 4beea6b0-cf95-449c-a1ec-78e16b247426
651651
m=video 9 UDP/TLS/RTP/SAVPF 96 127

peerconnection_go_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,7 @@ func TestTransceiverCreatedByRemoteSdpHasSameCodecOrderAsRemote(t *testing.T) {
13981398
o=- 4596489990601351948 2 IN IP4 127.0.0.1
13991399
s=-
14001400
t=0 0
1401+
a=group:BUNDLE 0 1
14011402
m=video 60323 UDP/TLS/RTP/SAVPF 98 94 106
14021403
a=ice-ufrag:1/MvHwjAyVf27aLu
14031404
a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
@@ -1458,6 +1459,7 @@ a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01
14581459
o=- 4596489990601351948 2 IN IP4 127.0.0.1
14591460
s=-
14601461
t=0 0
1462+
a=group:BUNDLE 0 1
14611463
m=video 60323 UDP/TLS/RTP/SAVPF 98 106
14621464
a=ice-ufrag:1/MvHwjAyVf27aLu
14631465
a=ice-pwd:3dBU7cFOBl120v33cynDvN1E
@@ -1548,7 +1550,7 @@ o=- 4596489990601351948 2 IN IP4 127.0.0.1
15481550
s=-
15491551
t=0 0
15501552
a=fingerprint:sha-256 F7:BF:B4:42:5B:44:C0:B9:49:70:6D:26:D7:3E:E6:08:B1:5B:25:2E:32:88:50:B6:3C:BE:4E:18:A7:2C:85:7C
1551-
a=group:BUNDLE 0 1
1553+
a=group:BUNDLE 0
15521554
a=msid-semantic:WMS *
15531555
m=video 9 UDP/TLS/RTP/SAVPF 97
15541556
c=IN IP4 0.0.0.0

peerconnection_media_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,9 @@ func TestPeerConnection_Simulcast_Probe(t *testing.T) {
10721072
for scanner.Scan() {
10731073
if strings.HasPrefix(scanner.Text(), "m=video") {
10741074
shouldDiscard = !shouldDiscard
1075+
} else if strings.HasPrefix(scanner.Text(), "a=group:BUNDLE") {
1076+
filtered += "a=group:BUNDLE 1 2\r\n"
1077+
continue
10751078
}
10761079

10771080
if !shouldDiscard {

peerconnection_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ const minimalOffer = `v=0
282282
o=- 4596489990601351948 2 IN IP4 127.0.0.1
283283
s=-
284284
t=0 0
285+
a=group:BUNDLE data
285286
a=msid-semantic: WMS
286287
m=application 47299 DTLS/SCTP 5000
287288
c=IN IP4 192.168.20.129

sdp.go

Lines changed: 114 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -719,96 +719,156 @@ func getPeerDirection(media *sdp.MediaDescription) RTPTransceiverDirection {
719719
return RTPTransceiverDirectionUnknown
720720
}
721721

722-
func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) {
723-
fingerprints := []string{}
722+
func extractBundleID(desc *sdp.SessionDescription) string {
723+
groupAttribute, _ := desc.Attribute(sdp.AttrKeyGroup)
724724

725-
if fingerprint, haveFingerprint := desc.Attribute("fingerprint"); haveFingerprint {
726-
fingerprints = append(fingerprints, fingerprint)
725+
isBundled := strings.Contains(groupAttribute, "BUNDLE")
726+
727+
if !isBundled {
728+
return ""
727729
}
728730

729-
for _, m := range desc.MediaDescriptions {
730-
if fingerprint, haveFingerprint := m.Attribute("fingerprint"); haveFingerprint {
731-
fingerprints = append(fingerprints, fingerprint)
732-
}
731+
bundleIDs := strings.Split(groupAttribute, " ")
732+
733+
if len(bundleIDs) < 2 {
734+
return ""
733735
}
734736

735-
if len(fingerprints) < 1 {
736-
return "", "", ErrSessionDescriptionNoFingerprint
737+
return bundleIDs[1]
738+
}
739+
740+
func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) { //nolint: gocognit
741+
fingerprint := ""
742+
743+
// Fingerprint on session level has highest priority
744+
if sessionFingerprint, haveFingerprint := desc.Attribute("fingerprint"); haveFingerprint {
745+
fingerprint = sessionFingerprint
737746
}
738747

739-
for _, m := range fingerprints {
740-
if m != fingerprints[0] {
741-
return "", "", ErrSessionDescriptionConflictingFingerprints
748+
if fingerprint == "" {
749+
bundleID := extractBundleID(desc)
750+
if bundleID != "" {
751+
// Locate the fingerprint of the bundled media section
752+
for _, m := range desc.MediaDescriptions {
753+
if mid, haveMid := m.Attribute("mid"); haveMid {
754+
if mid == bundleID && fingerprint == "" {
755+
if mediaFingerprint, haveFingerprint := m.Attribute("fingerprint"); haveFingerprint {
756+
fingerprint = mediaFingerprint
757+
}
758+
}
759+
}
760+
}
761+
} else {
762+
// Take the fingerprint from the first media section which has one.
763+
// Note: According to Bundle spec each media section would have it's own transport
764+
// with it's own cert and fingerprint each, so we would need to return a list.
765+
for _, m := range desc.MediaDescriptions {
766+
mediaFingerprint, haveFingerprint := m.Attribute("fingerprint")
767+
if haveFingerprint && fingerprint == "" {
768+
fingerprint = mediaFingerprint
769+
}
770+
}
742771
}
743772
}
744773

745-
parts := strings.Split(fingerprints[0], " ")
774+
if fingerprint == "" {
775+
return "", "", ErrSessionDescriptionNoFingerprint
776+
}
777+
778+
parts := strings.Split(fingerprint, " ")
746779
if len(parts) != 2 {
747780
return "", "", ErrSessionDescriptionInvalidFingerprint
748781
}
749782
return parts[1], parts[0], nil
750783
}
751784

752-
func extractICEDetails(desc *sdp.SessionDescription, log logging.LeveledLogger) (string, string, []ICECandidate, error) { // nolint:gocognit
785+
func extractICEDetailsFromMedia(media *sdp.MediaDescription, log logging.LeveledLogger) (string, string, []ICECandidate, error) {
786+
remoteUfrag := ""
787+
remotePwd := ""
753788
candidates := []ICECandidate{}
754-
remotePwds := []string{}
755-
remoteUfrags := []string{}
756789

790+
if ufrag, haveUfrag := media.Attribute("ice-ufrag"); haveUfrag {
791+
remoteUfrag = ufrag
792+
}
793+
if pwd, havePwd := media.Attribute("ice-pwd"); havePwd {
794+
remotePwd = pwd
795+
}
796+
for _, a := range media.Attributes {
797+
if a.IsICECandidate() {
798+
c, err := ice.UnmarshalCandidate(a.Value)
799+
if err != nil {
800+
if errors.Is(err, ice.ErrUnknownCandidateTyp) || errors.Is(err, ice.ErrDetermineNetworkType) {
801+
log.Warnf("Discarding remote candidate: %s", err)
802+
continue
803+
}
804+
return "", "", nil, err
805+
}
806+
807+
candidate, err := newICECandidateFromICE(c)
808+
if err != nil {
809+
return "", "", nil, err
810+
}
811+
812+
candidates = append(candidates, candidate)
813+
}
814+
}
815+
816+
return remoteUfrag, remotePwd, candidates, nil
817+
}
818+
819+
func extractICEDetails(desc *sdp.SessionDescription, log logging.LeveledLogger) (string, string, []ICECandidate, error) { // nolint:gocognit
820+
remoteCandidates := []ICECandidate{}
821+
remotePwd := ""
822+
remoteUfrag := ""
823+
824+
// Ufrag and Pw are allow at session level and thus have highest prio
757825
if ufrag, haveUfrag := desc.Attribute("ice-ufrag"); haveUfrag {
758-
remoteUfrags = append(remoteUfrags, ufrag)
826+
remoteUfrag = ufrag
759827
}
760828
if pwd, havePwd := desc.Attribute("ice-pwd"); havePwd {
761-
remotePwds = append(remotePwds, pwd)
829+
remotePwd = pwd
762830
}
763831

764-
for _, m := range desc.MediaDescriptions {
765-
if ufrag, haveUfrag := m.Attribute("ice-ufrag"); haveUfrag {
766-
remoteUfrags = append(remoteUfrags, ufrag)
767-
}
768-
if pwd, havePwd := m.Attribute("ice-pwd"); havePwd {
769-
remotePwds = append(remotePwds, pwd)
770-
}
832+
bundleID := extractBundleID(desc)
833+
missing := true
771834

772-
for _, a := range m.Attributes {
773-
if a.IsICECandidate() {
774-
c, err := ice.UnmarshalCandidate(a.Value)
835+
for _, m := range desc.MediaDescriptions {
836+
mid := getMidValue(m)
837+
// If bundled, only take ICE detail from bundle master section
838+
if bundleID != "" {
839+
if mid == bundleID {
840+
ufrag, pwd, candidates, err := extractICEDetailsFromMedia(m, log)
775841
if err != nil {
776-
if errors.Is(err, ice.ErrUnknownCandidateTyp) || errors.Is(err, ice.ErrDetermineNetworkType) {
777-
log.Warnf("Discarding remote candidate: %s", err)
778-
continue
779-
}
780842
return "", "", nil, err
781843
}
782-
783-
candidate, err := newICECandidateFromICE(c)
784-
if err != nil {
785-
return "", "", nil, err
844+
if remoteUfrag == "" && ufrag != "" {
845+
remoteUfrag = ufrag
846+
remotePwd = pwd
786847
}
787-
788-
candidates = append(candidates, candidate)
848+
remoteCandidates = candidates
849+
}
850+
} else if missing {
851+
// For not-bundled, take ICE details from the first media section
852+
ufrag, pwd, candidates, err := extractICEDetailsFromMedia(m, log)
853+
if err != nil {
854+
return "", "", nil, err
855+
}
856+
if remoteUfrag == "" && ufrag != "" {
857+
remoteUfrag = ufrag
858+
remotePwd = pwd
789859
}
860+
remoteCandidates = candidates
861+
missing = false
790862
}
791863
}
792864

793-
if len(remoteUfrags) == 0 {
865+
if remoteUfrag == "" {
794866
return "", "", nil, ErrSessionDescriptionMissingIceUfrag
795-
} else if len(remotePwds) == 0 {
867+
} else if remotePwd == "" {
796868
return "", "", nil, ErrSessionDescriptionMissingIcePwd
797869
}
798870

799-
for _, m := range remoteUfrags {
800-
if m != remoteUfrags[0] {
801-
return "", "", nil, ErrSessionDescriptionConflictingIceUfrag
802-
}
803-
}
804-
805-
for _, m := range remotePwds {
806-
if m != remotePwds[0] {
807-
return "", "", nil, ErrSessionDescriptionConflictingIcePwd
808-
}
809-
}
810-
811-
return remoteUfrags[0], remotePwds[0], candidates, nil
871+
return remoteUfrag, remotePwd, remoteCandidates, nil
812872
}
813873

814874
func haveApplicationMediaSection(desc *sdp.SessionDescription) bool {

0 commit comments

Comments
 (0)