From 95d45aabe736bb9d0b6521fb13c77a3222f27a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Gonz=C3=A1lez=20Florido?= Date: Mon, 8 Jul 2013 18:48:08 +0200 Subject: [PATCH 1/7] Update FEATURES.md --- docs/FEATURES.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 59eaf359..91e2f44b 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -13,9 +13,9 @@ RFC|Description|Coverage NkSIP features also: - * SIP Registrar (RAM storage only). - * A written from scratch, fully typed Erlang code easy to understand and extend. More than 50 unit tests. - * Hot code loading. + * Full RFC3261 coverage, including SIP Registrar (RAM storage only). + * A written from scratch, fully typed Erlang code easy to understand and extend, with more than 50 unit tests. + * Hot core and application code upgrade. * Very few external dependencies: [Lager](https://github.com/basho/lager) for error logging and [Cowboy](http://ninenines.eu") as TCP/SSL acceptor and Websocket server. * UDP, TCP and TLS transports, capable of handling thousands of simultaneous sessions. * Stateful proxy servers with serial and parallel forking. @@ -25,3 +25,4 @@ NkSIP features also: * SDP processing utilities. * Simple STUN server (for future SIP Outbound support). * Robust and highly scalable, using all available processor cores. + From 9eeff0f2f5da955b1c293accbc0fcae7e9f41d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Gonz=C3=A1lez=20Florido?= Date: Mon, 8 Jul 2013 19:02:54 +0200 Subject: [PATCH 2/7] Update ROADMAP.md --- docs/ROADMAP.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 44bc4268..647ba382 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -3,13 +3,14 @@ ROADMAP In no special order: -* More application examples +* SIP message processing engine rework. * _Bridge_ support for B2BUA. * _IPv6_. * _SIP Websockets_ support. * External control, to be able to use NkSIP without having to use Erlang or outside of the NkSIP Erlang VM. * Fully distributed, highly available version based on [Riak Core](https://github.com/basho/riak_core). * IMS and RCS extensions. +* More application examples. Many common SIP featured are still missing and will be addressed in following versions, including: From 9be980353cdb21f6bc7034eeefec3b10a6d75425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Gonz=C3=A1lez=20Florido?= Date: Mon, 8 Jul 2013 19:15:15 +0200 Subject: [PATCH 3/7] Update README.md --- README.md | 64 ++++++++++++++++++++++--------------------------------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index e46ffaa5..2fbe8fb7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ NkSIP is an Erlang SIP framework or _application server_, which greatly facilita **This software is still alpha quality!! Do not use it in production!!** -SIP is the standard protocol related to IP voice, video and remote sessions, supported by thousands of devices, softphones and network operators. It is the basic building block for most current voice or video enabled networks and it is the core protocol of the IP Multimedia Subsytem [IMS](https://en.wikipedia.org/wiki/IP_Multimedia_Subsystem). SIP is powerful and flexible, but also very complex to work with. SIP basic concepts are easy to understand, but developing robust, scalable, highly available applications is usually quite hard and time consuming, because of the many details you have to take into account. +SIP is the standard protocol related to IP voice, video and remote sessions, supported by thousands of devices, softphones and network operators. It is the basic building block for most current voice or video enabled networks and it is the core protocol of the IP Multimedia Subsytem ([IMS](https://en.wikipedia.org/wiki/IP_Multimedia_Subsystem)). SIP is powerful and flexible, but also very complex to work with. SIP basic concepts are easy to understand, but developing robust, scalable, highly available applications is usually quite hard and time consuming, because of the many details you have to take into account. NkSIP takes care of much of the SIP complexity, while allowing full access to requests and responses. @@ -13,7 +13,7 @@ NkSIP allows you to run any number of **SipApps**. To start a SipApp, you define NkSIP has a clean, written from scratch, [OTP compliant](http://www.erlang.org/doc/design_principles/users_guide.html) and [fully typed](http://www.erlang.org/doc/reference_manual/typespec.html) pure Erlang code. New RFCs and features can be implemented securely and quickly. The codebase includes currently more than 50 unit tests. If you want to customize the way NkSIP behaves beyond what the callback mechanism offers, it should be easy to understand the code and use it as a powerful base for your specific application server. -NkSIP is currently alpha quality, it probably has important bugs and is not **not yet production-ready**, but it is already very robust, thanks to its OTP design. Also thanks to its Erlang roots it can perform many actions while running: starting and stopping SipApps, hot code upgrades, configuration changes and even changing your application behavior and function callbacks on the fly. +NkSIP is currently alpha quality, it probably has important bugs and is **not yet production-ready**, but it is already very robust, thanks to its OTP design. Also thanks to its Erlang roots it can perform many actions while running: starting and stopping SipApps, hot code upgrades, configuration changes and even updating your application behavior and function callbacks on the fly. NkSIP scales automatically using all of the available cores on the machine. Without any serious optimization done yet, and using common hardware (4-core i7 MacMini), it is easy to get more than 1.000 call cycles (INVITE-ACK-BYE) or 8.000 stateless registrations per second. On the roadmap there is a **fully distributed version**, based on [Riak Core](https://github.com/basho/riak_core), that will allow you to add and remove nodes while running, scale as much as needed and offer a very high availability, all of it without changing your application. @@ -37,20 +37,19 @@ Current Features * Simple STUN server (for future SIP Outbound support). * Robust and highly scalable, using all available processor cores. -See [FEATURES.md](../nksip/FEATURES.md) for up to date RFC support. +See [FEATURES.md](docs/FEATURES.md) for up to date RFC support and the [ROADMAP.md](docs/ROADMAP.md). Documentation -------------- -Full documentation is available [here](http://kalta.github.io/nksip/doc/0.1.0/core/doc/index.html). +============= +Full documentation is available [here](http://kalta.github.io/nksip). +There are currently **three sample applications** included with NkSIP: + * [Simple PBX](http://kalta.github.io/nksip/docs/v0.1.0/nksip_pbx/index.html): Registrar server and forking proxy with endpoints monitoring. + * [LoadTest](http://kalta.github.io/nksip/docs/v0.1.0/nksip_loadtest/index.html): Heavy-load NkSIP testing. + * [Tutorial](docs/TUTORIAL.md): Code base for the included tutorial. -**Sample Applications** - -There are currently two samples applications included with NkSIP: - * NkPBX. Implements a SIP registrar and forking proxy, with endpoints monitoring. See [docs](http:kalta.github.io/nksip/doc/0.1.0/nksip_pbx/doc/index.html). - * NkLoadTest. Heavy-load NkSIP testing. See [docs](http:kalta.github.io/nksip/doc/0.1.0/nksip_loadtest/doc/index.html). Quick Start @@ -63,42 +62,31 @@ Quick Start > make tests ``` -Now you can start a simple SipApp using default `nksip_sipapp.erl` callback module: +You could also perform a heavy load test using the included application [nksip_loadtest](http://kalta.github.io/nksip/docs/v0.1.0/nksip_loadtest/index.html): +```erlang +> make loadtest +1> nksip_loadtest:full(). +``` +Now you can start a simple SipApp using the [client callback module](samples/nksip_tutorial/src/nksip_tutorial_sipapp_client.erl) included in the tutorial: ```erlang -> make shell -1> nksip:start(test1, nksip_sipapp, [], []) -2> nksip_uac:options(test1, "sip:sip2sip.info", []) +> make tutorial +1> nksip:start(test1, nksip_sipapp, [], []). +2> nksip_uac:options(test1, "sip:sip2sip.info", []). {ok, 200} ``` -You could also start the sample application `NkPBX` to have a target SIP server to test: -```erlang -3> nksip_pbx:start() -4> nksip_uac:register(test1, "sip:127.0.0.1;transport=tls", []) -{ok, 407} -``` - -We need a password. We use now `make_contact` option to generate a valid _Contact_ header: +From this point you can read the [tutorial](docs/TUTORIAL.md) or start hacking with the included [nksip_pbx](http://kalta.github.io/nksip/docs/v0.1.0/nksip_pbx/index.html) application: ```erlang -5> nksip_uac:register(test1, "sip:127.0.0.1;transport=tls", [{pass, "1234"}, make_contact]) -{ok, 200} -6> {reply, Reply1} = nksip_uac:register(test1, "sip:127.0.0.1;transport=tls", [{pass, "1234"}, full_response]). -7> nksip_response:headers(<>, Reply1) +> make pbx +1> nksip_pbx:start(). ``` -We have now registered. With the option `full_response` we get a full resonse object instead of simple the code. The server replies with the stored _Contact_. - -Roadmap -------- -See [ROADMAP.md](../nksip/ROADMAP.md) - - - - - - - +Contributing +============ +Please contribute with code, bug fixes, documentation fixes, testing with SIP devices or any other form. Use +GitHub Issues and Pull Requests, forking this repository. +Just make sure your code is dialyzer-friendly before summiting!! From 24d31487efd4d880217b7ae7d2de6c1735002c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Gonz=C3=A1lez=20Florido?= Date: Mon, 8 Jul 2013 19:15:57 +0200 Subject: [PATCH 4/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fbe8fb7..56dddc2a 100644 --- a/README.md +++ b/README.md @@ -88,5 +88,5 @@ Contributing Please contribute with code, bug fixes, documentation fixes, testing with SIP devices or any other form. Use GitHub Issues and Pull Requests, forking this repository. -Just make sure your code is dialyzer-friendly before summiting!! +Just make sure your code is dialyzer-friendly before submitting!! From d50323c2c567716ffbb579703e04102a88a95157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Gonz=C3=A1lez=20Florido?= Date: Mon, 8 Jul 2013 19:53:49 +0200 Subject: [PATCH 5/7] Update TUTORIAL.md --- docs/TUTORIAL.md | 84 ++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/TUTORIAL.md b/docs/TUTORIAL.md index 40531f88..0bbaf99a 100644 --- a/docs/TUTORIAL.md +++ b/docs/TUTORIAL.md @@ -1,7 +1,10 @@ Tutorial ======== -Firt of all install NkSIP +This a simple tutorial covering the basic aspects of NkSIP. + + +Firt of all install NkSIP: ``` > git clone https://github.com/kalta/nksip > cd nksip @@ -9,7 +12,7 @@ Firt of all install NkSIP > make tutorial ``` -Now you can start a simple SipApp proxy server using the included `nksip_tutorial_sipapp_server.erl` callback module. We will listen on all interfaces, port `5060` for udp and tcp and `5061` for tls. We also include the `registrar` option to tell NkSIP to process registrations (without having to implement the `register/3' callback function): +Now you can start a simple SipApp proxy server using the included [server callback module](../samples/nksip_tutorial/src/nksip_tutorial_sipapp_server.erl). We will listen on all interfaces, port `5060` for udp and tcp and `5061` for tls. We also include the `registrar` option to tell NkSIP to process registrations (without having to implement `register/3` callback function, see [nksip_siapp.erl](../nksip/src/nksip_sipapp.erl)): ```erlang 1> nksip:start(server, nksip_tutorial_sipapp_server, [server], [ @@ -20,7 +23,7 @@ Now you can start a simple SipApp proxy server using the included `nksip_tutoria ok ``` -Now we can start two clients, the first `client1` listens on `127.0.0.1` ports `5070` for udp and tcp and `5071` for tls, and the second `client2` listens on all interfaces, random ports. We also configure the _From_ to be used on each one, the second using `sips`: +Now we can start two clients, using the included [client callback module](../samples/nksip_tutorial/src/nksip_tutorial_sipapp_client.erl). The first is called `client1` and listens on `127.0.0.1` ports `5070` for udp and tcp and `5071` for tls, and the second is called `client2` and listens on all interfaces, random ports. We also configure the _From_ header to be used on each one, the second one, using `sips`: ```erlang 2> nksip:start(client1, nksip_tutorial_sipapp_client, [client1], @@ -30,20 +33,14 @@ Now we can start two clients, the first `client1` listens on `127.0.0.1` ports ` {transport, {tls, {127,0,0,1}, 5071}} ]). ok -3> nksip:start(client2, nksip_tutorial_sipapp_client, [client2], - [{from, "sips:client2@nksip"}]),1> nksip:start(server, nksip_sipapp_sample, [server], [registrar, {transport, {udp, {0,0,0,0}, 5060}}, {transport, {tls, {0,0,0,0}, 5061}}]). +3> nksip:start(client2, nksip_tutorial_sipapp_client, [client2], + [{from, "sips:client2@nksip"}]). ok ``` -server's callback module includes a synchronous call, let's test it: -```erlang -nksip:call(server, get_started). -{ok, ...} -``` - -From now on, you could start tracing to see all SIP messages on the console: `nksip_trace:start()' +From now on, you could start tracing to see all SIP messages on the console, using `nksip_trace:start().` -Let's try now to ping from client1 to client1 and to the server: +Let's try now to send an _OPTIONS_ from `client2` to `client1` and from `client1` to the `server`: ```erlang 4> nksip_uac:options(client2, "sip:127.0.0.1:5070", []). {ok, 200} @@ -51,13 +48,12 @@ Let's try now to ping from client1 to client1 and to the server: {ok, 407} ``` -Ops, the server didn't accept the request. In the callback module for the client, there is no -authentication related callback function implemented, so every request is accepted. For the server, in its callback module, requests must be authenticated: - +Oops, the `server` didn't accept the request. In the client callback module there is no authentication related callback function implemented, so every request is accepted. But server callback module is different: ```erlang %% @doc Called to check user's password. %% If the incoming user's realm is "nksip", the password for any user is "1234". -%% For other realms, no password is valid.get_user_pass(_User, <<"nksip">>, _From, State) -> +%% For other realms, no password is valid. +get_user_pass(_User, <<"nksip">>, _From, State) -> {reply, <<"1234">>, State}; get_user_pass(_User, _Realm, _From, State) -> {reply, false, State}. @@ -66,14 +62,14 @@ get_user_pass(_User, _Realm, _From, State) -> %% @doc Called to check if a request should be authorized. %% %% 1) We first check to see if the request is an in-dialog request, coming from -%% the same ip and port of a previously authorized request. +%% the same ip and port of a previously authorized request. %% 2) If not, we check if we have a previous authorized REGISTER request from %% the same ip and port. %% 3) Next, we check if the request has a valid authentication header with realm -%% "nksip". If `{{digest, <<"nksip">>}, true}' is present, the user has +%% "nksip". If '{{digest, <<"nksip">>}, true}' is present, the user has %% provided a valid password and it is authorized. -%% If `{{digest, <<"nksip">>}, false}' is present, we have presented -%% a challenge, but the user has failed it. We send 403. +%% If '{{digest, <<"nksip">>}, false}' is present, we have presented +%% a challenge, but the user has failed it. We send 403. %% 4) If no digest header is present, reply with a 407 response sending %% a challenge to the user. authorize(Auth, _ReqId, _From, State) -> @@ -93,7 +89,7 @@ authorize(Auth, _ReqId, _From, State) -> end. ``` -Lets try again with a password: +If we try again with a correct password: ```erlang 6> nksip_uac:options(client1, "sip:127.0.0.1", [{pass, "1234"}]). {ok, 200} @@ -110,7 +106,7 @@ Let's register now both clients with the server. We use the option `make_contact {ok, 200} ``` -Lets check the server has stored the registration. If we send a _REGISTER_ request with no _Contact_ header, the server will include one for each stored registration: +We can check that the server has stored the registration. If we send a _REGISTER_ request with no _Contact_ header, the server will include one for each stored registration. We usedthe option `full_response` to tell NkSIP to send the full response as the return, instead of only the code. ```erlang 10> {reply, Resp1} = nksip_uac:register(client2, "sip:127.0.0.1;transport=tls", [{pass, "1234"}, full_response]). @@ -121,10 +117,7 @@ Lets check the server has stored the registration. If we send a _REGISTER_ reque [<<">] ``` -We have used the option `full_response` to tell NkSIP to send as the full response in the return, instead of only the code. - -Now we can send the same _OPTIONS_ as before without the authentication, because the origin of the requests are already registered: - +Now, if we want to send the same _OPTIONS_ again, we don't need to include the authentication, because the origins of the requests are already registered: ```erlang 13> nksip_uac:options(client1, "sip:127.0.0.1", []). {ok, 200} @@ -132,31 +125,29 @@ Now we can send the same _OPTIONS_ as before without the authentication, because {ok, 200} ``` -Now let's send and _OPTIONS_ from client1 to client2 through the proxy. As it is already registered, we can use it registered name. We use the option `route` to send the request to the proxy. You usually include this option in the call to `nksip:start/4`, to send _every_ request to the proxy automatically. - +Now let's send and _OPTIONS_ from `client1` to `client2` through the proxy. As they are already registered, we can use their registered names or _address-of-record_. We use the option `route` to send the request to the proxy (you usually include this option in the call to `nksip:start/4`, to send _every_ request to the proxy automatically). +The first request is not authorized. The reason is that we are using a `sips` uri as a target, so NkSIP must use tls. But the origin port is then different from the one we registered, so we must authenticate again: ```erlang 15> nksip_uac:options(client1, "sips:client2@nksip", [{route, "sip:127.0.0.1;lr"}]). {ok, 407} 16> {reply, Resp2} = nksip_uac:options(client1, "sips:client2@nksip", - [{route, "sip:127.0.0.1;lr"}, full_response, - {pass, "1234"}]). + [{route, "sip:127.0.0.1;lr"}, full_response,{pass, "1234"}]). {reply, ...} 17> nksip_response:code(Resp2). 200 18> nksip_response:headers(<<"Nksip-Id">>, Resp2). [<<"client2">>] 19> {reply, Resp3} = nksip_uac:options(client2, "sip:client1@nksip", - [{route, "sips:127.0.0.1;lr"}, full_response]), + [{route, "sips:127.0.0.1;lr"}, full_response]). {reply, ...} 20> nksip_response:code(Resp3). 200 -21> nksip_response:headers(<<"Nksip-Id">>, Resp3), +21> nksip_response:headers(<<"Nksip-Id">>, Resp3). [<<"client1">>] ``` -The first request is not authorized. The reason is that we are using a sips uri as a target, so NkSIP must use tls. But the origin port is then different from the one we registered, so we -must authenticate. + The callback `options/3` is called for every client, which includes the custom header: @@ -167,7 +158,7 @@ options(_ReqId, _From, #state{id=Id}=State) -> {reply, {ok, Headers, <<>>, Opts}, State}. ``` -Now let's try a _INVITE_ from client2 to client1 through the proxy. NkSIP will call the callback `invite/4` in client1's callback module: +Now let's try a _INVITE_ from `client2` to `client1` through the proxy. NkSIP will call the callback `invite/4` in client1's callback module: ```erlang invite(_DialogId, ReqId, From, State) -> @@ -186,21 +177,29 @@ invite(_DialogId, ReqId, From, State) -> end. ``` -In the first call, since we don't include a body, client1 will reply `not_acceptable` (code 488). -In the second, we spawn a new process, reply a provisional 180 Ringing, wait two seconds and reply a 200 OK with the same body. After receiving each 2xx response to an _INVITE_, we must send an _ACK_ inmediatly. - +In the first call, since we don't include a body, client1 will reply `not_acceptable` (code `488`). +In the second, we _spawn_ a new process, reply a _provisional_ `180 Ringing`, wait two seconds and reply a `final` `200 Ok` with the same body. After receiving each `2xx` response to an _INVITE_, we must send an _ACK_ inmediatly: ```erlang 22> nksip_uac:invite(client2, "sip:client1@nksip", [{route, "sips:127.0.0.1;lr"}]). {ok, 488, ...} 23> {ok, 200, Dialog1} = nksip_uac:invite(client2, "sip:client1@nksip", - [{route, "sips:127.0.0.1;lr"}, {body, nksip_sdp:new()}]), + [{route, "sips:127.0.0.1;lr"}, {body, nksip_sdp:new()}]), ok = nksip_uac:ack(Dialog1, []). ok ``` -We have started a dialog: +The call is accepted and we have started a _dialog_: +```erlang 24> nksip_dialog:field(local_sdp, Dialog1). -... +``` + +You can _print_ all dialogs in the console. We see dialogs at `client1`, `client2` and at `server`. The three dialogs are the same actually, but in different SipApps: +```erlang +25> nksip_dialog:print_all(). +``` + +Ok, let's stop the call, the dialogs and the SipApps: +```erlang 25> nksip_uac:bye(Dialog1). ok 26> nksip:stop_all(). @@ -225,4 +224,5 @@ ok + From bbfd232d792e215cc068ddda0ac791aac8aae465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Gonz=C3=A1lez=20Florido?= Date: Mon, 8 Jul 2013 20:00:46 +0200 Subject: [PATCH 6/7] Update TUTORIAL.md --- docs/TUTORIAL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TUTORIAL.md b/docs/TUTORIAL.md index 0bbaf99a..449dfd02 100644 --- a/docs/TUTORIAL.md +++ b/docs/TUTORIAL.md @@ -206,7 +206,7 @@ ok ok ``` - +The full code for this tutorial is available [here](../samples/nksip_tutorial/src/nksip_tutorial.erl). From 277cc99efabbb280ddd52b5e999b3f72d749de2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Gonz=C3=A1lez=20Florido?= Date: Mon, 8 Jul 2013 20:07:19 +0200 Subject: [PATCH 7/7] Update TUTORIAL.md --- docs/TUTORIAL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/TUTORIAL.md b/docs/TUTORIAL.md index 449dfd02..54d0636e 100644 --- a/docs/TUTORIAL.md +++ b/docs/TUTORIAL.md @@ -195,14 +195,14 @@ The call is accepted and we have started a _dialog_: You can _print_ all dialogs in the console. We see dialogs at `client1`, `client2` and at `server`. The three dialogs are the same actually, but in different SipApps: ```erlang -25> nksip_dialog:print_all(). +25> nksip_dialog:get_all(). ``` Ok, let's stop the call, the dialogs and the SipApps: ```erlang -25> nksip_uac:bye(Dialog1). +26> nksip_uac:bye(Dialog1, []). ok -26> nksip:stop_all(). +27> nksip:stop_all(). ok ```