From 96eb58bcc3071986e0a5dd26cbcef4c37954a66e Mon Sep 17 00:00:00 2001 From: achingbrain Date: Fri, 7 Jun 2024 14:58:26 +0100 Subject: [PATCH] feat: update current remote description with fingerprint Adds a `currentRemoteDescription` field to `PeerConnection` which can be updated independently of `remoteDescription`, the idea being the `remoteDescription` is the offer received from the remote peer and the `currentRemoteDescription` is what has actually been negotiated with them. The `currentRemoteDescription` is initialised when the `remoteDescription` is set. Currently the `checkFingerprint` method updates the certificate fingerprint used by the connection to the remote peer but I guess other modifications may be useful. Exposes the `currentRemoteDescription` via a getter similar to `remoteDescription`. --- DOC.md | 18 ++++++++++++++ include/rtc/peerconnection.hpp | 1 + include/rtc/rtc.h | 1 + src/capi.cpp | 11 +++++++++ src/impl/peerconnection.cpp | 43 +++++++++++++++++++++++++++++----- src/impl/peerconnection.hpp | 8 ++++--- src/peerconnection.cpp | 5 ++++ 7 files changed, 78 insertions(+), 9 deletions(-) diff --git a/DOC.md b/DOC.md index 50ad1758b..c84e5e4de 100644 --- a/DOC.md +++ b/DOC.md @@ -278,6 +278,24 @@ Return value: the length of the string copied in buffer (including the terminati If `buffer` is `NULL`, the description is not copied but the size is still returned. +#### rtcGetCurrentRemoteDescription + +``` +int rtcGetCurrentRemoteDescription(int pc, char *buffer, int size) +``` + +Retrieves the currently negotiated remote description in SDP format. + +Arguments: + +- `pc`: the Peer Connection identifier +- `buffer`: a user-supplied buffer to store the description +- `size`: the size of `buffer` + +Return value: the length of the string copied in buffer (including the terminating null character) or a negative error code + +If `buffer` is `NULL`, the description is not copied but the size is still returned. + #### rtcGetLocalDescriptionType ``` diff --git a/include/rtc/peerconnection.hpp b/include/rtc/peerconnection.hpp index 9e49f80e1..b4a6a3b45 100644 --- a/include/rtc/peerconnection.hpp +++ b/include/rtc/peerconnection.hpp @@ -84,6 +84,7 @@ class RTC_CPP_EXPORT PeerConnection final : CheshireCat { bool hasMedia() const; optional localDescription() const; optional remoteDescription() const; + optional currentRemoteDescription() const; size_t remoteMaxMessageSize() const; optional localAddress() const; optional remoteAddress() const; diff --git a/include/rtc/rtc.h b/include/rtc/rtc.h index 1476b6a9a..0ac77df75 100644 --- a/include/rtc/rtc.h +++ b/include/rtc/rtc.h @@ -216,6 +216,7 @@ RTC_C_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid RTC_C_EXPORT int rtcGetLocalDescription(int pc, char *buffer, int size); RTC_C_EXPORT int rtcGetRemoteDescription(int pc, char *buffer, int size); +RTC_C_EXPORT int rtcGetCurrentRemoteDescription(int pc, char *buffer, int size); RTC_C_EXPORT int rtcGetLocalDescriptionType(int pc, char *buffer, int size); RTC_C_EXPORT int rtcGetRemoteDescriptionType(int pc, char *buffer, int size); diff --git a/src/capi.cpp b/src/capi.cpp index 6f8c790d2..1813a7f49 100644 --- a/src/capi.cpp +++ b/src/capi.cpp @@ -609,6 +609,17 @@ int rtcGetRemoteDescription(int pc, char *buffer, int size) { }); } +int rtcGetCurrentRemoteDescription(int pc, char *buffer, int size) { + return wrap([&] { + auto peerConnection = getPeerConnection(pc); + + if (auto desc = peerConnection->currentRemoteDescription()) + return copyAndReturn(string(*desc), buffer, size); + else + return RTC_ERR_NOT_AVAIL; + }); +} + int rtcGetLocalDescriptionType(int pc, char *buffer, int size) { return wrap([&] { auto peerConnection = getPeerConnection(pc); diff --git a/src/impl/peerconnection.cpp b/src/impl/peerconnection.cpp index 1d3611b2b..584f711b0 100644 --- a/src/impl/peerconnection.cpp +++ b/src/impl/peerconnection.cpp @@ -118,6 +118,11 @@ optional PeerConnection::remoteDescription() const { return mRemoteDescription; } +optional PeerConnection::currentRemoteDescription() const { + std::lock_guard lock(mCurrentRemoteDescriptionMutex); + return mCurrentRemoteDescription; +} + size_t PeerConnection::remoteMaxMessageSize() const { const size_t localMax = config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE); @@ -239,7 +244,7 @@ shared_ptr PeerConnection::initDtlsTransport() { throw std::logic_error("No underlying ICE transport for DTLS transport"); auto certificate = mCertificate.get(); - auto verifierCallback = weak_bind(&PeerConnection::checkFingerprint, this, _1); + auto verifierCallback = weak_bind(&PeerConnection::checkFingerprint, this, _1, fingerprintAlgorithm); auto dtlsStateChangeCallback = [this, weak_this = weak_from_this()](DtlsTransport::State transportState) { auto shared_this = weak_this.lock(); @@ -439,17 +444,21 @@ void PeerConnection::rollbackLocalDescription() { } } -bool PeerConnection::checkFingerprint(const std::string &fingerprint) const { +bool PeerConnection::checkFingerprint(const std::string &fingerprint, const CertificateFingerprint::Algorithm algorithm) { std::lock_guard lock(mRemoteDescriptionMutex); if (!mRemoteDescription || !mRemoteDescription->fingerprint()) return false; - if (config.disableFingerprintVerification) - return true; - auto expectedFingerprint = mRemoteDescription->fingerprint()->value; - if (expectedFingerprint == fingerprint) { + if (config.disableFingerprintVerification || expectedFingerprint == fingerprint) { PLOG_VERBOSE << "Valid fingerprint \"" << fingerprint << "\""; + + if (mCurrentRemoteDescription) { + std::lock_guard lock(mCurrentRemoteDescriptionMutex); + // update current remote description with incoming fingerprint + mCurrentRemoteDescription->setFingerprint(CertificateFingerprint{algorithm, fingerprint}); + } + return true; } @@ -1078,6 +1087,20 @@ void PeerConnection::processLocalCandidate(Candidate candidate) { &localCandidateCallback, std::move(candidate)); } +void PeerConnection::processCurrentRemoteDescription(Description description) { + { + // Set as current remote description + std::lock_guard lock(mCurrentRemoteDescriptionMutex); + + std::vector existingCandidates; + if (mCurrentRemoteDescription) + existingCandidates = mCurrentRemoteDescription->extractCandidates(); + + mCurrentRemoteDescription.emplace(description); + mCurrentRemoteDescription->addCandidates(std::move(existingCandidates)); + } +} + void PeerConnection::processRemoteDescription(Description description) { // Update the SSRC cache for existing tracks updateTrackSsrcCache(description); @@ -1124,6 +1147,14 @@ void PeerConnection::processRemoteCandidate(Candidate candidate) { candidate.resolve(Candidate::ResolveMode::Simple); mRemoteDescription->addCandidate(candidate); } + { + // Set as remote candidate on current remote description too + std::lock_guard lock(mCurrentRemoteDescriptionMutex); + if (mCurrentRemoteDescription->hasCandidate(candidate)) + return; // already in description, ignore + + mCurrentRemoteDescription->addCandidate(candidate); + } if (candidate.isResolved()) { iceTransport->addRemoteCandidate(std::move(candidate)); diff --git a/src/impl/peerconnection.hpp b/src/impl/peerconnection.hpp index 33dba4408..962296f1c 100644 --- a/src/impl/peerconnection.hpp +++ b/src/impl/peerconnection.hpp @@ -41,6 +41,7 @@ struct PeerConnection : std::enable_shared_from_this { optional localDescription() const; optional remoteDescription() const; + optional currentRemoteDescription() const; size_t remoteMaxMessageSize() const; shared_ptr initIceTransport(); @@ -53,7 +54,7 @@ struct PeerConnection : std::enable_shared_from_this { void endLocalCandidates(); void rollbackLocalDescription(); - bool checkFingerprint(const std::string &fingerprint) const; + bool checkFingerprint(const std::string &fingerprint, const CertificateFingerprint::Algorithm algorithm); void forwardMessage(message_ptr message); void forwardMedia(message_ptr message); void forwardBufferedAmount(uint16_t stream, size_t amount); @@ -77,6 +78,7 @@ struct PeerConnection : std::enable_shared_from_this { void processLocalDescription(Description description); void processLocalCandidate(Candidate candidate); void processRemoteDescription(Description description); + void processCurrentRemoteDescription(Description description); void processRemoteCandidate(Candidate candidate); string localBundleMid() const; @@ -135,8 +137,8 @@ struct PeerConnection : std::enable_shared_from_this { Processor mProcessor; optional mLocalDescription, mRemoteDescription; - optional mCurrentLocalDescription; - mutable std::mutex mLocalDescriptionMutex, mRemoteDescriptionMutex; + optional mCurrentLocalDescription, mCurrentRemoteDescription; + mutable std::mutex mLocalDescriptionMutex, mRemoteDescriptionMutex, mCurrentRemoteDescriptionMutex; shared_ptr mMediaHandler; diff --git a/src/peerconnection.cpp b/src/peerconnection.cpp index 0e146834b..fcc50e1c6 100644 --- a/src/peerconnection.cpp +++ b/src/peerconnection.cpp @@ -69,6 +69,10 @@ optional PeerConnection::remoteDescription() const { return impl()->remoteDescription(); } +optional PeerConnection::currentRemoteDescription() const { + return impl()->currentRemoteDescription(); +} + size_t PeerConnection::remoteMaxMessageSize() const { return impl()->remoteMaxMessageSize(); } bool PeerConnection::hasMedia() const { @@ -243,6 +247,7 @@ void PeerConnection::setRemoteDescription(Description description) { iceTransport->setRemoteDescription(description); // ICE transport might reject the description impl()->processRemoteDescription(std::move(description)); + impl()->processCurrentRemoteDescription(std::move(description)); impl()->changeSignalingState(newSignalingState); signalingLock.unlock();