node-rtc-connection 1.0.18 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -85
- package/dist/index.cjs +20 -5421
- package/dist/index.mjs +25 -5413
- package/dist/types/crypto/der.d.ts +107 -0
- package/dist/types/crypto/x509.d.ts +56 -0
- package/dist/types/datachannel/RTCDataChannel.d.ts +179 -0
- package/dist/types/dtls/RTCCertificate.d.ts +163 -0
- package/dist/types/dtls/cipher.d.ts +81 -0
- package/dist/types/dtls/connection.d.ts +81 -0
- package/dist/types/dtls/prf.d.ts +29 -0
- package/dist/types/dtls/protocol.d.ts +127 -0
- package/dist/types/foundation/ByteBufferQueue.d.ts +71 -0
- package/dist/types/foundation/RTCError.d.ts +152 -0
- package/dist/types/ice/RTCIceCandidate.d.ts +161 -0
- package/dist/types/ice/ice-agent.d.ts +154 -0
- package/dist/types/ice/stun-message.d.ts +92 -0
- package/dist/types/index.d.ts +29 -0
- package/dist/types/peerconnection/RTCPeerConnection.d.ts +74 -0
- package/dist/types/sctp/association.d.ts +77 -0
- package/dist/types/sctp/chunks.d.ts +200 -0
- package/dist/types/sctp/crc32c.d.ts +24 -0
- package/dist/types/sctp/datachannel-manager.d.ts +51 -0
- package/dist/types/sctp/dcep.d.ts +56 -0
- package/dist/types/sdp/RTCSessionDescription.d.ts +73 -0
- package/dist/types/sdp/sdp-utils.d.ts +103 -0
- package/dist/types/stun/stun-client.d.ts +119 -0
- package/dist/types/transport-stack.d.ts +68 -0
- package/package.json +26 -21
- package/src/crypto/der.ts +205 -0
- package/src/crypto/x509.ts +146 -0
- package/src/datachannel/RTCDataChannel.ts +388 -0
- package/src/dtls/RTCCertificate.ts +396 -0
- package/src/dtls/cipher.ts +198 -0
- package/src/dtls/connection.ts +974 -0
- package/src/dtls/prf.ts +62 -0
- package/src/dtls/protocol.ts +204 -0
- package/src/foundation/{ByteBufferQueue.js → ByteBufferQueue.ts} +74 -72
- package/src/foundation/{RTCError.js → RTCError.ts} +110 -60
- package/src/ice/{RTCIceCandidate.js → RTCIceCandidate.ts} +140 -92
- package/src/ice/ice-agent.ts +609 -0
- package/src/ice/stun-message.ts +260 -0
- package/src/index.ts +72 -0
- package/src/peerconnection/RTCPeerConnection.ts +430 -0
- package/src/sctp/association.ts +523 -0
- package/src/sctp/chunks.ts +350 -0
- package/src/sctp/crc32c.ts +57 -0
- package/src/sctp/datachannel-manager.ts +187 -0
- package/src/sctp/dcep.ts +94 -0
- package/src/sdp/{RTCSessionDescription.js → RTCSessionDescription.ts} +42 -29
- package/src/sdp/sdp-utils.ts +229 -0
- package/src/stun/stun-client.ts +936 -0
- package/src/transport-stack.ts +165 -0
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/src/datachannel/RTCDataChannel.js +0 -354
- package/src/dtls/RTCCertificate.js +0 -310
- package/src/dtls/RTCDtlsTransport.js +0 -247
- package/src/ice/RTCIceTransport.js +0 -998
- package/src/index.d.ts +0 -400
- package/src/index.js +0 -92
- package/src/network/network-transport.js +0 -478
- package/src/peerconnection/RTCPeerConnection.js +0 -851
- package/src/sctp/RTCSctpTransport.js +0 -253
- package/src/sdp/sdp-utils.js +0 -224
- package/src/stun/stun-client.js +0 -643
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file stun-message.ts
|
|
3
|
+
* @description STUN message codec for ICE connectivity checks (RFC 5389 / 8445).
|
|
4
|
+
* @module ice/stun-message
|
|
5
|
+
*
|
|
6
|
+
* Unlike the server-oriented stun-client.js (binding/allocate), this builds and
|
|
7
|
+
* validates the connectivity-check messages browsers require: USERNAME,
|
|
8
|
+
* MESSAGE-INTEGRITY (HMAC-SHA1 keyed by the peer's ice-pwd), FINGERPRINT
|
|
9
|
+
* (CRC-32 of the message Xored with 0x5354554e), PRIORITY, ICE-CONTROLLING/
|
|
10
|
+
* ICE-CONTROLLED, and USE-CANDIDATE.
|
|
11
|
+
*/
|
|
12
|
+
export declare const MAGIC_COOKIE = 554869826;
|
|
13
|
+
export declare const METHOD: Readonly<{
|
|
14
|
+
BINDING: 1;
|
|
15
|
+
}>;
|
|
16
|
+
export declare const CLASS: Readonly<{
|
|
17
|
+
REQUEST: 0;
|
|
18
|
+
INDICATION: 16;
|
|
19
|
+
SUCCESS: 256;
|
|
20
|
+
ERROR: 272;
|
|
21
|
+
}>;
|
|
22
|
+
export declare const MSG_TYPE: Readonly<{
|
|
23
|
+
BINDING_REQUEST: 1;
|
|
24
|
+
BINDING_SUCCESS: 257;
|
|
25
|
+
BINDING_ERROR: 273;
|
|
26
|
+
}>;
|
|
27
|
+
export declare const ATTR: Readonly<{
|
|
28
|
+
MAPPED_ADDRESS: 1;
|
|
29
|
+
USERNAME: 6;
|
|
30
|
+
MESSAGE_INTEGRITY: 8;
|
|
31
|
+
ERROR_CODE: 9;
|
|
32
|
+
XOR_MAPPED_ADDRESS: 32;
|
|
33
|
+
PRIORITY: 36;
|
|
34
|
+
USE_CANDIDATE: 37;
|
|
35
|
+
FINGERPRINT: 32808;
|
|
36
|
+
ICE_CONTROLLED: 32809;
|
|
37
|
+
ICE_CONTROLLING: 32810;
|
|
38
|
+
}>;
|
|
39
|
+
/** A single STUN attribute pending serialization. */
|
|
40
|
+
interface StunAttribute {
|
|
41
|
+
type: number;
|
|
42
|
+
value: Buffer;
|
|
43
|
+
}
|
|
44
|
+
/** Shape of a parsed STUN message. */
|
|
45
|
+
export interface ParsedStunMessage {
|
|
46
|
+
type: number;
|
|
47
|
+
transactionId: Buffer;
|
|
48
|
+
attrs: Map<number, Buffer>;
|
|
49
|
+
raw: Buffer;
|
|
50
|
+
}
|
|
51
|
+
export declare function crc32(buf: Buffer): number;
|
|
52
|
+
/**
|
|
53
|
+
* @class StunMessageBuilder
|
|
54
|
+
* @description Incrementally builds a STUN message, then appends
|
|
55
|
+
* MESSAGE-INTEGRITY and FINGERPRINT with the correct length pre-computation.
|
|
56
|
+
*/
|
|
57
|
+
export declare class StunMessageBuilder {
|
|
58
|
+
#private;
|
|
59
|
+
type: number;
|
|
60
|
+
transactionId: Buffer;
|
|
61
|
+
attrs: StunAttribute[];
|
|
62
|
+
constructor(type: number, transactionId?: Buffer);
|
|
63
|
+
addAttr(type: number, value: Buffer): this;
|
|
64
|
+
addUsername(username: string): this;
|
|
65
|
+
addPriority(priority: number): this;
|
|
66
|
+
addIceControlling(tieBreaker: Buffer): this;
|
|
67
|
+
addIceControlled(tieBreaker: Buffer): this;
|
|
68
|
+
addUseCandidate(): this;
|
|
69
|
+
addXorMappedAddress(address: string, port: number): this;
|
|
70
|
+
/**
|
|
71
|
+
* Finalize the message, appending MESSAGE-INTEGRITY (keyed by `password`)
|
|
72
|
+
* and FINGERPRINT. Both require the header length to include the attribute
|
|
73
|
+
* being computed, per RFC 5389 §15.4 / §15.5.
|
|
74
|
+
* @param {string} [password] - ICE password for MESSAGE-INTEGRITY
|
|
75
|
+
* @returns {Buffer}
|
|
76
|
+
*/
|
|
77
|
+
build(password?: string): Buffer;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse a STUN message. Returns null if not a STUN message.
|
|
81
|
+
* @param {Buffer} msg
|
|
82
|
+
* @returns {null|{type:number,transactionId:Buffer,attrs:Map<number,Buffer>,raw:Buffer}}
|
|
83
|
+
*/
|
|
84
|
+
export declare function parse(msg: Buffer): ParsedStunMessage | null;
|
|
85
|
+
/**
|
|
86
|
+
* Verify the MESSAGE-INTEGRITY of a parsed message against a password.
|
|
87
|
+
* @param {Buffer} msg - raw message
|
|
88
|
+
* @param {string} password
|
|
89
|
+
* @returns {boolean}
|
|
90
|
+
*/
|
|
91
|
+
export declare function verifyIntegrity(msg: Buffer, password: string): boolean;
|
|
92
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview node-rtc-connection - WebRTC DataChannel implementation for Node.js
|
|
3
|
+
*
|
|
4
|
+
* A from-scratch, pure-Node.js implementation of WebRTC peer connections and
|
|
5
|
+
* data channels (no native dependencies). Interoperates with browsers.
|
|
6
|
+
*
|
|
7
|
+
* This implementation focuses on DataChannel functionality without media streams.
|
|
8
|
+
* Features:
|
|
9
|
+
* - ICE (Interactive Connectivity Establishment), RFC 8445
|
|
10
|
+
* - STUN/TURN support for NAT traversal
|
|
11
|
+
* - DTLS 1.2 encryption (RFC 6347)
|
|
12
|
+
* - SCTP over DTLS + DCEP for data channels (RFC 8831 / 8832)
|
|
13
|
+
* - W3C-compatible RTCPeerConnection / RTCDataChannel API
|
|
14
|
+
*
|
|
15
|
+
* @license MIT
|
|
16
|
+
* @author nmhung1210
|
|
17
|
+
*/
|
|
18
|
+
import ByteBufferQueue from './foundation/ByteBufferQueue';
|
|
19
|
+
import RTCError from './foundation/RTCError';
|
|
20
|
+
import RTCIceCandidate from './ice/RTCIceCandidate';
|
|
21
|
+
import RTCCertificate from './dtls/RTCCertificate';
|
|
22
|
+
import { RTCDataChannel, RTCDataChannelState } from './datachannel/RTCDataChannel';
|
|
23
|
+
import { RTCSessionDescription, RTCSdpType } from './sdp/RTCSessionDescription';
|
|
24
|
+
import { RTCPeerConnection, RTCSignalingState, RTCIceGatheringState, RTCPeerConnectionState } from './peerconnection/RTCPeerConnection';
|
|
25
|
+
export { ByteBufferQueue, RTCError, RTCIceCandidate, RTCCertificate, RTCDataChannel, RTCDataChannelState, RTCSessionDescription, RTCSdpType, RTCPeerConnection, RTCSignalingState, RTCIceGatheringState, RTCPeerConnectionState, };
|
|
26
|
+
export declare const version: string;
|
|
27
|
+
export type { RTCDataChannelInit } from './datachannel/RTCDataChannel';
|
|
28
|
+
export type { RTCSessionDescriptionInit } from './sdp/RTCSessionDescription';
|
|
29
|
+
export type { TransportStackOptions } from './transport-stack';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file RTCPeerConnection.ts
|
|
3
|
+
* @description WebRTC peer connection driving a real ICE/DTLS/SCTP/DCEP stack.
|
|
4
|
+
* @module peerconnection/RTCPeerConnection
|
|
5
|
+
*
|
|
6
|
+
* This orchestrates signaling (offer/answer + ICE candidate trickle) on top of
|
|
7
|
+
* src/transport-stack.ts, which implements the actual on-the-wire protocols.
|
|
8
|
+
* It interoperates with browser RTCPeerConnection for data channels.
|
|
9
|
+
*/
|
|
10
|
+
import { EventEmitter } from 'events';
|
|
11
|
+
import RTCCertificate from '../dtls/RTCCertificate';
|
|
12
|
+
import { RTCSessionDescription, RTCSessionDescriptionInit } from '../sdp/RTCSessionDescription';
|
|
13
|
+
import { RTCDataChannel, RTCDataChannelInit } from '../datachannel/RTCDataChannel';
|
|
14
|
+
import { TransportStack } from '../transport-stack';
|
|
15
|
+
/** Configuration accepted by {@link RTCPeerConnection}. */
|
|
16
|
+
export interface RTCConfiguration {
|
|
17
|
+
iceServers?: unknown[];
|
|
18
|
+
iceTransportPolicy?: 'all' | 'relay';
|
|
19
|
+
certificates?: RTCCertificate[];
|
|
20
|
+
}
|
|
21
|
+
export declare const RTCSignalingState: Readonly<{
|
|
22
|
+
STABLE: "stable";
|
|
23
|
+
HAVE_LOCAL_OFFER: "have-local-offer";
|
|
24
|
+
HAVE_REMOTE_OFFER: "have-remote-offer";
|
|
25
|
+
HAVE_LOCAL_PRANSWER: "have-local-pranswer";
|
|
26
|
+
HAVE_REMOTE_PRANSWER: "have-remote-pranswer";
|
|
27
|
+
CLOSED: "closed";
|
|
28
|
+
}>;
|
|
29
|
+
export declare const RTCIceGatheringState: Readonly<{
|
|
30
|
+
NEW: "new";
|
|
31
|
+
GATHERING: "gathering";
|
|
32
|
+
COMPLETE: "complete";
|
|
33
|
+
}>;
|
|
34
|
+
export declare const RTCPeerConnectionState: Readonly<{
|
|
35
|
+
NEW: "new";
|
|
36
|
+
CONNECTING: "connecting";
|
|
37
|
+
CONNECTED: "connected";
|
|
38
|
+
DISCONNECTED: "disconnected";
|
|
39
|
+
FAILED: "failed";
|
|
40
|
+
CLOSED: "closed";
|
|
41
|
+
}>;
|
|
42
|
+
export declare class RTCPeerConnection extends EventEmitter {
|
|
43
|
+
#private;
|
|
44
|
+
constructor(configuration?: RTCConfiguration);
|
|
45
|
+
createDataChannel(label: string, options?: RTCDataChannelInit): RTCDataChannel;
|
|
46
|
+
createOffer(): Promise<RTCSessionDescription>;
|
|
47
|
+
createAnswer(): Promise<RTCSessionDescription>;
|
|
48
|
+
setLocalDescription(description?: RTCSessionDescriptionInit | RTCSessionDescription): Promise<void>;
|
|
49
|
+
setRemoteDescription(description: RTCSessionDescriptionInit): Promise<void>;
|
|
50
|
+
addIceCandidate(candidate: RTCIceCandidateInit | string): Promise<void>;
|
|
51
|
+
getConfiguration(): RTCConfiguration;
|
|
52
|
+
setConfiguration(configuration: RTCConfiguration): void;
|
|
53
|
+
close(): void;
|
|
54
|
+
get signalingState(): string;
|
|
55
|
+
get iceGatheringState(): string;
|
|
56
|
+
get iceConnectionState(): string;
|
|
57
|
+
get connectionState(): string;
|
|
58
|
+
get localDescription(): RTCSessionDescription | null;
|
|
59
|
+
get remoteDescription(): RTCSessionDescription | null;
|
|
60
|
+
get currentLocalDescription(): RTCSessionDescription | null;
|
|
61
|
+
get currentRemoteDescription(): RTCSessionDescription | null;
|
|
62
|
+
get pendingLocalDescription(): RTCSessionDescription | null;
|
|
63
|
+
get pendingRemoteDescription(): RTCSessionDescription | null;
|
|
64
|
+
get canTrickleIceCandidates(): boolean;
|
|
65
|
+
get sctp(): TransportStack['sctp'];
|
|
66
|
+
}
|
|
67
|
+
/** ICE candidate init shape (subset of the W3C dictionary). */
|
|
68
|
+
interface RTCIceCandidateInit {
|
|
69
|
+
candidate: string;
|
|
70
|
+
sdpMid?: string;
|
|
71
|
+
sdpMLineIndex?: number;
|
|
72
|
+
usernameFragment?: string;
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file association.ts
|
|
3
|
+
* @description Minimal SCTP association over a datagram channel (DTLS), scoped
|
|
4
|
+
* to the WebRTC data channel profile (RFC 8831).
|
|
5
|
+
* @module sctp/association
|
|
6
|
+
*
|
|
7
|
+
* Implements the four-way INIT/INIT-ACK/COOKIE-ECHO/COOKIE-ACK setup, DATA
|
|
8
|
+
* transmit/receive with TSN tracking and SACK, ordered and unordered delivery,
|
|
9
|
+
* and reassembly of fragmented user messages. Congestion control is
|
|
10
|
+
* intentionally simple (stop-and-go style with a generous rwnd) which is
|
|
11
|
+
* adequate for control/data-channel traffic and interoperates with usrsctp
|
|
12
|
+
* (the stack browsers use).
|
|
13
|
+
*
|
|
14
|
+
* The association rides on top of a reliable-ish datagram pipe provided by
|
|
15
|
+
* DTLS; SCTP still provides framing, ordering, multiplexing and ack'ing.
|
|
16
|
+
*/
|
|
17
|
+
import { EventEmitter } from 'events';
|
|
18
|
+
declare const SCTP_PORT = 5000;
|
|
19
|
+
declare const STATE: Readonly<{
|
|
20
|
+
CLOSED: "closed";
|
|
21
|
+
COOKIE_WAIT: "cookie-wait";
|
|
22
|
+
COOKIE_ECHOED: "cookie-echoed";
|
|
23
|
+
ESTABLISHED: "established";
|
|
24
|
+
}>;
|
|
25
|
+
type StateValue = (typeof STATE)[keyof typeof STATE];
|
|
26
|
+
/** Options for constructing an {@link SctpAssociation}. */
|
|
27
|
+
export interface SctpAssociationOptions {
|
|
28
|
+
/** the DTLS client initiates SCTP (RFC 8831) */
|
|
29
|
+
isClient?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** A complete user message surfaced via the 'message' event. */
|
|
32
|
+
export interface SctpMessage {
|
|
33
|
+
streamId: number;
|
|
34
|
+
ppid: number;
|
|
35
|
+
data: Buffer;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* @class SctpAssociation
|
|
39
|
+
* @extends EventEmitter
|
|
40
|
+
*
|
|
41
|
+
* Events:
|
|
42
|
+
* - 'established' association is up
|
|
43
|
+
* - 'message' ({streamId, ppid, data}) a complete user message arrived
|
|
44
|
+
* - 'output' (Buffer) an SCTP packet to hand to DTLS
|
|
45
|
+
* - 'close'
|
|
46
|
+
*/
|
|
47
|
+
declare class SctpAssociation extends EventEmitter {
|
|
48
|
+
#private;
|
|
49
|
+
isClient: boolean;
|
|
50
|
+
state: StateValue;
|
|
51
|
+
/**
|
|
52
|
+
* @param {Object} opts
|
|
53
|
+
* @param {boolean} opts.isClient - the DTLS client initiates SCTP (RFC 8831)
|
|
54
|
+
*/
|
|
55
|
+
constructor(opts?: SctpAssociationOptions);
|
|
56
|
+
/** Start the association (client sends INIT). */
|
|
57
|
+
start(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Feed an inbound SCTP packet (decrypted from DTLS).
|
|
60
|
+
* @param {Buffer} packet
|
|
61
|
+
*/
|
|
62
|
+
receivePacket(packet: Buffer): void;
|
|
63
|
+
/**
|
|
64
|
+
* Send a user message on a stream.
|
|
65
|
+
* @param {number} streamId
|
|
66
|
+
* @param {number} ppid
|
|
67
|
+
* @param {Buffer} data
|
|
68
|
+
* @param {Object} [opts]
|
|
69
|
+
* @param {boolean} [opts.unordered=false]
|
|
70
|
+
*/
|
|
71
|
+
sendData(streamId: number, ppid: number, data: Buffer, opts?: {
|
|
72
|
+
unordered?: boolean;
|
|
73
|
+
}): void;
|
|
74
|
+
/** Gracefully close the association. */
|
|
75
|
+
shutdown(): void;
|
|
76
|
+
}
|
|
77
|
+
export { SctpAssociation, STATE, SCTP_PORT };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file chunks.ts
|
|
3
|
+
* @description SCTP common header and chunk encode/parse (RFC 4960 + RFC 8260
|
|
4
|
+
* for I-DATA is NOT used; classic DATA only). Scoped to the WebRTC profile.
|
|
5
|
+
* @module sctp/chunks
|
|
6
|
+
*/
|
|
7
|
+
export declare const CHUNK_TYPE: Readonly<{
|
|
8
|
+
DATA: 0;
|
|
9
|
+
INIT: 1;
|
|
10
|
+
INIT_ACK: 2;
|
|
11
|
+
SACK: 3;
|
|
12
|
+
HEARTBEAT: 4;
|
|
13
|
+
HEARTBEAT_ACK: 5;
|
|
14
|
+
ABORT: 6;
|
|
15
|
+
SHUTDOWN: 7;
|
|
16
|
+
SHUTDOWN_ACK: 8;
|
|
17
|
+
ERROR: 9;
|
|
18
|
+
COOKIE_ECHO: 10;
|
|
19
|
+
COOKIE_ACK: 11;
|
|
20
|
+
SHUTDOWN_COMPLETE: 14;
|
|
21
|
+
FORWARD_TSN: 192;
|
|
22
|
+
}>;
|
|
23
|
+
export declare const PARAM_TYPE: Readonly<{
|
|
24
|
+
HEARTBEAT_INFO: 1;
|
|
25
|
+
STATE_COOKIE: 7;
|
|
26
|
+
UNRECOGNIZED_PARAM: 8;
|
|
27
|
+
COOKIE_PRESERVATIVE: 9;
|
|
28
|
+
SUPPORTED_ADDR_TYPES: 11;
|
|
29
|
+
FORWARD_TSN_SUPPORTED: 49152;
|
|
30
|
+
SUPPORTED_EXTENSIONS: 32776;
|
|
31
|
+
}>;
|
|
32
|
+
export declare const PPID: Readonly<{
|
|
33
|
+
DCEP: 50;
|
|
34
|
+
STRING: 51;
|
|
35
|
+
BINARY: 53;
|
|
36
|
+
STRING_EMPTY: 56;
|
|
37
|
+
BINARY_EMPTY: 57;
|
|
38
|
+
STRING_PARTIAL: 54;
|
|
39
|
+
BINARY_PARTIAL: 52;
|
|
40
|
+
}>;
|
|
41
|
+
export interface CommonHeader {
|
|
42
|
+
srcPort: number;
|
|
43
|
+
dstPort: number;
|
|
44
|
+
verificationTag: number;
|
|
45
|
+
checksum: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ParsedChunk {
|
|
48
|
+
type: number;
|
|
49
|
+
flags: number;
|
|
50
|
+
length: number;
|
|
51
|
+
body: Buffer;
|
|
52
|
+
}
|
|
53
|
+
export interface ParsedParam {
|
|
54
|
+
type: number;
|
|
55
|
+
length: number;
|
|
56
|
+
value: Buffer;
|
|
57
|
+
}
|
|
58
|
+
export interface InitBodyParams {
|
|
59
|
+
initiateTag: number;
|
|
60
|
+
a_rwnd: number;
|
|
61
|
+
outStreams: number;
|
|
62
|
+
inStreams: number;
|
|
63
|
+
initialTSN: number;
|
|
64
|
+
}
|
|
65
|
+
export interface ParsedInitBody {
|
|
66
|
+
initiateTag: number;
|
|
67
|
+
a_rwnd: number;
|
|
68
|
+
outStreams: number;
|
|
69
|
+
inStreams: number;
|
|
70
|
+
initialTSN: number;
|
|
71
|
+
params: ParsedParam[];
|
|
72
|
+
}
|
|
73
|
+
export interface DataBodyParams {
|
|
74
|
+
tsn: number;
|
|
75
|
+
streamId: number;
|
|
76
|
+
streamSeq: number;
|
|
77
|
+
ppid: number;
|
|
78
|
+
userData: Buffer;
|
|
79
|
+
unordered?: boolean;
|
|
80
|
+
beginning?: boolean;
|
|
81
|
+
ending?: boolean;
|
|
82
|
+
}
|
|
83
|
+
export interface EncodedDataBody {
|
|
84
|
+
flags: number;
|
|
85
|
+
body: Buffer;
|
|
86
|
+
}
|
|
87
|
+
export interface ParsedDataBody {
|
|
88
|
+
unordered: boolean;
|
|
89
|
+
beginning: boolean;
|
|
90
|
+
ending: boolean;
|
|
91
|
+
tsn: number;
|
|
92
|
+
streamId: number;
|
|
93
|
+
streamSeq: number;
|
|
94
|
+
ppid: number;
|
|
95
|
+
userData: Buffer;
|
|
96
|
+
}
|
|
97
|
+
export interface SackBodyParams {
|
|
98
|
+
cumulativeTSNAck: number;
|
|
99
|
+
a_rwnd: number;
|
|
100
|
+
gapBlocks?: Array<[number, number]>;
|
|
101
|
+
dupTSNs?: number[];
|
|
102
|
+
}
|
|
103
|
+
export interface ParsedSackBody {
|
|
104
|
+
cumulativeTSNAck: number;
|
|
105
|
+
a_rwnd: number;
|
|
106
|
+
gapBlocks: Array<[number, number]>;
|
|
107
|
+
dupTSNs: number[];
|
|
108
|
+
}
|
|
109
|
+
/** Round a length up to the next 4-byte boundary. */
|
|
110
|
+
export declare function pad4(n: number): number;
|
|
111
|
+
/**
|
|
112
|
+
* Encode the 12-byte SCTP common header (checksum left as 0; filled by crc32c).
|
|
113
|
+
* @param {number} srcPort
|
|
114
|
+
* @param {number} dstPort
|
|
115
|
+
* @param {number} verificationTag
|
|
116
|
+
* @returns {Buffer}
|
|
117
|
+
*/
|
|
118
|
+
export declare function encodeCommonHeader(srcPort: number, dstPort: number, verificationTag: number): Buffer;
|
|
119
|
+
/**
|
|
120
|
+
* Parse the common header.
|
|
121
|
+
* @param {Buffer} packet
|
|
122
|
+
* @returns {{srcPort:number,dstPort:number,verificationTag:number,checksum:number}}
|
|
123
|
+
*/
|
|
124
|
+
export declare function parseCommonHeader(packet: Buffer): CommonHeader;
|
|
125
|
+
/**
|
|
126
|
+
* Wrap a chunk body with the 4-byte chunk header, padded to 4 bytes.
|
|
127
|
+
* @param {number} type
|
|
128
|
+
* @param {number} flags
|
|
129
|
+
* @param {Buffer} body
|
|
130
|
+
* @returns {Buffer}
|
|
131
|
+
*/
|
|
132
|
+
export declare function encodeChunk(type: number, flags: number, body: Buffer): Buffer;
|
|
133
|
+
/**
|
|
134
|
+
* Parse all chunks out of an SCTP packet (after the 12-byte common header).
|
|
135
|
+
* @param {Buffer} packet
|
|
136
|
+
* @returns {Array<{type:number,flags:number,length:number,body:Buffer}>}
|
|
137
|
+
*/
|
|
138
|
+
export declare function parseChunks(packet: Buffer): ParsedChunk[];
|
|
139
|
+
/**
|
|
140
|
+
* Encode a TLV parameter, padded to 4 bytes.
|
|
141
|
+
* @param {number} type
|
|
142
|
+
* @param {Buffer} value
|
|
143
|
+
* @returns {Buffer}
|
|
144
|
+
*/
|
|
145
|
+
export declare function encodeParam(type: number, value: Buffer): Buffer;
|
|
146
|
+
/**
|
|
147
|
+
* Parse TLV parameters from a buffer.
|
|
148
|
+
* @param {Buffer} buf
|
|
149
|
+
* @returns {Array<{type:number,length:number,value:Buffer}>}
|
|
150
|
+
*/
|
|
151
|
+
export declare function parseParams(buf: Buffer): ParsedParam[];
|
|
152
|
+
/**
|
|
153
|
+
* Build an INIT or INIT_ACK fixed body (without parameters).
|
|
154
|
+
* @param {Object} p
|
|
155
|
+
* @param {number} p.initiateTag
|
|
156
|
+
* @param {number} p.a_rwnd - advertised receiver window
|
|
157
|
+
* @param {number} p.outStreams
|
|
158
|
+
* @param {number} p.inStreams
|
|
159
|
+
* @param {number} p.initialTSN
|
|
160
|
+
* @returns {Buffer}
|
|
161
|
+
*/
|
|
162
|
+
export declare function encodeInitBody({ initiateTag, a_rwnd, outStreams, inStreams, initialTSN }: InitBodyParams): Buffer;
|
|
163
|
+
/**
|
|
164
|
+
* Parse an INIT/INIT_ACK body.
|
|
165
|
+
* @param {Buffer} body
|
|
166
|
+
* @returns {{initiateTag:number,a_rwnd:number,outStreams:number,inStreams:number,initialTSN:number,params:Array}}
|
|
167
|
+
*/
|
|
168
|
+
export declare function parseInitBody(body: Buffer): ParsedInitBody;
|
|
169
|
+
/**
|
|
170
|
+
* Encode a DATA chunk body (RFC 4960 §3.3.1).
|
|
171
|
+
* @param {Object} p
|
|
172
|
+
* @param {number} p.tsn
|
|
173
|
+
* @param {number} p.streamId
|
|
174
|
+
* @param {number} p.streamSeq
|
|
175
|
+
* @param {number} p.ppid
|
|
176
|
+
* @param {Buffer} p.userData
|
|
177
|
+
* @returns {{flags:number, body:Buffer}}
|
|
178
|
+
*/
|
|
179
|
+
export declare function encodeDataBody({ tsn, streamId, streamSeq, ppid, userData, unordered, beginning, ending }: DataBodyParams): EncodedDataBody;
|
|
180
|
+
/**
|
|
181
|
+
* Parse a DATA chunk body.
|
|
182
|
+
* @param {number} flags
|
|
183
|
+
* @param {Buffer} body
|
|
184
|
+
*/
|
|
185
|
+
export declare function parseDataBody(flags: number, body: Buffer): ParsedDataBody;
|
|
186
|
+
/**
|
|
187
|
+
* Encode a SACK chunk body (cumulative ack only, no gap/dup for simplicity but
|
|
188
|
+
* gap blocks supported via params).
|
|
189
|
+
* @param {Object} p
|
|
190
|
+
* @param {number} p.cumulativeTSNAck
|
|
191
|
+
* @param {number} p.a_rwnd
|
|
192
|
+
* @param {Array<[number,number]>} [p.gapBlocks] - [start,end] offsets from cumAck+1
|
|
193
|
+
* @param {Array<number>} [p.dupTSNs]
|
|
194
|
+
* @returns {Buffer}
|
|
195
|
+
*/
|
|
196
|
+
export declare function encodeSackBody({ cumulativeTSNAck, a_rwnd, gapBlocks, dupTSNs }: SackBodyParams): Buffer;
|
|
197
|
+
/**
|
|
198
|
+
* Parse a SACK chunk body.
|
|
199
|
+
*/
|
|
200
|
+
export declare function parseSackBody(body: Buffer): ParsedSackBody;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file crc32c.ts
|
|
3
|
+
* @description CRC-32C (Castagnoli) checksum for SCTP packets (RFC 4960 App. B,
|
|
4
|
+
* polynomial 0x1EDC6F41), reflected input/output, used with the SCTP-specific
|
|
5
|
+
* byte ordering described in RFC 4960 §6.8 / RFC 3309.
|
|
6
|
+
* @module sctp/crc32c
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Compute the raw reflected CRC-32C over a buffer.
|
|
10
|
+
* @returns unsigned 32-bit
|
|
11
|
+
*/
|
|
12
|
+
export declare function crc32c(buf: Buffer): number;
|
|
13
|
+
/**
|
|
14
|
+
* Insert the SCTP checksum into a packet. The checksum field (bytes 8..11 of
|
|
15
|
+
* the common header) is zeroed, the CRC computed over the whole packet, then
|
|
16
|
+
* written back in little-endian byte order (RFC 4960 §6.8, RFC 3309).
|
|
17
|
+
* @param packet - full SCTP packet (header + chunks)
|
|
18
|
+
* @returns the same packet, checksum filled in
|
|
19
|
+
*/
|
|
20
|
+
export declare function applyChecksum(packet: Buffer): Buffer;
|
|
21
|
+
/**
|
|
22
|
+
* Validate the checksum of a received SCTP packet.
|
|
23
|
+
*/
|
|
24
|
+
export declare function verifyChecksum(packet: Buffer): boolean;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file datachannel-manager.ts
|
|
3
|
+
* @description Bridges SCTP streams + DCEP to RTCDataChannel instances.
|
|
4
|
+
* @module sctp/datachannel-manager
|
|
5
|
+
*
|
|
6
|
+
* Responsibilities:
|
|
7
|
+
* - Allocate SCTP stream IDs per RFC 8832 §6: the DTLS client (a=setup:active)
|
|
8
|
+
* uses even stream IDs, the DTLS server uses odd.
|
|
9
|
+
* - Send DATA_CHANNEL_OPEN and await DATA_CHANNEL_ACK; respond to inbound OPEN.
|
|
10
|
+
* - Map outgoing string/binary sends to the correct PPID, and incoming PPIDs
|
|
11
|
+
* back to string/binary, including the EMPTY variants for zero-length data.
|
|
12
|
+
*/
|
|
13
|
+
import { EventEmitter } from 'events';
|
|
14
|
+
import type { SctpAssociation } from './association';
|
|
15
|
+
import { RTCDataChannel } from '../datachannel/RTCDataChannel';
|
|
16
|
+
/** Subset of RTCDataChannelInit used when opening/accepting channels. */
|
|
17
|
+
interface ChannelInit {
|
|
18
|
+
ordered?: boolean;
|
|
19
|
+
maxRetransmits?: number | null;
|
|
20
|
+
maxPacketLifeTime?: number | null;
|
|
21
|
+
protocol?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Information surfaced on the 'open-request' event for an inbound channel. */
|
|
24
|
+
export interface OpenRequestInfo {
|
|
25
|
+
streamId: number;
|
|
26
|
+
label: string;
|
|
27
|
+
protocol: string;
|
|
28
|
+
ordered: boolean;
|
|
29
|
+
channelType: number;
|
|
30
|
+
reliabilityParameter: number;
|
|
31
|
+
}
|
|
32
|
+
declare class DataChannelManager extends EventEmitter {
|
|
33
|
+
#private;
|
|
34
|
+
/**
|
|
35
|
+
* @param {import('./association').SctpAssociation} association
|
|
36
|
+
* @param {boolean} isDtlsClient - true if we are the DTLS client (even IDs)
|
|
37
|
+
*/
|
|
38
|
+
constructor(association: SctpAssociation, isDtlsClient: boolean);
|
|
39
|
+
/**
|
|
40
|
+
* Open a channel initiated locally.
|
|
41
|
+
* @param {import('../datachannel/RTCDataChannel').RTCDataChannel} channel
|
|
42
|
+
* @param {Object} init - { ordered, maxRetransmits, maxPacketLifeTime, protocol }
|
|
43
|
+
*/
|
|
44
|
+
openChannel(channel: RTCDataChannel, init?: ChannelInit): void;
|
|
45
|
+
/**
|
|
46
|
+
* Register an inbound channel (created in response to 'open-request') and
|
|
47
|
+
* attach its sender.
|
|
48
|
+
*/
|
|
49
|
+
acceptChannel(channel: RTCDataChannel, info: OpenRequestInfo): void;
|
|
50
|
+
}
|
|
51
|
+
export { DataChannelManager };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file dcep.ts
|
|
3
|
+
* @description Data Channel Establishment Protocol (RFC 8832) message codec.
|
|
4
|
+
* @module sctp/dcep
|
|
5
|
+
*
|
|
6
|
+
* DCEP runs on PPID 50 and negotiates a data channel on an SCTP stream:
|
|
7
|
+
* DATA_CHANNEL_OPEN (0x03) -> DATA_CHANNEL_ACK (0x02)
|
|
8
|
+
*/
|
|
9
|
+
export declare const MESSAGE_TYPE: Readonly<{
|
|
10
|
+
DATA_CHANNEL_ACK: 2;
|
|
11
|
+
DATA_CHANNEL_OPEN: 3;
|
|
12
|
+
}>;
|
|
13
|
+
export declare const CHANNEL_TYPE: Readonly<{
|
|
14
|
+
RELIABLE: 0;
|
|
15
|
+
RELIABLE_UNORDERED: 128;
|
|
16
|
+
PARTIAL_RELIABLE_REXMIT: 1;
|
|
17
|
+
PARTIAL_RELIABLE_REXMIT_UNORDERED: 129;
|
|
18
|
+
PARTIAL_RELIABLE_TIMED: 2;
|
|
19
|
+
PARTIAL_RELIABLE_TIMED_UNORDERED: 130;
|
|
20
|
+
}>;
|
|
21
|
+
export interface OpenParams {
|
|
22
|
+
channelType: number;
|
|
23
|
+
priority?: number;
|
|
24
|
+
reliabilityParameter?: number;
|
|
25
|
+
label?: string;
|
|
26
|
+
protocol?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface DecodedOpen {
|
|
29
|
+
channelType: number;
|
|
30
|
+
priority: number;
|
|
31
|
+
reliabilityParameter: number;
|
|
32
|
+
label: string;
|
|
33
|
+
protocol: string;
|
|
34
|
+
unordered: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Encode a DATA_CHANNEL_OPEN message.
|
|
38
|
+
* @param {Object} p
|
|
39
|
+
* @param {number} p.channelType
|
|
40
|
+
* @param {number} p.priority
|
|
41
|
+
* @param {number} p.reliabilityParameter
|
|
42
|
+
* @param {string} p.label
|
|
43
|
+
* @param {string} p.protocol
|
|
44
|
+
* @returns {Buffer}
|
|
45
|
+
*/
|
|
46
|
+
export declare function encodeOpen({ channelType, priority, reliabilityParameter, label, protocol }: OpenParams): Buffer;
|
|
47
|
+
/**
|
|
48
|
+
* Decode a DATA_CHANNEL_OPEN message.
|
|
49
|
+
* @param {Buffer} buf
|
|
50
|
+
* @returns {Object}
|
|
51
|
+
*/
|
|
52
|
+
export declare function decodeOpen(buf: Buffer): DecodedOpen;
|
|
53
|
+
/** Encode a DATA_CHANNEL_ACK message. */
|
|
54
|
+
export declare function encodeAck(): Buffer;
|
|
55
|
+
/** Return the message type of a DCEP buffer. */
|
|
56
|
+
export declare function messageType(buf: Buffer): number;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file RTCSessionDescription.ts
|
|
3
|
+
* @description Session Description Protocol (SDP) representation
|
|
4
|
+
* @module sdp/RTCSessionDescription
|
|
5
|
+
*
|
|
6
|
+
* Implements the W3C RTCSessionDescription interface
|
|
7
|
+
* (https://www.w3.org/TR/webrtc/#rtcsessiondescription-class).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* RTCSdpType - Types of session descriptions
|
|
11
|
+
* @readonly
|
|
12
|
+
* @enum {string}
|
|
13
|
+
*/
|
|
14
|
+
export declare const RTCSdpType: Readonly<Record<string, string>>;
|
|
15
|
+
/**
|
|
16
|
+
* Session description init object.
|
|
17
|
+
*/
|
|
18
|
+
export interface RTCSessionDescriptionInit {
|
|
19
|
+
type?: string;
|
|
20
|
+
sdp?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* JSON representation of an RTCSessionDescription.
|
|
24
|
+
*/
|
|
25
|
+
export interface RTCSessionDescriptionJSON {
|
|
26
|
+
type: string | null;
|
|
27
|
+
sdp: string | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* @class RTCSessionDescription
|
|
31
|
+
* @description Represents a WebRTC session description (offer/answer)
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const desc = new RTCSessionDescription({
|
|
35
|
+
* type: 'offer',
|
|
36
|
+
* sdp: 'v=0\r\no=- 123456 2 IN IP4 127.0.0.1\r\n...'
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
export declare class RTCSessionDescription {
|
|
40
|
+
#private;
|
|
41
|
+
/**
|
|
42
|
+
* Create an RTCSessionDescription instance.
|
|
43
|
+
* @param {Object} [init] - Session description init
|
|
44
|
+
* @param {string} [init.type] - SDP type (offer/answer/pranswer/rollback)
|
|
45
|
+
* @param {string} [init.sdp] - SDP string
|
|
46
|
+
*/
|
|
47
|
+
constructor(init?: RTCSessionDescriptionInit);
|
|
48
|
+
/**
|
|
49
|
+
* Get the SDP type.
|
|
50
|
+
* @returns {string|null} SDP type
|
|
51
|
+
*/
|
|
52
|
+
get type(): string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Set the SDP type.
|
|
55
|
+
* @param {string} value - SDP type
|
|
56
|
+
*/
|
|
57
|
+
set type(value: string | null);
|
|
58
|
+
/**
|
|
59
|
+
* Get the SDP string.
|
|
60
|
+
* @returns {string|null} SDP string
|
|
61
|
+
*/
|
|
62
|
+
get sdp(): string | null;
|
|
63
|
+
/**
|
|
64
|
+
* Set the SDP string.
|
|
65
|
+
* @param {string} value - SDP string
|
|
66
|
+
*/
|
|
67
|
+
set sdp(value: string | null);
|
|
68
|
+
/**
|
|
69
|
+
* Convert to JSON representation.
|
|
70
|
+
* @returns {Object} JSON representation
|
|
71
|
+
*/
|
|
72
|
+
toJSON(): RTCSessionDescriptionJSON;
|
|
73
|
+
}
|