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 网络编程

Download history 683/week @ 2024-03-13 708/week @ 2024-03-20 295/week @ 2024-03-27 297/week @ 2024-04-03 620/week @ 2024-04-10 1016/week @ 2024-04-17 319/week @ 2024-04-24 264/week @ 2024-05-01 396/week @ 2024-05-08 322/week @ 2024-05-15 514/week @ 2024-05-22 562/week @ 2024-05-29 651/week @ 2024-06-05 464/week @ 2024-06-12 633/week @ 2024-06-19 120/week @ 2024-06-26

1,994 每月下载次数
用于 3 crates

自定义许可证

230KB
5.5K SLoC

Rsip

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

一个通用的 SIP 库。它可以解析和生成所有 SIP 结构。

类似于 HTTP,这个 crate 是一个通用的库,用于处理与 SIP 协议一起工作时遇到的常见类型。您会发现 SipMessage 以及其 RequestResponse 变体类型,以及所有这些组件,如 MethodVersion、非常灵活的 UriStatusCode 等。

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,以提高类型安全性。
  • 按需提供类型化头部,如FromToContactVia等。按需类型化头部背后的原因有两点
    • 性能和内存原因:只有在需要时才会解析头部
    • 它还允许你即使在类型化头部存在错误、对端有错误或存在以前从未见过的边缘/新情况时,仍然有一个可工作的Rust SIP解析器。
  • 虽然性能始终是目标,但用户友好性和可用性是主要目标。许多有用的函数和转换使事情变得简单:)
  • 非常简单的代码结构使其很容易扩展并添加新的类型化头部。只要你能够做nom相关的事情,就很简单。目标是添加许多最新RFC(如PASSporTSHAKENpush 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