#!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: . # # 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" log_name="smsc" log_prefix_mode=1 log_prefix="{$mt $hdr(CSeq) $ci $cfg(route)} " /* 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=SMSC_SERVER # ------------------ module loading ---------------------------------- mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/lib/kamailio/modules_k/:/usr/lib/kamailio/modules/:/usr/lib/x86_64-linux-gnu/kamailio/modules/:/usr/local/lib64/kamailio/modules" # (we try both the lib64 and the lib directory) loadmodule "tm.so" loadmodule "tmx.so" loadmodule "smsops.so" loadmodule "xlog.so" loadmodule "maxfwd.so" loadmodule "textops.so" loadmodule "sl.so" loadmodule "sanity.so" loadmodule "siputils.so" loadmodule "pv.so" loadmodule "uac.so" loadmodule "http_client.so" loadmodule "xhttp.so" loadmodule "utils.so" loadmodule "json.so" loadmodule "enum.so" loadmodule "db_mysql.so" loadmodule "dialplan.so" loadmodule "sqlops.so" loadmodule "htable.so" loadmodule "rtimer.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "pua.so" loadmodule "pua_reginfo.so" modparam("sqlops", "sqlcon", SMS_DB_URL) modparam("dialplan", "db_url", DIALPLAN_PUA_DB_URL) modparam("uac", "restore_mode", "none") modparam("htable", "htable", "publish_sent=>size=8;autoexpire=SUBSCRIBE_EXPIRE") modparam("htable", "htable", "sms_retries=>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:SMSC_SERVER") modparam("pua_reginfo", "publish_reginfo", 0) modparam("pua", "db_url", DIALPLAN_PUA_DB_URL) ####### 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) = "sip:"+SMSC_SERVER; $uac_req(turi) = $ai; $uac_req(hdrs) = "Content-Type: application/vnd.3gpp.sms\r\nRequest-Disposition: no-fork\r\nAccept-Contact: *;+g.3gpp.smsip\r\n"; $uac_req(body) = $smsack; uac_req_send(); $avp(from) = $(ai{uri.user}); $avp(to) = $tpdu(destination); $avp(dcs) = $tpdu(coding); # 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"); xlog("DCS $avp(dcs)\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", "OK - [$si:$sp]"); } ###################################################################### # 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"); xlog("DCS $avp(dcs)\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); $tpdu(coding) = $avp(dcs); $uac_req(method) = "MESSAGE"; $uac_req(ruri) = "sip:"+$avp(to)+"@"+DOMAIN; $uac_req(furi) = "sip:"+SMSC_SERVER; $uac_req(turi) = "sip:"+$avp(to)+"@"+DOMAIN; $uac_req(hdrs) = "Content-Type: application/vnd.3gpp.sms\r\nRequest-Disposition: no-fork\r\nAccept-Contact: *;+g.3gpp.smsip\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_client_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"); xlog("DCS $avp(dcs)\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, dcs, valid) values ('$(avp(from){s.escape.common})', '$(avp(to){s.escape.common})', '$(avp(text){s.escape.common})', $avp(dcs), now());")) return 1; else return -1; } ###################################################################### # SMS Handling ###################################################################### route[SMS_WORKER] { sql_query("sms", "select id, caller, callee, text, dcs from messages;", "q"); if ($dbr(q=>rows) > 0) { $var(i) = 0; while ($var(i) < $dbr(q=>rows)) { if ($sht(sms_retries=>$dbr(q=>[$var(i),0])) == $null) { $sht(sms_retries=>$dbr(q=>[$var(i),0])) = 0; } else { $sht(sms_retries=>$dbr(q=>[$var(i),0])) = $sht(sms_retries=>$dbr(q=>[$var(i),0])) + 1; } if ($sht(sms_retries=>$dbr(q=>[$var(i),0])) > 2) { xlog("Dropping SMS [$dbr(q=>[$var(i),3])] TO $dbr(q=>[$var(i),2]) after 2 retries \n"); sql_query("sms", "delete from messages where id=$dbr(q=>[$var(i),0]);"); $sht(sms_retries=>$dbr(q=>[$var(i),0])) = $null; } else { $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]); $avp(dcs) = $dbr(q=>[$var(i),4]); 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"); xlog("DCS $avp(dcs)\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) && ($uac_req(hdrs) != $null) && ($uac_req(hdrs) != "")) { $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);"); } }