|
@@ -0,0 +1,418 @@
|
|
|
+#!KAMAILIO
|
|
|
+#
|
|
|
+# This config file implements the basic P-CSCF functionality
|
|
|
+# - web: http://www.kamailio.org
|
|
|
+# - git: http://sip-router.org
|
|
|
+#
|
|
|
+# Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php
|
|
|
+# for an explanation of possible statements, functions and parameters.
|
|
|
+#
|
|
|
+# Direct your questions about this file to: <[email protected]>.
|
|
|
+#
|
|
|
+# For more information about the various parameters, functions and statements
|
|
|
+# try http://sip-router.org/wiki/ .
|
|
|
+#
|
|
|
+
|
|
|
+include_file "smsc.cfg"
|
|
|
+
|
|
|
+####### Global Parameters #########
|
|
|
+debug=2
|
|
|
+log_stderror=no
|
|
|
+sip_warning=no
|
|
|
+children=4
|
|
|
+
|
|
|
+user_agent_header="User-Agent: Kamailio SMSC"
|
|
|
+server_header="Server: Kamailio SMSC"
|
|
|
+
|
|
|
+/* comment the next line to enable the auto discovery of local aliases
|
|
|
+ based on reverse DNS on IPs (default on) */
|
|
|
+auto_aliases=no
|
|
|
+
|
|
|
+check_via=no # (cmd. line: -v)
|
|
|
+dns=no # (cmd. line: -r)
|
|
|
+rev_dns=no # (cmd. line: -R)
|
|
|
+tcp_accept_no_cl=yes
|
|
|
+
|
|
|
+#!define SMS_3GPP 1
|
|
|
+#!define SMS_TEXT 2
|
|
|
+
|
|
|
+alias=SERVER
|
|
|
+
|
|
|
+# ------------------ module loading ----------------------------------
|
|
|
+mpath="/root/kamailio-devel/lib64/kamailio/modules"
|
|
|
+# (we try both the lib64 and the lib directory)
|
|
|
+
|
|
|
+loadmodule "tm"
|
|
|
+loadmodule "tmx"
|
|
|
+loadmodule "smsops"
|
|
|
+loadmodule "xlog"
|
|
|
+loadmodule "maxfwd"
|
|
|
+loadmodule "textops"
|
|
|
+loadmodule "sl"
|
|
|
+loadmodule "sanity"
|
|
|
+loadmodule "siputils"
|
|
|
+loadmodule "pv"
|
|
|
+loadmodule "uac"
|
|
|
+loadmodule "xhttp"
|
|
|
+loadmodule "utils"
|
|
|
+loadmodule "json"
|
|
|
+loadmodule "enum"
|
|
|
+loadmodule "db_mysql"
|
|
|
+loadmodule "dialplan"
|
|
|
+loadmodule "sqlops"
|
|
|
+loadmodule "htable"
|
|
|
+loadmodule "rtimer"
|
|
|
+loadmodule "usrloc"
|
|
|
+loadmodule "registrar"
|
|
|
+loadmodule "pua"
|
|
|
+loadmodule "pua_reginfo"
|
|
|
+
|
|
|
+modparam("sqlops","sqlcon","sms=>mysql://root@localhost/smsc")
|
|
|
+modparam("dialplan|pua", "db_url", "mysql://root@localhost/kamailio")
|
|
|
+modparam("uac","restore_mode","none")
|
|
|
+modparam("enum", "domain_suffix", "DOMAIN.")
|
|
|
+
|
|
|
+modparam("htable", "htable", "publish_sent=>size=8;autoexpire=SUBSCRIBE_EXPIRE")
|
|
|
+
|
|
|
+# time interval set to 3 seconds
|
|
|
+modparam("rtimer", "timer", "name=sms;interval=3;mode=1;")
|
|
|
+modparam("rtimer", "exec", "timer=sms;route=SMS_WORKER")
|
|
|
+
|
|
|
+modparam("pua_reginfo", "server_address", "sip:reginfo@DOMAIN")
|
|
|
+modparam("pua_reginfo", "publish_reginfo", 0)
|
|
|
+
|
|
|
+####### Routing Logic ########
|
|
|
+# Main SIP request routing logic
|
|
|
+# - processing of any incoming SIP request starts with this route
|
|
|
+
|
|
|
+route {
|
|
|
+ xlog("L_DBG", "$rm ($fu ($si:$sp) to $tu, $ci)\n");
|
|
|
+
|
|
|
+ # per request initial checks
|
|
|
+ route(REQINIT);
|
|
|
+
|
|
|
+ if (is_method("NOTIFY")) {
|
|
|
+ route(NOTIFY);
|
|
|
+ send_reply("202", "Accepted");
|
|
|
+ exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!is_method("MESSAGE")) {
|
|
|
+ append_to_reply("Allow: MESSAGE,NOTIFY\r\n");
|
|
|
+ send_reply("405", "Method not allowed");
|
|
|
+ exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($cT == "application/vnd.3gpp.sms") {
|
|
|
+ route(SMS_FROM_3GPP);
|
|
|
+ } else if ($cT == "text/plain") {
|
|
|
+ route(SMS_FROM_SIP);
|
|
|
+ } else {
|
|
|
+ send_reply("488", "Content-Type not supported");
|
|
|
+ exit;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# Helper routes (Basic-Checks, NAT-Handling/RTP-Control, XML-RPC)
|
|
|
+######################################################################
|
|
|
+# Per SIP request initial checks
|
|
|
+route[REQINIT] {
|
|
|
+ # Trace this message
|
|
|
+
|
|
|
+ if (!mf_process_maxfwd_header("10")) {
|
|
|
+ sl_send_reply("483","Too Many Hops");
|
|
|
+ exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!sanity_check("1511", "7")) {
|
|
|
+ xlog("Malformed SIP message from $si:$sp\n");
|
|
|
+ exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ # Reply to OPTIONS:
|
|
|
+ if (is_method("OPTIONS") && (uri==myself)) {
|
|
|
+ options_reply();
|
|
|
+ exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ # Ignore Re-Transmits:
|
|
|
+ if (t_lookup_request()) {
|
|
|
+ exit;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# SMS from VoLTE Handsets
|
|
|
+######################################################################
|
|
|
+route[SMS_FROM_3GPP] {
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("3GPP-SMS: $rm ($fu ($si:$sp) to $tu, $ci)\n");
|
|
|
+ xlog("SMS for $tpdu(destination) \"$tpdu(payload)\" (Valid: $tpdu(validity) )\n");
|
|
|
+#!endif
|
|
|
+ send_reply("202", "Accepted");
|
|
|
+
|
|
|
+ if (isRPDATA()) {
|
|
|
+ $uac_req(method) = "MESSAGE";
|
|
|
+ $uac_req(ruri) = $ai;
|
|
|
+ $uac_req(furi) = $ai;
|
|
|
+ $uac_req(turi) = $ai;
|
|
|
+ $uac_req(hdrs) = "Content-Type: application/vnd.3gpp.sms\r\nRequest-Disposition: fork, parallel\r\nAccept-Contact: *;+g.3gpp.smsip;require;explicit\r\n";
|
|
|
+ $uac_req(body) = $smsack;
|
|
|
+ uac_req_send();
|
|
|
+
|
|
|
+
|
|
|
+ $avp(from) = $(ai{uri.user});
|
|
|
+ $avp(to) = $tpdu(destination);
|
|
|
+ # Translate "To":
|
|
|
+ dp_translate("1", "$avp(to)/$avp(to)");
|
|
|
+
|
|
|
+ $avp(text) = $tpdu(payload);
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("SMS from 3GPP/VoLTE\n");
|
|
|
+ xlog("-------------------------------------\n");
|
|
|
+ xlog("FROM $avp(from)\n");
|
|
|
+ xlog("TO $avp(to)\n");
|
|
|
+ xlog("TEXT $avp(text)\n");
|
|
|
+#!endif
|
|
|
+ route(SMS);
|
|
|
+ }
|
|
|
+
|
|
|
+ exit;
|
|
|
+}
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# SMS from OTT Handsets
|
|
|
+######################################################################
|
|
|
+route[SMS_FROM_SIP] {
|
|
|
+ send_reply("200", "OK");
|
|
|
+
|
|
|
+ $avp(from) = $(ai{uri.user});
|
|
|
+ # Translate "To":
|
|
|
+ $avp(to) = $tU;
|
|
|
+ dp_translate("1", "$avp(to)/$avp(to)");
|
|
|
+ $avp(text) = $rb;
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("SMS from SIP/OTT\n");
|
|
|
+ xlog("-------------------------------------\n");
|
|
|
+ xlog("FROM $avp(from)\n");
|
|
|
+ xlog("TO $avp(to)\n");
|
|
|
+ xlog("TEXT $avp(text)\n");
|
|
|
+#!endif
|
|
|
+ route(SMS);
|
|
|
+
|
|
|
+ exit;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# SMS from other networks
|
|
|
+######################################################################
|
|
|
+event_route[xhttp:request] {
|
|
|
+ if ($(hu{url.querystring}{s.len}) > 0) {
|
|
|
+ $avp(from) = $(hu{url.querystring}{param.value,msisdn,&});
|
|
|
+ $avp(to) = $(hu{url.querystring}{param.value,to,&});
|
|
|
+ $avp(text) = $(hu{url.querystring}{param.value,text,&}{s.replace,+,%20}{s.unescape.user});
|
|
|
+ $avp(from_outbound) = 1;
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("SMS from Outbound ($hu)\n");
|
|
|
+ xlog("-------------------------------------\n");
|
|
|
+ xlog("FROM $avp(from)\n");
|
|
|
+ xlog("TO $avp(to)\n");
|
|
|
+ xlog("TEXT $avp(text)\n");
|
|
|
+#!endif
|
|
|
+ if ($avp(to) == "491771782261")
|
|
|
+ $avp(to) = "494046895124";
|
|
|
+ if ($avp(to) == "491771782319")
|
|
|
+ $avp(to) = "494034927220";
|
|
|
+
|
|
|
+ route(SMS);
|
|
|
+ }
|
|
|
+
|
|
|
+ xhttp_reply("200", "OK", "text/html", "<html><body>OK - [$si:$sp]</body></html>");
|
|
|
+}
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# SMS to VoLTE Handsets
|
|
|
+######################################################################
|
|
|
+route[SMS_TO_3GPP] {
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("SMS to 3GPP/VoLTE\n");
|
|
|
+ xlog("-------------------------------------\n");
|
|
|
+ xlog("FROM $avp(from)\n");
|
|
|
+ xlog("TO $avp(to)\n");
|
|
|
+ xlog("TEXT $avp(text)\n");
|
|
|
+#!endif
|
|
|
+
|
|
|
+ // Construct a new SMS-Body:
|
|
|
+ $rpdata(all) = $null;
|
|
|
+ $rpdata(type) = 1; // RP-DATA: Network to UE
|
|
|
+ $rpdata(reference) = $avp(id);
|
|
|
+ $rpdata(originator) = $avp(from);
|
|
|
+ $tpdu(type) = 4; // SMS-Deliver
|
|
|
+ $tpdu(origen) = $avp(from); // The Destination becomes the originator of the SMS
|
|
|
+ $tpdu(payload) = $avp(text);
|
|
|
+
|
|
|
+ $uac_req(method) = "MESSAGE";
|
|
|
+ $uac_req(ruri) = "sip:"+$avp(to)+"@"+DOMAIN;
|
|
|
+ $uac_req(furi) = "sip:"+$avp(to)+"@"+DOMAIN;
|
|
|
+ $uac_req(turi) = "sip:"+$avp(to)+"@"+DOMAIN;
|
|
|
+ $uac_req(hdrs) = "Content-Type: application/vnd.3gpp.sms\r\nRequest-Disposition: fork, parallel\r\nAccept-Contact: *;+g.3gpp.smsip;require;explicit\r\nX-MSG-ID: "+$avp(id)+"\r\n";
|
|
|
+ $uac_req(body) = $smsbody;
|
|
|
+ $uac_req(evroute)=1;
|
|
|
+ uac_req_send();
|
|
|
+ sql_query("sms", "delete from messages where id=$avp(id);");
|
|
|
+}
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# SMS to OTT-Handsets
|
|
|
+######################################################################
|
|
|
+route[SMS_TO_SIP] {
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("SMS to SIP/OTT\n");
|
|
|
+ xlog("-------------------------------------\n");
|
|
|
+ xlog("FROM $avp(from)\n");
|
|
|
+ xlog("TO $avp(to)\n");
|
|
|
+ xlog("TEXT $avp(text)\n");
|
|
|
+#!endif
|
|
|
+
|
|
|
+ $uac_req(method) = "MESSAGE";
|
|
|
+ $uac_req(ruri) = "sip:"+$avp(to)+"@"+DOMAIN;
|
|
|
+ $uac_req(furi) = "sip:+"+$avp(from)+"@"+DOMAIN;
|
|
|
+ $uac_req(turi) = "sip:"+$avp(to)+"@"+DOMAIN;
|
|
|
+ $uac_req(hdrs) = "Content-Type: text/plain\r\nX-MSG-ID: "+$avp(id)+"\r\n";
|
|
|
+ $uac_req(evroute)=1;
|
|
|
+ $uac_req(body) = $avp(text);
|
|
|
+
|
|
|
+ uac_req_send();
|
|
|
+}
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# SMS to Outbound
|
|
|
+######################################################################
|
|
|
+route[SMS_TO_OUTBOUND] {
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("SMS to Outbound\n");
|
|
|
+ xlog("-------------------------------------\n");
|
|
|
+ xlog("FROM $avp(from)\n");
|
|
|
+ xlog("TO $avp(to)\n");
|
|
|
+ xlog("TEXT $avp(text)\n");
|
|
|
+#!endif
|
|
|
+ if ($avp(from_outbound) == 1) {
|
|
|
+ xlog("Not sending: FROM and TO Outbound!\n");
|
|
|
+ return 1;
|
|
|
+ exit;
|
|
|
+ }
|
|
|
+ if ($avp(from) == "494046895124")
|
|
|
+ $avp(from) = "491771782261";
|
|
|
+ if ($avp(from) == "494034927220")
|
|
|
+ $avp(from) = "491771782319";
|
|
|
+
|
|
|
+ http_query("https://rest.nexmo.com/sms/json?api_key=NEXMO_APIKEY&api_secret=NEXMO_APISECRET&from=$avp(from)&to=$avp(to)&text=$(avp(text){s.escape.user})", "$var(result)");
|
|
|
+ if ($retcode != 200) return -1;
|
|
|
+ json_get_field("$var(result)", "messages", "$var(messages)");
|
|
|
+ json_get_field("$var(messages)", "status", "$var(status)");
|
|
|
+ if ($var(status) != 0) return -1;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# SMS Handling
|
|
|
+######################################################################
|
|
|
+route[SMS] {
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("SMS-Task\n");
|
|
|
+ xlog("-------------------------------------\n");
|
|
|
+ xlog("FROM $avp(from)\n");
|
|
|
+ xlog("TO $avp(to)\n");
|
|
|
+ xlog("TEXT $avp(text)\n");
|
|
|
+#!endif
|
|
|
+
|
|
|
+ # Query ENUM: Local number?
|
|
|
+ $var(enum) = "+"+$avp(to);
|
|
|
+ if (!enum_pv_query("$var(enum)")) {
|
|
|
+ route(SMS_TO_OUTBOUND);
|
|
|
+ return $retcode;
|
|
|
+ }
|
|
|
+ if (sql_query("sms", "insert into messages (caller, callee, text, valid) values ('$(avp(from){s.escape.common})', '$(avp(to){s.escape.common})', '$(avp(text){s.escape.common})', now());"))
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+######################################################################
|
|
|
+# SMS Handling
|
|
|
+######################################################################
|
|
|
+route[SMS_WORKER] {
|
|
|
+ sql_query("sms", "select id, caller, callee, text from messages;", "q");
|
|
|
+ if ($dbr(q=>rows) > 0) {
|
|
|
+ $var(i) = 0;
|
|
|
+ while ($var(i) < $dbr(q=>rows)) {
|
|
|
+ $avp(id) = $dbr(q=>[$var(i),0]);
|
|
|
+ $avp(from) = $dbr(q=>[$var(i),1]);
|
|
|
+ $avp(to) = $dbr(q=>[$var(i),2]);
|
|
|
+ $avp(text) = $dbr(q=>[$var(i),3]);
|
|
|
+
|
|
|
+ route(SEND_SMS);
|
|
|
+#!ifdef WITH_DEBUG
|
|
|
+ xlog("ID $avp(id)\n");
|
|
|
+ xlog("FROM $avp(from)\n");
|
|
|
+ xlog("TO $avp(to)\n");
|
|
|
+ xlog("TEXT $avp(text)\n");
|
|
|
+#!endif
|
|
|
+ $var(i) = $var(i) + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sql_result_free("q");
|
|
|
+}
|
|
|
+
|
|
|
+route[NOTIFY] {
|
|
|
+ if (has_body("application/reginfo+xml")) {
|
|
|
+ if (reginfo_handle_notify("location"))
|
|
|
+ send_reply("202", "Accepted");
|
|
|
+ } else {
|
|
|
+ send_reply("503", "Invalid Content-Type");
|
|
|
+ }
|
|
|
+ exit;
|
|
|
+}
|
|
|
+
|
|
|
+route[SEND_SMS] {
|
|
|
+ $var(uri) = "sip:"+$avp(to)+"@"+DOMAIN;
|
|
|
+
|
|
|
+ if (reg_fetch_contacts("location", "$var(uri)", "caller")) {
|
|
|
+ $var(j) = 0;
|
|
|
+ $var(is3gpp) = 0;
|
|
|
+ while($var(j) < $(ulc(caller=>count))) {
|
|
|
+ $var(k) = 0;
|
|
|
+ while($var(k) < $(ulc(caller=>addr)[$var(j)]{param.count})) {
|
|
|
+ if ($(ulc(caller=>addr)[$var(j)]{param.name,$var(k)}) == "+g.3gpp.smsip")
|
|
|
+ $var(is3gpp) = 1;
|
|
|
+ $var(k) = $var(k) + 1;
|
|
|
+ }
|
|
|
+ if ($var(is3gpp) == 1)
|
|
|
+ route(SMS_TO_3GPP);
|
|
|
+ else
|
|
|
+ route(SMS_TO_SIP);
|
|
|
+ $var(j) = $var(j) + 1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if ($sht(publish_sent=>$var(uri)) == $null) {
|
|
|
+ reginfo_subscribe("$var(uri)", "SUBSCRIBE_EXPIRE");
|
|
|
+ $sht(publish_sent=>$var(uri)) = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+event_route [tm:local-request] {
|
|
|
+ if (is_method("SUBSCRIBE")) {
|
|
|
+ append_hf("P-Asserted-Identity: $ru\r\n");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+event_route[uac:reply] {
|
|
|
+ if (($uac_req(evtype) == 1) && ($uac_req(evcode) == 200)) {
|
|
|
+ $var(msgid) = $(uac_req(hdrs){line.sw,X-MSG-ID:}{s.substr,10,0}{s.int});
|
|
|
+ sql_query("sms", "delete from messages where id=$var(msgid);");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|