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

feat: support setting ICE ufrag and pwd, disabling fingerprint validation and specifying certificates #256

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
28 changes: 28 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ export interface RtcConfig {
bindAddress?: string;
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;
}

export const enum RelayType {
Expand Down Expand Up @@ -65,6 +72,27 @@ export const enum DescriptionType {
}
```

**setLocalDescription: (sdp: string, init?: LocalDescriptionInit) => void**

Set Local Description and optionally the ICE ufrag/pwd to use. These should not
be set as they will be generated automatically as per the spec.
```
export interface LocalDescriptionInit {
iceUfrag?: string;
icePwd?: string;
}
```

**remoteFingerprint: () => CertificateFingerprint**

Returns the certificate fingerprint used by the remote peer
```
export interface CertificateFingerprint {
value: string;
algorithm: 'sha-1' | 'sha-224' | 'sha-256' | 'sha-384' | 'sha-512' | 'md5' | 'md2';
}
```

**addRemoteCandidate: (candidate: string, mid: string) => void**

Add remote candidate info
Expand Down
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
1 change: 1 addition & 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
Loading