set proper peer dtls setup on ice restart received
This commit is contained in:
parent
0a3947b8e3
commit
70b5d8d81a
|
@ -303,16 +303,18 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final boolean isOffer = rtpContentMap.emptyCandidates();
|
final boolean isOffer = rtpContentMap.emptyCandidates();
|
||||||
if (isOffer) {
|
final RtpContentMap restartContentMap;
|
||||||
Log.d(Config.LOGTAG, "received offer to restart ICE " + newCredentials);
|
|
||||||
} else {
|
|
||||||
Log.d(Config.LOGTAG, "received confirmation of ICE restart" + newCredentials);
|
|
||||||
}
|
|
||||||
//TODO rewrite setup attribute
|
|
||||||
//https://groups.google.com/g/discuss-webrtc/c/DfpIMwvUfeM
|
|
||||||
//https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-dtls-sdp-15#section-5.5
|
|
||||||
final RtpContentMap restartContentMap = existing.modifiedCredentials(newCredentials);
|
|
||||||
try {
|
try {
|
||||||
|
if (isOffer) {
|
||||||
|
Log.d(Config.LOGTAG, "received offer to restart ICE " + newCredentials.values());
|
||||||
|
restartContentMap = existing.modifiedCredentials(newCredentials, IceUdpTransportInfo.Setup.ACTPASS);
|
||||||
|
} else {
|
||||||
|
final IceUdpTransportInfo.Setup setup = getPeerDtlsSetup();
|
||||||
|
Log.d(Config.LOGTAG, "received confirmation of ICE restart" + newCredentials.values()+" peer_setup="+setup);
|
||||||
|
// DTLS setup attribute needs to be rewritten to reflect current peer state
|
||||||
|
// https://groups.google.com/g/discuss-webrtc/c/DfpIMwvUfeM
|
||||||
|
restartContentMap = existing.modifiedCredentials(newCredentials, setup);
|
||||||
|
}
|
||||||
if (applyIceRestart(jinglePacket, restartContentMap, isOffer)) {
|
if (applyIceRestart(jinglePacket, restartContentMap, isOffer)) {
|
||||||
return isOffer;
|
return isOffer;
|
||||||
} else {
|
} else {
|
||||||
|
@ -329,6 +331,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IceUdpTransportInfo.Setup getPeerDtlsSetup() {
|
||||||
|
final IceUdpTransportInfo.Setup responderSetup = this.responderRtpContentMap.getDtlsSetup();
|
||||||
|
if (responderSetup == null || responderSetup == IceUdpTransportInfo.Setup.ACTPASS) {
|
||||||
|
throw new IllegalStateException("Invalid DTLS setup value in responder content map");
|
||||||
|
}
|
||||||
|
return isInitiator() ? responderSetup : responderSetup.flip();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean applyIceRestart(final JinglePacket jinglePacket, final RtpContentMap restartContentMap, final boolean isOffer) throws ExecutionException, InterruptedException {
|
private boolean applyIceRestart(final JinglePacket jinglePacket, final RtpContentMap restartContentMap, final boolean isOffer) throws ExecutionException, InterruptedException {
|
||||||
final SessionDescription sessionDescription = SessionDescription.of(restartContentMap);
|
final SessionDescription sessionDescription = SessionDescription.of(restartContentMap);
|
||||||
final org.webrtc.SessionDescription.Type type = isOffer ? org.webrtc.SessionDescription.Type.OFFER : org.webrtc.SessionDescription.Type.ANSWER;
|
final org.webrtc.SessionDescription.Type type = isOffer ? org.webrtc.SessionDescription.Type.OFFER : org.webrtc.SessionDescription.Type.ANSWER;
|
||||||
|
@ -1496,7 +1506,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
|
webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, "received failure to our ice restart");
|
Log.d(Config.LOGTAG, "received failure to our ice restart");
|
||||||
//TODO handle tie-break. Rollback?
|
//TODO ignore tie break (maybe rollback?)
|
||||||
|
//TODO handle other errors
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
@ -104,7 +105,8 @@ public class RtpContentMap {
|
||||||
if (fingerprint == null || Strings.isNullOrEmpty(fingerprint.getContent()) || Strings.isNullOrEmpty(fingerprint.getHash())) {
|
if (fingerprint == null || Strings.isNullOrEmpty(fingerprint.getContent()) || Strings.isNullOrEmpty(fingerprint.getHash())) {
|
||||||
throw new SecurityException(String.format("Use of DTLS-SRTP (XEP-0320) is required for content %s", entry.getKey()));
|
throw new SecurityException(String.format("Use of DTLS-SRTP (XEP-0320) is required for content %s", entry.getKey()));
|
||||||
}
|
}
|
||||||
if (Strings.isNullOrEmpty(fingerprint.getSetup())) {
|
final IceUdpTransportInfo.Setup setup = fingerprint.getSetup();
|
||||||
|
if (setup == null) {
|
||||||
throw new SecurityException(String.format("Use of DTLS-SRTP (XEP-0320) is required for content %s but missing setup attribute", entry.getKey()));
|
throw new SecurityException(String.format("Use of DTLS-SRTP (XEP-0320) is required for content %s but missing setup attribute", entry.getKey()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +150,14 @@ public class RtpContentMap {
|
||||||
return Maps.transformValues(contents, dt -> dt.transport.getCredentials());
|
return Maps.transformValues(contents, dt -> dt.transport.getCredentials());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IceUdpTransportInfo.Setup getDtlsSetup() {
|
||||||
|
final Set<IceUdpTransportInfo.Setup> setups = ImmutableSet.copyOf(Collections2.transform(
|
||||||
|
contents.values(),
|
||||||
|
dt->dt.transport.getFingerprint().getSetup()
|
||||||
|
));
|
||||||
|
return setups.size() == 1 ? Iterables.getFirst(setups, null) : null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean emptyCandidates() {
|
public boolean emptyCandidates() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (DescriptionTransport descriptionTransport : contents.values()) {
|
for (DescriptionTransport descriptionTransport : contents.values()) {
|
||||||
|
@ -156,13 +166,13 @@ public class RtpContentMap {
|
||||||
return count == 0;
|
return count == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RtpContentMap modifiedCredentials(Map<String, IceUdpTransportInfo.Credentials> credentialsMap) {
|
public RtpContentMap modifiedCredentials(Map<String, IceUdpTransportInfo.Credentials> credentialsMap, final IceUdpTransportInfo.Setup setup) {
|
||||||
final ImmutableMap.Builder<String, DescriptionTransport> contentMapBuilder = new ImmutableMap.Builder<>();
|
final ImmutableMap.Builder<String, DescriptionTransport> contentMapBuilder = new ImmutableMap.Builder<>();
|
||||||
for (final Map.Entry<String, DescriptionTransport> content : contents.entrySet()) {
|
for (final Map.Entry<String, DescriptionTransport> content : contents.entrySet()) {
|
||||||
final RtpDescription rtpDescription = content.getValue().description;
|
final RtpDescription rtpDescription = content.getValue().description;
|
||||||
IceUdpTransportInfo transportInfo = content.getValue().transport;
|
IceUdpTransportInfo transportInfo = content.getValue().transport;
|
||||||
final IceUdpTransportInfo.Credentials credentials = Objects.requireNonNull(credentialsMap.get(content.getKey()));
|
final IceUdpTransportInfo.Credentials credentials = Objects.requireNonNull(credentialsMap.get(content.getKey()));
|
||||||
final IceUdpTransportInfo modifiedTransportInfo = transportInfo.modifyCredentials(credentials);
|
final IceUdpTransportInfo modifiedTransportInfo = transportInfo.modifyCredentials(credentials, setup);
|
||||||
contentMapBuilder.put(content.getKey(), new DescriptionTransport(rtpDescription, modifiedTransportInfo));
|
contentMapBuilder.put(content.getKey(), new DescriptionTransport(rtpDescription, modifiedTransportInfo));
|
||||||
}
|
}
|
||||||
return new RtpContentMap(this.group, contentMapBuilder.build());
|
return new RtpContentMap(this.group, contentMapBuilder.build());
|
||||||
|
|
|
@ -156,7 +156,10 @@ public class SessionDescription {
|
||||||
final IceUdpTransportInfo.Fingerprint fingerprint = transport.getFingerprint();
|
final IceUdpTransportInfo.Fingerprint fingerprint = transport.getFingerprint();
|
||||||
if (fingerprint != null) {
|
if (fingerprint != null) {
|
||||||
mediaAttributes.put("fingerprint", fingerprint.getHash() + " " + fingerprint.getContent());
|
mediaAttributes.put("fingerprint", fingerprint.getHash() + " " + fingerprint.getContent());
|
||||||
mediaAttributes.put("setup", fingerprint.getSetup());
|
final IceUdpTransportInfo.Setup setup = fingerprint.getSetup();
|
||||||
|
if (setup != null) {
|
||||||
|
mediaAttributes.put("setup", setup.toString().toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final ImmutableList.Builder<Integer> formatBuilder = new ImmutableList.Builder<>();
|
final ImmutableList.Builder<Integer> formatBuilder = new ImmutableList.Builder<>();
|
||||||
for (RtpDescription.PayloadType payloadType : description.getPayloadTypes()) {
|
for (RtpDescription.PayloadType payloadType : description.getPayloadTypes()) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
|
@ -83,11 +84,19 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
|
||||||
return transportInfo;
|
return transportInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IceUdpTransportInfo modifyCredentials(Credentials credentials) {
|
public IceUdpTransportInfo modifyCredentials(final Credentials credentials, final Setup setup) {
|
||||||
final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
|
final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
|
||||||
transportInfo.setAttribute("ufrag", credentials.ufrag);
|
transportInfo.setAttribute("ufrag", credentials.ufrag);
|
||||||
transportInfo.setAttribute("pwd", credentials.password);
|
transportInfo.setAttribute("pwd", credentials.password);
|
||||||
transportInfo.setChildren(getChildren());
|
for (final Element child : getChildren()) {
|
||||||
|
if (child.getName().equals("fingerprint") && Namespace.JINGLE_APPS_DTLS.equals(child.getNamespace())) {
|
||||||
|
final Fingerprint fingerprint = new Fingerprint();
|
||||||
|
fingerprint.setAttributes(new Hashtable<>(child.getAttributes()));
|
||||||
|
fingerprint.setContent(child.getContent());
|
||||||
|
fingerprint.setAttribute("setup", setup.toString().toLowerCase(Locale.ROOT));
|
||||||
|
transportInfo.addChild(fingerprint);
|
||||||
|
}
|
||||||
|
}
|
||||||
return transportInfo;
|
return transportInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,8 +346,31 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
|
||||||
return this.getAttribute("hash");
|
return this.getAttribute("hash");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSetup() {
|
public Setup getSetup() {
|
||||||
return this.getAttribute("setup");
|
final String setup = this.getAttribute("setup");
|
||||||
|
return setup == null ? null : Setup.of(setup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Setup {
|
||||||
|
ACTPASS, PASSIVE, ACTIVE;
|
||||||
|
|
||||||
|
public static Setup of(String setup) {
|
||||||
|
try {
|
||||||
|
return valueOf(setup.toUpperCase(Locale.ROOT));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Setup flip() {
|
||||||
|
if (this == PASSIVE) {
|
||||||
|
return ACTIVE;
|
||||||
|
}
|
||||||
|
if (this == ACTIVE) {
|
||||||
|
return PASSIVE;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(this.name()+" can not be flipped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue