6 个版本 (3 个破坏性更新)
0.4.0 | 2022年4月1日 |
---|---|
0.3.0 | 2022年2月1日 |
0.2.0 | 2021年9月7日 |
0.1.2 | 2021年7月21日 |
#1219 in 网络编程
1,994 每月下载次数
用于 3 crates
230KB
5.5K SLoC
Rsip
一个通用的 SIP 库。它可以解析和生成所有 SIP 结构。
类似于 HTTP,这个 crate 是一个通用的库,用于处理与 SIP 协议一起工作时遇到的常见类型。您会发现 SipMessage
以及其 Request
和 Response
变体类型,以及所有这些组件,如 Method
、Version
、非常灵活的 Uri
、StatusCode
等。
Rsip 可以使用 nom 解析器解析来自字节、&str 或 String 的消息,并且还可以使用有用的结构体生成 SIP 消息。
您在这个 crate 中不会找到发送请求或启动 SIP 服务器的实现。由于 SIP 协议的特性,SIP 服务器通常非常复杂,通常位于不同的 crates/libs 中。Rsip 旨在成为 Rust 的 de-facto SIP 基础库。它最初是为 viska 构建的,但后来被拆分到一个不同的 crate 中。
它受到了 libsip 的启发,但在解析、灵活性和安全性方面采取了不同的路径。
有关定位 SIP 服务器(RFC3263),请查看 rsip-dns 库。
特性
- 这个玩意儿速度很快,使用nom进行基本的消息解析,并且只有在需要时才会解析头部,按需进行。目的是通过提供非拥有变体(
&str
和&[u8]
)来使其更快。 - 到处都是强(新)类型。即使底层类型是String,所有内容都是NewType,以提高类型安全性。
- 按需提供类型化头部,如
From
、To
、Contact
、Via
等。按需类型化头部背后的原因有两点- 性能和内存原因:只有在需要时才会解析头部
- 它还允许你即使在类型化头部存在错误、对端有错误或存在以前从未见过的边缘/新情况时,仍然有一个可工作的Rust SIP解析器。
- 虽然性能始终是目标,但用户友好性和可用性是主要目标。许多有用的函数和转换使事情变得简单:)
- 非常简单的代码结构使其很容易扩展并添加新的类型化头部。只要你能够做nom相关的事情,就很简单。目标是添加许多最新RFC(如PASSporT、SHAKEN、push notifications等)的许多类型化头部。
- 提供一些额外服务,如摘要认证生成器/验证器等。目标是添加许多辅助服务。
架构
rsip中的每个类型都有一个标记器。这目前还没有由类型系统强制执行,但很快就会是这样。简而言之,对于每个rsip类型,我们都有
- 标记化:在最底层,我们有
Tokenizer
,它能够对输入进行标记化。所有常见的标记化器都接受抽象输入,要么是&str
,要么是&[u8]
,因此它可以在输入是纯字节或输入已经被解析并且是String
/&str
(如头部)时重用。 - 解析:一旦输入被标记化,然后有从相关类型标记化器到实际类型的
TryFrom
实现。这是解析步骤,其中标记(形式为&str
或&[u8]
)被转换为整数、字符串和rsip类型。 - 每个rsip类型实现
Display
特质,因此有一个表示形式。
示例
例如,生成在RFC3665第2.1节中找到的Register请求
REGISTER sips:ss2.biloxi.example.com SIP/2.0
Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashds7
Max-Forwards: 70
From: Bob <sips:bob@biloxi.example.com>;tag=a73kszlfl
To: Bob <sips:bob@biloxi.example.com>
Call-ID: 1j9FpLxk3uxtm8tn@biloxi.example.com
CSeq: 1 REGISTER
Contact: <sips:bob@client.biloxi.example.com>
Content-Length: 0
可以这样完成
fn generate_register_request() -> rsip::SipMessage {
let mut headers: rsip::Headers = Default::default();
let base_uri = rsip::Uri {
scheme: Some(rsip::Scheme::Sips),
auth: Some(("bob", Option::<String>::None).into()),
host_with_port: rsip::Domain::from("biloxi.example.com").into(),
..Default::default()
};
headers.push(
rsip::typed::Via {
version: rsip::Version::V2,
transport: rsip::Transport::Tls,
uri: rsip::Uri {
host_with_port: (rsip::Domain::from("client.biloxi.example.com"), 5060).into(),
..Default::default()
},
params: vec![rsip::Param::Branch(rsip::param::Branch::new(
"z9hG4bKnashds7",
))],
}
.into(),
);
headers.push(rsip::headers::MaxForwards::default().into());
headers.push(
rsip::typed::From {
display_name: Some("Bob".into()),
uri: base_uri.clone(),
params: vec![rsip::Param::Tag(rsip::param::Tag::new("a73kszlfl"))],
}
.into(),
);
headers.push(
rsip::typed::To {
display_name: Some("Bob".into()),
uri: base_uri.clone(),
params: Default::default(),
}
.into(),
);
headers.push(rsip::headers::CallId::default().into());
headers.push(
rsip::typed::CSeq {
seq: 1,
method: rsip::Method::Register,
}
.into(),
);
headers.push(
rsip::typed::Contact {
display_name: None,
uri: base_uri,
params: Default::default(),
}
.into(),
);
headers.push(rsip::headers::ContentLength::default().into());
rsip::Request {
method: rsip::Method::Register,
uri: rsip::Uri {
scheme: Some(rsip::Scheme::Sips),
host_with_port: rsip::Domain::from("ss2.biloxi.example.com").into(),
..Default::default()
},
version: rsip::Version::V2,
headers: headers,
body: Default::default(),
}
.into()
}
响应也可以类似地生成
pub fn create_unauthorized_from(request: rsip::Request) -> Result<rsip::SipMessage, crate::Error> {
//imports helpful header traits
use rsip::prelude::*;
let mut headers: rsip::Headers = Default::default();
headers.push(request.via_header()?.clone().into());
headers.push(request.from_header()?.clone().into());
let mut to = request.to_header()?.typed()?;
to.with_tag("1410948204".into());
headers.push(to.into());
headers.push(request.call_id_header()?.clone().into());
headers.push(request.cseq_header()?.clone().into());
headers.push(rsip::Header::ContentLength(Default::default()));
headers.push(rsip::Header::Server(Default::default()));
headers.push(
rsip::typed::WwwAuthenticate {
realm: "atlanta.example.com".into(),
nonce: "ea9c8e88df84f1cec4341ae6cbe5a359".into(),
algorithm: Some(rsip::headers::auth::Algorithm::Md5),
qop: Some(rsip::headers::auth::Qop::Auth),
stale: Some("FALSE".into()),
opaque: Some("".into()),
..Default::default()
}
.into(),
);
Ok(rsip::Response {
status_code: 401.into(),
headers,
version: rsip::Version::V2,
body: Default::default()
}
.into())
}
这将生成以下内容
SIP/2.0 401 Unauthorized
Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashds7
;received=192.0.2.201
From: Bob <sips:bob@biloxi.example.com>;tag=a73kszlfl
To: Bob <sips:bob@biloxi.example.com>;tag=1410948204
Call-ID: 1j9FpLxk3uxtm8tn@biloxi.example.com
CSeq: 1 REGISTER
WWW-Authenticate: Digest realm="atlanta.example.com", qop="auth",
nonce="ea9c8e88df84f1cec4341ae6cbe5a359",
opaque="", stale=FALSE, algorithm=MD5
Content-Length: 0
待办事项
- 改进错误
- 编写更多测试,特别是边缘情况
- 将标记器作为每个在此库中定义的类型关联泛型类型
- 实现更多常见特质,如Hash等
依赖关系
~4.5MB
~90K SLoC