├── README.md
├── client
├── asset-manifest.json
├── config.js
├── favicon.ico
├── index.html
├── manifest.json
├── precache-manifest.2da2d0d268e58779c701c9f88d99aed8.js
├── service-worker.js
└── static
│ ├── css
│ ├── main.5d4f940f.chunk.css
│ └── main.5d4f940f.chunk.css.map
│ ├── js
│ ├── 1.56b88b18.chunk.js
│ ├── 1.56b88b18.chunk.js.map
│ ├── main.410ce508.chunk.js
│ ├── main.410ce508.chunk.js.map
│ ├── runtime~main.229c360f.js
│ └── runtime~main.229c360f.js.map
│ └── media
│ └── background.5a9013a5.svg
├── etc
├── default
│ ├── coturn
│ ├── kamailio
│ └── ngcp-rtpengine-daemon
├── kamailio
│ ├── kamailio-old.cfg
│ ├── kamailio.cfg
│ ├── kamctlrc
│ └── tls.cfg
├── network
│ └── if-up.d
│ │ └── iptables
├── nginx
│ ├── conf.d
│ │ └── default.conf
│ ├── nginx.conf
│ └── sites-available
│ │ └── default
├── rtpengine
│ └── rtpengine.conf
└── turnserver.conf
├── images
├── webrtc-sip.png
└── webrtc-sip.svg
└── iptables.sh
/README.md:
--------------------------------------------------------------------------------
1 | # WEBRTC to SIP client and server
2 | How to setup Kamailio + RTPEngine + TURN server to enable calling between WebRTC client and legacy SIP clients. This config is IPv6 enabled by default. This setup will bridge SRTP --> RTP and ICE --> nonICE to make a WebRTC client (sip.js) be able to call legacy SIP clients. The WebRTC client can be found [here](https://github.com/havfo/SipCaller).
3 |
4 | This setup is for Debian 12 Bookworm.
5 |
6 | This setup is configured to run with the following services:
7 |
8 | - Kamailio + RTPEngine + Nginx (proxy + WebRTC client) + coturn
9 |
10 | The configuration is setup to always bridge via RTPEngine. To change the behavior, take a look in the `NATMANAGE` route.
11 |
12 | ## Architecture
13 | 
14 |
15 | ## Get certificates
16 | For the certificates you need, a simple solution is Let's Encrypt certificates. They will work for both Kamailio TLS, Nginx TLS and TURN TLS. Run the following (you must stop services running on port 443 during certificate request/renewal):
17 | ```bash
18 | apt-get install certbot
19 | certbot certonly --standalone -d YOUR-DOMAIN
20 | ```
21 | You will then find the certificates under:
22 | ```bash
23 | /etc/letsencrypt/live/YOUR-DOMAIN/privkey.pem
24 | /etc/letsencrypt/live/YOUR-DOMAIN/fullchain.pem
25 | ```
26 |
27 | ## Get configuration files
28 | ```bash
29 | git clone https://github.com/havfo/WEBRTC-to-SIP.git
30 | cd WEBRTC-to-SIP
31 | find . -type f -print0 | xargs -0 sed -i 's/XXXXXX-XXXXXX/PUT-IPV6-OF-YOUR-SIP-SERVER-HERE/g'
32 | find . -type f -print0 | xargs -0 sed -i 's/XXXXX-XXXXX/PUT-IPV4-OF-YOUR-SIP-SERVER-HERE/g'
33 | find . -type f -print0 | xargs -0 sed -i 's/XXXX-XXXX/PUT-DOMAIN-OF-YOUR-SIP-SERVER-HERE/g'
34 | ```
35 |
36 | ## Install RTPEngine
37 | This will do the SRTP-RTP bridging needed to make WebRTC clients talk to legacy SIP server/clients. You can find the latest build instructions in their [readme](https://github.com/sipwise/rtpengine#on-a-debian-system).
38 |
39 | The easiest way of installing is to get it from Sipwise repository:
40 | ```bash
41 | echo 'deb https://deb.sipwise.com/spce/mr11.5.1/ bookworm main' > /etc/apt/sources.list.d/sipwise.list
42 | echo 'deb-src https://deb.sipwise.com/spce/mr11.5.1/ bookworm main' >> /etc/apt/sources.list.d/sipwise.list
43 | wget -q -O - https://deb.sipwise.com/spce/keyring/sipwise-keyring-bootstrap.gpg | apt-key add -
44 | apt-get update
45 | apt-get install -y ngcp-keyring ngcp-rtpengine
46 | ```
47 |
48 | After you have successfully installed RTPEngine, copy the configuration from this repository.
49 | ```bash
50 | cd WEBRTC-to-SIP
51 | cp etc/default/ngcp-rtpengine-daemon /etc/default/
52 | cp etc/rtpengine/rtpengine.conf /etc/rtpengine/
53 | /etc/init.d/ngcp-rtpengine-daemon restart
54 | ```
55 |
56 | ## Install IPTables firewall (optional)
57 | RTPEngine handles the chain for itself, but make sure to not block the RTP-ports it is using. Take a look in iptables.sh for details, and apply it by doing the following. This will persist after reboot. You can run the iptables.sh script at any time after it is set up.
58 | ```bash
59 | cd WEBRTC-to-SIP
60 | chmod +x iptables.sh
61 | cp etc/network/if-up.d/iptables /etc/network/if-up.d/
62 | chmod +x /etc/network/if-up.d/iptables
63 | touch /etc/iptables/firewall.conf
64 | touch /etc/iptables/firewall6.conf
65 | ./iptables.sh
66 | ```
67 |
68 | ## Install Kamailio
69 | ```bash
70 | apt-get install kamailio kamailio-websocket-modules kamailio-mysql-modules kamailio-tls-modules kamailio-presence-modules mysql-server
71 | cd WEBRTC-to-SIP
72 | cp etc/kamailio/* /etc/kamailio/
73 | kamdbctl create
74 | ```
75 | Select yes (Y) to all options.
76 |
77 | ```bash
78 | kamctl add websip websip
79 | service kamailio restart
80 | ```
81 |
82 | ## Install WebRTC client
83 | This will install the client that can be found [here](https://github.com/havfo/SipCaller).
84 |
85 | Install Nginx:
86 | ```sh
87 | apt-get update
88 | apt-get install nginx libnginx-mod-stream
89 | cd WEBRTC-to-SIP
90 | cp etc/nginx/nginx.conf /etc/nginx/
91 | cp etc/nginx/conf.d/default.conf /etc/nginx/conf.d/
92 | cp -r client/* /var/www/html/
93 | service nginx restart
94 | ```
95 |
96 | ## Install TURN server
97 | ```sh
98 | apt-get install coturn
99 | cp etc/default/coturn /etc/default/
100 | cp etc/turnserver.conf /etc/
101 | service coturn restart
102 | ```
103 |
104 | ## Testing
105 | You should now be able to go to https://XXXX-XXXX/ and call legacy SIP clients. Click the account icon in the top right corner and add the following settings:
106 |
107 | - Display name: Whatever
108 | - SIP URI: websip@XXXX-XXXX
109 | - Password: websip
110 | - Outbound Proxy: wss://XXXX-XXXX/ws
111 |
112 | To manually configure other TURN servers, change the config in `client/config.js`.
113 |
--------------------------------------------------------------------------------
/client/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "main.css": "/static/css/main.5d4f940f.chunk.css",
3 | "main.js": "/static/js/main.410ce508.chunk.js",
4 | "main.js.map": "/static/js/main.410ce508.chunk.js.map",
5 | "static/js/1.56b88b18.chunk.js": "/static/js/1.56b88b18.chunk.js",
6 | "static/js/1.56b88b18.chunk.js.map": "/static/js/1.56b88b18.chunk.js.map",
7 | "runtime~main.js": "/static/js/runtime~main.229c360f.js",
8 | "runtime~main.js.map": "/static/js/runtime~main.229c360f.js.map",
9 | "static/media/index.css": "/static/media/background.5a9013a5.svg",
10 | "static/css/main.5d4f940f.chunk.css.map": "/static/css/main.5d4f940f.chunk.css.map",
11 | "index.html": "/index.html",
12 | "precache-manifest.2da2d0d268e58779c701c9f88d99aed8.js": "/precache-manifest.2da2d0d268e58779c701c9f88d99aed8.js",
13 | "service-worker.js": "/service-worker.js"
14 | }
--------------------------------------------------------------------------------
/client/config.js:
--------------------------------------------------------------------------------
1 | var iceServers = [
2 | {
3 | urls : 'turn:XXXX-XXXX:443?transport=tcp',
4 | username : 'websip',
5 | credential : 'websip'
6 | },
7 | {
8 | urls : 'turns:XXXX-XXXX:80?transport=tcp',
9 | username : 'websip',
10 | credential : 'websip'
11 | }
12 | ];
13 |
--------------------------------------------------------------------------------
/client/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/havfo/WEBRTC-to-SIP/65d9e7684ce3fb140a147470268e8dd50c8f243a/client/favicon.ico
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
SipCaller
--------------------------------------------------------------------------------
/client/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "SipCaller",
3 | "name": "SIP user agent",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/client/precache-manifest.2da2d0d268e58779c701c9f88d99aed8.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = [
2 | {
3 | "revision": "5a9013a55d9599bc8b5afa277aca900b",
4 | "url": "/static/media/background.5a9013a5.svg"
5 | },
6 | {
7 | "revision": "229c360febb4351a89df",
8 | "url": "/static/js/runtime~main.229c360f.js"
9 | },
10 | {
11 | "revision": "410ce5084caac7b605bc",
12 | "url": "/static/js/main.410ce508.chunk.js"
13 | },
14 | {
15 | "revision": "56b88b1883f1a1d7cd2e",
16 | "url": "/static/js/1.56b88b18.chunk.js"
17 | },
18 | {
19 | "revision": "410ce5084caac7b605bc",
20 | "url": "/static/css/main.5d4f940f.chunk.css"
21 | },
22 | {
23 | "revision": "0e29d683cec21d3306ebf3af25813fec",
24 | "url": "/index.html"
25 | }
26 | ];
--------------------------------------------------------------------------------
/client/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");
15 |
16 | importScripts(
17 | "/precache-manifest.2da2d0d268e58779c701c9f88d99aed8.js"
18 | );
19 |
20 | workbox.clientsClaim();
21 |
22 | /**
23 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
24 | * requests for URLs in the manifest.
25 | * See https://goo.gl/S9QRab
26 | */
27 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
28 | workbox.precaching.suppressWarnings();
29 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
30 |
31 | workbox.routing.registerNavigationRoute("/index.html", {
32 |
33 | blacklist: [/^\/_/,/\/[^\/]+\.[^\/]+$/],
34 | });
35 |
--------------------------------------------------------------------------------
/client/static/css/main.5d4f940f.chunk.css:
--------------------------------------------------------------------------------
1 | html{font-family:Roboto;font-weight:300}body,html{height:100%;overflow-x:hidden;overflow-y:hidden}body{margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:#646464;background-image:url(/static/media/background.5a9013a5.svg);background-attachment:fixed;background-position:50%;background-size:cover;background-repeat:no-repeat}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}#sipcaller,.LoadingView{height:100%;width:100%}
2 | /*# sourceMappingURL=main.5d4f940f.chunk.css.map */
--------------------------------------------------------------------------------
/client/static/css/main.5d4f940f.chunk.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["/home/havfo/Utvikling/SipCaller/app/src/index.css","main.5d4f940f.chunk.css","/home/havfo/Utvikling/SipCaller/app/src/components/LoadingView.css"],"names":[],"mappings":"AAAA,KAGC,kBAAA,CACA,eCGD,CDEA,UAPC,WAAA,CAGA,iBAAA,CACA,iBCmBD,CDhBA,KAKC,QAAA,CACA,SAAA,CACA,mIAAA,CACA,kCAAA,CACA,iCAAA,CACA,wBAAA,CACA,2DAAyC,CACzC,2BAAA,CACA,uBAAA,CACA,qBAAA,CACA,2BCCD,CDEA,KAEC,uECCD,CC9BA,wBAEC,WAAA,CACA,UDsCD","file":"main.5d4f940f.chunk.css","sourcesContent":["html\n{\n\theight: 100%;\n\tfont-family: 'Roboto';\n\tfont-weight: 300;\n\toverflow-x: hidden;\n\toverflow-y: hidden;\n}\n\nbody\n{\n\theight: 100%;\n\toverflow-x: hidden;\n\toverflow-y: hidden;\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n\t-webkit-font-smoothing: antialiased;\n\t-moz-osx-font-smoothing: grayscale;\n\tbackground-color: rgba(100, 100, 100, 1.0);\n\tbackground-image: url('./background.svg');\n\tbackground-attachment: fixed;\n\tbackground-position: center;\n\tbackground-size: cover;\n\tbackground-repeat: no-repeat;\n}\n\ncode\n{\n\tfont-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\n\n#sipcaller\n{\n\theight: 100%;\n\twidth: 100%;\n}","html\n{\n\theight: 100%;\n\tfont-family: 'Roboto';\n\tfont-weight: 300;\n\toverflow-x: hidden;\n\toverflow-y: hidden;\n}\n\nbody\n{\n\theight: 100%;\n\toverflow-x: hidden;\n\toverflow-y: hidden;\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n\t-webkit-font-smoothing: antialiased;\n\t-moz-osx-font-smoothing: grayscale;\n\tbackground-color: rgba(100, 100, 100, 1.0);\n\tbackground-image: url(/static/media/background.5a9013a5.svg);\n\tbackground-attachment: fixed;\n\tbackground-position: center;\n\tbackground-size: cover;\n\tbackground-repeat: no-repeat;\n}\n\ncode\n{\n\tfont-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\n\n#sipcaller\n{\n\theight: 100%;\n\twidth: 100%;\n}\n.LoadingView\n{\n\theight: 100%;\n\twidth: 100%;\n}\n",".LoadingView\n{\n\theight: 100%;\n\twidth: 100%;\n}"]}
--------------------------------------------------------------------------------
/client/static/js/main.410ce508.chunk.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[0],{112:function(e,t,n){"use strict";var a=n(29),r=n(27),i=n(90),o=n(7),s=n(191),c=n.n(s),u=function(e){var t=e.type,n=void 0===t?"info":t,a=e.text,r=e.timeout;if(!r)switch(n){case"warning":case"error":r=5e3;break;case"success":case"info":default:r=3e3}var i={id:c()({length:6}).toLowerCase(),type:n,text:a,timeout:r};return function(e){e(o.b(i)),setTimeout(function(){e(o.f(i.id))},r)}},l=n(17),d=n(83);n.d(t,"a",function(){return m});var p,f=new d.a("SipCaller"),m=function(){function e(){Object(a.a)(this,e),f.debug("constructor()"),this._ua=null,this._init()}return Object(r.a)(e,null,[{key:"init",value:function(e){p=e.store}}]),Object(r.a)(e,[{key:"_init",value:function(){f.debug("_init()"),p.getState().user.autoRegister&&this.register()}},{key:"register",value:function(){var e=this;f.debug("register()");var t=p.getState().user,n=t.displayName,a=t.sipUri,r=t.password,s=t.outboundProxy,c=window.iceServers;p.dispatch(o.m()),this._ua=new i.UA({uri:a,password:r,displayName:n,transportOptions:{wsServers:[s],traceSip:!0},sessionDescriptionHandlerFactoryOptions:{peerConnectionOptions:{rtcConfiguration:{iceServers:c}}}}),this._ua.on("registered",function(){f.debug("Registered"),p.dispatch(u({type:"success",text:"Successfully registered."})),p.dispatch(o.o({registrationMessage:"Success"})),p.dispatch(o.n({registered:!0}))}),this._ua.on("registrationFailed",function(e,t){f.debug("Registration failed [cause: %s]",t),p.dispatch(u({type:"error",text:"Registration failed: ".concat(t)})),p.dispatch(o.o({registrationMessage:t})),p.dispatch(o.n({registered:!1}))}),this._ua.on("unregistered",function(e,t){f.debug("Unregistered [cause: %s]",t),p.dispatch(u({text:"Unregistered: ".concat(t)})),p.dispatch(o.o({registrationMessage:t})),p.dispatch(o.n({registered:!1}))}),this._ua.on("invite",function(t){f.debug("Incoming invite [sipSession: %o]",t),p.dispatch(u({text:"Incoming call from: ".concat(t.remoteIdentity.uri.user)})),e._handleSession(t,l.d)})}},{key:"unRegister",value:function(){f.debug("unRegister()"),this._ua.unregister()}},{key:"_handleSession",value:function(e,t){var n=this;f.debug("_handleSession() [sipSession: %o]",e);var a=Date.now();e.on("trackAdded",function(){f.debug("SipSession trackAdded [sipSession: %o]",e);var t=e.sessionDescriptionHandler.peerConnection,n=new MediaStream;t.getReceivers().forEach(function(e){e.track&&n.addTrack(e.track)}),p.dispatch(o.c({sipSession:e,remoteStream:n}));var a=new MediaStream;t.getSenders().forEach(function(e){e.track&&a.addTrack(e.track)}),p.dispatch(o.a({sipSession:e,localStream:a}))}),e.on("replaced",function(a){f.debug("SipSession replaced [oldSipSession: %o, newSipSession: %o]",e,a),n._handleSession(a,t)}),e.on("referRequested",function(t){t instanceof i.ReferClientContext&&(t.on("referRequestAccepted",function(){p.dispatch(o.q({sipSession:e,sessionState:l.h}))}),t.on("referRequestRejected",function(){p.dispatch(o.q({sipSession:e,sessionState:l.i}))})),t instanceof i.ReferServerContext&&t.accept()}),e.on("directionChanged",function(){"sendrecv"===e.sessionDescriptionHandler.getDirection()?f.debug("SipSession not on hold [sipSession: %o]",e):f.debug("SipSession on hold [sipSession: %o]",e)}),e.on("progress",function(t){f.debug("SipSession progress [response: %o, sipSession: %o]",t,e),p.dispatch(o.q({sipSession:e,sessionState:l.g}))}),e.on("accepted",function(t){f.debug("SipSession accepted [data: %o, sipSession: %o]",t,e),p.dispatch(o.q({sipSession:e,sessionState:l.a}))}),e.on("bye",function(t){f.debug("SipSession bye [request: %o, sipSession: %o]",t,e),p.dispatch(o.q({sipSession:e,sessionState:l.k}))}),e.on("cancel",function(){f.debug("SipSession canceled [sipSession: %o]",e),p.dispatch(o.q({sipSession:e,sessionState:l.b}))}),e.on("rejected",function(t,n){f.debug("SipSession rejected [response: %o, cause: %s, sipSession: %o]",t,n,e),p.dispatch(o.q({sipSession:e,sessionState:l.j}))}),e.on("failed",function(t,n){f.debug("SipSession failed [response: %o, cause: %s, sipSession: %o]",t,n,e),p.dispatch(o.q({sipSession:e,sessionState:l.c}))}),e.on("terminated",function(t,n){f.debug("SipSession terminated [message: %o, cause: %s, sipSession: %o]",t,n,e),p.dispatch(u({text:"Call terminated: ".concat(e.remoteIdentity.uri.user)})),p.dispatch(o.q({sipSession:e,sessionState:l.k}));var r=e.remoteIdentity.displayName||e.remoteIdentity.uri.user,i=e.remoteIdentity.uri.toString();p.dispatch(o.e({displayName:r,sipUri:i,startTime:a})),setTimeout(function(){if(f.debug("SipSession removed [sipSession: %o]",e),p.dispatch(o.g({sipSession:e})),!p.getState().userStatus.currentSession){var t=p.getState().sessions;t&&p.dispatch(o.i({currentSession:Object.keys(t)[0]}))}},3e3)}),p.dispatch(o.d({sipSession:e,direction:t}))}},{key:"accept",value:function(e){f.debug("accept() [sipSession: %o]",e);var t=p.getState().user.videoEnabled;e.accept({sessionDescriptionHandlerOptions:{constraints:{audio:!0,video:t}}})}},{key:"terminate",value:function(e){f.debug("terminate() [sipSession: %o]",e),e.terminate()}},{key:"invite",value:function(e){f.debug("invite() [sipUri: %s]",e);var t=p.getState().user.videoEnabled,n=this._ua.invite(e,{sessionDescriptionHandlerOptions:{constraints:{audio:!0,video:t}}});this._handleSession(n,l.f),p.dispatch(o.i({currentSession:n.request.callId}))}},{key:"refer",value:function(e,t){f.debug("refer() [sipSession: %o, sipUri: %s]",e,t),e.refer(t)}},{key:"toggleMedia",value:function(e,t,n){f.debug("toggleMedia() [sipSession: %o, type: %s, mute: %s]",e,t,n);var a=e.request.callId,r=p.getState().sessions[a].remoteStream;if(r)if("audio"===t)r.getAudioTracks()[0].enabled=!n,p.dispatch(o.t({sipSession:e}));else{if("video"!==t)throw new Error("Unknown media type.");r.getVideoTracks()[0].enabled=!n,p.dispatch(o.u({sipSession:e}))}}},{key:"toggleMyMedia",value:function(e,t,n){if(f.debug("toggleMyMedia() [session: %o, type: %s, mute: %s]",e,t,n),"audio"===t)e.localStream.getAudioTracks()[0].enabled=!n;else{if("video"!==t)throw new Error("Unknown media type.");e.localStream.getVideoTracks()[0].enabled=!n}}}]),e}()},17:function(e,t,n){"use strict";n.d(t,"e",function(){return a}),n.d(t,"g",function(){return r}),n.d(t,"j",function(){return i}),n.d(t,"a",function(){return o}),n.d(t,"b",function(){return s}),n.d(t,"c",function(){return c}),n.d(t,"k",function(){return u}),n.d(t,"h",function(){return l}),n.d(t,"i",function(){return d}),n.d(t,"d",function(){return p}),n.d(t,"f",function(){return f});var a=Symbol("NEW"),r=Symbol("PROGRESS"),i=Symbol("REJECTED"),o=(Symbol("IGNORED"),Symbol("ACCEPTED")),s=Symbol("CANCELED"),c=Symbol("FAILED"),u=Symbol("TERMINATED"),l=(Symbol("REFERRED"),Symbol("REFER_REQUEST_ACCEPTED")),d=Symbol("REFER_REQUEST_REJECTED"),p=(Symbol("REFER_PROGRESS"),Symbol("REFER_ACCEPTED"),Symbol("REFER_REJECTED"),Symbol("INCOMING")),f=Symbol("OUTGOING")},189:function(e,t,n){"use strict";var a=n(0),r=n.n(a);n(377);t.a=function(){return r.a.createElement("div",{className:"LoadingView"})}},190:function(e,t,n){"use strict";n.d(t,"a",function(){return r});var a=Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));function r(e){if("serviceWorker"in navigator){if(new URL("",window.location.href).origin!==window.location.origin)return;window.addEventListener("load",function(){var t="".concat("","/service-worker.js");a?(!function(e,t){fetch(e).then(function(n){var a=n.headers.get("content-type");404===n.status||null!=a&&-1===a.indexOf("javascript")?navigator.serviceWorker.ready.then(function(e){e.unregister().then(function(){window.location.reload()})}):i(e,t)}).catch(function(){})}(t,e),navigator.serviceWorker.ready.then(function(){})):i(t,e)})}}function i(e,t){navigator.serviceWorker.register(e).then(function(e){e.onupdatefound=function(){var n=e.installing;null!=n&&(n.onstatechange=function(){"installed"===n.state&&(navigator.serviceWorker.controller?t&&t.onUpdate&&t.onUpdate(e):t&&t.onSuccess&&t.onSuccess(e))})}}).catch(function(){})}},192:function(e,t,n){"use strict";var a=n(29),r=n(27),i=n(45),o=n(44),s=n(46),c=n(31),u=n(0),l=n.n(u),d=n(21),p=n(20),f=n(185),m=n.n(f),S=n(186),E=n.n(S),h=n(18),g=n.n(h),b=n(36),y=n.n(b),v=n(107),O=n.n(v),R=n(89),_=n.n(R),T=n(188),w=n.n(T),I=n(106),C=n.n(I),j=n(110),k=n.n(j),N=n(108),A=n.n(N),M=n(109),U=n.n(M),D=n(187),x=n.n(D),P=n(30),G=n(7),L=n(34),q=n.n(L),V=n(24),W=n.n(V),F=n(28),H=n.n(F),z=n(49),B=n.n(z),Y=n(170),J=n.n(Y),Q=n(171),X=n.n(Q),K=Object(P.b)(Object(d.b)(function(e){return{displayName:e.user.displayName,sipUri:e.user.sipUri,password:e.user.password,outboundProxy:e.user.outboundProxy,autoRegister:e.user.autoRegister,registered:e.userStatus.registered}},function(e){return{setDisplayName:function(t){return e(G.j({displayName:t}))},setSipUri:function(t){return e(G.r({sipUri:t}))},setPassword:function(t){return e(G.l({password:t}))},setOutboundProxy:function(t){return e(G.k({outboundProxy:t}))},setAutoRegister:function(t){return e(G.h({autoRegister:t}))}}})(Object(p.withStyles)(function(e){return{paper:{padding:2*e.spacing.unit,width:"20vw"}}})(function(e){var t=e.sipCaller,n=e.displayName,a=e.sipUri,r=e.password,i=e.outboundProxy,o=e.autoRegister,s=e.setDisplayName,c=e.setSipUri,u=e.setPassword,d=e.setOutboundProxy,p=e.setAutoRegister,f=e.registered,m=e.classes;return l.a.createElement(q.a,{className:m.paper},f?l.a.createElement(W.a,{container:!0,spacing:8},l.a.createElement(W.a,{item:!0,xs:12},l.a.createElement(g.a,{variant:"body1",noWrap:!0},n),l.a.createElement(g.a,{noWrap:!0},a),l.a.createElement(g.a,{variant:"caption",noWrap:!0},"Registered")),l.a.createElement(W.a,{item:!0,xs:12},l.a.createElement(H.a,{variant:"contained",color:"secondary",onClick:function(){return t.unRegister()}},"Unregister"))):l.a.createElement(W.a,{container:!0,spacing:8},l.a.createElement(W.a,{item:!0,xs:12},l.a.createElement(B.a,{id:"displayname",label:"Displayname",value:n||"",style:{width:"100%"},onChange:function(e){return s(e.target.value)}})),l.a.createElement(W.a,{item:!0,xs:12},l.a.createElement(B.a,{id:"sipuri",label:"SIP URI",type:"email",value:a||"",style:{width:"100%"},onChange:function(e){return c(e.target.value)}})),l.a.createElement(W.a,{item:!0,xs:12},l.a.createElement(B.a,{id:"password",label:"Password",type:"password",value:r||"",style:{width:"100%"},onChange:function(e){return u(e.target.value)}})),l.a.createElement(W.a,{item:!0,xs:12},l.a.createElement(B.a,{id:"outboundproxy",label:"Outbound Proxy",value:i||"",style:{width:"100%"},onChange:function(e){return d(e.target.value)}})),l.a.createElement(W.a,{item:!0,xs:12},l.a.createElement(J.a,{control:l.a.createElement(X.a,{checked:o,onChange:function(){return p(!o)}}),label:"Autoregister"})),l.a.createElement(W.a,{item:!0,xs:12},l.a.createElement(H.a,{variant:"contained",color:"primary",onClick:function(){return t.register()}},"Register"))))}))),$=n(65),Z=n.n($),ee=n(66),te=n.n(ee),ne=n(84),ae=n.n(ne),re=Object(d.b)(function(e){return{sessionHistory:e.sessionHistory}},function(e){return{setRequestUri:function(t){return e(G.p({requestUri:t}))}}})(Object(p.withStyles)(function(e){return{paper:{padding:2*e.spacing.unit}}})(function(e){var t=e.sessionHistory,n=e.setRequestUri,a=e.classes;return l.a.createElement(q.a,{className:a.paper},t.length>0?l.a.createElement(Z.a,null,t.map(function(e,t){return l.a.createElement(te.a,{key:t,button:!0,onClick:function(){return n(e.sipUri)}},l.a.createElement(ae.a,{primary:e.displayName,secondary:e.sipUri}))})):l.a.createElement(g.a,{color:"inherit",noWrap:!0},"No history"))})),ie=n(17),oe=n(85),se=n.n(oe),ce=n(86),ue=n.n(ce),le=Object(P.b)(Object(d.b)(function(e){return{sessions:e.sessions,currentSession:e.userStatus.currentSession}},function(e){return{setCurrentSession:function(t){return e(G.i({currentSession:t}))}}})(Object(p.withStyles)(function(e){return{paper:{padding:2*e.spacing.unit},iconGreen:{color:"green"},iconRed:{color:"red"}}})(function(e){var t=e.sipCaller,n=e.sessions,a=e.currentSession,r=e.setCurrentSession,i=e.classes;return l.a.createElement(q.a,{className:i.paper},Object.keys(n).length>0?l.a.createElement(Z.a,null,Object.keys(n).map(function(e){var o=n[e],s=o.sipSession,c=s.remoteIdentity.displayName||s.remoteIdentity.uri.user,u=s.remoteIdentity.uri.toString(),d=o.direction===ie.d&&o.sessionState===ie.e;return l.a.createElement(te.a,{key:e,button:!0,selected:e===a,onClick:function(){return r(e)},disabled:o.sessionState===ie.k},l.a.createElement(ae.a,{primary:c,secondary:u}),l.a.createElement(y.a,{className:d?i.iconGreen:i.iconRed,"aria-label":"Answer",onClick:function(){d?t.accept(s):t.terminate(s)}},d?l.a.createElement(se.a,null):l.a.createElement(ue.a,null)))})):l.a.createElement(g.a,{color:"inherit",noWrap:!0},"No sessions"))}))),de=n(43),pe=n(64),fe=n.n(pe),me=Object(P.b)(Object(d.b)(function(e){return{requestUri:e.user.requestUri,registered:e.userStatus.registered}},function(e){return{setRequestUri:function(t){return e(G.p({requestUri:t}))}}})(Object(p.withStyles)(function(e){return{grid:{width:"50vw"},call:Object(c.a)({position:"relative",borderRadius:e.shape.borderRadius,backgroundColor:Object(de.fade)(e.palette.common.white,.15),"&:hover":{backgroundColor:Object(de.fade)(e.palette.common.white,.25)},marginLeft:0,width:"100%"},e.breakpoints.up("sm"),{marginLeft:3*e.spacing.unit,width:"auto"}),callIcon:{width:5*e.spacing.unit,height:"100%",position:"absolute",pointerEvents:"none",display:"flex",alignItems:"center",justifyContent:"center"},inputRoot:{color:"inherit",width:"100%"},inputInput:Object(c.a)({paddingTop:e.spacing.unit,paddingRight:e.spacing.unit,paddingBottom:e.spacing.unit,paddingLeft:5*e.spacing.unit,transition:e.transitions.create("width"),width:"100%"},e.breakpoints.up("md"),{width:"100%"})}})(function(e){var t=e.sipCaller,n=e.requestUri,a=e.registered,r=e.setRequestUri,i=e.classes;return l.a.createElement(W.a,{className:i.grid,container:!0,spacing:8},l.a.createElement(W.a,{item:!0,xs:9},l.a.createElement("div",{className:i.call},l.a.createElement("div",{className:i.callIcon},l.a.createElement(se.a,null)),l.a.createElement(fe.a,{placeholder:"SIP URI",classes:{root:i.inputRoot,input:i.inputInput},value:n||"",onChange:function(e){return r(e.target.value)},onKeyPress:function(e){"Enter"===e.key&&(e.preventDefault(),t.invite(n))},autoFocus:!0}))),l.a.createElement(W.a,{item:!0,xs:3},l.a.createElement(H.a,{variant:"contained",color:"primary",disabled:!a,onClick:function(){return t.invite(n)}},"Call")))}))),Se=function(e){function t(e){var n;return Object(a.a)(this,t),(n=Object(i.a)(this,Object(o.a)(t).call(this,e)))._mediaStream=null,n}return Object(s.a)(t,e),Object(r.a)(t,[{key:"render",value:function(){var e=this.props.classes;return l.a.createElement("video",{className:e.videoView,ref:"video",autoPlay:!0,playsInline:!0})}},{key:"componentDidMount",value:function(){var e=this.props.mediaStream;this._setStream(e)}},{key:"componentWillReceiveProps",value:function(e){var t=e.mediaStream;this._setStream(t)}},{key:"_setStream",value:function(e){if(this._mediaStream!==e){this._mediaStream=e;var t=this.refs.video;t.srcObject=e||null}}}]),t}(u.Component),Ee=Object(p.withStyles)(function(){return{videoView:{objectFit:"cover",width:"100%",height:"100%"}}})(Se),he=n(67),ge=n.n(he),be=n(182),ye=n.n(be),ve=n(181),Oe=n.n(ve),Re=n(180),_e=n.n(Re),Te=n(179),we=n.n(Te),Ie=n(178),Ce=n.n(Ie),je=n(87),ke=n.n(je),Ne=n(88),Ae=n.n(Ne),Me=n(172),Ue=n.n(Me),De=n(173),xe=n.n(De),Pe=n(177),Ge=n.n(Pe),Le=n(175),qe=n.n(Le),Ve=n(176),We=n.n(Ve),Fe=n(174),He=n.n(Fe),ze=function(e){function t(){var e,n;Object(a.a)(this,t);for(var r=arguments.length,s=new Array(r),c=0;c0&&void 0!==arguments[0]?arguments[0]:d,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"SET_USER":var n=t.payload,a=n.displayName,r=n.sipUri,i=n.password,o=n.outboundProxy,s=n.autoRegister;return Object(l.a)({},e,{displayName:a,sipUri:r,password:i,outboundProxy:o,autoRegister:s});case"SET_DISPLAY_NAME":var c=t.payload.displayName;return Object(l.a)({},e,{displayName:c});case"SET_SIP_URI":var u=t.payload.sipUri;return Object(l.a)({},e,{sipUri:u});case"SET_PASSWORD":var p=t.payload.password;return Object(l.a)({},e,{password:p});case"SET_OUTBOUND_PROXY":var f=t.payload.outboundProxy;return Object(l.a)({},e,{outboundProxy:f});case"SET_AUTO_REGISTER":var m=t.payload.autoRegister;return Object(l.a)({},e,{autoRegister:m});case"TOGGLE_VIDEO":var S=!e.videoEnabled;return Object(l.a)({},e,{videoEnabled:S});case"SET_REQUEST_URI":var E=t.payload.requestUri;return Object(l.a)({},e,{requestUri:E});case"SET_TRANSFER_URI":var h=t.payload.transferUri;return Object(l.a)({},e,{transferUri:h});default:return e}},f=n(31),m=n(169),S=n.n(m),E={sipSession:null,direction:null,remoteStream:null,remoteAudioMuted:!1,remoteVideoMuted:!1,localStream:null,localAudioMuted:!1,localVideoMuted:!1,sessionState:n(17).e},h=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:E,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"ADD_SESSION":var n=t.payload,a=n.sipSession,r=n.direction;return Object(l.a)({},e,{sipSession:a,direction:r});case"ADD_REMOTE_STREAM":var i=t.payload.remoteStream;return Object(l.a)({},e,{remoteStream:i});case"ADD_LOCAL_STREAM":var o=t.payload.localStream;return Object(l.a)({},e,{localStream:o});case"SET_SESSION_STATE":var s=t.payload.sessionState;return Object(l.a)({},e,{sessionState:s});case"TOGGLE_LOCAL_AUDIO":var c=!e.localAudioMuted;return Object(l.a)({},e,{localAudioMuted:c});case"TOGGLE_LOCAL_VIDEO":var u=!e.localVideoMuted;return Object(l.a)({},e,{localVideoMuted:u});case"TOGGLE_REMOTE_AUDIO":var d=!e.remoteAudioMuted;return Object(l.a)({},e,{remoteAudioMuted:d});case"TOGGLE_REMOTE_VIDEO":var p=!e.remoteVideoMuted;return Object(l.a)({},e,{remoteVideoMuted:p});default:return e}},g=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"ADD_SESSION":var n=t.payload.sipSession.request.callId;return Object(l.a)({},e,Object(f.a)({},n,h(void 0,t)));case"REMOVE_SESSION":var a=t.payload.sipSession.request.callId;return S()(e,a);case"TOGGLE_LOCAL_AUDIO":case"TOGGLE_LOCAL_VIDEO":case"TOGGLE_REMOTE_AUDIO":case"TOGGLE_REMOTE_VIDEO":case"ADD_REMOTE_STREAM":case"ADD_LOCAL_STREAM":case"SET_SESSION_STATE":var r=t.payload.sipSession.request.callId,i=e[r];if(!i)throw new Error("No session found");return Object(l.a)({},e,Object(f.a)({},r,h(i,t)));default:return e}},b=n(57),y=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"ADD_SESSION_ENTRY":var n=t.payload,a=n.displayName,r=n.sipUri,i=n.direction,o=n.startTime;return[].concat(Object(b.a)(e),[{displayName:a,sipUri:r,direction:i,startTime:o}]);case"CLEAR_SESSION_HISTORY":return[];default:return e}},v={registerInProgress:!1,registered:!1,registrationMessage:null,currentSession:null},O=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:v,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"SET_REGISTERED":var n=t.payload.registered;return Object(l.a)({},e,{registered:n,registerInProgress:!1});case"SET_REGISTRATION_MESSAGE":var a=t.payload.registrationMessage;return Object(l.a)({},e,{registrationMessage:a});case"SET_REGISTER_IN_PROGRESS":return Object(l.a)({},e,{registerInProgress:!0});case"ADD_SESSION":var r=t.payload.sipSession.request.callId;return e.currentSession?e:Object(l.a)({},e,{currentSession:r});case"REMOVE_SESSION":var i=t.payload.sipSession.request.callId;return e.currentSession===i?Object(l.a)({},e,{currentSession:null}):e;case"SET_CURRENT_SESSION":var o=t.payload.currentSession;return Object(l.a)({},e,{currentSession:o});default:return e}},R=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"ADD_NOTIFICATION":var n=t.payload.notification;return[].concat(Object(b.a)(e),[n]);case"REMOVE_NOTIFICATION":var a=t.payload.notificationId;return e.filter(function(e){return e.id!==a});case"REMOVE_ALL_NOTIFICATIONS":return[];default:return e}},_=Object(a.c)({user:p,sessions:g,sessionHistory:y,userStatus:O,notifications:R});n.d(t,"b",function(){return C}),n.d(t,"a",function(){return j});var T={key:"root",storage:s.a,stateReconciler:u.a,blacklist:["sessions","userStatus"]},w=[r.a],I=Object(i.a)(T,_),C=Object(a.d)(I,a.a.apply(void 0,w)),j=Object(i.b)(C)}},[[193,2,1]]]);
2 | //# sourceMappingURL=main.410ce508.chunk.js.map
--------------------------------------------------------------------------------
/client/static/js/runtime~main.229c360f.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];csize=8;autoexpire=300;")
247 | #!endif
248 |
249 | #!ifdef WITH_DEBUG
250 | # ----- debugger params -----
251 | modparam("debugger", "cfgtrace", 1)
252 | #!endif
253 |
254 | ####### Routing Logic ########
255 | request_route {
256 |
257 | #!ifdef WITH_HOMER
258 | # start duplicate the SIP message here
259 | sip_trace();
260 | setflag(22);
261 | #!endif
262 |
263 | # per request initial checks
264 | route(REQINIT);
265 |
266 | #!ifdef WITH_WEBSOCKETS
267 | if (nat_uac_test(64)) {
268 | # Do NAT traversal stuff for requests from a WebSocket
269 | # connection - even if it is not behind a NAT!
270 | # This won't be needed in the future if Kamailio and the
271 | # WebSocket client support Outbound and Path.
272 | force_rport();
273 | if (is_method("REGISTER")) {
274 | fix_nated_register();
275 | } else {
276 | if (!add_contact_alias()) {
277 | xlog("L_ERR", "Error aliasing contact <$ct>\n");
278 | sl_send_reply("400", "Bad Request");
279 | exit;
280 | }
281 | }
282 | }
283 | #!endif
284 |
285 | # NAT detection
286 | route(NATDETECT);
287 |
288 | # CANCEL processing
289 | if (is_method("CANCEL"))
290 | {
291 | if (t_check_trans()) {
292 | route(RELAY);
293 | }
294 | exit;
295 | }
296 |
297 | # handle requests within SIP dialogs
298 | route(WITHINDLG);
299 |
300 | ### only initial requests (no To tag)
301 |
302 | t_check_trans();
303 |
304 | # authentication
305 | route(AUTH);
306 |
307 | # record routing for dialog forming requests (in case they are routed)
308 | # - remove preloaded route headers
309 | remove_hf("Route");
310 | if (is_method("INVITE|SUBSCRIBE"))
311 | record_route();
312 |
313 | route(RTP_BRIDGE);
314 |
315 | # dispatch requests to foreign domains
316 | route(SIPOUT);
317 |
318 | ### requests for my local domains
319 |
320 | # handle presence related requests
321 | route(PRESENCE);
322 |
323 | # handle registrations
324 | route(REGISTRAR);
325 |
326 | if ($rU==$null)
327 | {
328 | # request with no Username in RURI
329 | sl_send_reply("484","Address Incomplete");
330 | exit;
331 | }
332 |
333 | # user location service
334 | route(LOCATION);
335 | }
336 |
337 | # Wrapper for relaying requests
338 | route[RELAY] {
339 |
340 | # enable additional event routes for forwarded requests
341 | # - serial forking, RTP relaying handling, a.s.o.
342 | if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) {
343 | if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH");
344 | }
345 | if (is_method("INVITE|SUBSCRIBE|UPDATE")) {
346 | if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY");
347 | }
348 | if (is_method("INVITE")) {
349 | if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE");
350 | }
351 |
352 | if (!t_relay()) {
353 | sl_reply_error();
354 | }
355 | exit;
356 | }
357 |
358 | # Per SIP request initial checks
359 | route[REQINIT] {
360 | #!ifdef WITH_ANTIFLOOD
361 | # flood dection from same IP and traffic ban for a while
362 | # be sure you exclude checking trusted peers, such as pstn gateways
363 | # - local host excluded (e.g., loop to self)
364 | if(src_ip!=myself)
365 | {
366 | if($sht(ipban=>$si)!=$null)
367 | {
368 | # ip is already blocked
369 | xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n");
370 | exit;
371 | }
372 | if (!pike_check_req())
373 | {
374 | xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n");
375 | $sht(ipban=>$si) = 1;
376 | exit;
377 | }
378 | }
379 | #!endif
380 |
381 | if (!mf_process_maxfwd_header("10")) {
382 | sl_send_reply("483","Too Many Hops");
383 | exit;
384 | }
385 |
386 | if(!sanity_check("1511", "7"))
387 | {
388 | xlog("Malformed SIP message from $si:$sp\n");
389 | exit;
390 | }
391 | }
392 |
393 | # Handle requests within SIP dialogs
394 | route[WITHINDLG] {
395 | if (has_totag()) {
396 | # sequential request withing a dialog should
397 | # take the path determined by record-routing
398 | if (loose_route()) {
399 | #!ifdef WITH_WEBSOCKETS
400 | if ($du == "") {
401 | if (!handle_ruri_alias()) {
402 | xlog("L_ERR", "Bad alias <$ru>\n");
403 | sl_send_reply("400", "Bad Request");
404 | exit;
405 | }
406 | }
407 | #!endif
408 | route(DLGURI);
409 | if ( is_method("ACK") ) {
410 | # ACK is forwarded statelessy
411 | route(NATMANAGE);
412 | }
413 | else if ( is_method("NOTIFY") ) {
414 | # Add Record-Route for in-dialog NOTIFY as per RFC 6665.
415 | record_route();
416 | }
417 | route(RELAY);
418 | } else {
419 | if (is_method("SUBSCRIBE") && uri == myself) {
420 | # in-dialog subscribe requests
421 | route(PRESENCE);
422 | exit;
423 | }
424 | if ( is_method("ACK") ) {
425 | if ( t_check_trans() ) {
426 | # no loose-route, but stateful ACK;
427 | # must be an ACK after a 487
428 | # or e.g. 404 from upstream server
429 | route(RELAY);
430 | exit;
431 | } else {
432 | # ACK without matching transaction ... ignore and discard
433 | exit;
434 | }
435 | }
436 | sl_send_reply("404","Not here");
437 | }
438 | exit;
439 | }
440 | }
441 |
442 | # Handle SIP registrations
443 | route[REGISTRAR] {
444 | if (is_method("REGISTER"))
445 | {
446 | if(isflagset(FLT_NATS))
447 | {
448 | setbflag(FLB_NATB);
449 | # uncomment next line to do SIP NAT pinging
450 | ## setbflag(FLB_NATSIPPING);
451 | }
452 | if (!save("location"))
453 | sl_reply_error();
454 |
455 | exit;
456 | }
457 | }
458 |
459 | # USER location service
460 | route[LOCATION] {
461 | $avp(oexten) = $rU;
462 | if (!lookup("location")) {
463 | $var(rc) = $rc;
464 | t_newtran();
465 | switch ($var(rc)) {
466 | case -1:
467 | case -3:
468 | send_reply("404", "Not Found");
469 | exit;
470 | case -2:
471 | send_reply("405", "Method Not Allowed");
472 | exit;
473 | }
474 | }
475 |
476 | route(RELAY);
477 | exit;
478 | }
479 |
480 | # Presence server route
481 | route[PRESENCE] {
482 | if(!is_method("PUBLISH|SUBSCRIBE"))
483 | return;
484 |
485 | if(is_method("SUBSCRIBE") && $hdr(Event)=="message-summary") {
486 | # returns here if no voicemail server is configured
487 | sl_send_reply("404", "No voicemail service");
488 | exit;
489 | }
490 |
491 | #!ifdef WITH_PRESENCE
492 | if (!t_newtran())
493 | {
494 | sl_reply_error();
495 | exit;
496 | }
497 |
498 | if(is_method("PUBLISH"))
499 | {
500 | handle_publish();
501 | t_release();
502 | } else if(is_method("SUBSCRIBE")) {
503 | handle_subscribe();
504 | t_release();
505 | }
506 | exit;
507 | #!endif
508 |
509 | # if presence enabled, this part will not be executed
510 | if (is_method("PUBLISH") || $rU==$null)
511 | {
512 | sl_send_reply("404", "Not here");
513 | exit;
514 | }
515 | return;
516 | }
517 |
518 | # Authentication route
519 | route[AUTH] {
520 | #!ifdef WITH_AUTH
521 |
522 | if (is_method("REGISTER") || from_uri==myself)
523 | {
524 | # authenticate requests
525 | if (!auth_check("$fd", "subscriber", "1")) {
526 | auth_challenge("$fd", "0");
527 | exit;
528 | }
529 | # user authenticated - remove auth header
530 | if(!is_method("REGISTER|PUBLISH"))
531 | consume_credentials();
532 | }
533 | # if caller is not local subscriber, then check if it calls
534 | # a local destination, otherwise deny, not an open relay here
535 | if (from_uri!=myself && uri!=myself)
536 | {
537 | sl_send_reply("403","Not relaying");
538 | exit;
539 | }
540 |
541 | #!endif
542 | return;
543 | }
544 |
545 | # Caller NAT detection route
546 | route[NATDETECT] {
547 | force_rport();
548 | if (nat_uac_test("19")) {
549 | if (is_method("REGISTER")) {
550 | fix_nated_register();
551 | } else {
552 | if(is_first_hop())
553 | set_contact_alias();
554 | }
555 | setflag(FLT_NATS);
556 | }
557 | return;
558 | }
559 |
560 | # NAT handling
561 | route[NATMANAGE] {
562 | if (is_request()) {
563 | if(has_totag()) {
564 | if(check_route_param("nat=yes")) {
565 | setbflag(FLB_NATB);
566 | }
567 | }
568 | }
569 | if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB)))
570 | return;
571 |
572 | if (is_request()) {
573 | if (!has_totag()) {
574 | if(t_is_branch_route()) {
575 | add_rr_param(";nat=yes");
576 | }
577 | }
578 | }
579 | if (is_reply()) {
580 | if(isbflagset(FLB_NATB)) {
581 | if(is_first_hop())
582 | set_contact_alias();
583 | }
584 | }
585 | return;
586 | }
587 |
588 | # URI update for dialog requests
589 | route[DLGURI] {
590 | if(!isdsturiset()) {
591 | handle_ruri_alias();
592 | }
593 | return;
594 | }
595 |
596 | # Routing to foreign domains
597 | route[SIPOUT] {
598 | if (!uri==myself)
599 | {
600 | append_hf("P-hint: outbound\r\n");
601 | route(RELAY);
602 | }
603 | }
604 |
605 | route[RTP_BRIDGE] {
606 | #!ifdef WITH_ALWAYS_BRIDGE
607 | if (is_method("INVITE")) {
608 | if ($ru =~ "transport=ws") {
609 | xlog("L_INFO", "SIP -> WebRTC, bridging RTP->SRTP and adding ICE");
610 | rtpengine_manage("trust-address replace-origin replace-session-connection rtcp-mux-accept rtcp-mux-offer ICE=force UDP/TLS/RTP/SAVPF");
611 | t_on_reply("REPLY_FROM_WS");
612 | } else if ($proto =~ "ws") {
613 | xlog("L_INFO", "WebRTC -> SIP, bridging SRTP->RTP and removing ICE");
614 | rtpengine_manage("trust-address replace-origin replace-session-connection rtcp-mux-demux ICE=remove RTP/AVP");
615 | t_on_reply("REPLY_TO_WS");
616 | }
617 | }
618 | #!endif
619 | }
620 |
621 | # manage outgoing branches
622 | branch_route[MANAGE_BRANCH] {
623 | xdbg("new branch [$T_branch_idx] to $ru\n");
624 | route(NATMANAGE);
625 | }
626 |
627 | onreply_route[REPLY_TO_WS] {
628 | xlog("L_INFO", "Reply from softphone: $rs");
629 |
630 | if (t_check_status("183")) {
631 | change_reply_status("180", "Ringing");
632 | remove_body();
633 | route(NATMANAGE);
634 | exit;
635 | }
636 |
637 | if(!(status=~"[12][0-9][0-9]") || !(sdp_content()))
638 | return;
639 |
640 | rtpengine_manage();
641 |
642 | route(NATMANAGE);
643 | }
644 |
645 | onreply_route[REPLY_FROM_WS] {
646 |
647 | xlog("L_INFO", "Reply from webrtc client: $rs");
648 |
649 | if(status=~"[12][0-9][0-9]") {
650 | rtpengine_manage();
651 | route(NATMANAGE);
652 | }
653 | }
654 |
655 | # manage incoming replies
656 | onreply_route[MANAGE_REPLY] {
657 | xdbg("incoming reply\n");
658 | if(status=~"[12][0-9][0-9]")
659 | route(NATMANAGE);
660 | }
661 |
662 | # manage failure routing cases
663 | failure_route[MANAGE_FAILURE] {
664 | xlog("L_INFO", "Failure: $rs");
665 | #!ifndef WITH_ALWAYS_BRIDGE
666 | if (t_check_status("488") && sdp_content()) {
667 | if ($ru =~ "transport=ws") {
668 | xlog("L_INFO", "WebRTC client responded 488 Not Supported Here, bridging RTP->SRTP and adding ICE");
669 | rtpengine_offer("trust-address replace-origin replace-session-connection ICE=force rtcp-mux-accept rtcp-mux-offer UDP/TLS/RTP/SAVPF");
670 | t_on_reply("REPLY_FROM_WS");
671 | } else if ($proto =~ "ws") {
672 | xlog("L_INFO", "SIP client at the other end responded 488 Not Supported Here, bridging SRTP->RTP and removing ICE");
673 | rtpengine_offer("trust-address replace-origin replace-session-connection rtcp-mux-demux ICE=remove RTP/AVP");
674 | t_on_reply("REPLY_TO_WS");
675 | }
676 |
677 | append_branch();
678 | route(RELAY);
679 | }
680 | #!endif
681 | }
682 |
683 | #!ifdef WITH_WEBSOCKETS
684 | onreply_route {
685 | if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT)
686 | && !(proto == WS || proto == WSS))) {
687 | xlog("L_WARN", "SIP response received on $Rp\n");
688 | drop;
689 | }
690 |
691 | if (nat_uac_test(64)) {
692 | # Do NAT traversal stuff for replies to a WebSocket connection
693 | # - even if it is not behind a NAT!
694 | # This won't be needed in the future if Kamailio and the
695 | # WebSocket client support Outbound and Path.
696 | add_contact_alias();
697 | }
698 | }
699 |
700 | event_route[xhttp:request] {
701 | set_reply_close();
702 | set_reply_no_connect();
703 |
704 | if ($Rp != MY_WS_PORT
705 | #!ifdef WITH_TLS
706 | && $Rp != MY_WSS_PORT
707 | #!endif
708 | ) {
709 | xlog("L_WARN", "HTTP request received on $Rp\n");
710 | xhttp_reply("403", "Forbidden", "", "");
711 | exit;
712 | }
713 |
714 | xlog("L_DBG", "HTTP Request Received\n");
715 |
716 | if ($hdr(Upgrade)=~"websocket"
717 | && $hdr(Connection)=~"Upgrade"
718 | && $rm=~"GET") {
719 |
720 | # Validate Host - make sure the client is using the correct
721 | # alias for WebSockets
722 | if ($hdr(Host) == $null || !is_myself("sip:" + $hdr(Host))) {
723 | xlog("L_WARN", "Bad host $hdr(Host)\n");
724 | xhttp_reply("403", "Forbidden", "", "");
725 | exit;
726 | }
727 |
728 | # Optional... validate Origin - make sure the client is from an
729 | # authorised website. For example,
730 | #
731 | # if ($hdr(Origin) != "https://example.com"
732 | # && $hdr(Origin) != "https://example.com") {
733 | # xlog("L_WARN", "Unauthorised client $hdr(Origin)\n");
734 | # xhttp_reply("403", "Forbidden", "", "");
735 | # exit;
736 | # }
737 |
738 | # Optional... perform HTTP authentication
739 |
740 | # ws_handle_handshake() exits (no further configuration file
741 | # processing of the request) when complete.
742 | if (ws_handle_handshake())
743 | {
744 | # Optional... cache some information about the
745 | # successful connection
746 | exit;
747 | }
748 | }
749 |
750 | xhttp_reply("404", "Not Found", "", "");
751 | }
752 |
753 | event_route[websocket:closed] {
754 | xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
755 | }
756 | #!endif
757 |
--------------------------------------------------------------------------------
/etc/kamailio/kamailio.cfg:
--------------------------------------------------------------------------------
1 | #!KAMAILIO
2 | #
3 |
4 | #!define WITH_MYSQL
5 | #!define WITH_AUTH
6 | #!define WITH_USRLOCDB
7 | #!define WITH_TLS
8 | #!define WITH_HOMER
9 | #!define WITH_WEBSOCKETS
10 | #!define WITH_ANTIFLOOD
11 | #!define WITH_IPV6
12 | ##!define WITH_BRIDGE_ON_FAIL
13 | #!define WITH_LOCALHOST_WS
14 | ##!define WITH_LOCALHOST_SIP
15 |
16 | #!substdef "!MY_SIP_PORT!5060!g"
17 | #!substdef "!MY_SIPS_PORT!5061!g"
18 | #!substdef "!MY_WS_PORT!8080!g"
19 | #!substdef "!MY_WSS_PORT!4443!g"
20 |
21 | #!substdef "!MY_IP4_ADDR!XXXXX-XXXXX!g"
22 | #!substdef "!IP4_LOCALHOST!127.0.0.1!g"
23 | #!substdef "!MY_WS4_ADDR!tcp:MY_IP4_ADDR:MY_WS_PORT!g"
24 | #!substdef "!MY_WSS4_ADDR!tls:MY_IP4_ADDR:MY_WSS_PORT!g"
25 | #!substdef "!LOCALHOST_WS4_ADDR!tcp:IP4_LOCALHOST:MY_WS_PORT!g"
26 | #!substdef "!LOCALHOST_WSS4_ADDR!tls:IP4_LOCALHOST:MY_WSS_PORT!g"
27 |
28 | #!ifdef WITH_IPV6
29 | #!substdef "!MY_IP6_ADDR![XXXXXX-XXXXXX]!g"
30 | #!substdef "!IP6_LOCALHOST![::1]!g"
31 | #!substdef "!MY_WS6_ADDR!tcp:MY_IP6_ADDR:MY_WS_PORT!g"
32 | #!substdef "!MY_WSS6_ADDR!tls:MY_IP6_ADDR:MY_WSS_PORT!g"
33 | #!substdef "!LOCALHOST_WS6_ADDR!tcp:IP6_LOCALHOST:MY_WS_PORT!g"
34 | #!substdef "!LOCALHOST_WSS6_ADDR!tls:IP6_LOCALHOST:MY_WSS_PORT!g"
35 | #!endif
36 |
37 | #!substdef "!MY_DOMAIN!XXXX-XXXX!g"
38 |
39 | # *** Value defines - IDs used later in config
40 | #!ifdef WITH_MYSQL
41 | # - database URL - used to connect to database server by modules such
42 | # as: auth_db, acc, usrloc, a.s.o.
43 | #!ifndef DBURL
44 | #!define DBURL "mysql://kamailio:kamailiorw@localhost/kamailio"
45 | #!endif
46 | #!endif
47 |
48 | # - flags
49 | # FLT_ - per transaction (message) flags
50 | # FLB_ - per branch flags
51 | #!define FLT_NATS 5
52 |
53 | #!define FLB_NATB 6
54 | #!define FLB_NATSIPPING 7
55 | #!define FLB_RTPWS 8
56 | #!define FLB_IPV6 9
57 | #!define FLB_V4V6 10
58 | #!define FLB_BRIDGE 11
59 |
60 | ####### Global Parameters #########
61 |
62 | ### LOG Levels: 3=DBG, 2=INFO, 1=NOTICE, 0=WARN, -1=ERR
63 | #!ifdef WITH_DEBUG
64 | debug=4
65 | log_stderror=no
66 | #!else
67 | debug=2
68 | log_stderror=no
69 | #!endif
70 |
71 | memdbg=5
72 | memlog=5
73 |
74 | log_facility=LOG_LOCAL0
75 |
76 | fork=yes
77 | children=4
78 |
79 | port=MY_SIP_PORT
80 | tls_port_no=MY_SIPS_PORT
81 |
82 | #!ifdef WITH_TLS
83 | enable_tls=yes
84 | #!endif
85 |
86 |
87 | listen=MY_IP4_ADDR
88 | #!ifdef WITH_LOCALHOST_SIP
89 | listen=IP4_LOCALHOST
90 | #!endif
91 | #!ifdef WITH_IPV6
92 | listen=MY_IP6_ADDR
93 | #!ifdef WITH_LOCALHOST_SIP
94 | listen=IP6_LOCALHOST
95 | #!endif
96 | #!endif
97 |
98 | #!ifdef WITH_WEBSOCKETS
99 | listen=MY_WS4_ADDR
100 | #!ifdef WITH_LOCALHOST_WS
101 | listen=LOCALHOST_WS4_ADDR
102 | #!endif
103 | #!ifdef WITH_IPV6
104 | listen=MY_WS6_ADDR
105 | #!ifdef WITH_LOCALHOST_WS
106 | listen=LOCALHOST_WS6_ADDR
107 | #!endif
108 | #!endif
109 | #!ifdef WITH_TLS
110 | listen=MY_WSS4_ADDR
111 | #!ifdef WITH_LOCALHOST_WS
112 | listen=LOCALHOST_WSS4_ADDR
113 | #!endif
114 | #!ifdef WITH_IPV6
115 | listen=MY_WSS6_ADDR
116 | #!ifdef WITH_LOCALHOST_WS
117 | listen=LOCALHOST_WSS6_ADDR
118 | #!endif
119 | #!endif
120 | #!endif
121 | #!endif
122 |
123 | use_dns_cache = on # Use KAMAILIO internal DNS cache
124 | use_dns_failover = on # Depends on KAMAILIO internal DNS cache
125 | dns_srv_loadbalancing = on #
126 | dns_try_naptr = on #
127 | dns_retr_time=1 # Time in seconds before retrying a DNS request
128 | dns_retr_no=3 # Number of DNS retransmissions before giving up
129 |
130 | # Set protocol preference order - ignore target priority
131 | dns_naptr_ignore_rfc= yes # Ignore target NAPTR priority
132 | dns_tls_pref=50 # First priority: TLS
133 | dns_tcp_pref=30 # Second priority: TCP
134 | dns_udp_pref=10 # Third priority: UDP
135 |
136 | tcp_connection_lifetime=3604
137 | tcp_accept_no_cl=yes
138 | tcp_rd_buf_size=16384
139 |
140 |
141 | # set paths to location of modules (to sources or installation folders)
142 | #!ifdef WITH_SRCPATH
143 | mpath="modules/"
144 | #!else
145 | mpath="/usr/lib/x86_64-linux-gnu/kamailio/modules/"
146 | #!endif
147 |
148 | #!ifdef WITH_MYSQL
149 | loadmodule "db_mysql.so"
150 | #!endif
151 |
152 | loadmodule "kex.so"
153 | loadmodule "corex.so"
154 | loadmodule "tm.so"
155 | loadmodule "tmx.so"
156 | loadmodule "sl.so"
157 | loadmodule "rr.so"
158 | loadmodule "pv.so"
159 | loadmodule "maxfwd.so"
160 | loadmodule "usrloc.so"
161 | loadmodule "registrar.so"
162 | loadmodule "textops.so"
163 | loadmodule "siputils.so"
164 | loadmodule "xlog.so"
165 | loadmodule "sanity.so"
166 | loadmodule "ctl.so"
167 | loadmodule "cfg_rpc.so"
168 | loadmodule "sdpops.so"
169 | loadmodule "textopsx.so"
170 |
171 | #!ifdef WITH_AUTH
172 | loadmodule "auth.so"
173 | loadmodule "auth_db.so"
174 | #!ifdef WITH_IPAUTH
175 | loadmodule "permissions.so"
176 | #!endif
177 | #!endif
178 |
179 | #!ifdef WITH_PRESENCE
180 | loadmodule "presence.so"
181 | loadmodule "presence_xml.so"
182 | #!endif
183 |
184 | #!ifdef WITH_TLS
185 | loadmodule "tls.so"
186 | #!endif
187 |
188 | #!ifdef WITH_HOMER
189 | loadmodule "siptrace.so"
190 | #!endif
191 |
192 | #!ifdef WITH_WEBSOCKETS
193 | loadmodule "xhttp.so"
194 | loadmodule "websocket.so"
195 | loadmodule "nathelper.so"
196 | loadmodule "rtpengine.so"
197 | #!endif
198 |
199 | #!ifdef WITH_ANTIFLOOD
200 | loadmodule "htable.so"
201 | loadmodule "pike.so"
202 | #!endif
203 |
204 | #!ifdef WITH_DEBUG
205 | loadmodule "debugger.so"
206 | #!endif
207 |
208 | # ----------------- setting module-specific parameters ---------------
209 |
210 |
211 | # ----- rr params -----
212 | # add value to ;lr param to cope with most of the UAs
213 | modparam("rr", "enable_full_lr", 1)
214 | # do not append from tag to the RR (no need for this script)
215 | modparam("rr", "append_fromtag", 0)
216 |
217 |
218 | # ----- registrar params -----
219 | modparam("registrar", "method_filtering", 1)
220 | # max value for expires of registrations
221 | modparam("registrar", "max_expires", 3600)
222 |
223 |
224 | # ----- usrloc params -----
225 | /* enable DB persistency for location entries */
226 | #!ifdef WITH_USRLOCDB
227 | modparam("usrloc", "db_url", DBURL)
228 | modparam("usrloc", "db_mode", 2)
229 | #!endif
230 |
231 |
232 | # ----- auth_db params -----
233 | #!ifdef WITH_AUTH
234 | modparam("auth_db", "db_url", DBURL)
235 | modparam("auth_db", "calculate_ha1", 1)
236 | modparam("auth_db", "password_column", "password")
237 | modparam("auth_db", "load_credentials", "")
238 | #!endif
239 |
240 | #!ifdef WITH_PRESENCE
241 | # ----- presence params -----
242 | modparam("presence", "db_url", DBURL)
243 |
244 | # ----- presence_xml params -----
245 | modparam("presence_xml", "db_url", DBURL)
246 | modparam("presence_xml", "force_active", 1)
247 | #!endif
248 |
249 |
250 | ##!ifdef WITH_NAT
251 | # ----- rtpproxy params -----
252 | modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:22222")
253 | modparam("rtpengine", "extra_id_pv", "$avp(extra_id)")
254 |
255 | # ----- nathelper params -----
256 | modparam("nathelper", "natping_interval", 30)
257 | modparam("nathelper", "ping_nated_only", 1)
258 | modparam("nathelper", "sipping_bflag", FLB_NATSIPPING)
259 | modparam("nathelper", "sipping_from", "sip:pinger@XXXX-XXXX")
260 | modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
261 | modparam("usrloc", "nat_bflag", FLB_NATB)
262 | ##!endif
263 |
264 | # ----- corex params -----
265 | modparam("corex", "alias_subdomains", "MY_DOMAIN")
266 |
267 | #!ifdef WITH_TLS
268 | # ----- tls params -----
269 | modparam("tls", "config", "/etc/kamailio/tls.cfg")
270 | modparam("tls", "tls_force_run", 1)
271 | #!endif
272 |
273 | #!ifdef WITH_WEBSOCKETS
274 | modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
275 | #!endif
276 |
277 | #!ifdef WITH_HOMER
278 | #Siptrace
279 | modparam("siptrace", "duplicate_uri", "sip:127.0.0.1:9060")
280 | modparam("siptrace", "hep_mode_on", 1)
281 | modparam("siptrace", "trace_to_database", 0)
282 | modparam("siptrace", "trace_flag", 22)
283 | modparam("siptrace", "trace_on", 1)
284 | #!endif
285 |
286 | #!ifdef WITH_ANTIFLOOD
287 | # ----- pike params -----
288 | modparam("pike", "sampling_time_unit", 2)
289 | modparam("pike", "reqs_density_per_unit", 16)
290 | modparam("pike", "remove_latency", 4)
291 |
292 | # ----- htable params -----
293 | # ip ban htable with autoexpire after 5 minutes
294 | modparam("htable", "htable", "ipban=>size=8;autoexpire=300;")
295 | #!endif
296 |
297 | #!ifdef WITH_DEBUG
298 | # ----- debugger params -----
299 | modparam("debugger", "cfgtrace", 1)
300 | #!endif
301 |
302 | ####### Routing Logic ########
303 | request_route {
304 | #!ifdef WITH_HOMER
305 | # start duplicate the SIP message here
306 | sip_trace();
307 | setflag(22);
308 | #!endif
309 |
310 | # per request initial checks
311 | route(REQINIT);
312 |
313 | xlog("L_INFO", "START: $rm from $fu (IP:$si:$sp)\n");
314 |
315 | #!ifdef WITH_WEBSOCKETS
316 | if (nat_uac_test(64)) {
317 | # Do NAT traversal stuff for requests from a WebSocket
318 | # connection - even if it is not behind a NAT!
319 | # This won't be needed in the future if Kamailio and the
320 | # WebSocket client support Outbound and Path.
321 | force_rport();
322 | if (is_method("REGISTER")) {
323 | fix_nated_register();
324 | } else if (!add_contact_alias()) {
325 | xlog("L_ERR", "Error aliasing contact <$ct>\n");
326 | sl_send_reply("400", "Bad Request");
327 | exit;
328 | }
329 | }
330 | #!endif
331 |
332 | # NAT detection
333 | route(NATDETECT);
334 |
335 | # CANCEL processing
336 | if (is_method("CANCEL")) {
337 | if (t_check_trans()) {
338 | route(RELAY);
339 | }
340 | exit;
341 | }
342 |
343 | # handle requests within SIP dialogs
344 | route(WITHINDLG);
345 |
346 | ### only initial requests (no To tag)
347 |
348 | t_check_trans();
349 |
350 | # authentication
351 | route(AUTH);
352 |
353 | # record routing for dialog forming requests (in case they are routed)
354 | # - remove preloaded route headers
355 | remove_hf("Route");
356 | if (is_method("INVITE|SUBSCRIBE")) {
357 | record_route();
358 | }
359 |
360 | # dispatch requests to foreign domains
361 | route(SIPOUT);
362 |
363 | ### requests for my local domains
364 |
365 | # handle presence related requests
366 | route(PRESENCE);
367 |
368 | # handle registrations
369 | route(REGISTRAR);
370 |
371 | if ($rU == $null) {
372 | # request with no Username in RURI
373 | sl_send_reply("484","Address Incomplete");
374 | exit;
375 | }
376 |
377 | # user location service
378 | route(LOCATION);
379 | }
380 |
381 | # Wrapper for relaying requests
382 | route[RELAY] {
383 | # enable additional event routes for forwarded requests
384 | # - serial forking, RTP relaying handling, a.s.o.
385 | if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) {
386 | if (!t_is_set("branch_route")) {
387 | t_on_branch("MANAGE_BRANCH");
388 | }
389 | }
390 |
391 | if (is_method("INVITE|SUBSCRIBE|UPDATE")) {
392 | if (!t_is_set("onreply_route")) {
393 | t_on_reply("MANAGE_REPLY");
394 | }
395 | }
396 |
397 | if (is_method("INVITE")) {
398 | if (!t_is_set("failure_route")) {
399 | t_on_failure("MANAGE_FAILURE");
400 | }
401 | }
402 |
403 | if (!t_relay()) {
404 | sl_reply_error();
405 | }
406 | exit;
407 | }
408 |
409 | # Per SIP request initial checks
410 | route[REQINIT] {
411 | #!ifdef WITH_ANTIFLOOD
412 | # flood dection from same IP and traffic ban for a while
413 | # be sure you exclude checking trusted peers, such as pstn gateways
414 | # - local host excluded (e.g., loop to self)
415 | if (src_ip != myself) {
416 | if ($sht(ipban=>$si) != $null) {
417 | # ip is already blocked
418 | xdbg("request from blocked IP - $rm from $fu (IP:$si:$sp)\n");
419 | exit;
420 | }
421 |
422 | if (!pike_check_req()) {
423 | xlog("L_ALERT","ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n");
424 | $sht(ipban=>$si) = 1;
425 | exit;
426 | }
427 | }
428 | #!endif
429 |
430 | if (!mf_process_maxfwd_header("10")) {
431 | sl_send_reply("483","Too Many Hops");
432 | exit;
433 | }
434 |
435 | if (!sanity_check("1511", "7")) {
436 | xlog("Malformed SIP message from $si:$sp\n");
437 | exit;
438 | }
439 | }
440 |
441 | # Handle requests within SIP dialogs
442 | route[WITHINDLG] {
443 | if (has_totag()) {
444 | # sequential request withing a dialog should
445 | # take the path determined by record-routing
446 | if (loose_route()) {
447 | #!ifdef WITH_WEBSOCKETS
448 | if ($du == "") {
449 | if (!handle_ruri_alias()) {
450 | xlog("L_ERR", "Bad alias <$ru>\n");
451 | sl_send_reply("400", "Bad Request");
452 | exit;
453 | }
454 | }
455 | #!endif
456 | route(DLGURI);
457 | if (is_method("ACK")) {
458 | # ACK is forwarded statelessy
459 | route(NATMANAGE);
460 | } else if (is_method("NOTIFY")) {
461 | # Add Record-Route for in-dialog NOTIFY as per RFC 6665.
462 | record_route();
463 | }
464 | route(RELAY);
465 | } else {
466 | if (is_method("SUBSCRIBE") && uri == myself) {
467 | # in-dialog subscribe requests
468 | route(PRESENCE);
469 | exit;
470 | }
471 | if (is_method("ACK")) {
472 | if (t_check_trans()) {
473 | # no loose-route, but stateful ACK;
474 | # must be an ACK after a 487
475 | # or e.g. 404 from upstream server
476 | route(RELAY);
477 | exit;
478 | } else {
479 | # ACK without matching transaction ... ignore and discard
480 | exit;
481 | }
482 | }
483 | sl_send_reply("404","Not here");
484 | }
485 | exit;
486 | }
487 | }
488 |
489 | # Handle SIP registrations
490 | route[REGISTRAR] {
491 | if (is_method("REGISTER")) {
492 | if (isflagset(FLT_NATS)) {
493 | setbflag(FLB_NATB);
494 | # uncomment next line to do SIP NAT pinging
495 | ## setbflag(FLB_NATSIPPING);
496 | }
497 |
498 | #!ifdef WITH_IPV6
499 | if (af == INET6) {
500 | setbflag(FLB_IPV6);
501 | }
502 | #!endif
503 |
504 | if (!save("location")) {
505 | sl_reply_error();
506 | }
507 |
508 | exit;
509 | }
510 | }
511 |
512 | # USER location service
513 | route[LOCATION] {
514 | if (!lookup("location")) {
515 | $var(rc) = $rc;
516 | t_newtran();
517 | switch ($var(rc)) {
518 | case -1:
519 | case -3:
520 | send_reply("404", "Not Found");
521 | exit;
522 | case -2:
523 | send_reply("405", "Method Not Allowed");
524 | exit;
525 | }
526 | }
527 |
528 | route(RELAY);
529 | exit;
530 | }
531 |
532 | # Presence server route
533 | route[PRESENCE] {
534 | if (!is_method("PUBLISH|SUBSCRIBE")) {
535 | return;
536 | }
537 |
538 | if (is_method("SUBSCRIBE") && $hdr(Event) == "message-summary") {
539 | # returns here if no voicemail server is configured
540 | sl_send_reply("404", "No voicemail service");
541 | exit;
542 | }
543 |
544 | #!ifdef WITH_PRESENCE
545 | if (!t_newtran()) {
546 | sl_reply_error();
547 | exit;
548 | }
549 |
550 | if (is_method("PUBLISH")) {
551 | handle_publish();
552 | t_release();
553 | } else if (is_method("SUBSCRIBE")) {
554 | handle_subscribe();
555 | t_release();
556 | }
557 | exit;
558 | #!endif
559 |
560 | # if presence enabled, this part will not be executed
561 | if (is_method("PUBLISH") || $rU == $null) {
562 | sl_send_reply("404", "Not here");
563 | exit;
564 | }
565 | return;
566 | }
567 |
568 | # Authentication route
569 | route[AUTH] {
570 | #!ifdef WITH_AUTH
571 | if (is_method("REGISTER") || from_uri == myself) {
572 | # authenticate requests
573 | if (!auth_check("$fd", "subscriber", "1")) {
574 | auth_challenge("$fd", "0");
575 | exit;
576 | }
577 | # user authenticated - remove auth header
578 | if (!is_method("REGISTER|PUBLISH")) {
579 | consume_credentials();
580 | }
581 | }
582 | # if caller is not local subscriber, then check if it calls
583 | # a local destination, otherwise deny, not an open relay here
584 | if (from_uri != myself && uri != myself) {
585 | sl_send_reply("403","Not relaying");
586 | exit;
587 | }
588 |
589 | #!endif
590 | return;
591 | }
592 |
593 | # Caller NAT detection route
594 | route[NATDETECT] {
595 | #!ifdef WITH_IPV6
596 | if(af==INET6) {
597 | return;
598 | }
599 | #!endif
600 |
601 | force_rport();
602 | if (nat_uac_test("19")) {
603 | if (is_method("REGISTER")) {
604 | fix_nated_register();
605 | } else if (is_first_hop()) {
606 | set_contact_alias();
607 | }
608 | setflag(FLT_NATS);
609 | }
610 | return;
611 | }
612 |
613 | # NAT handling
614 | route[NATMANAGE] {
615 | if (is_request()) {
616 | if (has_totag()) {
617 | if (check_route_param("nat=yes")) {
618 | setbflag(FLB_NATB);
619 | }
620 |
621 | if (check_route_param("rtp=bridge")) {
622 | setbflag(FLB_BRIDGE);
623 | }
624 |
625 | if (check_route_param("rtp=ws")) {
626 | setbflag(FLB_RTPWS);
627 | }
628 |
629 | #!ifdef WITH_IPV6
630 | if (check_route_param("rtp=v46")) {
631 | setbflag(FLB_V4V6);
632 | }
633 | #!endif
634 | }
635 | }
636 |
637 | if (!isbflagset(FLB_BRIDGE)) {
638 | return;
639 | }
640 |
641 | if (
642 | !(isflagset(FLT_NATS)
643 | || isbflagset(FLB_NATB)
644 | || isbflagset(FLB_RTPWS)
645 | #!ifdef WITH_IPV6
646 | || isbflagset(FLB_V4V6)
647 | #!endif
648 | )) {
649 | return;
650 | }
651 |
652 | $xavp(r=>$T_branch_idx) = "replace-origin replace-session-connection";
653 |
654 | if (!nat_uac_test("8")) {
655 | $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " trust-address";
656 | }
657 |
658 |
659 | if (is_request()) {
660 | if (!has_totag()) {
661 | if (!t_is_failure_route()) {
662 | $avp(extra_id) = @via[1].branch + $T_branch_idx;
663 | $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " via-branch=extra";
664 | }
665 | }
666 | }
667 |
668 | if (is_reply()) {
669 | $avp(extra_id) = @via[2].branch + $T_branch_idx;
670 | $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " via-branch=extra";
671 | }
672 |
673 | #!ifdef WITH_IPV6
674 | if (af == INET && isbflagset(FLB_IPV6)) { # IPv4 --> IPv6
675 | $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " address-family=IP6";
676 | } else if (af == INET6 && !isbflagset(FLB_IPV6)) { # IPv6 --> IPv4
677 | $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " address-family=IP4";
678 | }
679 | #!endif
680 |
681 | if (isbflagset(FLB_RTPWS)) {
682 | if ($proto =~ "ws") { # web --> SIP
683 | $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " rtcp-mux-demux DTLS=off SDES-off ICE=remove RTP/AVP";
684 | } else { # SIP --> web
685 | $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " rtcp-mux-offer generate-mid DTLS=passive SDES-off ICE=force RTP/SAVPF";
686 | }
687 | } else {
688 | if ($proto =~ "ws") { # web --> web
689 | $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + " generate-mid DTLS=passive SDES-off ICE=force";
690 | }
691 | # else {
692 | # $xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + "";
693 | # }
694 | }
695 |
696 | xlog("L_INFO", "NATMANAGE branch_id:$T_branch_idx ruri: $ru, method:$rm, status:$rs, extra_id: $avp(extra_id), rtpengine_manage: $xavp(r=>$T_branch_idx)\n");
697 |
698 | rtpengine_manage($xavp(r=>$T_branch_idx));
699 |
700 | if (is_request()) {
701 | if (!has_totag()) {
702 | if (t_is_branch_route()) {
703 | if (isbflagset(FLB_NATB)) {
704 | add_rr_param(";nat=yes");
705 | }
706 |
707 | if (isbflagset(FLB_BRIDGE)) {
708 | add_rr_param(";rtp=bridge");
709 | }
710 |
711 | if (isbflagset(FLB_RTPWS)) {
712 | add_rr_param(";rtp=ws");
713 | }
714 |
715 | #!ifdef WITH_IPV6
716 | if (isbflagset(FLB_V4V6)) {
717 | add_rr_param(";rtp=v46");
718 | }
719 | #!endif
720 | }
721 | }
722 | }
723 |
724 | if (is_reply()) {
725 | if (isbflagset(FLB_NATB)) {
726 | if (is_first_hop()) {
727 | if (af == INET) {
728 | set_contact_alias();
729 | }
730 | }
731 | }
732 | }
733 | return;
734 | }
735 |
736 | # URI update for dialog requests
737 | route[DLGURI] {
738 | if (!isdsturiset()) {
739 | handle_ruri_alias();
740 | }
741 | return;
742 | }
743 |
744 | # Routing to foreign domains
745 | route[SIPOUT] {
746 | if (!uri == myself) {
747 | append_hf("P-hint: outbound\r\n");
748 | route(RELAY);
749 | }
750 | }
751 |
752 | route[BRIDGING] {
753 | if (!has_totag()) {
754 | if ($proto =~ "ws" && !($ru =~ "transport=ws")) { # Coming from WS, NOT to WS
755 | setbflag(FLB_RTPWS); # Need bridging
756 | } else if (!($proto =~ "ws") && $ru =~ "transport=ws") { # Coming from NOT WS, going to WS
757 | setbflag(FLB_RTPWS); # Need bridging
758 | }
759 |
760 | #!ifdef WITH_IPV6
761 | if (af == INET6 && !isbflagset(FLB_IPV6)) {
762 | setbflag(FLB_V4V6);
763 | } else if(af == INET && isbflagset(FLB_IPV6)) {
764 | setbflag(FLB_V4V6);
765 | }
766 | #!endif
767 | }
768 | }
769 |
770 | # manage outgoing branches
771 | branch_route[MANAGE_BRANCH] {
772 | xlog("L_INFO", "MANAGE_BRANCH: New branch [$T_branch_idx] to $ru\n");
773 |
774 | t_on_branch_failure("rtpengine");
775 |
776 | #!ifndef WITH_BRIDGE_ON_FAIL
777 | setbflag(FLB_BRIDGE);
778 | #!endif
779 |
780 | route(BRIDGING);
781 | route(NATMANAGE);
782 | }
783 |
784 | # manage incoming replies
785 | onreply_route[MANAGE_REPLY] {
786 | xdbg("incoming reply\n");
787 | if (status =~ "[12][0-9][0-9]") {
788 | route(NATMANAGE);
789 | }
790 | }
791 |
792 | # manage failure routing cases
793 | failure_route[MANAGE_FAILURE] {
794 | xlog("L_INFO", "Failure: $rs");
795 | }
796 |
797 | #!ifdef WITH_WEBSOCKETS
798 | onreply_route {
799 | if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT)
800 | && !(proto == WS || proto == WSS))) {
801 | xlog("L_WARN", "SIP response received on $Rp\n");
802 | drop;
803 | }
804 |
805 | if (nat_uac_test(64)) {
806 | # Do NAT traversal stuff for replies to a WebSocket connection
807 | # - even if it is not behind a NAT!
808 | # This won't be needed in the future if Kamailio and the
809 | # WebSocket client support Outbound and Path.
810 | add_contact_alias();
811 | }
812 | }
813 |
814 | event_route[tm:branch-failure:rtpengine] {
815 | xlog("L_INFO", "BRANCH FAILED: $sel(via[1].branch) + $T_branch_idx");
816 |
817 | #!ifdef WITH_BRIDGE_ON_FAIL
818 | if (!isbflagset(FLB_BRIDGE) && t_check_status("415|488")) {
819 | t_reuse_branch();
820 | setbflag(FLB_BRIDGE);
821 | xlog("L_INFO", "event_route[branch-failure:rtpengine]: trying again\n");
822 |
823 | route(RELAY);
824 | } else {
825 | $avp(extra_id) = @via[1].branch + $T_branch_idx;
826 | rtpengine_delete("via-branch=extra");
827 | xlog("L_INFO", "event_route[branch-failure:rtpengine]: failed\n");
828 | }
829 | #!else
830 | $avp(extra_id) = @via[1].branch + $T_branch_idx;
831 | rtpengine_delete("via-branch=extra");
832 | #!endif
833 | }
834 |
835 | event_route[xhttp:request] {
836 | set_reply_close();
837 | set_reply_no_connect();
838 |
839 | if ($Rp != MY_WS_PORT
840 | #!ifdef WITH_TLS
841 | && $Rp != MY_WSS_PORT
842 | #!endif
843 | ) {
844 | xlog("L_WARN", "HTTP request received on $Rp\n");
845 | xhttp_reply("403", "Forbidden", "", "");
846 | exit;
847 | }
848 |
849 | xlog("L_INFO", "HTTP Request Received\n");
850 |
851 | if ($hdr(Upgrade) =~ "websocket"
852 | && $hdr(Connection) =~ "Upgrade"
853 | && $rm =~ "GET"
854 | ) {
855 |
856 | # Validate Host - make sure the client is using the correct
857 | # alias for WebSockets
858 | if ($hdr(Host) == $null || !is_myself("sip:" + $hdr(Host))) {
859 | xlog("L_WARN", "Bad host $hdr(Host)\n");
860 | xhttp_reply("403", "Forbidden", "", "");
861 | exit;
862 | }
863 |
864 | # Optional... validate Origin - make sure the client is from an
865 | # authorised website. For example,
866 | #
867 | # if ($hdr(Origin) != "https://example.com"
868 | # && $hdr(Origin) != "https://example.com") {
869 | # xlog("L_WARN", "Unauthorised client $hdr(Origin)\n");
870 | # xhttp_reply("403", "Forbidden", "", "");
871 | # exit;
872 | # }
873 |
874 | # Optional... perform HTTP authentication
875 |
876 | # ws_handle_handshake() exits (no further configuration file
877 | # processing of the request) when complete.
878 | if (ws_handle_handshake()) {
879 | # Optional... cache some information about the
880 | # successful connection
881 | exit;
882 | }
883 | }
884 |
885 | xhttp_reply("404", "Not Found", "", "");
886 | }
887 |
888 | event_route[websocket:closed] {
889 | xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
890 | }
891 | #!endif
892 |
--------------------------------------------------------------------------------
/etc/kamailio/kamctlrc:
--------------------------------------------------------------------------------
1 | # $Id$
2 | #
3 | # The Kamailio configuration file for the control tools.
4 | #
5 | # Here you can set variables used in the kamctl and kamdbctl setup
6 | # scripts. Per default all variables here are commented out, the control tools
7 | # will use their internal default values.
8 |
9 | ## your SIP domain
10 | SIP_DOMAIN=XXXX-XXXX
11 |
12 | ## chrooted directory
13 | # $CHROOT_DIR="/path/to/chrooted/directory"
14 |
15 | ## database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT, or SQLITE
16 | # by default none is loaded
17 | #
18 | # If you want to setup a database with kamdbctl, you must at least specify
19 | # this parameter.
20 | DBENGINE=MYSQL
21 |
22 | ## database host
23 | DBHOST=localhost
24 |
25 | ## database name (for ORACLE this is TNS name)
26 | DBNAME=kamailio
27 |
28 | # database path used by dbtext, db_berkeley or sqlite
29 | # DB_PATH="/usr/local/etc/kamailio/dbtext"
30 |
31 | ## database read/write user
32 | DBRWUSER="kamailio"
33 |
34 | ## password for database read/write user
35 | DBRWPW="kamailiorw"
36 |
37 | ## database read only user
38 | DBROUSER="kamailioro"
39 |
40 | ## password for database read only user
41 | DBROPW="kamailioro"
42 |
43 | ## database access host (from where is kamctl used)
44 | # DBACCESSHOST=192.168.0.1
45 |
46 | ## database super user (for ORACLE this is 'scheme-creator' user)
47 | # DBROOTUSER="root"
48 |
49 | # user name column
50 | # USERCOL="username"
51 |
52 |
53 | # SQL definitions
54 | # If you change this definitions here, then you must change them
55 | # in db/schema/entities.xml too.
56 | # FIXME
57 |
58 | # FOREVER="2030-05-28 21:32:15"
59 | # DEFAULT_Q="1.0"
60 |
61 |
62 | # Program to calculate a message-digest fingerprint
63 | # MD5="md5sum"
64 |
65 | # awk tool
66 | # AWK="awk"
67 |
68 | # gdb tool
69 | # GDB="gdb"
70 |
71 | # If you use a system with a grep and egrep that is not 100% gnu grep compatible,
72 | # e.g. solaris, install the gnu grep (ggrep) and specify this below.
73 | #
74 | # grep tool
75 | # GREP="grep"
76 |
77 | # egrep tool
78 | # EGREP="egrep"
79 |
80 | # sed tool
81 | # SED="sed"
82 |
83 | # tail tool
84 | # LAST_LINE="tail -n 1"
85 |
86 | # expr tool
87 | # EXPR="expr"
88 |
89 |
90 | # Describe what additional tables to install. Valid values for the variables
91 | # below are yes/no/ask. With ask (default) it will interactively ask the user
92 | # for an answer, while yes/no allow for automated, unassisted installs.
93 | #
94 |
95 | # If to install tables for the modules in the EXTRA_MODULES variable.
96 | # INSTALL_EXTRA_TABLES=ask
97 |
98 | # If to install presence related tables.
99 | # INSTALL_PRESENCE_TABLES=ask
100 |
101 | # If to install uid modules related tables.
102 | # INSTALL_DBUID_TABLES=ask
103 |
104 | # Define what module tables should be installed.
105 | # If you use the postgres database and want to change the installed tables, then you
106 | # must also adjust the STANDARD_TABLES or EXTRA_TABLES variable accordingly in the
107 | # kamdbctl.base script.
108 |
109 | # Kamailio standard modules
110 | # STANDARD_MODULES="standard acc lcr domain group permissions registrar usrloc msilo
111 | # alias_db uri_db speeddial avpops auth_db pdt dialog dispatcher
112 | # dialplan"
113 |
114 | # Kamailio extra modules
115 | # EXTRA_MODULES="imc cpl siptrace domainpolicy carrierroute userblacklist htable purple sca"
116 |
117 |
118 | ## type of aliases used: DB - database aliases; UL - usrloc aliases
119 | ## - default: none
120 | # ALIASES_TYPE="DB"
121 |
122 | ## control engine: FIFO or UNIXSOCK
123 | ## - default FIFO
124 | # CTLENGINE="FIFO"
125 |
126 | ## path to FIFO file
127 | # FIFOPATH="/var/run/kamailio/kamailio_fifo"
128 |
129 | ## check ACL names; default on (1); off (0)
130 | # VERIFY_ACL=1
131 |
132 | ## ACL names - if VERIFY_ACL is set, only the ACL names from below list
133 | ## are accepted
134 | # ACL_GROUPS="local ld int voicemail free-pstn"
135 |
136 | ## verbose - debug purposes - default '0'
137 | # VERBOSE=1
138 |
139 | ## do (1) or don't (0) store plaintext passwords
140 | ## in the subscriber table - default '1'
141 | # STORE_PLAINTEXT_PW=0
142 |
143 | ## Kamailio START Options
144 | ## PID file path - default is: /var/run/kamailio.pid
145 | # PID_FILE=/var/run/kamailio/kamailio.pid
146 |
147 | ## Extra start options - default is: not set
148 | # example: start Kamailio with 64MB share memory: STARTOPTIONS="-m 64"
149 | # STARTOPTIONS=
150 |
151 |
--------------------------------------------------------------------------------
/etc/kamailio/tls.cfg:
--------------------------------------------------------------------------------
1 | #
2 | # Example Kamailio TLS Configuration File
3 | #
4 |
5 | [server:default]
6 | method = TLSv1+
7 | verify_certificate = no
8 | require_certificate = no
9 | private_key = privkey.pem
10 | certificate = fullchain.pem
11 |
12 | [client:default]
13 | verify_certificate = yes
14 | require_certificate = yes
15 |
--------------------------------------------------------------------------------
/etc/network/if-up.d/iptables:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Restore iptables from stored table
4 | iptables-restore < /etc/iptables/firewall.conf
5 | ip6tables-restore < /etc/iptables/firewall6.conf
6 |
--------------------------------------------------------------------------------
/etc/nginx/conf.d/default.conf:
--------------------------------------------------------------------------------
1 | server_tokens off;
2 |
3 | add_header X-Frame-Options SAMEORIGIN;
4 | add_header X-Content-Type-Options nosniff;
5 | add_header X-XSS-Protection "1; mode=block";
6 | add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com https://assets.zendesk.com https://connect.facebook.net; img-src 'self' https://ssl.google-analytics.com https://s-static.ak.facebook.com https://assets.zendesk.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://assets.zendesk.com; font-src 'self' https://themes.googleusercontent.com; frame-src https://assets.zendesk.com https://www.facebook.com https://s-static.ak.facebook.com https://tautt.zendesk.com; object-src 'none'";
7 |
8 | # redirect all http traffic to https
9 | server {
10 | listen 3480 default_server;
11 | listen [::]:3480 default_server;
12 |
13 | server_name _;
14 |
15 | return 301 https://$host$request_uri;
16 | }
17 |
18 | server {
19 | listen 3443 ssl http2;
20 |
21 | server_name _;
22 |
23 | root /var/www/html;
24 | index index.html index.htm;
25 |
26 | ssl_certificate /etc/letsencrypt/live/XXXX-XXXX/fullchain.pem;
27 | ssl_certificate_key /etc/letsencrypt/live/XXXX-XXXX/privkey.pem;
28 |
29 | ssl_session_cache shared:SSL:50m;
30 | ssl_session_timeout 1d;
31 | ssl_session_tickets off;
32 |
33 | # ssl_dhparam /etc/nginx/ssl/dhparam.pem;
34 | ssl_prefer_server_ciphers on;
35 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
36 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
37 |
38 | add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
39 |
40 | location /ws {
41 | proxy_pass https://127.0.0.1:4443/;
42 | proxy_http_version 1.1;
43 | proxy_set_header Upgrade $http_upgrade;
44 | proxy_set_header Connection "upgrade";
45 | proxy_read_timeout 86400;
46 | }
47 | }
48 |
49 | server {
50 | listen [::]:3443 ssl http2;
51 |
52 | server_name _;
53 |
54 | root /var/www/html;
55 | index index.html index.htm;
56 |
57 | ssl_certificate /etc/letsencrypt/live/XXXX-XXXX/fullchain.pem;
58 | ssl_certificate_key /etc/letsencrypt/live/XXXX-XXXX/privkey.pem;
59 |
60 | ssl_session_cache shared:SSL:50m;
61 | ssl_session_timeout 1d;
62 | ssl_session_tickets off;
63 |
64 | # ssl_dhparam /etc/nginx/ssl/dhparam.pem;
65 | ssl_prefer_server_ciphers on;
66 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
67 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
68 |
69 | add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
70 |
71 | location /ws {
72 | proxy_pass https://[::1]:4443/;
73 | proxy_http_version 1.1;
74 | proxy_set_header Upgrade $http_upgrade;
75 | proxy_set_header Connection "upgrade";
76 | proxy_read_timeout 86400;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/etc/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 |
2 | user nginx;
3 | worker_processes 1;
4 |
5 | error_log /var/log/nginx/error.log warn;
6 | pid /var/run/nginx.pid;
7 |
8 |
9 | events {
10 | worker_connections 1024;
11 | }
12 |
13 |
14 | http {
15 | include /etc/nginx/mime.types;
16 | default_type application/octet-stream;
17 |
18 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
19 | '$status $body_bytes_sent "$http_referer" '
20 | '"$http_user_agent" "$http_x_forwarded_for"';
21 |
22 | access_log /var/log/nginx/access.log main;
23 |
24 | sendfile on;
25 | #tcp_nopush on;
26 |
27 | keepalive_timeout 65;
28 |
29 | #gzip on;
30 |
31 | include /etc/nginx/conf.d/*.conf;
32 | }
33 |
34 | stream {
35 | log_format basic '$remote_addr [$time_local] '
36 | '$protocol $status $bytes_sent $bytes_received '
37 | '$session_time';
38 |
39 | access_log /var/log/nginx/stream.log basic;
40 | error_log /var/log/nginx/stream-error.log debug;
41 |
42 | upstream turns_ipv4_80 {
43 | server XXXXX-XXXXX:3479;
44 | }
45 |
46 | upstream http_ipv4_80 {
47 | server 127.0.0.1:3480;
48 | }
49 |
50 | map $ssl_preread_protocol $upstream_ipv4_80 {
51 | default turns_ipv4_80;
52 | "" http_ipv4_80;
53 | }
54 |
55 | upstream turns_ipv6_80 {
56 | server [XXXXXX-XXXXXX]:3479;
57 | }
58 |
59 | upstream http_ipv6_80 {
60 | server [::1]:3480;
61 | }
62 |
63 | map $ssl_preread_protocol $upstream_ipv6_80 {
64 | default turns_ipv6_80;
65 | "" http_ipv6_80;
66 | }
67 |
68 | upstream turn_ipv4_443 {
69 | server XXXXX-XXXXX:3478;
70 | }
71 |
72 | upstream https_ipv4_443 {
73 | server 127.0.0.1:3443;
74 | }
75 |
76 | map $ssl_preread_protocol $upstream_ipv4_443 {
77 | default https_ipv4_443;
78 | "" turn_ipv4_443;
79 | }
80 |
81 | upstream turn_ipv6_443 {
82 | server [XXXXXX-XXXXXX]:3478;
83 | }
84 |
85 | upstream https_ipv6_443 {
86 | server [::1]:3443;
87 | }
88 |
89 | map $ssl_preread_protocol $upstream_ipv6_443 {
90 | default https_ipv6_443;
91 | "" turn_ipv6_443;
92 | }
93 |
94 | server {
95 | listen 80;
96 | proxy_pass $upstream_ipv4_80;
97 | ssl_preread on;
98 | proxy_buffer_size 16k;
99 | }
100 |
101 | server {
102 | listen [::]:80;
103 | proxy_pass $upstream_ipv6_80;
104 | ssl_preread on;
105 | proxy_buffer_size 16k;
106 | }
107 |
108 | server {
109 | listen 443;
110 | proxy_pass $upstream_ipv4_443;
111 | ssl_preread on;
112 | proxy_buffer_size 16k;
113 | }
114 |
115 | server {
116 | listen [::]:443;
117 | proxy_pass $upstream_ipv6_443;
118 | ssl_preread on;
119 | proxy_buffer_size 16k;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/etc/nginx/sites-available/default:
--------------------------------------------------------------------------------
1 | server_tokens off;
2 |
3 | add_header X-Frame-Options SAMEORIGIN;
4 | add_header X-Content-Type-Options nosniff;
5 | add_header X-XSS-Protection "1; mode=block";
6 | add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ssl.google-analytics.com https://assets.zendesk.com https://connect.facebook.net; img-src 'self' https://ssl.google-analytics.com https://s-static.ak.facebook.com https://assets.zendesk.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://assets.zendesk.com; font-src 'self' https://themes.googleusercontent.com; frame-src https://assets.zendesk.com https://www.facebook.com https://s-static.ak.facebook.com https://tautt.zendesk.com; object-src 'none'";
7 |
8 | # redirect all http traffic to https
9 | server {
10 | listen 80 default_server;
11 | listen [::]:80 default_server;
12 |
13 | server_name _;
14 |
15 | return 301 https://$host$request_uri;
16 | }
17 |
18 | server {
19 | listen 443 ssl http2;
20 |
21 | server_name _;
22 |
23 | root /var/www/html;
24 | index index.html index.htm;
25 |
26 | ssl_certificate /etc/letsencrypt/live/XXXX-XXXX/fullchain.pem;
27 | ssl_certificate_key /etc/letsencrypt/live/XXXX-XXXX/privkey.pem;
28 |
29 | ssl_session_cache shared:SSL:50m;
30 | ssl_session_timeout 1d;
31 | ssl_session_tickets off;
32 |
33 | # ssl_dhparam /etc/nginx/ssl/dhparam.pem;
34 | ssl_prefer_server_ciphers on;
35 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
36 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
37 |
38 | add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
39 |
40 | location /ws {
41 | proxy_pass https://127.0.0.1:4443/;
42 | proxy_http_version 1.1;
43 | proxy_set_header Upgrade $http_upgrade;
44 | proxy_set_header Connection "upgrade";
45 | proxy_read_timeout 86400;
46 | }
47 | }
48 |
49 | server {
50 | listen [::]:443 ssl http2;
51 |
52 | server_name _;
53 |
54 | root /var/www/html;
55 | index index.html index.htm;
56 |
57 | ssl_certificate /etc/letsencrypt/live/XXXX-XXXX/fullchain.pem;
58 | ssl_certificate_key /etc/letsencrypt/live/XXXX-XXXX/privkey.pem;
59 |
60 | ssl_session_cache shared:SSL:50m;
61 | ssl_session_timeout 1d;
62 | ssl_session_tickets off;
63 |
64 | # ssl_dhparam /etc/nginx/ssl/dhparam.pem;
65 | ssl_prefer_server_ciphers on;
66 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
67 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
68 |
69 | add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
70 |
71 | location /ws {
72 | proxy_pass https://[::1]:4443/;
73 | proxy_http_version 1.1;
74 | proxy_set_header Upgrade $http_upgrade;
75 | proxy_set_header Connection "upgrade";
76 | proxy_read_timeout 86400;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/etc/rtpengine/rtpengine.conf:
--------------------------------------------------------------------------------
1 | [rtpengine]
2 |
3 | table = 0
4 | # no-fallback = false
5 | ### for userspace forwarding only:
6 | # table = -1
7 |
8 | ### a single interface:
9 | interface = pub/XXXXX-XXXXX;pub/XXXXXX-XXXXXX
10 | ### separate multiple interfaces with semicolons:
11 | # interface = internal/12.23.34.45;external/23.34.45.54
12 | ### for different advertised address:
13 | # interface = 12.23.34.45!23.34.45.56
14 |
15 |
16 | listen-ng = 127.0.0.1:22222
17 | # listen-tcp = 25060
18 | # listen-udp = 12222
19 |
20 | timeout = 60
21 | silent-timeout = 3600
22 | tos = 184
23 | # delete-delay = 30
24 | # final-timeout = 10800
25 |
26 | # foreground = false
27 | # pidfile = /var/run/ngcp-rtpengine-daemon.pid
28 | # num-threads = 16
29 |
30 | port-min = 16384
31 | port-max = 16485
32 | # max-sessions = 5000
33 |
34 | # recording-dir = /var/spool/rtpengine
35 | # recording-method = proc
36 | # recording-format = raw
37 |
38 | # redis = 127.0.0.1:6379/5
39 | # redis-write = password@12.23.34.45:6379/42
40 | # redis-num-threads = 8
41 | # no-redis-required = false
42 | # redis-expires = 86400
43 |
44 | # b2b-url = http://127.0.0.1:8090/
45 | # xmlrpc-format = 0
46 |
47 | # log-level = 6
48 | # log-stderr = false
49 | # log-facility = daemon
50 | # log-facility-cdr = local0
51 | # log-facility-rtcp = local1
52 |
53 | # graphite = 127.0.0.1:9006
54 | # graphite-interval = 60
55 | # graphite-prefix = foobar.
56 |
57 | # homer = 127.0.0.1:9060
58 | # homer-protocol = udp
59 | # homer-id = 2001
60 |
61 | # sip-source = false
62 | # dtls-passive = false
63 |
--------------------------------------------------------------------------------
/etc/turnserver.conf:
--------------------------------------------------------------------------------
1 | listening-ip=XXXXX-XXXXX
2 | listening-ip=XXXXXX-XXXXXX
3 |
4 | fingerprint
5 | lt-cred-mech
6 | user=websip:websip
7 | realm=XXXX-XXXX
8 | log-file=/var/log/turn.log
9 | simple-log
10 |
11 | cert=/etc/letsencrypt/live/XXXX-XXXX/fullchain.pem
12 | pkey=/etc/letsencrypt/live/XXXX-XXXX/privkey.pem
13 |
--------------------------------------------------------------------------------
/images/webrtc-sip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/havfo/WEBRTC-to-SIP/65d9e7684ce3fb140a147470268e8dd50c8f243a/images/webrtc-sip.png
--------------------------------------------------------------------------------
/images/webrtc-sip.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/iptables.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Variables used in the script
4 | IPTABLES="/sbin/iptables"
5 | IP6TABLES="/sbin/ip6tables"
6 | RTP="16384:16485"
7 | TURN="49152:65535"
8 |
9 | #Flush tables
10 | $IPTABLES -F
11 | $IPTABLES -X
12 |
13 | $IP6TABLES -F
14 | $IP6TABLES -X
15 |
16 | #Default policy
17 | $IPTABLES -P INPUT DROP
18 | $IPTABLES -P FORWARD ACCEPT
19 | $IPTABLES -P OUTPUT ACCEPT
20 |
21 | $IP6TABLES -P INPUT DROP
22 | $IP6TABLES -P FORWARD ACCEPT
23 | $IP6TABLES -P OUTPUT ACCEPT
24 |
25 | # Allow replies to outgoing requests
26 | $IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
27 | $IP6TABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
28 |
29 | # Allow ping
30 | $IPTABLES -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
31 |
32 | $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-request -j ACCEPT
33 | $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j ACCEPT
34 | $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 133 -m hl --hl-eq 255 -j ACCEPT
35 | $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 134 -m hl --hl-eq 255 -j ACCEPT
36 | $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 135 -m hl --hl-eq 255 -j ACCEPT
37 | $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 136 -m hl --hl-eq 255 -j ACCEPT
38 | $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 137 -m hl --hl-eq 255 -j ACCEPT
39 |
40 | # loopback rules
41 | $IPTABLES -A INPUT -i lo -j ACCEPT
42 | $IP6TABLES -A INPUT -i lo -j ACCEPT
43 |
44 | # SIP
45 | $IPTABLES -A INPUT -p tcp --dport 5061 -j ACCEPT
46 | $IPTABLES -A INPUT -p tcp --dport 5060 -j ACCEPT
47 | $IPTABLES -A INPUT -p udp --dport 5060 -j ACCEPT
48 | $IPTABLES -A INPUT -p tcp --dport 4443 -j ACCEPT
49 | $IPTABLES -A INPUT -p tcp --dport 8080 -j ACCEPT
50 | $IP6TABLES -A INPUT -p tcp --dport 5061 -j ACCEPT
51 | $IP6TABLES -A INPUT -p tcp --dport 5060 -j ACCEPT
52 | $IP6TABLES -A INPUT -p udp --dport 5060 -j ACCEPT
53 | $IP6TABLES -A INPUT -p tcp --dport 4443 -j ACCEPT
54 | $IP6TABLES -A INPUT -p tcp --dport 8080 -j ACCEPT
55 |
56 | # HTTP (load + web)
57 | $IPTABLES -A INPUT -p tcp --dport 80 -j ACCEPT
58 | $IPTABLES -A INPUT -p tcp --dport 443 -j ACCEPT
59 | $IPTABLES -A INPUT -p tcp --dport 3443 -j ACCEPT
60 | $IPTABLES -A INPUT -p tcp --dport 3480 -j ACCEPT
61 | $IP6TABLES -A INPUT -p tcp --dport 80 -j ACCEPT
62 | $IP6TABLES -A INPUT -p tcp --dport 443 -j ACCEPT
63 | $IP6TABLES -A INPUT -p tcp --dport 3443 -j ACCEPT
64 | $IP6TABLES -A INPUT -p tcp --dport 3480 -j ACCEPT
65 |
66 | # TURN
67 | $IPTABLES -A INPUT -p udp --dport 3478 -j ACCEPT
68 | $IPTABLES -A INPUT -p tcp --dport 3478 -j ACCEPT
69 | $IPTABLES -A INPUT -p tcp --dport 3479 -j ACCEPT
70 | $IP6TABLES -A INPUT -p udp --dport 3478 -j ACCEPT
71 | $IP6TABLES -A INPUT -p tcp --dport 3478 -j ACCEPT
72 | $IP6TABLES -A INPUT -p tcp --dport 3479 -j ACCEPT
73 |
74 | # RTPEngine
75 | $IPTABLES -I INPUT -p udp -j RTPENGINE --dport $RTP --id 0
76 | $IP6TABLES -I INPUT -p udp -j RTPENGINE --dport $RTP --id 0
77 |
78 | # Allow TURN RTP
79 | $IPTABLES -I INPUT -p udp --dport $TURN -j ACCEPT
80 | $IP6TABLES -I INPUT -p udp --dport $TURN -j ACCEPT
81 |
82 | # Brute-force block
83 | $IPTABLES -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --set --name DEFAULT --rsource
84 | $IP6TABLES -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --set --name DEFAULT --rsource
85 | $IPTABLES -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --update --seconds 180 --hitcount 3 --name DEFAULT --rsource -j DROP
86 | $IP6TABLES -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --update --seconds 180 --hitcount 3 --name DEFAULT --rsource -j DROP
87 |
88 | # Allow SSH
89 | $IPTABLES -A INPUT -p tcp --dport 22 -j ACCEPT
90 | $IP6TABLES -A INPUT -p tcp --dport 22 -j ACCEPT
91 |
92 | # Save IP-tables and save for enabling after reboot
93 | /sbin/iptables-save > /etc/iptables/firewall.conf
94 | /sbin/ip6tables-save > /etc/iptables/firewall6.conf
95 |
--------------------------------------------------------------------------------