On this page

DTLS

History
Source Code: lib/dtls.js

稳定性:1 - 实验性

node:dtls 模块提供了在 UDP 之上实现的 Datagram Transport Layer Security(DTLS)协议。DTLS 为基于数据报的通信提供与 TLS 等价的 安全保障,包括机密性、完整性和身份认证。

要使用此模块,必须在构建时通过 --experimental-dtls 配置标志启用, 并在运行时通过 --experimental-dtls CLI 标志启用。

使用 [权限模型][] 时,必须传入 --allow-net 标志以允许 DTLS 网络操作。 如果没有它,调用 dtls.connect()dtls.listen() 将抛出 ERR_ACCESS_DENIED 错误。

node --permission --allow-fs-read=* --experimental-dtls index.mjs
Error: 对此 API 的访问已受限。请使用 --allow-net 来管理权限。
  code: 'ERR_ACCESS_DENIED',
  permission: 'Net',
}

即使没有 --allow-net,也允许创建一个 DTLSEndpoint 实例而不进行连接或监听, 因为在调用 dtls.connect()dtls.listen() 之前不会发生网络 I/O。

DTLS 专为 UDP 传输而设计,并在以下几个关键方面与 TLS 不同:

  • 不保证流式传输:消息可能乱序到达或丢失。 DTLS 保留数据报语义。
  • 一个套接字,多个对端:单个 UDP 套接字可服务多个 DTLS 会话。DTLSEndpoint 负责管理这种多路复用。
  • Cookie 交换:DTLS 服务器使用无状态 cookie 机制 (HelloVerifyRequest)来防止拒绝服务放大攻击。
  • 重传:由于 UDP 不保证送达,DTLS 会在内部处理握手重传。
M

dtls.listen

History
dtls.listen(callback, options): void
Attributes
callback:<Function>
服务器接受的每个新的 DTLS 会话都会调用此回调。
session:
{DTLSSession} 新会话。
options:<Object>
PEM 格式的服务器证书。 必需。
PEM 格式的服务器私钥。 必需。
要绑定的端口。 必需。
要绑定的地址。 默认: '0.0.0.0'
PEM 格式的 CA 证书。
ciphers:<string>
OpenSSL 密码套件列表字符串。
ALPN 协议名称。
以冒号分隔的 SRTP 保护配置文件名称 (例如, 'SRTP_AES128_CM_SHA1_80:SRTP_AEAD_AES_128_GCM' )。
requestCert:<boolean>
请求客户端证书。 默认: false
DTLS 记录的最大传输单元。  默认: 1200
返回:{DTLSEndpoint}

创建一个绑定到指定地址和端口的 DTLS 服务器。该服务器 使用基于 HMAC 的自动 cookie 交换进行 DoS 防护。

import { listen } from 'node:dtls';
import { readFileSync } from 'node:fs';

const endpoint = listen((session) => {
  session.onmessage = (data) => {
    console.log('收到:', data.toString());
    session.send('pong');
  };

  session.onhandshake = (protocol) => {
    console.log('握手完成:', protocol);
  };
}, {
  cert: readFileSync('server-cert.pem'),
  key: readFileSync('server-key.pem'),
  port: 4433,
});

console.log('DTLS server listening on', endpoint.address);
M

dtls.connect

History
dtls.connect(host, port, options?): void
Attributes
要连接的远程主机。
要连接的远程端口。
options:<Object>
PEM 格式的 CA 证书。
PEM 格式的客户端证书。
PEM 格式的客户端私钥。
rejectUnauthorized:<boolean>
拒绝无法验证证书的连接。 默认: true
bindHost:<string>
本地绑定地址。 默认: '0.0.0.0'
bindPort:<number>
本地绑定端口。 默认: 0 (临时端口)。
ALPN 协议名称。
SRTP 保护配置文件名称。
最大传输单元。 默认: 1200
返回:{DTLSSession}

连接到 DTLS 服务器。返回一个 DTLSSession,其 opened 属性是一个 Promise,在握手完成时解析。

import { connect } from 'node:dtls';
import { readFileSync } from 'node:fs';

const session = connect('localhost', 4433, {
  ca: [readFileSync('ca-cert.pem')],
});

await session.opened;
session.send('hello');

session.onmessage = (data) => {
  console.log('收到:', data.toString());
};

类:DTLSEndpoint

History

管理一个 UDP 套接字并对 DTLS 会话进行多路复用。

  • 返回:<Object> { address, family, port }

该端点绑定到的本地地址。

  • 返回:{DTLSEndpointState}

包含以下属性的共享状态对象:

Attributes
bound:<boolean>
listening:<boolean>
closing:<boolean>
destroyed:<boolean>
sessionCount:<number>
P

endpoint.stats

History

为此端点收集的统计信息。只读。该统计对象是 实时的,并随着数据流经端点由 C++ 内部更新。

Attributes

当为 true 时,端点会拒绝新的传入连接。可用于实现背压。

endpoint.close(): void
  • 返回:<Promise> 在端点完全关闭时解析。

优雅地关闭端点。在释放 UDP 套接字之前,所有活动会话都会通过 close_notify 警报关闭。

endpoint.destroy(error?): void

立即销毁端点,不发送 close_notify 警报。

Attributes
在端点完全关闭时解析。
endpoint[Symbol.asyncDispose](): void

等同于调用 endpoint.close()

类:DTLSEndpoint.Stats

History

端点收集到的统计信息视图。

P

endpointStats.createdAt

History
  • 类型:<bigint> 指示端点创建时间的时间戳。只读。
P

endpointStats.destroyedAt

History
  • 类型:<bigint> 指示端点销毁时间的时间戳。只读。
P

endpointStats.bytesReceived

History
  • 类型:<bigint> 此端点接收的字节总数。只读。
P

endpointStats.bytesSent

History
  • 类型:<bigint> 此端点发送的字节总数。只读。
P

endpointStats.packetsReceived

History
  • 类型:<bigint> 此端点接收的 UDP 数据包总数。只读。
P

endpointStats.packetsSent

History
  • 类型:<bigint> 此端点发送的 UDP 数据包总数。只读。
P

endpointStats.serverSessions

History
  • 类型:<bigint> 此端点接受的由对端发起的会话总数。只读。
P

endpointStats.clientSessions

History
  • 类型:<bigint> 由此端点发起的会话总数。只读。
P

endpointStats.serverBusyCount

History
  • 类型:<bigint> 因端点被标记为忙碌而被拒绝的传入连接总数。只读。
P

endpointStats.isConnected

History

如果统计对象仍连接到底层端点,则为 true。 一旦端点被销毁,统计信息就会变为过时快照。

类:DTLSSession

History

表示与单个远程对端的 DTLS 关联。

session.send(data): void
Attributes
要发送的数据。
返回: <number> 写入 DTLS 层的字节数。

向对端发送应用数据。数据在通过 UDP 发送之前会由 DTLS 加密。 只能在握手完成后调用(session.opened 已解析)。

session.close(): void
  • 返回:<Promise> 在会话关闭时解析。

通过发送 close_notify 警报来启动优雅的 DTLS 关闭。

session.destroy(error?): void

立即销毁会话,不发送 close_notify

Attributes
在 DTLS 握手完成时解析,并返回  { protocol }
Attributes
在会话完全关闭时解析。
  • 返回:<Object> { address, family, port }
  • 返回:<string> 协商得到的 DTLS 协议版本 (例如,'DTLSv1.2')。
  • 返回:<Object> { name, standardName, version }
P

session.stats

History

为此会话收集的统计信息。只读。该统计对象是 实时的,并随着数据流经会话而更新。

session.exportKeyingMaterial(length, label, context?): void
Attributes
length:<number>
要导出的字节数。
label:<string>
导出的密钥材料标签。
context:
{Buffer} 可选上下文值。
返回:{Buffer}

从 DTLS 会话导出密钥材料,如 RFC 5705 所定义。 这通常与 DTLS-SRTP 一起使用,以为媒体流派生加密密钥。

类:DTLSSession.Stats

History

会话收集到的统计信息视图。

P

sessionStats.createdAt

History
  • 类型:<bigint> 指示会话创建时间的时间戳。只读。
P

sessionStats.destroyedAt

History
  • 类型:<bigint> 指示会话销毁时间的时间戳。只读。
P

sessionStats.closingAt

History
  • 类型:<bigint> 指示调用 close() 时间的时间戳。只读。
P

sessionStats.handshakeCompletedAt

History
  • 类型:<bigint> 指示 DTLS 握手完成时间的时间戳。只读。
P

sessionStats.bytesReceived

History
  • 类型:<bigint> 接收到的应用数据字节总数。只读。
P

sessionStats.bytesSent

History
  • 类型:<bigint> 发送的应用数据字节总数。只读。
P

sessionStats.messagesReceived

History
  • 类型:<bigint> 接收到的应用消息总数。只读。
P

sessionStats.messagesSent

History
  • 类型:<bigint> 发送的应用消息总数。只读。
P

sessionStats.retransmitCount

History
  • 类型:<bigint> DTLS 握手重传总次数。只读。
P

sessionStats.isConnected

History

如果统计对象仍连接到底层会话,则为 true。 一旦会话被销毁,统计信息就会变为过时快照。

Attributes
data:
{Buffer}

设置后可接收来自对端的应用数据。

Attributes

设置后可接收错误通知。

Attributes

设置后可接收握手完成通知。

Attributes

设置后可接收 TLS 密钥日志行(用于使用 Wireshark 调试)。

session[Symbol.asyncDispose](): void

等同于调用 session.close()

DTLS-SRTP 被 WebRTC 用于媒体加密。DTLS 握手会协商 SRTP 保护配置文件并提供密钥材料。

import { listen, connect } from 'node:dtls';
import { readFileSync } from 'node:fs';

// 带 SRTP 的服务器
const server = listen((session) => {
  session.onhandshake = () => {
    console.log('SRTP 配置文件:', session.srtpProfile);
    const keys = session.exportKeyingMaterial(
      60,
      'EXTRACTOR-dtls_srtp',
    );
    console.log('SRTP 密钥材料:', keys);
  };
}, {
  cert: readFileSync('server-cert.pem'),
  key: readFileSync('server-key.pem'),
  port: 5004,
  srtp: 'SRTP_AES128_CM_SHA1_80:SRTP_AEAD_AES_128_GCM',
});

// 带 SRTP 的客户端
const session = connect('localhost', 5004, {
  rejectUnauthorized: false,
  srtp: 'SRTP_AEAD_AES_128_GCM:SRTP_AES128_CM_SHA1_80',
});

await session.opened;
console.log('协商的 SRTP:', session.srtpProfile);
const keys = session.exportKeyingMaterial(60, 'EXTRACTOR-dtls_srtp');

由于 libuv 当前不支持路径 MTU 发现,DTLS 模块使用保守的默认 MTU 1200 字节。这个值适用于几乎所有网络路径,但在本地网络中可能并非最优。

可以通过 mtu 选项配置 MTU:

// 用于你已知路径 MTU 的本地网络
const endpoint = listen(callback, {
  // ...
  mtu: 1400,
});

允许的最小 MTU 为 256 字节。最大值为 65535。