Crates.io | rsip |
lib.rs | rsip |
version | 0.4.0 |
source | src |
created_at | 2021-07-16 13:30:40.167718 |
updated_at | 2022-04-01 04:33:26.887286 |
description | SIP Rust library, parser & generator of SIP |
homepage | |
repository | https://github.com/vasilakisfil/rsip |
max_upload_size | |
id | 423626 |
size | 407,811 |
A common general purpose library for SIP. It can parse and generate all SIP structures.
Like HTTP, this crate is a general purpose library for common types found when
working with the SIP protocol.
You’ll find the SipMessage
and its Request
and Response
variant types for working
as either a client or a server as well as all of their components, like Method
,
Version
, a very flexible Uri
, StatusCode
etc.
Rsip is capable of parsing messages from bytes, &str or String using nom parser and can also generate SIP messages using helpful structs.
You will notably not find an implementation of sending requests or spinning up a SIP server in this crate. SIP servers, by nature of SIP protocol, are very complex usually and will sit at different crates/libs. Rsip is intended to be the de-facto SIP base library for Rust. It was built to be used inside viska initially but then was split to a different crate.
It was inspired by libsip but has taken a bit different path regarding parsing, flexibility & safety.
For locating SIP servers (RFC3263) take a look on rsip-dns library.
&str
and &[u8]
)From
, To
, Contact
, Via
etc
The reasoning behind on demand strongly typed headers is 2 fold:
Each type in rsip has a tokenizer attached. This is not enforced by the type system yet, however very soon this will be the case. In brief, for every rsip type we have:
Tokenizer
which is capable of tokenizing the input.
All common tokenizers accept abstract input, either &str
or &[u8]
so it can be reused when
the input is plain bytes, or when the input has already been parsed and it's a String
/&str
,
like the headers.TryFrom
impls from the relevant type
tokenizer to the actual type.
This is the parsing step where tokens (in the form of &str
or &[u8]
) are transformed to
integers, strings and rsip types.Display
trait and hence has a representation.For instance, generating the Register request found in section 2.1 of RFC3665
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
can be done like that:
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()
}
And the response similarly can be generated:
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())
}
which generates the following:
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