#dns #sip #srv #ip-address #voip #3263

rsip-dns

SIP Rust 库,实现 RFC 3263,基于 rsip 实现

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

License: MIT Build status Crates.io Version Minimum rustc version

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 以获取 ip 地址
    • 使用给定传输的默认端口,并尝试每个(ip,默认端口,给定传输)
4. 没有端口或传输的域名
  • 执行 NAPTR 查询以获取所有替换域名
    • 对于每个替换域名,执行 SRV 查找
      • 根据支持的传输过滤 SRV 结果,然后根据优先级/权重排序
      • 对于每个 SRV 结果,执行 A 或 AAAA
        • 对于找到的每个地址记录,使用(ip,srv 端口,srv 传输)
  • 如果没有找到 NAPTR,则为支持的每个传输(带 sips 和不带 sips,如果上下文中支持安全且给定传输)构建和 执行 SRV 查找
    • 对于每个 SRV 结果,执行 A 或 AAAA
      • 对于找到的每个地址记录,使用(ip,srv 端口,srv 传输)
  • 如果没有找到 SRV 记录
    • 根据是否是 SIP 或 SIPS URI 使用默认传输
    • 使用给定默认传输的默认端口
    • 执行 A 或 AAAA 记录查找以获取 IP 地址
      • 对于找到的每个 ip 地址,使用(ip,默认端口,默认传输)

使用 ResolvableExt 特性重用结构

如果您注意上面的部分,有许多可重用的组件。例如,(2) 重新使用了 (1),而 (3) 重新使用了 (2)(它重新使用了 (1)),(4) 重新使用了所有前面的。

代码结构通过定义 ResolvableExt 特性、Resolvable 类型以及基于 Resolvable 构建或实现 ResolvableExt 特性的其他类型来遵循此模式。

待办事项

  • 改进错误
  • 添加示例

依赖项

~7–11MB
~203K SLoC