3 个版本
0.1.4 | 2022 年 4 月 1 日 |
---|---|
0.1.3 | 2021 年 9 月 7 日 |
0.1.2 | 2021 年 9 月 7 日 |
2 在 #voip
每月 37 次下载
295KB
7K SLoC
rsip-dns
SIP Rust 库,实现 RFC 3263,基于 rsip 实现
简介
此库实现了 RFC3263 中定义的所有必要 DNS 程序,允许客户端或服务器将 SIP URI 解析为 (ip, 端口, 传输) 元组。 rsip-dns
使用惰性枚举器架构,类似于 Stream(但不实现 Stream 特性),这意味着只有在需要时才会执行对 DNS 客户端的查询。
示例
创建上下文
首先,你需要指定一个 Context
,它将作为 Lookup
的指南。 Context
除了其他功能之外,还期望任何实现 DnsClient
特性的内容。有关更多信息,请参阅该部分。 rsip-dns
为该特性提供了默认实现,您可以在 trust-dns
功能标志下使用它。
use rsip_dns::*;
use std::net::{IpAddr, Ipv4Addr};
use rsip::{Transport, Port, Host};
let context = Context {
secure: true,
host: Host::from(IpAddr::V4(Ipv4Addr::new(192, 168, 2, 13))),
transport: Some(Transport::Udp),
port: Some(Port::from(5060)),
dns_client: my_dns_client,
supported_transports: Default::default()
};
在这里,我们手动创建了一个上下文,但您也可以使用 Context::initialize_from
方法从一个 URL 创建上下文。
例如
use rsip_dns::*;
use rsip::prelude::*;
let uri = rsip::Uri {
scheme: Some(rsip::Scheme::Sip),
host_with_port: ("example.com", 5060).into(),
..Default::default()
};
let context = Context::initialize_from(
uri,
dns_client,
SupportedTransports::any(),
).expect("uri and supported transports don't overlap");
查找
一旦你有了 Context
,你则需要从它创建一个 Lookup
。基本上,你感兴趣从 Lookup
中使用的只有一个(异步)方法,即 resolve_next
,实际上这是从 ResolvableExt
特性来的。
let mut lookup = Lookup::from(context);
while let target = lookup.resolve_next().await {
match target {
Some(Target {
ip_addr,
port,
transport,
}) => println!("next tuple: ({:?}, {:?}, {:?})", ip_addr, port, transport),
None => break,
}
}
对于每一次迭代,Lookup
会确保它懒性地使用底层 DNS 客户端。例如,在 SRV 记录的情况下,它首先解析 A/AAAA 记录的第一个 SRV 记录,然后移动到下一个。通常你会在前 1-2 次迭代中找到你想要的内容,但根据 RFC3263,如果你没有端口和传输,并且 NAPTR 记录没有响应,你可能需要进行 10 次甚至更多的 DNS 查询来解析对等(ip,端口,传输)元组。可能 DNS 客户端可以使用某种形式的缓存,但这取决于您,因为您需要提供一个实现 DnsClient
特性的 DNS 客户端。
解析下一个 (ip,端口,传输) 元组
RFC 3263 详细说明了根据端口和/或传输是否存在来确定 (ip, 端口, 传输) 三元组的过程,但基本上有4种不同的情况
1. IP 地址
在这种情况下,给出了一个 IP 地址,无论端口/传输是否可用。
- 如果给出了传输,则应使用它,否则使用默认传输 SIP 方案(如果是 sip,则 TLS,否则 UDP)
- 如果给出了端口,则应使用它,否则使用解析传输的默认端口
- 使用(给定 ip,给定或默认端口,给定或默认传输)
2. 带端口的域名
在这种情况下,目标是域名,并且还给出了端口。
- 如果也给出了传输,则应使用它,否则使用默认传输 SIP 方案(如果是 sip,则 TLS,否则 UDP)
- 执行 域名的 A 或 AAAA 记录查找以获取 IP 地址
- 对于找到的每个 ip 地址,使用(解析 ip,给定端口,给定或默认传输)
3. 带传输的域名
- 执行 支持的传输的 SRV 查找(应考虑 sips 或 sip 方案)
- 对于每个 SRV 结果,执行 A 或 AAAA
- 对于找到的每个地址记录,使用(ip,srv 端口,给定传输)
- 对于每个 SRV 结果,执行 A 或 AAAA
- 如果没有找到 SRV 记录,则 执行 A 或 AAAA 以获取 ip 地址
- 使用给定传输的默认端口,并尝试每个(ip,默认端口,给定传输)
4. 没有端口或传输的域名
- 执行 NAPTR 查询以获取所有替换域名
- 对于每个替换域名,执行 SRV 查找
- 根据支持的传输过滤 SRV 结果,然后根据优先级/权重排序
- 对于每个 SRV 结果,执行 A 或 AAAA
- 对于找到的每个地址记录,使用(ip,srv 端口,srv 传输)
- 对于每个替换域名,执行 SRV 查找
- 如果没有找到 NAPTR,则为支持的每个传输(带 sips 和不带 sips,如果上下文中支持安全且给定传输)构建和 执行 SRV 查找
- 对于每个 SRV 结果,执行 A 或 AAAA
- 对于找到的每个地址记录,使用(ip,srv 端口,srv 传输)
- 对于每个 SRV 结果,执行 A 或 AAAA
- 如果没有找到 SRV 记录
- 根据是否是 SIP 或 SIPS URI 使用默认传输
- 使用给定默认传输的默认端口
- 执行 A 或 AAAA 记录查找以获取 IP 地址
- 对于找到的每个 ip 地址,使用(ip,默认端口,默认传输)
使用 ResolvableExt
特性重用结构
如果您注意上面的部分,有许多可重用的组件。例如,(2) 重新使用了 (1),而 (3) 重新使用了 (2)(它重新使用了 (1)),(4) 重新使用了所有前面的。
代码结构通过定义 ResolvableExt
特性、Resolvable
类型以及基于 Resolvable
构建或实现 ResolvableExt
特性的其他类型来遵循此模式。
待办事项
- 改进错误
- 添加示例
依赖项
~7–11MB
~203K SLoC