Giter Site home page Giter Site logo

ersip's Introduction

Build Status Coverage Status

Erlang SIP and SDP library

Documentation

Full module documentation is available here.

Approach to design

What you cannot find in ersip:

  • Supervisors
  • Servers
  • ETS
  • Dependencies

You can find only pure functions that are compliant to SIP specification.

Real world examples

Working examples:

Provided primitives

Primitives that provided by SIP stack:

  • Connection parser split streams and retrieve low level SIP messages (ersip_conn)
  • Low level SIP message processing (ersip_msg)
  • Lazy high-level SIP message processing (ersip_sipmsg)
  • Transactions support (ersip_trans)
  • Basic UAS support (ersip_uas)
  • Registrar function support (ersip_registrar)
  • SIP stateful proxy function support (ersip_proxy)
  • SIP common dialog support (ersip_dialog)

Supported RFC

RFC supported in this SIP stack:

  • RFC 2543 SIP: Session Initiation Protocol (backward compatibility)
  • RFC 3261 SIP: Session Initiation Protocol (partially):
    • 7 SIP Messages
    • 8.2 UAS Behavior
    • 9 Canceling a Request
    • 10 Registration - Registrar part only
    • 12 Dialogs
    • 16 Proxy Behavior
    • 17 Transactions
    • 18 Transport
    • 19 Common Message Components
  • RFC 3262 Reliability of Provisional Responses in the Session Initiation Protocol (SIP)
    • 6 Definition of the PRACK Method
    • 7 Header Field Definitions
    • 7.1 RSeq
    • 7.2 RAck
  • RFC 6026 Correct Transaction Handling for 2xx Responses to Session Initiation Protocol (SIP) INVITE Requests
  • RFC 3581 An Extension to the Session Initiation Protocol (SIP) for Symmetric Response Routing (rport)
  • RFC 4475 Session Initiation Protocol (SIP) Torture Test Messages
  • RFC 5118 Session Initiation Protocol (SIP) Torture Test Messages for Internet Protocol Version 6 (IPv6)
  • RFC 4566 SDP: Session Description Protocol (partially)
  • RFC 3515 The Session Initiation Protocol (SIP) Refer Method (header/method only):
    • 2.1 The Refer-To Header Field
  • RFC 6665 SIP-Specific Event Notification (header/method only)
    • 8.2.1. "Event" Header Field
    • 8.2.3. "Subscription-State" Header Field
  • RFC 3891 The Session Initiation Protocol (SIP) "Replaces" Header (header only)
    • 6.1. The Replaces Header
  • RFC 3966 The tel URI for Telephone Numbers

License

MIT

Roadmap

  • Basic low-level parser (completed)
  • Essential headers support (completed)
  • High-level SIP message (completed)
  • Stateless proxy support (completed)
    • Request passing (completed)
    • Response passing (completed)
  • Transaction support (completed)
    • Transaction identification (completed)
    • Non-INVITE transaction (completed)
    • INVITE transaction (completed)
  • Registrar support (completed)
  • Parser limits enforcement (completed)
  • Stateful proxy support (completed)
  • Common dialog support (completed)
  • SDP support (completed)
  • Detailed documentation and tutorials
  • Authorization and proxy authorization
  • INVITE dialog support
  • High-level UA support

ersip's People

Contributors

0x42 avatar bottleneko avatar hagane avatar juise avatar kpribylov avatar odobenus avatar polinamityakina avatar poroh avatar tobias-tress 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ersip's Issues

broadcast adresses

ersip allows to use broadcast ip addresses in the VIA header. Kind of

Via: SIP/2.0/UDP 255.255.255.255

I believe it should be counted as error

header names must be trimed

the headers like
To : somevalue
leads to errors. The point is -- spaces after header name, before colon. Parsing returns {error,{header_error,{from,{no_required_header,<<"from">>}}}}

parsed structure contains:

{hdr_key,<<"from   ">>} =>
               {header,<<"from   ">>,
                       {hdr_key,<<"from   ">>},
                       [<<"sip:[email protected]">>]}

it looks like there are some unwanted spaces in the header name

UAS: crash crash in case of bad To header

-spec header_inspection(ersip_sipmsg:sipmsg(), uas()) -> steps_result().
header_inspection(SipMsg, #uas{options = Options} = UAS) ->
    case ersip_sipmsg:parse(SipMsg, [to, callid, cseq, require]) of
        {error, _} = ParseError ->
            {reply, make_bad_request(SipMsg, ParseError, Options)};
        {ok, SipMsg1} ->
            %% We cannot check loops & merged requests here because it
            %% requires global context
            steps([fun check_ruri/2,
                   fun maybe_check_require/2],
                  SipMsg1,
                  UAS)
    end.

make_bad_request definitely crash here in case bad Call-Id/From/To/CSeq/Topmost Via

URI parameters

1> ersip_uri:parse(<<"sip:a.com;ttl=1">>).
{ok,{uri,{scheme,sip},
         {sip_uri_data,undefined,
                       {hostname,<<"a.com">>},
                       undefined,
                       #{ttl => 1},
                       #{}}}}
2> ersip_uri:parse(<<"sip:a.com;tt%6C=1">>).
{ok,{uri,{scheme,sip},
         {sip_uri_data,undefined,
                       {hostname,<<"a.com">>},
                       undefined,
                       #{<<"tt%6c">> => <<"1">>},
                       #{}}}}

But must be equal

function_clause in case of ip6 and port in via

{error,function_clause,
                        [{ersip_hdr_via,parse_sent_by_host_port,
                             [<<":19823">>,port,
                              #{host => {ipv6,{0,0,0,0,0,0,0,1}},
                                rest => <<";branch=z9hG4bKbh19">>}],
                             [{file,
                                  "/home/odobenus/devel/ersip/_build/test/lib/ersip/src/ersip_hdr_via.erl"},
                              {line,206}]},

the header is

Via: SIP/2.0/UDP [::1]:19823;branch=z9hG4bKbh19

Error in specifications

headers = #{} :: #{binary() := ersip_hdr:header()},

From code (ex. set_header()) it is clear that type of headers map should be #{ersip_hdr:header_key() := ersip_hdr:header()}.

Also ersip_sipmsg defines headers to be #{atom() := value()}, while functions in ersip_siphdr allow header key to be atom() or binary(). Ex.

Header :: known_header() | binary(),

Currently this complicates code reading.

dialyzer

dialyzer on master shows

ersip/src/ersip_registrar_binding.erl:56: Invalid type specification for function ersip_registrar_binding:update/4. The success typing is (pos_integer(),{'callid',binary()},non_neg_integer(),#binding{contact::{'contact','undefined' | {'display_name',binary() | [any()]},{'uri',{,},{,} | {,,,,,}},[{,}]},callid::{'callid',binary()},cseq::non_neg_integer(),expires::non_neg_integer()}) -> #binding{contact::{'contact','undefined' | {'display_name',binary() | [any()]},{'uri',{,},{,} | {,,,,,}},[{,}]},callid::{'callid',binary()},cseq::non_neg_integer(),expires::pos_integer()}

via 'sip version' does not support inner spaces

sip version in the request line cannot contain any spaces. But in the Via header, actually it can.

RFC 3261:

Specifically, LWS on either side of the   ":" or "/" is allowed, as shown here:

      Via: SIP / 2.0 / UDP first.example.com: 4000;ttl=16
        ;maddr=224.2.0.1 ;branch=z9hG4bKa7c6a8dlze.1

abnf:

Via               =  ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm)
via-parm          =  sent-protocol LWS sent-by *( SEMI via-params )
sent-protocol     =  protocol-name SLASH protocol-version   SLASH transport
SLASH   =  SWS "/" SWS  ;  slash

But now parsing gives error for such headers:

<<"SIP / 2.0 / UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1">>

{error,{no_separator,47, <<" / 2.0 / UDP bigbox3.site3.atlanta.com;branch=z9"...>>}}

Proxy: Record-Route in REGISTER/OPTIONS requests

Right now proxy forwards all requests in the same manner (if record-route uri is set then it adds it to all reqursts). Because Record-route is header related only to dialogs then proxy should not add rr header to requests that for sure not creating dialog such as:

  • OPTIONS
  • REGISTER

ICE

Very nice project! I'm working on a WebRTC implementation and use it for SDP.

I'm wondering if there's any plan to implementing ICE (RFC 8445) or if that is outside the scope of this project?

URI parameters are not in the same order with original uri

When we try to create a new request we use following

Original URI = "sip:[email protected]:45831;rinstance=54d13cbf8a4c07df;transport=UDP"
When we try to create a new request with above uri its pramater order changed and appears as follow
"sip:[email protected]:45831;transport=UDP;rinstance=54d13cbf8a4c07df"
This is because when ruri is parsed a predefined record is used and there transport param precedes to other params

:ersip_uri.parse( "sip:[email protected]:45831;rinstance=54d13cbf8a4c07df;transport=UDP")
{:ok,
{:uri, :sip,
{:sip_uri, false, {:user, "bob"}, {:ipv4, {192, 168, 1, 59}}, "192.168.1.59",
45831,
%{:transport => {:transport, :udp}, "rinstance" => "54d13cbf8a4c07df"},
%{}}}}

Is it possible to parse uri in the same order as the original uri?

Via: unexpected empty key in params

11> ersip_hdr_via:parse(<<"SIP/2.0/TCP 192.168.1.1:5090">>).                                                     
{ok,{via,{sent_protocol,<<"SIP">>,<<"2.0">>,{transport,tcp}},
         {sent_by,{ipv4,{192,168,1,1}},5090},
         #{<<>> => true}}}

Incorrect take_via when one line of Via header contains multiple values separated by commas

One line of Via header may contain multiple values (multiple via) separated by commas
For example:
Via: SIP/2.0/UDP 127.0.0.1:5060;branch=z9hG4bK6hh35hsh460350psr7svreer7,SIP/2.0/UDP 127.0.0.2:5060;branch=z9hG4bK03v084g25gh20v0pxr263vg66,SIP/2.0/UDP 127.0.0.3:5060;branch=z9hG4bK4a47.8726b736377fdd93dd24ffb625e68c98.0

If you parse such header with ersip_hdr_via:parse() it will return the first VIA from the line and ignore other values/vias.
As a result ersip_conn:take_via does not work as expected. It should take only the first value of VIA and leave rest VIAs, but now it remove first line of VIA even if it contains multiple values.

Test to reproduce:

Msg = {message,{response,200,<<"OK">>},#{{hdr_key,via} =>
    {header,<<"Via">>,{hdr_key,via},
     [<<"SIP/2.0/UDP 192.168.31.56:5063;branch=z9hG4bK6hh35hsh460350psr7svreer7,SIP/2.0/UDP 127.0.0.2:5060;branch=z9hG4bK03v084g25gh20v0pxr263vg66,SIP/2.0/UDP 127.0.0.3:5060;branch=z9hG4bK4a47.8726b736377fdd93dd24ffb625e68c98.0">>]}},[<<>>],undefined}.

Conn = {sip_conn,{{ipv4,{192,168,31,56}},5063},{{ipv4,{192,168,31,56}},11000},{transport,udp},#{source_id => #{}},
            undefined}.

ersip_conn:take_via(Msg, Conn).

Now returned message has empty VIA

    {message,{response,200,<<"OK">>},
             #{{hdr_key,via} => {header,<<"Via">>,{hdr_key,via},[]}},
             [<<>>],
             undefined}}

But should contain rest values of the VIA line

    {message,{response,200,<<"OK">>},
             #{{hdr_key,via} =>
                   {header,<<"Via">>,
                           {hdr_key,via},
                           [<<"SIP/2.0/UDP 127.0.0.2:5060;branch=z9hG4bK03v084g25gh20v0pxr263vg66,S"...>>]}},
             [<<>>],
             undefined}}

function clause in the ersip_hdr_fromto:parse

H = ersip_hdr:new(<<"From">>),
  H1 = ersip_hdr:add_value( <<"sip:example.net;a">>, H),
  {ok, _} = ersip_hdr_fromto:parse(H1),

gives

Failure/Error: {error,function_clause,
                        [{ersip_bin,trim_head_lws,  [novalue],
                             [{file,
                                  "/home/alexey/sand/new/ersip/_build/test/lib/ersip/src/ersip_bin.erl"},
                              {line,69}]},

via received + raw ip6 = parse error

{error,  {header_error,   {topmost_via,   {invalid_received,<<"2001:db8::9:255">>}}}

header is:

Via: SIP/2.0/UDP [2001:db8::9:1];received=2001:db8::9:255;branch=z9hG4bKas3
via-received   = "received"   EQUAL   ( IPv4address  /  IPv6address ) 

RURI manipulation error in ersip_sipmsg

Following simple code snipped raise an erlang:error.

raw_ruri_manipulation_test() ->
    SipMsg = default_sipmsg(),
    RURI = ersip_sipmsg:ruri(SipMsg),
    SipMsg2 = ersip_sipmsg:set_ruri(RURI, SipMsg),
    RawMsg = ersip_sipmsg:raw_message(SipMsg2),
    {ok, _} = ersip_sipmsg:parse(RawMsg, all).

Stack trace suggests type error when ersip_sipmsg:set_ruri(_) calls ersip_msg:set(ruri, _) to set raw value as iolist() instead of binary().

in function binary:split/2
  called as split([<<"sip">>,58,[[<<"bob">>,64],<<"biloxi.com">>,[],[]]],<<":">>)
in call from ersip_uri:split_scheme/1 (/Users/anton/Workspace/ersip/_build/test/lib/ersip/src/ersip_uri.erl, line 487)
in call from ersip_uri:parse/1 (/Users/anton/Workspace/ersip/_build/test/lib/ersip/src/ersip_uri.erl, line 103)
in call from ersip_sipmsg:ruri_from_raw/1 (/Users/anton/Workspace/ersip/_build/test/lib/ersip/src/ersip_sipmsg.erl, line 368)
in call from ersip_sipmsg:create_from_raw/1 (/Users/anton/Workspace/ersip/_build/test/lib/ersip/src/ersip_sipmsg.erl, line 309)
in call from ersip_sipmsg:parse/2 (/Users/anton/Workspace/ersip/_build/test/lib/ersip/src/ersip_sipmsg.erl, line 208)
in call from ersip_sipmsg_test:raw_ruri_manipulation_test/0 (/Users/anton/Workspace/ersip/_build/test/lib/ersip/test/ersip_sipmsg_test.erl, line 442)
**error:badarg

Unlimited SIP message size

Current parser can consume unlimited amount of memory for message.
Limitation mechanisms must be implemented to avoid out of memory attack.

Following limitation must be added:

  • Max first line length
  • Max header length
  • Max number of headers
  • Max body length

or

  • Max total message length

Host comparison

In some situation it is required to check that hosts are equal.
This implemented as comparison of "keys" (ersip_host:make_key/1 function)

Problem is: keys are not equal for domain names:
"bigbox3.site3.atlanta.com." (with root domain) and "bigbox3.site3.atlanta.com" (without root domain).

Test for this bug

REGISTER example

This looks like a promising minimal dependency SIP stack for OTP :-)

Do you have an example for a REGISTER command?

Registrar: expires randomization

Registrar should support randomization of expiration time. Right now it registers exactly with requested time that may cause distributed clients sync after any event (like re-provisioning etc).

Error in URI parser

As far as I know RFC allows arbitrary number of parameters in SIP URI. But ersip_uri module fails to parse more then two parameters in URI. This is easily reproduced with following example:

parse_three_params_test() ->
  Uri = ersip_uri:make(<<"sip:[email protected];param1=value1;param2=value2;param3=value3">>),
  Params = #{<<"param1">> => <<"value1">>,
             <<"param2">> => <<"value2">>,
             <<"param3">> => <<"value3">>},
  ?assertEqual(Params, ersip_uri:params(Uri)).

That results in error:

**error:{assertEqual,[{module,ersip_uri_test},
              {line,318},
              {expression,"ersip_uri : params ( Uri )"},
              {expected,#{<<"param1">> => <<"value1">>,
                          <<"param2">> => <<"value2">>,
                          <<"param3">> => <<"value3">>}},
              {value,#{<<"param1">> => <<"value1">>,
                       <<"param2">> => <<"value2;param3=value3">>}}]}

Note that <<"sip:[email protected];param1=value1;param2=value2">> is parsed correctly, other number of params wasn't tested.

wrong hostname parsing

parse_ipv4_address(R);

This code assumes all hosts that start with digit are ip address. But as the matter of fact rules for hostname is

host   = hostname  /  IPv4address  /  IPv6reference 
hostname   =*( domainlabel   "." )   toplabel   [ "." ] 
domainlabel   = alphanum  /  alphanum   *( alphanum  /  "-" )   alphanum 
toplabel   = ALPHA  /  ALPHA   *( alphanum  /  "-" )   alphanum 

it allows hosts like 192.168.10.ru .So. It's bad idea to make conclusion what type of host by first char

non token chars in the display name

from and to fields can have display name as set of tokens, or quoted string. That;s why header

From: Bond, James  <sip:[email protected]>

should return parse error, as comma is not allowed in the tokens

token is 1*( alphanum / "-" / "." / "!" / "%" / "*" / "_" / "+" / "`" / "'" / "~" )

Pattern matching instead of binary:part

split_hostport(<<$[, _/binary>> = IPv6RefPort) ->

split_hostport(<<$[, _/binary>> = IPv6RefPort) ->
    case binary:match(IPv6RefPort, <<"]">>) of
        nomatch ->
            {error, {einval, invalid_ipv6_reference}};
        {Pos, 1} when Pos + 1 =:= byte_size(IPv6RefPort) ->
            %% No port specified
            {ok, {IPv6RefPort, <<>>}};
        {Pos, 1} ->
            Host = binary:part(IPv6RefPort, {0, Pos+1}),
            Rest = binary:part(IPv6RefPort, {Pos+1, byte_size(IPv6RefPort)-Pos-1}),
            case Rest of
                <<$:, Port/binary>> when Port =/= <<>> ->
                    {ok, {Host, Port}};
                Else ->
                    {error, {invalid_port, Else}}
            end
end;

Simplified version

split_hostport(<<$[, _/binary>> = IPv6RefPort) ->
    case binary:match(IPv6RefPort, <<"]">>) of
        nomatch ->
            {error, {einval, invalid_ipv6_reference}};
        {Pos, 1} ->
            Pos1 = Pos+1,
            <<Host:Pos1/binary, Rest/binary>>  = IPv6RefPort, ,
            case Rest of
                <<>> -> 
                    {ok, {Host, <<>>}};
                <<$:, Port/binary>> when Port =/= <<>> ->
                    {ok, {Host, Port}};
                Else ->
                    {error, {invalid_port, Else}}
            end
end;

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.