From 5071895772c42699bad6cfc982c89e3faccad29f Mon Sep 17 00:00:00 2001 From: Daniel-Constantin Mierla Date: Mon, 24 Apr 2017 16:36:27 +0200 Subject: [PATCH] misc/examples: more complex example of using sqlang kemi interpreter --- .../kemi/kamailio-basic-kemi-sqlang.sq | 382 ++++++++++++++++++ misc/examples/kemi/kamailio-basic-kemi.cfg | 20 +- 2 files changed, 395 insertions(+), 7 deletions(-) create mode 100644 misc/examples/kemi/kamailio-basic-kemi-sqlang.sq diff --git a/misc/examples/kemi/kamailio-basic-kemi-sqlang.sq b/misc/examples/kemi/kamailio-basic-kemi-sqlang.sq new file mode 100644 index 00000000000..d2cbcb598a7 --- /dev/null +++ b/misc/examples/kemi/kamailio-basic-kemi-sqlang.sq @@ -0,0 +1,382 @@ +// Kamailio - equivalent of routing blocks in JavaScript +// +// KSR - the new dynamic object exporting Kamailio functions +// + +// global variables corresponding to defined values (e.g., flags) in kamailio.cfg +local FLAGS = { + FLT_ACC=1, + FLT_ACCMISSED=2, + FLT_ACCFAILED=3, + FLT_NATS=5, + + FLB_NATB=6, + FLB_NATSIPPING=7 +} + +// SIP request routing +// equivalent of request_route{} +function ksr_request_route() +{ + local METHOD = KSR.pv.get("$rm"); + // KSR.sl.sl_send_reply(100,"Intelligent trying"); + // KSR.info("===== request - from kamailio lua script\n"); + + // per request initial checks + ksr_route_reqinit(); + + // NAT detection + ksr_route_natdetect(); + + // CANCEL processing + if (METHOD == "CANCEL") { + if(KSR.tm.t_check_trans()>0) { + ksr_route_relay(); + } + return; + } + + // handle requests within SIP dialogs + ksr_route_withindlg(); + + // -- only initial requests (no To tag) + + // handle retransmissions + if (KSR.tmx.t_precheck_trans()>0) { + KSR.tm.t_check_trans(); + return; + } + if (KSR.tm.t_check_trans()==0) { return; } + + // authentication + ksr_route_auth(); + + // record routing for dialog forming requests (in case they are routed) + // - remove preloaded route headers + KSR.hdr.remove("Route"); + if (METHOD=="INVITE" || METHOD=="SUBSCRIBE") { + KSR.rr.record_route(); + } + + // account only INVITEs + if (METHOD=="INVITE") { + KSR.setflag(FLAGS.FLT__ACC); // do accounting + } + + // dispatch requests to foreign domains + ksr_route_sipout(); + + // -- requests for my local domains + + // handle registrations + ksr_route_registrar(); + + if (KSR.pv.is_null("$rU")) { + // request with no Username in RURI + KSR.sl.sl_send_reply(484, "Address Incomplete"); + return; + } + + // user location service + ksr_route_location(); + + return; +} + +// wrapper around tm relay function +function ksr_route_relay() +{ + // enable additional event routes for forwarded requests + // - serial forking, RTP relaying handling, a.s.o. + local METHOD = KSR.pv.get("$rm"); + if (METHOD=="INVITE" || METHOD=="BYE" || METHOD=="SUBSCRIBE" + || METHOD="UPDATE") { + if (KSR.tm.t_is_set("branch_route")<0) { + KSR.tm.t_on_branch("ksr_branch_manage"); + } + } + if (METHOD=="INVITE" || METHOD=="SUBSCRIBE" || METHOD=="UPDATE") { + if (KSR.tm.t_is_set("onreply_route")<0) { + KSR.tm.t_on_reply("ksr_onreply_manage"); + } + } + + if (METHOD=="INVITE") { + if (KSR.tm.t_is_set("failure_route")<0) { + KSR.tm.t_on_failure("MANAGE_FAILURE"); + } + } + + if (KSR.tm.t_relay()<0) { + KSR.sl.sl_reply_error(); + } + KSR.x.exit(); +} + + +// Per SIP request initial checks +function ksr_route_reqinit() +{ + if (!KSR.is_myself(KSR.pv.get("$si"))) { + if (!KSR.pv.is_null("$sht(ipban=>$si)")) { + // ip is already blocked + KSR.dbg("request from blocked IP - " + KSR.pv.get("$rm") + + " from " + KSR.pv.get("$fu") + " (IP:" + + KSR.pv.get("$si") + ":" + KSR.pv.get("$sp") + ")\n"); + KSR.x.exit(); + } + if (KSR.pike.pike_check_req()<0) { + KSR.err("ALERT: pike blocking " + KSR.pv.get("$rm") + + " from " + KSR.pv.get("$fu") + " (IP:" + + KSR.pv.get("$si") + ":" + KSR.pv.get("$sp") + ")\n"); + KSR.pv.seti("$sht(ipban=>$si)", 1); + KSR.x.exit(); + } + } + if (!KSR.pv.is_null("$ua")) { + local UA = KSR.pv.get("$ua"); + if (UA.indexOf("friendly-scanner")>=0 || UA.indexOf("sipcli")>=0) { + KSR.sl.sl_send_reply(200, "OK"); + KSR.x.exit(); + } + } + + if (KSR.maxfwd.process_maxfwd(10) < 0) { + KSR.sl.sl_send_reply(483,"Too Many Hops"); + KSR.x.exit(); + } + + if (KSR.pv.get("$rm")=="OPTIONS" + && KSR.is_myself(KSR.pv.get("$ru")) + && KSR.pv.is_null("$rU")) { + KSR.sl.sl_send_reply(200,"Keepalive"); + KSR.x.exit(); + } + + if (KSR.sanity.sanity_check(1511, 7)<0) { + KSR.err("Malformed SIP message from " + + KSR.pv.get("$si") + ":" + KSR.pv.get("$sp") + "\n"); + KSR.x.exit(); + } +} + + +// Handle requests within SIP dialogs +function ksr_route_withindlg() +{ + if (KSR.siputils.has_totag()<0) { return; } + + local METHOD = KSR.pv.get("$rm"); + // sequential request withing a dialog should + // take the path determined by record-routing + if (KSR.rr.loose_route()>0) { + ksr_route_dlguri(); + if (METHOD=="BYE") { + KSR.setflag(FLAGS.FLT__ACC); // do accounting ... + KSR.setflag(FLAGS.FLT__ACCFAILED); // ... even if the transaction fails + } else if (METHOD=="ACK") { + // ACK is forwarded statelessy + ksr_route_natmanage(); + } else if (METHOD=="NOTIFY") { + // Add Record-Route for in-dialog NOTIFY as per RFC 6665. + KSR.rr.record_route(); + } + ksr_route_relay(); + KSR.x.exit(); + } + if (METHOD=="ACK") { + if (KSR.tm.t_check_trans() >0) { + // no loose-route, but stateful ACK; + // must be an ACK after a 487 + // or e.g. 404 from upstream server + ksr_route_relay(); + KSR.x.exit(); + } else { + // ACK without matching transaction ... ignore and discard + KSR.x.exit(); + } + } + KSR.sl.sl_send_reply(404, "Not here"); + KSR.x.exit(); +} + +// Handle SIP registrations +function ksr_route_registrar() +{ + if (KSR.pv.get("$rm")!="REGISTER") { return; } + if (KSR.isflagset(FLAGS.FLT__NATS)) { + KSR.setbflag(FLB_NATB); + // do SIP NAT pinging + KSR.setbflag(FLB_NATSIPPING); + } + if (KSR.registrar.save("location", 0)<0) { + KSR.sl.sl_reply_error(); + } + KSR.x.exit(); +} + +// User location service +function ksr_route_location() +{ + local rc = KSR.registrar.lookup("location"); + if (rc<0) { + KSR.tm.t_newtran(); + if (rc==-1 || rc==-3) { + KSR.sl.send_reply("404", "Not Found"); + KSR.x.exit(); + } else if (rc==-2) { + KSR.sl.send_reply("405", "Method Not Allowed"); + KSR.x.exit(); + } + } + + // when routing via usrloc, log the missed calls also + if (KSR.pv.get("$rm")=="INVITE") { + KSR.setflag(FLAGS.FLT__ACCMISSED); + } + + ksr_route_relay(); + KSR.x.exit(); +} + + +// IP authorization and user uthentication +function ksr_route_auth() +{ + local METHOD = KSR.pv.get("$rm"); + if (METHOD!="REGISTER") { + if (KSR.permissions.allow_source_address(1)>0) { + // source IP allowed + return; + } + } + + if (METHOD=="REGISTER" || KSR.is_myself(KSR.pv.get("$fu"))) { + // authenticate requests + if (KSR.auth_db.auth_check(KSR.pv.get("$fd"), "subscriber", 1)<0) { + KSR.auth.auth_challenge(KSR.pv.get("$fd"), 0); + KSR.x.exit(); + } + // user authenticated - remove auth header + if (METHOD!="REGISTER" && METHOD!="PUBLISH") { + KSR.auth.consume_credentials(); + } + } + + // if caller is not local subscriber, then check if it calls + // a local destination, otherwise deny, not an open relay here + if ((! KSR.is_myself(KSR.pv.get("$fu")) + && (! KSR.is_myself(KSR.pv.get("$ru"))))) { + KSR.sl.sl_send_reply(403,"Not relaying"); + KSR.x.exit(); + } + + return; +} + +// Caller NAT detection +function ksr_route_natdetect() +{ + KSR.force_rport(); + if (KSR.nathelper.nat_uac_test(19)>0) { + if (KSR.pv.get("$rm")=="REGISTER") { + KSR.nathelper.fix_nated_register(); + } else if (KSR.siputils.is_first_hop()>0) { + KSR.nathelper.set_contact_alias(); + } + KSR.setflag(FLAGS.FLT__NATS); + } + return; +} + +// RTPProxy control +function ksr_route_natmanage() +{ + if (KSR.siputils.is_request()>0) { + if (KSR.siputils.has_totag()>0) { + if (KSR.rr.check_route_param("nat=yes")>0) { + KSR.setbflag(FLB_NATB); + } + } + } + if (! (KSR.isflagset(FLAGS.FLT__NATS) || KSR.isbflagset(FLB_NATB))) { + return; + } + + KSR.rtpproxy.rtpproxy_manage("co"); + + if (KSR.siputils.is_request()>0) { + if (! KSR.siputils.has_totag()) { + if (KSR.tmx.t_is_branch_route()>0) { + KSR.rr.add_rr_param(";nat=yes"); + } + } + } + if (KSR.siputils.is_reply()>0) { + if (KSR.isbflagset(FLB_NATB)) { + KSR.nathelper.set_contact_alias(); + } + } + return; +} + +// URI update for dialog requests +function ksr_route_dlguri() +{ + if (! KSR.isdsturiset()) { + KSR.nathelper.handle_ruri_alias(); + } + return; +} + +// Routing to foreign domains +function ksr_route_sipout() +{ + if (KSR.is_myself(KSR.pv.get("$ru"))) { return; } + + KSR.hdr.append_hf("P-Hint: outbound\r\n"); + ksr_route_relay(); + KSR.xexit();; +} + +// Manage outgoing branches +// equivalent of branch_route[...]{} +function ksr_branch_manage() +{ + KSR.dbg("new branch [" + KSR.pv.get("$T_branch_idx]") + + " to " + KSR.pv.get("$ru") + "\n"); + ksr_route_natmanage(); + return; +} + +// Manage incoming replies +// equivalent of onreply_route[...]{} +function ksr_onreply_manage() +{ + KSR.dbg("incoming reply\n"); + local scode = KSR.pv.get("$rs"); + if (scode>100 && scode<=299) { + ksr_route_natmanage(); + } + return; +} + +// Manage failure routing cases +// equivalent of failure_route[...]{} +function ksr_failure_manage() +{ + ksr_route_natmanage(); + + if (KSR.tm.t_is_canceled()>0) { + return; + } + return; +} + +// SIP response handling +// equivalent of reply_route{} +function ksr_reply_route() +{ + KSR.info("===== response - from kamailio JS script\n"); + return; +} diff --git a/misc/examples/kemi/kamailio-basic-kemi.cfg b/misc/examples/kemi/kamailio-basic-kemi.cfg index 41f06ba7ea6..8fe3616da22 100644 --- a/misc/examples/kemi/kamailio-basic-kemi.cfg +++ b/misc/examples/kemi/kamailio-basic-kemi.cfg @@ -125,7 +125,7 @@ latency_cfg_log=2 log_facility=LOG_LOCAL0 fork=yes -children=4 +children=2 /* uncomment the next line to disable TCP (default on) */ #disable_tcp=yes @@ -155,12 +155,8 @@ tcp_connection_lifetime=3605 ####### Modules Section ######## -# set paths to location of modules (to sources or installation folders) -#!ifdef WITH_SRCPATH -mpath="modules" -#!else -mpath="/usr/local/lib/kamailio/modules/" -#!endif +/* set paths to location of modules (to sources or installation folders) */ +# mpath="/usr/local/lib/kamailio/modules/" #!ifdef WITH_MYSQL loadmodule "db_mysql.so" @@ -223,6 +219,10 @@ loadmodule "app_python.so" loadmodule "app_jsdt.so" #!endif +#!ifdef WITH_CFGSQLANG +loadmodule "app_sqlang.so" +#!endif + # ----------------- setting module-specific parameters --------------- # ----- jsonrpcs params ----- @@ -368,6 +368,10 @@ cfgengine "lua" modparam("app_jsdt", "load", "/usr/local/etc/kamailio/kamailio-basic-kemi-jsdt.js") cfgengine "jsdt" #!else +#!ifdef WITH_CFGSQLANG +modparam("app_sqlang", "load", "/usr/local/etc/kamailio/kamailio-basic-kemi-sqlang.sq") +cfgengine "sqlang" +#!else cfgengine "native" include_file "/usr/local/etc/kamailio/kamailio-basic-kemi-native.cfg" #!endif @@ -376,3 +380,5 @@ include_file "/usr/local/etc/kamailio/kamailio-basic-kemi-native.cfg" #!endif +#!endif +