Giter Club home page Giter Club logo

rsip's Introduction

Rsip

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

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.

Features

  • This thing is fast, uses nom for basic message parsing and headers are parsed only when needed, on-demand. Intentions are to make it even faster by providing non-owning variants (&str and &[u8])
  • Strong (new)types everywhere. Even if underlying type is String, everything is a NewType for better type safety.
  • Provides typed headers on demand, like From, To, Contact, Via etc The reasoning behind on demand strongly typed headers is 2 fold:
    • perfromance & memory reasons: headers are parsed only when needed
    • it enables you to still have a working Rust SIP parser in case a typed header has a bug, the peer has a bug or there is an edge/new case never seen before.
  • While performance is always a goal, user friendliness and usability is the main goal. A lot of helpful functions and convertions to make things easy :)
  • Very simple code structure make it super easy to extend and add new typed headers As long as you can do nom stuff, it's straightforward. The goal is to add many typed headers of latest RFCs like PASSporT, SHAKEN, push notifications etc
  • Provides some extra services like Digest auth generator/validator etc Intention is to add many helper services.

Architecture

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:

  • Tokenizing: in the lowest level we have the 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.
  • Parsing: once the input has been tokenized, then there are 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.
  • each rsip type implements the Display trait and hence has a representation.

Examples

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:[email protected]>;tag=a73kszlfl
To: Bob <sips:[email protected]>
Call-ID: [email protected]
CSeq: 1 REGISTER
Contact: <sips:[email protected]>
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:[email protected]>;tag=a73kszlfl
To: Bob <sips:[email protected]>;tag=1410948204
Call-ID: [email protected]
CSeq: 1 REGISTER
WWW-Authenticate: Digest realm="atlanta.example.com", qop="auth",
 nonce="ea9c8e88df84f1cec4341ae6cbe5a359",
 opaque="", stale=FALSE, algorithm=MD5
Content-Length: 0

To Do

  • improve errors
  • write more tests, especially around edge cases
  • Make tokenizer an associated generic type on each type defined in this lib
  • implement more common traits like Hash etc

rsip's People

Contributors

elasticrash avatar g-mpuzyna avatar vasilakisfil avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rsip's Issues

Add Stir Shaken

I have forked the repo and trying to add stir shaken, but I my limited parsing is making it difficult.
The two new filed in sip that I am trying to add are "Identity" and "P-Asserted-Identity"
P-Asserted_Identity is
[Quoted string]Vec URLs are comma separated
Identity is
unquoted string;Vec where string is a base64 JWT, params are semicolon separated (key value pair)

I plan to use a JWT library to parse the JWT

I would like some help with the parsing, I am willing to be coding, testing and and create a PR.

Contributing to rsip

I am moving over here from the SIP server. I was reading over the locating sip server literature with RFC 3263. After reading through your code, you are doing a lot of nuanced things that I don't fully understand the reason for and I realized I'm not comfortable enough with Rust to contribute to the issue that was initially brought up.
I would definitely take some real easy issue if there is one. Otherwise, I am putting this on the backburner while I start the Rust portion of another personal project I'm doing. It was really cool looking through the code and see what someone who really knows their stuff is doing with the language.

Apologies if this is the wrong forum for this, I just didn't want to say I will contribute and then disappear.

example parsing header?

I'm trying to use this crate to parse SIP headers but I'm having a bit of a hard time finding how to actually go from a String or &str into a header, for example From, To.
Is there some code I can look at in order to do this?

Doesn't seem to build

When creating a new project with cargo new test then adding this as the only dependency in the cargo.toml file,I get the following error with cargo run:

error[E0391]: cycle detected when computing the supertraits of `headers::typed::TypedHeader`
  --> /rsip-0.2.0/src/headers/typed/mod.rs:25:1
   |
25 | / pub trait TypedHeader<'a>:
26 | |     std::fmt::Debug
27 | |     + std::fmt::Display
28 | |     + std::cmp::PartialEq
...  |
35 | |     type Tokenizer: Tokenize<'a>;
36 | | }
   | |_^
   |
   = note: ...which again requires computing the supertraits of `headers::typed::TypedHeader`, completing the cycle
note: cycle used when collecting item types in module `headers::typed`
  --> /rsip-0.2.0/src/headers/typed/mod.rs:25:1
   |
25 | / pub trait TypedHeader<'a>:
26 | |     std::fmt::Debug
27 | |     + std::fmt::Display
28 | |     + std::cmp::PartialEq
...  |
35 | |     type Tokenizer: Tokenize<'a>;
36 | | }
   | |_^

Any ideas?

Parse error when Request has a body

I am getting the following parser error
rsip error: could not parse part: Nom(Tag): , Nom(Alt)

When I receive, and try to parse an INVITE with a body.

This is an example of how the last part of the INVITE Request and its body looks like

.... Accept: application/sdp\r\n Content-Type: application/sdp\r\n Content-Length: 406\r\n\r\n v=0\r\n o=aculab-02360801 2108656133 265635486 IN IP4 XX.XX.XX.XX\r\n s=-\r\n c=IN IP4 XX.XX.XX.XX\r\n t=0 0\r\n m=audio 47580 RTP/AVP 120 0 18 8 9 96\r\n c=IN IP4 XX.XX.XX.XX\r\n a=rtpmap:96 telephone-event/8000\r\n a=fmtp:96 0-15\r\n a=ptime:20\r\n a=rtpmap:120 OPUS/48000/2\r\n a=fmtp:120 minptime=20; maxplaybackrate=16000; maxaveragebitrate=24000; useinbandfec=1\r\n a=rtpmap:18 G729/8000\r\n a=fmtp:18 annexb=yes\r\n a=rtpmap:9 G722/8000\r\n"

After writing a few dozens tests I realized that the issue is that it does not parse the colon : character in the attributes (https://datatracker.ietf.org/doc/html/rfc4566#section-5.13) correctly

I might try to fix the issue myself, but I thought it would be nice to raise an issue so as to be aware of it

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.