misc/examples/kemi: fixed setting the failure route callback
[sip-router] / misc / examples / kemi / kamailio-basic-kemi-lua.lua
1 -- Kamailio - equivalent of routing blocks in Lua
2 --
3 -- KSR - the new dynamic object exporting Kamailio functions (kemi)
4 -- sr - the old static object exporting Kamailio functions
5 --
6
7 -- Relevant remarks:
8 --  * do not execute Lua 'exit' - that will kill Lua interpreter which is
9 --  embedded in Kamailio, resulting in killing Kamailio
10 --  * use KSR.x.exit() to trigger the stop of executing the script
11 --  * KSR.drop() is only marking the SIP message for drop, but doesn't stop
12 --  the execution of the script. Use KSR.x.exit() after it or KSR.x.drop()
13 --
14
15
16 -- global variables corresponding to defined values (e.g., flags) in kamailio.cfg
17 FLT_ACC=1
18 FLT_ACCMISSED=2
19 FLT_ACCFAILED=3
20 FLT_NATS=5
21
22 FLB_NATB=6
23 FLB_NATSIPPING=7
24
25 -- SIP request routing
26 -- equivalent of request_route{}
27 function ksr_request_route()
28
29         -- per request initial checks
30         ksr_route_reqinit();
31
32         -- NAT detection
33         ksr_route_natdetect();
34
35         -- CANCEL processing
36         if KSR.pv.get("$rm") == "CANCEL" then
37                 if KSR.tm.t_check_trans()>0 then
38                         ksr_route_relay();
39                 end
40                 return 1;
41         end
42
43         -- handle requests within SIP dialogs
44         ksr_route_withindlg();
45
46         -- -- only initial requests (no To tag)
47
48         -- handle retransmissions
49         if KSR.tmx.t_precheck_trans()>0 then
50                 KSR.tm.t_check_trans();
51                 return 1;
52         end
53         if KSR.tm.t_check_trans()==0 then return 1 end
54
55         -- authentication
56         ksr_route_auth();
57
58         -- record routing for dialog forming requests (in case they are routed)
59         -- - remove preloaded route headers
60         KSR.hdr.remove("Route");
61         if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then
62                 KSR.rr.record_route();
63         end
64
65         -- account only INVITEs
66         if KSR.pv.get("$rm")=="INVITE" then
67                 KSR.setflag(FLT_ACC); -- do accounting
68         end
69
70         -- dispatch requests to foreign domains
71         ksr_route_sipout();
72
73         -- -- requests for my local domains
74
75         -- handle registrations
76         ksr_route_registrar();
77
78         if KSR.pv.is_null("$rU") then
79                 -- request with no Username in RURI
80                 KSR.sl.sl_send_reply(484,"Address Incomplete");
81                 return 1;
82         end
83
84         -- user location service
85         ksr_route_location();
86
87         return 1;
88 end
89
90 -- wrapper around tm relay function
91 function ksr_route_relay()
92         -- enable additional event routes for forwarded requests
93         -- - serial forking, RTP relaying handling, a.s.o.
94         if string.find("INVITE,BYE,SUBSCRIBE,UPDATE", KSR.pv.get("$rm")) then
95                 if KSR.tm.t_is_set("branch_route")<0 then
96                         KSR.tm.t_on_branch("ksr_branch_manage");
97                 end
98         end
99         if string.find("INVITE,SUBSCRIBE,UPDATE", KSR.pv.get("$rm")) then
100                 if KSR.tm.t_is_set("onreply_route")<0 then
101                         KSR.tm.t_on_reply("ksr_onreply_manage");
102                 end
103         end
104
105         if KSR.pv.get("$rm")=="INVITE" then
106                 if KSR.tm.t_is_set("failure_route")<0 then
107                         KSR.tm.t_on_failure("ksr_failure_manage");
108                 end
109         end
110
111         if KSR.tm.t_relay()<0 then
112                 KSR.sl.sl_reply_error();
113         end
114         KSR.x.exit();
115 end
116
117
118 -- Per SIP request initial checks
119 function ksr_route_reqinit()
120         if not KSR.is_myself(KSR.pv.get("$si")) then
121                 if not KSR.pv.is_null("$sht(ipban=>$si)") then
122                         -- ip is already blocked
123                         KSR.dbg("request from blocked IP - " .. KSR.pv.get("$rm")
124                                         .. " from " .. KSR.pv.get("$fu") .. " (IP:"
125                                         .. KSR.pv.get("$si") .. ":" .. KSR.pv.get("$sp") .. ")\n");
126                         KSR.x.exit();
127                 end
128                 if KSR.pike.pike_check_req()<0 then
129                         KSR.err("ALERT: pike blocking " .. KSR.pv.get("$rm")
130                                         .. " from " .. KSR.pv.get("$fu") .. " (IP:"
131                                         .. KSR.pv.get("$si") .. ":" .. KSR.pv.get("$sp") .. ")\n");
132                         KSR.pv.seti("$sht(ipban=>$si)", 1);
133                         KSR.x.exit();
134                 end
135         end
136         if (not KSR.pv.is_null("$ua"))
137                         and (string.find(KSR.pv.get("$ua"), "friendly-scanner")
138                                 or string.find(KSR.pv.get("$ua"), "sipcli")) then
139                 KSR.sl.sl_send_reply(200, "OK");
140                 KSR.x.exit();
141         end
142
143         if KSR.maxfwd.process_maxfwd(10) < 0 then
144                 KSR.sl.sl_send_reply(483,"Too Many Hops");
145                 KSR.x.exit();
146         end
147
148         if KSR.pv.get("$rm")=="OPTIONS"
149                         and KSR.is_myself(KSR.pv.get("$ru"))
150                         and KSR.pv.is_null("$rU") then
151                 KSR.sl.sl_send_reply(200,"Keepalive");
152                 KSR.x.exit();
153         end
154
155         if KSR.sanity.sanity_check(1511, 7)<0 then
156                 KSR.err("Malformed SIP message from "
157                                 .. KSR.pv.get("$si") .. ":" .. KSR.pv.get("$sp") .."\n");
158                 KSR.x.exit();
159         end
160
161 end
162
163
164 -- Handle requests within SIP dialogs
165 function ksr_route_withindlg()
166         if KSR.siputils.has_totag()<0 then return 1; end
167
168         -- sequential request withing a dialog should
169         -- take the path determined by record-routing
170         if KSR.rr.loose_route()>0 then
171                 ksr_route_dlguri();
172                 if KSR.pv.get("$rm")=="BYE" then
173                         KSR.setflag(FLT_ACC); -- do accounting ...
174                         KSR.setflag(FLT_ACCFAILED); -- ... even if the transaction fails
175                 elseif KSR.pv.get("$rm")=="ACK" then
176                         -- ACK is forwarded statelessly
177                         ksr_route_natmanage();
178                 elseif  KSR.pv.get("$rm")=="NOTIFY" then
179                         -- Add Record-Route for in-dialog NOTIFY as per RFC 6665.
180                         KSR.rr.record_route();
181                 end
182                 ksr_route_relay();
183                 KSR.x.exit();
184         end
185         if KSR.pv.get("$rm")=="ACK" then
186                 if KSR.tm.t_check_trans() >0 then
187                         -- no loose-route, but stateful ACK;
188                         -- must be an ACK after a 487
189                         -- or e.g. 404 from upstream server
190                         ksr_route_relay();
191                         KSR.x.exit();
192                 else
193                         -- ACK without matching transaction ... ignore and discard
194                         KSR.x.exit();
195                 end
196         end
197         KSR.sl.sl_send_reply(404, "Not here");
198         KSR.x.exit();
199 end
200
201 -- Handle SIP registrations
202 function ksr_route_registrar()
203         if KSR.pv.get("$rm")~="REGISTER" then return 1; end
204         if KSR.isflagset(FLT_NATS) then
205                 KSR.setbflag(FLB_NATB);
206                 -- do SIP NAT pinging
207                 KSR.setbflag(FLB_NATSIPPING);
208         end
209         if KSR.registrar.save("location", 0)<0 then
210                 KSR.sl.sl_reply_error();
211         end
212         KSR.x.exit();
213 end
214
215 -- User location service
216 function ksr_route_location()
217         local rc = KSR.registrar.lookup("location");
218         if rc<0 then
219                 KSR.tm.t_newtran();
220                 if rc==-1 or rc==-3 then
221                         KSR.sl.send_reply("404", "Not Found");
222                         KSR.x.exit();
223                 elseif rc==-2 then
224                         KSR.sl.send_reply("405", "Method Not Allowed");
225                         KSR.x.exit();
226                 end
227         end
228
229         -- when routing via usrloc, log the missed calls also
230         if KSR.pv.get("$rm")=="INVITE" then
231                 KSR.setflag(FLT_ACCMISSED);
232         end
233
234         ksr_route_relay();
235         KSR.x.exit();
236 end
237
238
239 -- IP authorization and user uthentication
240 function ksr_route_auth()
241
242         if KSR.pv.get("$rm")~="REGISTER" then
243                 if KSR.permissions.allow_source_address(1)>0 then
244                         -- source IP allowed
245                         return 1;
246                 end
247         end
248
249         if KSR.pv.get("$rm")=="REGISTER" or KSR.is_myself(KSR.pv.get("$fu")) then
250                 -- authenticate requests
251                 if KSR.auth_db.auth_check(KSR.pv.get("$fd"), "subscriber", 1)<0 then
252                         KSR.auth.auth_challenge(KSR.pv.get("$fd"), 0);
253                         KSR.x.exit();
254                 end
255                 -- user authenticated - remove auth header
256                 if not string.find("REGISTER,PUBLISH", KSR.pv.get("$rm")) then
257                         KSR.auth.consume_credentials();
258                 end
259         end
260
261         -- if caller is not local subscriber, then check if it calls
262         -- a local destination, otherwise deny, not an open relay here
263         if (not KSR.is_myself(KSR.pv.get("$fu"))
264                         and (not KSR.is_myself(KSR.pv.get("$ru")))) then
265                 KSR.sl.sl_send_reply(403,"Not relaying");
266                 KSR.x.exit();
267         end
268
269         return 1;
270 end
271
272 -- Caller NAT detection
273 function ksr_route_natdetect()
274         KSR.force_rport();
275         if KSR.nathelper.nat_uac_test(19)>0 then
276                 if KSR.pv.get("$rm")=="REGISTER" then
277                         KSR.nathelper.fix_nated_register();
278                 elseif KSR.siputils.is_first_hop()>0 then
279                         KSR.nathelper.set_contact_alias();
280                 end
281                 KSR.setflag(FLT_NATS);
282         end
283         return 1;
284 end
285
286 -- RTPProxy control
287 function ksr_route_natmanage()
288         if KSR.siputils.is_request()>0 then
289                 if KSR.siputils.has_totag()>0 then
290                         if KSR.rr.check_route_param("nat=yes")>0 then
291                                 KSR.setbflag(FLB_NATB);
292                         end
293                 end
294         end
295         if (not (KSR.isflagset(FLT_NATS) or KSR.isbflagset(FLB_NATB))) then
296                 return 1;
297         end
298
299         KSR.rtpproxy.rtpproxy_manage("co");
300
301         if KSR.siputils.is_request()>0 then
302                 if not KSR.siputils.has_totag() then
303                         if KSR.tmx.t_is_branch_route()>0 then
304                                 KSR.rr.add_rr_param(";nat=yes");
305                         end
306                 end
307         end
308         if KSR.siputils.is_reply()>0 then
309                 if KSR.isbflagset(FLB_NATB) then
310                         KSR.nathelper.set_contact_alias();
311                 end
312         end
313         return 1;
314 end
315
316 -- URI update for dialog requests
317 function ksr_route_dlguri()
318         if not KSR.isdsturiset() then
319                 KSR.nathelper.handle_ruri_alias();
320         end
321         return 1;
322 end
323
324 -- Routing to foreign domains
325 function ksr_route_sipout()
326         if KSR.is_myself(KSR.pv.get("$ru")) then return 1; end
327
328         KSR.hdr.append_hf("P-Hint: outbound\r\n");
329         ksr_route_relay();
330         KSR.x.exit();
331 end
332
333 -- Manage outgoing branches
334 -- equivalent of branch_route[...]{}
335 function ksr_branch_manage()
336         KSR.dbg("new branch [".. KSR.pv.get("$T_branch_idx]")
337                                 .. " to ".. KSR.pv.get("$ru") .. "\n");
338         ksr_route_natmanage();
339         return 1;
340 end
341
342 -- Manage incoming replies
343 -- equivalent of onreply_route[...]{}
344 function ksr_onreply_manage()
345         KSR.dbg("incoming reply\n");
346         local scode = KSR.pv.get("$rs");
347         if scode>100 and scode<299 then
348                 ksr_route_natmanage();
349         end
350         return 1;
351 end
352
353 -- Manage failure routing cases
354 -- equivalent of failure_route[...]{}
355 function ksr_failure_manage()
356         ksr_route_natmanage();
357
358         if KSR.tm.t_is_canceled()>0 then
359                 return 1;
360         end
361         return 1;
362 end
363
364 -- SIP response handling
365 -- equivalent of reply_route{}
366 function ksr_reply_route()
367         KSR.info("===== response - from kamailio lua script\n");
368         return 1;
369 end