Coder Social home page Coder Social logo

balena / elixir-sippet Goto Github PK

View Code? Open in Web Editor NEW
77.0 5.0 22.0 471 KB

An Elixir library designed to be used as SIP protocol middleware.

License: BSD 3-Clause "New" or "Revised" License

Elixir 52.91% Makefile 1.19% C++ 40.17% C 5.73%
protocol transport-protocols elixir-library sip-library middleware hex datagram

elixir-sippet's People

Contributors

balena avatar blackham avatar brendanball avatar martinos avatar mickel8 avatar nitrino avatar pramsky avatar sgfn avatar xadhoom 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

Watchers

 avatar  avatar  avatar  avatar  avatar

elixir-sippet's Issues

Error, module could not be loaded", Sippet.Transaction

When I send an OPTION request, I get the following error message:

sipsak -vv -c sip:[email protected] -s sip:nobody@localhost
18:49:09.272 [error] GenStateMachine {Sippet.Transactions.Registry, ~K[z9hG4bK.74bac126|:options|127.0.0.1:62145]} terminating
** (ErlangError) Erlang error: {:"module could not be loaded", Sippet.Transaction}
    (sippet) lib/sippet/transactions/server/non_invite.ex:12: Sippet.Transactions.Server.NonInvite.trying/3
    (stdlib) gen_statem.erl:1240: :gen_statem.call_state_function/5
    (stdlib) gen_statem.erl:1058: :gen_statem.loop_event_enter/11
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

Here is my Core implementation:

defmodule MyCore do
  use Sippet.Core
  require Logger
  alias Sippet.Transaction
  alias Sippet.Message

  def receive_request(request, server_key) do
    Logger.info("logging request: #{inspect(request)}")
    request |> Message.to_response(302) |> Transaction.send_response(server_key)
  end
end

Maybe I don't understand exactly how to declare a Core module.

{:error, {:error, :multiple_definition}}

I am seeing some SIP traffic with two P-Asserted-Identity fields

...
P-Asserted-Identity: <sip:[email protected];user=phone;cpc=ordinary>
P-Asserted-Identity: <tel:5554443333;phone-context=+55;cpc=ordinary>
...

It looks like the RFC allows one SIP and one TEL P-Asserted-Identity.

https://www.ietf.org/rfc/rfc3325.txt
If there is no P-Asserted-Identity header field present, a proxy MAY
add one containing at most one SIP or SIPS URI, and at most one tel
URL.

error: ‘numeric_limits’ is not a member of ‘std’

Problem:

/home/your_mom/dev/my_bed/deps/sippet/c_src/utils.cc: In static member function ‘static bool {anonymous}::IteratorRangeToNumber<IteratorRangeToNumberTraits>::Invoke(const_iterator, const_iterator, value_type*)’:
/home/your_mom/dev/my_bed/deps/sippet/c_src/utils.cc:99:17: error: ‘numeric_limits’ is not a member of ‘std’
99 |       if (!std::numeric_limits<value_type>::is_signed) {

Hack:
Edit the ./deps/sippet/c_src/utils.cc and add #include < limits >

#include <string>
#include <iostream>
#include <limits>

Fix:
Eh... I'm happy with the hack.

System:
Arch Linux

brett@beefstick: /tmp $ uname -a
Linux beefstick 6.1.15-1-lts #1 SMP PREEMPT_DYNAMIC Fri, 03 Mar 2023 12:22:08 +0000 x86_64 GNU/Linux
brett@beefstick: /tmp $ gcc --version
gcc (GCC) 12.2.1 20230201
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Running multiple Core on different ports

I am wondering if there's a way of running 2 instances of Sippet that listen on two different ports. But since I am fairly new to Elixir, I don't see if it's event possible with the current code base. Am I right?

Unable to link Sippet on Apple Silicon with LDFLAGS set

Hi,
It appears that because of the structure of the Makefile, having LDFLAGS set in the env makes the compilation of Sippet fail on Apple Silicon.

In our app, we're using Sippet as well as fast_tls. The issue is, fast_tls needs LDFLAGS to contain the path to the OpenSSL library (i.e. LDFLAGS=-L/opt/homebrew/opt/openssl/lib), and will not compile otherwise. When we try to compile everything at once, though, linking of Sippet fails because the Makefile uses conditional variable assignment ?= and does not overwrite/append to LDFLAGS if it is already set -- which is the case.

As far as I've tested, the only linker flag necessary for Sippet to compile successfully with this setup is -undefined suppress.

The most straightforward way of alleviating this issue I can think of is to always append the -undefined suppress flag, since it is necessary to link anyway. This is a simple fix, and doesn't change the general behaviour of the Makefile:

diff --git a/c_src/Makefile b/c_src/Makefile
index c20a402..a6dc5f1 100644
--- a/c_src/Makefile
+++ b/c_src/Makefile
@@ -73,7 +73,9 @@ endif
        CC ?= cc
        CFLAGS ?= -O3 -std=c11 $(ARCHFLAGS) -fstack-protector -Wall -Wmissing-prototypes
        CXXFLAGS ?= -O3 -std=c++11 $(ARCHFLAGS) -fstack-protector -Wall
-       LDFLAGS ?= $(ARCHFLAGS) -flat_namespace -undefined suppress
+       LDFLAGS ?= $(ARCHFLAGS) -flat_namespace
+       # Necessary to link successfully on Apple Silicon
+       LDFLAGS += -undefined suppress
 else ifeq ($(PLATFORM),freebsd)
        CC ?= cc
        CFLAGS ?= -O3 -std=c11 -finline-functions -fstack-protector -Wall -Wmissing-prototypes

Please let me know whether you're OK with this solution, and I'll be happy to make the relevant PR.

Logs from the failed linking for reference:

ld: Undefined symbols:
  _enif_alloc_binary, referenced from:
      (anonymous namespace)::ParseMultipleUriParams(enif_environment_t*, std::__1::__wrap_iter<char const*>, std::__1::__wrap_iter<char const*>) in parser.o
      ...
  _enif_get_list_cell, referenced from:
      parse_wrapper(enif_environment_t*, int, unsigned long const*) in parser.o
  _enif_get_map_value, referenced from:
      parse_wrapper(enif_environment_t*, int, unsigned long const*) in parser.o
  [...] etc.

100 Trying is not returned after sending a response

Hi,
Thank you for this library, I try to implement a scenario such as receive invite request, send 100 Trying, send 302 Moved Temporarily. Bu after returning 302 from my Sippet.Core module 100 Trying is not send because Sippet.Transactions.Server.Invite state is changed by outgoing response.

The another thing is after sending 302 outgoing response, Sippet.Transactions.Server.Invite retrying the send the the same response about 10 times within a timeout period.

Is this a normal behaviour?

My Sippet.Core receive_request function;

@spec receive_request(Message.request, Transactions.Server.t | nil) :: any
  def receive_request(incoming_request, server_key) do
    if incoming_request.start_line.method == :invite do
      incoming_request |> Sippet.Message.to_response(302) |> Sippet.Transactions.send_response(server_key)
    end
  end

Improve explanation for the Sippet.send function in README.md

I am trying migrate my app to the latest version of Sippet and I don't understand how to send a response back to the caller.

When I read the README file, I see

Sippet.send(:sippet, request)

I don't understand what is the :sippet atom. Should it be the stack name?

Prior to the migration my Core module add a function as such:

  def receive_request(request, server_key) do
    request
    |> except_to_500(&Auth.auth(&1))
    |> Transactions.send_response(server_key)
  end

I am wondering how can I migrate this to the latest version of the library.

Support for TCP transport

We will be using a sippet library, but noticed there is no tcp transport. Is adding tcp transport something hard to current implementation?

Compilation issue

When I try to compile an app that uses the library I get the following error

could not compile dependency :sippet, "mix compile" failed. You can recompile this dependency with "mix deps.compile sippet", update it with "mix deps.update sippet" or clean it with "mix deps.clean sippet"
** (ArgumentError) all arguments for System.cmd/3 must be binaries
    (elixir) lib/system.ex:625: System.cmd/3
    /Users/mchabot/dev/ispt/test_sip/deps/sippet/mix.exs:85: Mix.Tasks.Compile.Make.run/1

I am on Elixir 1.8

Simple example

Hello @balena, it would be great if you put up a simple ping pong example between two SIP user agents that talk to the SIP Server.

New release

Is it possible to make a release with a bug fix from #34?

Memory leak

I see an constant increase of memory when running an app.

I have noticed that the sippet supervisor consumes more and more memory.

iex(as@stirsrv01)1> Supervisor.count_children(:as_stack_sup)
%{active: 47, specs: 173756, supervisors: 1, workers: 173755}

I think that the application uses a simple Supervisor and not a DynamicSupervisor.

This is very easy to reproduce, just let the application run while sending calls to it then call the Supervisor.count_children() function.

Error sending response

When I send a request to my app, I get the following error:

07:59:58.395 [error] GenServer #PID<0.199.0> terminating
** (FunctionClauseError) no function clause matching in :inet_udp.getserv/1
    (kernel) inet_udp.erl:38: :inet_udp.getserv({62421, ""})
    (kernel) gen_udp.erl:127: :gen_udp.send/4
    (sippet) lib/sippet/transports/udp/sender.ex:36: Sippet.Transports.UDP.Sender.handle_cast/2
    (stdlib) gen_server.erl:616: :gen_server.try_dispatch/4
    (stdlib) gen_server.erl:686: :gen_server.handle_msg/6
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

This is caused by this line:

https://github.com/balena/elixir-sippet/blob/master/lib/sippet/transports.ex#L85

The Integer.parse/1 function now returns a tuple.

https://hexdocs.pm/elixir/Integer.html#parse/2

I think the solution would be something like

 %{"rport" => rport} -> Integer.parse(rport) |> elem(0)

:from does not have tag

When I send an OPTION request to the server using sipsak

sipsak -vv -c sip:[email protected] -s sip:nobody@localhost

I get the following warning from the server:

discarded request, ":from does not have tag"

I looked at the source code and it seems that the tag is not even parsed. Do I miss something ?

I tried many other request and I had the same issue.

NIF does not compile on M1 mac

It was easy enough to fix though, I went into c_src/Makefile and removed the -arch x86_64s under ifeq ($(UNAME_SYS), Darwin), then did a make clean && make. That seemed to get things working (I think, I haven't played around with this too much yet).

How to properly handle transactions in current version?

I am looking at this library as a relatively simple "client" or "phone side" SIP element for testing purposes. In my implementation every "User Agent" in this case is a GenServer that spawns it's own Sippet, and Transport, and I route to the different agents using a simple PID registry.

I have read all around these issues and the docs, but I seem to have missed something critical about Transaction handling.
My client and server Transactions (in this case a REGISTER attempt) time out in the :trying state.

def handle_call({:authenticate, response, _key}, _caller, agent) do
  {:ok, new} = DigestAuth.make_request(List.first(agent.messages), response, fn _ -> 
    {:ok, agent.account.sip_user, agent.account.sip_password} end, [])

  new_req =
    new_req
    |> Message.update_header(:cseq, fn {seq, method} ->
      {seq + 1, method}
    end)
    |> Message.update_header_front(:via, fn {ver, proto, hostport, params} ->
      {ver, proto, hostport, %{params | "branch" => Message.create_branch()}}
    end)
    |> Message.update_header(:from, fn {name, uri, params} ->
      {name, uri, %{params | "tag" => Message.create_tag()}}
    end)

  Sippet.send(agent.registered_name, new_req)
......
end

here is my "Core":

def receive_request(%Message{start_line: %RequestLine{}} = request, nil) do
    GenServer.call(route_agent(request), {request})
  end

  def receive_request(%Message{} = incoming_request, server_key) do
    GenServer.call(route_agent(request), {request})
  end

  def receive_response(%Message{start_line: %StatusLine{status_code: status_code}} = incoming_response, client_key) when status_code in [401,407] do
    GenServer.call(route_agent(incoming_response.headers.to), {:authenticate, incoming_response, client_key})
  end

And In this case I get a "200 OK" from the proxy I'm registering through, but the transaction doesn't move beyond the :trying state.
I feel like I'm doing something wrong here. Can someone give me some pointers?

Tests does not pass on Elixir 1.7

Here the error that I've got while running mix test on the master branch:

== Compilation error in file test/transaction_client_invite_test.exs ==
** (CompileError) test/transaction_client_invite_test.exs:73: cannot invoke remote function :erlang.*/2 inside match
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
    (stdlib) lists.erl:1355: :lists.mapfoldl/3
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
    (elixir) src/elixir_fn.erl:14: anonymous fn/4 in :elixir_fn.expand/3
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
    (elixir) src/elixir_fn.erl:19: :elixir_fn.expand/3
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
    (stdlib) lists.erl:1355: :lists.mapfoldl/3
    (elixir) expanding macro: Kernel.|>/2

Sample MyCore implementation

Could you please provide a sample MyCore implementation? Reason I'm asking is that I've done all other configuration/setup (as explained in the readme) and I'm able to see started plug 127.0.0.1:5060/udp logged message when I start the application, however none of the log messages from my custom core implementation appear when I send a sample INVITE request.

For the reference - my custom core implementation:

defmodule ZCore do
  require Logger
  alias Sippet.Transactions
  alias Sippet.Message

  @behaviour Sippet.Core

  require Logger


  @impl Sippet.Core
  def receive_request(req, serverTxKey) do
    Logger.debug(fn -> "#{inspect self()} receive_request:: req => #{inspect req}; serverTxKey => #{inspect serverTxKey}" end)
    req |> Message.to_response(100) |> Transactions.send_response(serverTxKey)
  end

  @impl Sippet.Core
  def receive_response(resp, clientTxKey) do
    Logger.debug(fn -> "#{inspect self()} receive_response:: resp => #{inspect resp}; clientTxKey => #{inspect clientTxKey}" end)
  end

  @impl Sippet.Core
  def receive_error(reason, txKey) do
    Logger.debug(fn -> "#{inspect self()} receive_error:: reason => #{inspect reason}; txKey => #{inspect txKey}" end)
  end

end

As you might have guessed - an Elixir beginner here so any other relevant comments/suggestions are welcome.

receive_request got called but client side still timeouts

Hello hello. First, thanks for implementing this!

I added config Sippet.Core and the receive_request callback got called but the client still timeouts. It is the Telephone macOS client. Could you give me some hint on what to do next?

Thank you!

When I try to run test - Compilation fails on Mac

cc /Users/krishna/projects/elixir-sippet/c_src/string_piece.o /Users/krishna/projects/elixir-sippet/c_src/utils.o /Users/krishna/projects/elixir-sippet/c_src/parser.o /Users/krishna/projects/elixir-sippet/c_src/tokenizer.o /Users/krishna/projects/elixir-sippet/c_src/prtime.o -arch x86_64 -flat_namespace -undefined suppress -shared -lstdc++ -L /usr/local/Cellar/erlang/23.1.1/lib/erlang/usr/lib -lerl_interface -lei -o /Users/krishna/projects/elixir-sippet/priv/sippet_nif.so
ld: library not found for -lerl_interface
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [/Users/krishna/projects/elixir-sippet/priv/sippet_nif.so] Error 1

Client transaction already exists

First of all, this looks like a great application, still investigating, but thanks for making this available!

I have created a simple project using elixir-sippet 1.0.7.

My aim is to create a program that does (many) outbound registrations with authentication for different contacts (so one contact per registration).

I am able to send out a REGISTER request, by creating one as follows:

req = """
REGISTER sip:foo.com SIP/2.0
Via: SIP/2.0/UDP 192.168.64.1:5060;branch=#{Sippet.Message.create_branch()}
From: <sip:[email protected]>;tag=#{Sippet.Message.create_tag()}
To: <sip:[email protected]>
CSeq: 1 REGISTER
Call-ID: #{Sippet.Message.create_call_id()}
Contact: <sip:[email protected]:5060>
Expires: 300
""" |> Sippet.Message.parse!()

And then sending it out as follows:
Sippet.send :mystack, req

I see the request being sent out to the registrar and a 401 challenge being responded by it.

I have a simple Core registered that just prints out the arguments incoming_response and client_key passed to the receive_response function.

Now I'd like to send a second request, but with the proper Authorization header in it.

First, I tried to generate a new request based on the sent request and received response:
{:ok, auth_req} = Sippet.DigestAuth.make_request req, resp, fn(_realm) -> {:ok, "phone1", "topsecret"} end

Then incremented the cseq by one as follows:
auth_req_inc_cseq = put_in auth_req.headers.cseq, {2, :register}

But when then trying to send that request, it says the client transaction already exists.

I was thinking I may need to change the via branch, but still. If the response to a request is received, where does the transaction then remain ? Should I remove it myself ?

What would be the proper way to go for this ?

Thanks!

Params with empty value should not display =

I am receiving request that has a Via header that has a rport flag. Here is the original header

Via: SIP/2.0/UDP 205.205.74.6:5060;rport;branch=z9hG4bK-26320-1-0

When I return the response, the Via header is serialized this way:

Via: SIP/2.0/UDP 205.205.74.6:5060;rport=;received=127.0.0.1;branch=z9hG4bK-26320-1-0

Note that the rport= param is invalid. https://www.tech-invite.com/fo-abnf/tinv-fo-abnf-sip-h-via.html#via-params.

I think that the value of a parameter cannot be an empty string.

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.