Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable28] fix: Check media direction in internal signaling messages #13493

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions lib/Controller/SignalingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,23 @@ public function sendMessages(string $token, string $messages): DataResponse {
if (!$participant->hasModeratorPermissions(false)) {
break;
}
} elseif ($decodedMessage['type'] === 'offer' || $decodedMessage['type'] === 'answer') {
$room = $this->manager->getRoomForSession($this->userId, $message['sessionId']);
$participant = $this->participantService->getParticipantBySession($room, $message['sessionId']);

if (!($participant->getPermissions() & Attendee::PERMISSIONS_PUBLISH_AUDIO) && $decodedMessage['roomType'] === 'video'
&& $this->isTryingToPublishMedia($decodedMessage['payload']['sdp'], 'audio')) {
break;
}
if (!($participant->getPermissions() & Attendee::PERMISSIONS_PUBLISH_VIDEO) && $decodedMessage['roomType'] === 'video'
&& $this->isTryingToPublishMedia($decodedMessage['payload']['sdp'], 'video')) {
break;
}
if (!($participant->getPermissions() & Attendee::PERMISSIONS_PUBLISH_SCREEN) && $decodedMessage['roomType'] === 'screen'
&& ($this->isTryingToPublishMedia($decodedMessage['payload']['sdp'], 'audio')
|| $this->isTryingToPublishMedia($decodedMessage['payload']['sdp'], 'video'))) {
break;
}
}

$this->messages->addMessage($message['sessionId'], $decodedMessage['to'], json_encode($decodedMessage));
Expand All @@ -356,6 +373,84 @@ public function sendMessages(string $token, string $messages): DataResponse {
return new DataResponse($response);
}

/**
* Returns whether the SDP is trying to publish the given media based on the
* media direction.
*
* The SDP is trying to publish if the related media description contains a
* media direction of either "sendrecv" or "sendonly". If no media direction
* is provided in a media description the media direction in the session
* description is used instead. If that is not provided either then
* "sendrecv" is assumed.
*
* See https://www.rfc-editor.org/rfc/rfc8866.html#name-media-direction-attributes
*
* @param string $sdp the SDP to check
* @param string $media the media to check, either "audio" or "video"
* @return bool true if it is trying to publish, false otherwise
*/
private function isTryingToPublishMedia(string $sdp, string $media): bool {
$lines = preg_split('/\r\n|\n|\r/', $sdp);

$sessionMediaDirectionIndex = -1;
$mediaDirectionIndex = -1;
$mediaDescriptionIndex = -1;
$matchingMediaDescriptionIndex = -1;

for ($i = 0; $i < count($lines); $i++) {
if (strpos($lines[$i], 'a=sendrecv') === 0
|| strpos($lines[$i], 'a=sendonly') === 0
|| strpos($lines[$i], 'a=recvonly') === 0
|| strpos($lines[$i], 'a=inactive') === 0) {
$mediaDirectionIndex = $i;

if ($mediaDescriptionIndex < 0) {
$sessionMediaDirectionIndex = $mediaDirectionIndex;
}

if ($matchingMediaDescriptionIndex >= 0
&& $matchingMediaDescriptionIndex >= $mediaDescriptionIndex
&& $mediaDirectionIndex > $matchingMediaDescriptionIndex
&& (strpos($lines[$mediaDirectionIndex], 'a=sendrecv') === 0
|| strpos($lines[$mediaDirectionIndex], 'a=sendonly') === 0)) {
return true;
}
} elseif (strpos($lines[$i], 'm=') === 0) {
// No media direction in previous matching media description,
// fallback to media direction in the session description or, if
// not set, default to "sendrecv".
if ($matchingMediaDescriptionIndex >= 0
&& $matchingMediaDescriptionIndex >= $mediaDescriptionIndex
&& $mediaDirectionIndex < $matchingMediaDescriptionIndex
&& ($sessionMediaDirectionIndex < 0
|| strpos($lines[$sessionMediaDirectionIndex], 'a=sendrecv') === 0
|| strpos($lines[$sessionMediaDirectionIndex], 'a=sendonly') === 0)) {
return true;
}

$mediaDescriptionIndex = $i;

if (strpos($lines[$i], 'm=' . $media) === 0) {
$matchingMediaDescriptionIndex = $i;
}
}
}

// No media direction in last matching media description, fallback to
// media direction in the session description or, if not set, default to
// "sendrecv".
if ($matchingMediaDescriptionIndex >= 0
&& $matchingMediaDescriptionIndex >= $mediaDescriptionIndex
&& $mediaDirectionIndex < $matchingMediaDescriptionIndex
&& ($sessionMediaDirectionIndex < 0
|| strpos($lines[$sessionMediaDirectionIndex], 'a=sendrecv') === 0
|| strpos($lines[$sessionMediaDirectionIndex], 'a=sendonly') === 0)) {
return true;
}

return false;
}

/**
* Get signaling messages
*
Expand Down
168 changes: 168 additions & 0 deletions tests/php/Controller/SignalingControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,174 @@ private function recreateSignalingController() {
);
}

public static function dataIsTryingToPublishMedia(): array {
// For simplicity the SDP contains only the relevant fields and it is
// not a valid SDP
return [
// Audio publisher/receiver
[
"m=audio 42108 RTP/AVP 0\n" .
"a=sendrecv\n",
true, false,
],
// Audio publisher/receiver with data channel
[
"m=audio 42108 RTP/AVP 0\n" .
"a=sendrecv\n" .
"m=application 8 UDP/DTLS/SCTP webrtc-datachannel\n" .
"a=sendrecv\n",
true, false,
],
// Video publisher
[
"m=video 42108 RTP/AVP 0\n" .
"a=sendonly\n",
false, true,
],
// Video publisher with data channel
[
"m=video 42108 RTP/AVP 0\n" .
"a=sendonly\n" .
"m=application 8 UDP/DTLS/SCTP webrtc-datachannel\n" .
"a=sendrecv\n",
false, true,
],
// Audio and video publisher/receiver
[
"m=audio 42108 RTP/AVP 0\n" .
"a=sendrecv\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=sendrecv\n",
true, true,
],
// Audio and video publisher/receiver with data channel
[
"m=audio 42108 RTP/AVP 0\n" .
"a=sendrecv\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=sendrecv\n" .
"m=application 8 UDP/DTLS/SCTP webrtc-datachannel\n" .
"a=sendrecv\n",
true, true,
],
// Audio and video receiver with data channel
[
"m=audio 42108 RTP/AVP 0\n" .
"a=recvonly\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=recvonly\n" .
"m=application 8 UDP/DTLS/SCTP webrtc-datachannel\n" .
"a=sendrecv\n",
false, false,
],
// Audio receiver and video inactive
[
"m=audio 42108 RTP/AVP 0\n" .
"a=recvonly\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=inactive\n",
false, false,
],
// Audio receiver with session publisher/receiver direction
[
"a=sendrecv\n" .
"m=audio 42108 RTP/AVP 0\n" .
"a=recvonly\n",
false, false,
],
// Video inactive with session publisher direction and data channel
[
"a=sendonly\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=inactive\n" .
"m=application 8 UDP/DTLS/SCTP webrtc-datachannel\n" .
"a=sendrecv\n",
false, false,
],
// Audio and video with session publisher/receiver direction
[
"a=sendrecv\n" .
"m=audio 42108 RTP/AVP 0\n" .
"m=video 42108 RTP/AVP 0\n",
true, true,
],
// Audio and video with session publisher direction and data channel
[
"a=sendonly\n" .
"m=audio 42108 RTP/AVP 0\n" .
"m=video 42108 RTP/AVP 0\n" .
"m=application 8 UDP/DTLS/SCTP webrtc-datachannel\n" .
"a=sendrecv\n",
true, true,
],
// Audio and video with session receiver direction and data channel
[
"a=recvonly\n" .
"m=audio 42108 RTP/AVP 0\n" .
"m=video 42108 RTP/AVP 0\n" .
"m=application 8 UDP/DTLS/SCTP webrtc-datachannel\n" .
"a=sendrecv\n",
false, false,
],
// Audio and video with implicit publisher/receiver direction
[
"m=audio 42108 RTP/AVP 0\n" .
"m=video 42108 RTP/AVP 0\n",
true, true,
],
// Audio and video with implicit publisher/receiver direction and
// data channel
[
"m=audio 42108 RTP/AVP 0\n" .
"m=video 42108 RTP/AVP 0\n" .
"m=application 8 UDP/DTLS/SCTP webrtc-datachannel\n",
true, true,
],
// No audio and video description with session publisher direction
[
"a=sendonly\n",
false, false,
],
// Several audio and video with mixed directions
[
"m=audio 42108 RTP/AVP 0\n" .
"a=inactive\n" .
"m=audio 42108 RTP/AVP 0\n" .
"a=sendrecv\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=recvonly\n" .
"m=audio 42108 RTP/AVP 0\n" .
"a=recvonly\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=recvonly\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=inactive\n",
true, false,
],
// Several mixed directions in a single media description (not a
// valid SDP, but just in case)
[
"m=audio 42108 RTP/AVP 0\n" .
"a=inactive\n" .
"a=sendrecv\n" .
"a=recvonly\n" .
"m=video 42108 RTP/AVP 0\n" .
"a=sendrecv\n" .
"a=recvonly\n" .
"a=inactive\n",
true, true,
],
];
}

/**
* @dataProvider dataIsTryingToPublishMedia
*/
public function testIsTryingToPublishMedia(string $sdp, bool $expectedAudioResult, bool $expectedVideoResult) {
$this->assertSame($expectedAudioResult, self::invokePrivate($this->controller, 'isTryingToPublishMedia', [$sdp, 'audio']));
$this->assertSame($expectedVideoResult, self::invokePrivate($this->controller, 'isTryingToPublishMedia', [$sdp, 'video']));
}

private function validateBackendRandom($data, $random, $checksum) {
if (empty($random) || strlen($random) < 32) {
return false;
Expand Down
Loading