Skip to content

Commit

Permalink
feat: support setting ICE ufrag and pwd, and missing config values
Browse files Browse the repository at this point in the history
Updates the implementation to match the latest libdatachannel with
features for setting ICE ufrag/pwd and reading the remote cert
fingerprint.

Also adds pass-through for missing config values.
  • Loading branch information
achingbrain committed Jun 12, 2024
1 parent a862c23 commit 4cb6ad1
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 3 deletions.
24 changes: 23 additions & 1 deletion lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,17 @@ export interface RtcConfig {
enableIceTcp?: boolean;
enableIceUdpMux?: boolean;
disableAutoNegotiation?: boolean;
disableFingerprintVerification?: boolean;
disableAutoGathering?: boolean;
forceMediaTransport?: boolean;
portRangeBegin?: number;
portRangeEnd?: number;
maxMessageSize?: number;
mtu?: number;
iceTransportPolicy?: TransportPolicy;
certificatePemFile?: string;
keyPemFile?: string;
keyPemPass?: string;
}

// Lowercase to match the description type string from libdatachannel
Expand All @@ -77,6 +82,11 @@ export const enum ReliabilityType {
Timed = 2,
}

export interface LocalDescriptionInit {
iceUfrag?: string;
icePwd?: string;
}

export interface DataChannelInitConfig {
protocol?: string;
negotiated?: boolean;
Expand Down Expand Up @@ -277,13 +287,25 @@ export class WebSocketServer {
onClient(cb: (ws: WebSocket) => void): void;
}

export interface CertificateFingerprint {
/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/RTCCertificate/getFingerprints#value
*/
value: string;
/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/RTCCertificate/getFingerprints#algorithm
*/
algorithm: 'sha-1' | 'sha-224' | 'sha-256' | 'sha-384' | 'sha-512' | 'md5' | 'md2';
}

export class PeerConnection {
constructor(peerName: string, config: RtcConfig);
close(): void;
setLocalDescription(type?: DescriptionType): void;
setLocalDescription(type?: DescriptionType, init?: LocalDescriptionInit): void;
setRemoteDescription(sdp: string, type: DescriptionType): void;
localDescription(): { type: string; sdp: string } | null;
remoteDescription(): { type: string; sdp: string } | null;
remoteFingerprint(): CertificateFingerprint;
addRemoteCandidate(candidate: string, mid: string): void;
createDataChannel(label: string, config?: DataChannelInitConfig): DataChannel;
addTrack(media: Video | Audio): Track;
Expand Down
75 changes: 73 additions & 2 deletions src/peer-connection-wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Napi::Object PeerConnectionWrapper::Init(Napi::Env env, Napi::Object exports)
InstanceMethod("setRemoteDescription", &PeerConnectionWrapper::setRemoteDescription),
InstanceMethod("localDescription", &PeerConnectionWrapper::localDescription),
InstanceMethod("remoteDescription", &PeerConnectionWrapper::remoteDescription),
InstanceMethod("remoteFingerprint", &PeerConnectionWrapper::remoteFingerprint),
InstanceMethod("addRemoteCandidate", &PeerConnectionWrapper::addRemoteCandidate),
InstanceMethod("createDataChannel", &PeerConnectionWrapper::createDataChannel),
InstanceMethod("addTrack", &PeerConnectionWrapper::addTrack),
Expand Down Expand Up @@ -202,6 +203,10 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
if (config.Get("disableAutoNegotiation").IsBoolean())
rtcConfig.disableAutoNegotiation = config.Get("disableAutoNegotiation").As<Napi::Boolean>();

// disableAutoGathering option
if (config.Get("disableAutoGathering").IsBoolean())
rtcConfig.disableAutoGathering = config.Get("disableAutoGathering").As<Napi::Boolean>();

// forceMediaTransport option
if (config.Get("forceMediaTransport").IsBoolean())
rtcConfig.forceMediaTransport = config.Get("forceMediaTransport").As<Napi::Boolean>();
Expand Down Expand Up @@ -234,6 +239,22 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
}
}

// Allow skipping fingerprint validation
if (config.Get("disableFingerprintVerification").IsBoolean()) {
rtcConfig.disableFingerprintVerification = config.Get("disableFingerprintVerification").As<Napi::Boolean>();
}

// Specify certificate to use if set
if (config.Get("certificatePemFile").IsString()) {
rtcConfig.certificatePemFile = config.Get("certificatePemFile").As<Napi::String>().ToString();
}
if (config.Get("keyPemFile").IsString()) {
rtcConfig.keyPemFile = config.Get("keyPemFile").As<Napi::String>().ToString();
}
if (config.Get("keyPemPass").IsString()) {
rtcConfig.keyPemPass = config.Get("keyPemPass").As<Napi::String>().ToString();
}

// Create peer-connection
try
{
Expand Down Expand Up @@ -314,6 +335,7 @@ void PeerConnectionWrapper::setLocalDescription(const Napi::CallbackInfo &info)
}

rtc::Description::Type type = rtc::Description::Type::Unspec;
rtc::LocalDescriptionInit init;

// optional
if (length > 0)
Expand All @@ -339,7 +361,29 @@ void PeerConnectionWrapper::setLocalDescription(const Napi::CallbackInfo &info)
type = rtc::Description::Type::Rollback;
}

mRtcPeerConnPtr->setLocalDescription(type);
// optional
if (length > 1)
{
PLOG_DEBUG << "setLocalDescription() called with LocalDescriptionInit";

if (info[1].IsObject())
{
PLOG_DEBUG << "setLocalDescription() called with LocalDescriptionInit as object";
Napi::Object obj = info[1].As<Napi::Object>();

if (obj.Get("iceUfrag").IsString()) {
PLOG_DEBUG << "setLocalDescription() has ufrag";
init.iceUfrag = obj.Get("iceUfrag").As<Napi::String>();
}

if (obj.Get("icePwd").IsString()) {
PLOG_DEBUG << "setLocalDescription() has password";
init.icePwd = obj.Get("icePwd").As<Napi::String>();
}
}
}

mRtcPeerConnPtr->setLocalDescription(type, init);
}

void PeerConnectionWrapper::setRemoteDescription(const Napi::CallbackInfo &info)
Expand Down Expand Up @@ -1049,7 +1093,34 @@ Napi::Value PeerConnectionWrapper::maxMessageSize(const Napi::CallbackInfo &info

try
{
return Napi::Number::New(env, mRtcPeerConnPtr->remoteMaxMessageSize());
return Napi::Array::New(env, mRtcPeerConnPtr->remoteMaxMessageSize());
}
catch (std::exception &ex)
{
Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
return Napi::Number::New(info.Env(), 0);
}
}

Napi::Value PeerConnectionWrapper::remoteFingerprint(const Napi::CallbackInfo &info)
{
PLOG_DEBUG << "remoteFingerprints() called";
Napi::Env env = info.Env();

if (!mRtcPeerConnPtr)
{
return Napi::Number::New(info.Env(), 0);
}

try
{
auto fingerprint = mRtcPeerConnPtr->remoteFingerprint();

Napi::Object fingerprintObject = Napi::Object::New(env);
fingerprintObject.Set("value", fingerprint.value);
fingerprintObject.Set("algorithm", rtc::CertificateFingerprint::AlgorithmIdentifier(fingerprint.algorithm));

return fingerprintObject;
}
catch (std::exception &ex)
{
Expand Down
2 changes: 2 additions & 0 deletions src/peer-connection-wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class PeerConnectionWrapper : public Napi::ObjectWrap<PeerConnectionWrapper>
Napi::Value iceState(const Napi::CallbackInfo &info);
Napi::Value signalingState(const Napi::CallbackInfo &info);
Napi::Value gatheringState(const Napi::CallbackInfo &info);
Napi::Value remoteFingerprint(const Napi::CallbackInfo &info);

// Callbacks
void onLocalDescription(const Napi::CallbackInfo &info);
Expand Down Expand Up @@ -81,6 +82,7 @@ class PeerConnectionWrapper : public Napi::ObjectWrap<PeerConnectionWrapper>
// Helpers
std::string candidateTypeToString(const rtc::Candidate::Type &type);
std::string candidateTransportTypeToString(const rtc::Candidate::TransportType &transportType);
void addToIceServerList(Napi::Env env, Napi::Value obj, std::vector<rtc::IceServer> list);
};

#endif // PEER_CONNECTION_WRAPPER_H

0 comments on commit 4cb6ad1

Please sign in to comment.