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