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 @@ -16,12 +16,19 @@ 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;
disableFingerprintVerification?: boolean;
certificatePemFile?: string;
keyPemFile?: string;
keyPemPass?: string;
}

export const enum RelayType {
Expand Down Expand Up @@ -69,6 +76,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
70 changes: 68 additions & 2 deletions src/cpp/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 @@ -214,6 +215,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 @@ -251,6 +256,17 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
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 @@ -331,6 +347,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 @@ -356,7 +373,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 @@ -1002,7 +1041,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/cpp/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
5 changes: 3 additions & 2 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import nodeDataChannel from './node-datachannel';
import _DataChannelStream from './datachannel-stream';
import { WebSocketServer } from './websocket-server';
import { Channel, DataChannelInitConfig, DescriptionType, Direction, LogLevel, RtcConfig, RTCIceConnectionState, RTCIceGatheringState, RTCPeerConnectionState, RTCSignalingState, SctpSettings, SelectedCandidateInfo } from './types';
import { CertificateFingerprint, Channel, DataChannelInitConfig, DescriptionType, Direction, LocalDescriptionInit, LogLevel, RtcConfig, RTCIceConnectionState, RTCIceGatheringState, RTCPeerConnectionState, RTCSignalingState, SctpSettings, SelectedCandidateInfo } from './types';
import { WebSocket } from './websocket';

export function preload(): void { nodeDataChannel.preload(); }
Expand Down Expand Up @@ -113,10 +113,11 @@ export const DataChannel: {

export interface PeerConnection {
close(): void;
setLocalDescription(type?: DescriptionType): void;
setLocalDescription(type?: DescriptionType, init?: LocalDescriptionInit): void;
setRemoteDescription(sdp: string, type: DescriptionType): void;
localDescription(): { type: DescriptionType; sdp: string } | null;
remoteDescription(): { type: DescriptionType; sdp: string } | null;
remoteFingerprint(): CertificateFingerprint;
addRemoteCandidate(candidate: string, mid: string): void;
createDataChannel(label: string, config?: DataChannelInitConfig): DataChannel;
addTrack(media: Video | Audio): Track;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/node-datachannel.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
let nodeDataChannel = require('../../build/Release/node_datachannel.node');
import nodeDataChannel = require('../../build/Release/node_datachannel.node');
export default nodeDataChannel;
19 changes: 19 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ export interface RtcConfig {
mtu?: number;
iceTransportPolicy?: TransportPolicy;
disableFingerprintVerification?: boolean;
disableAutoGathering?: boolean;
certificatePemFile?: string;
keyPemFile?: string;
keyPemPass?: string;
}

// Lowercase to match the description type string from libdatachannel
Expand All @@ -97,6 +101,10 @@ export type RTCIceGathererState = "complete" | "gathering" | "new";
export type RTCIceGatheringState = "complete" | "gathering" | "new";
export type RTCSignalingState = "closed" | "have-local-offer" | "have-local-pranswer" | "have-remote-offer" | "have-remote-pranswer" | "stable";

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

export interface DataChannelInitConfig {
protocol?: string;
Expand All @@ -107,6 +115,17 @@ export interface DataChannelInitConfig {
maxRetransmits?: number; // Reliability
}

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 interface SelectedCandidateInfo {
address: string;
port: number;
Expand Down
Loading