diff --git a/.env b/.env index a155c1a..c5efbc0 100644 --- a/.env +++ b/.env @@ -104,3 +104,6 @@ OSMOMSC_IP=172.22.0.31 # OSMOHLR OSMOHLR_IP=172.22.0.32 + +# SMSC +SMSC_IP=172.22.0.33 diff --git a/dns/dns_init.sh b/dns/dns_init.sh index 4a1a73e..b81d722 100755 --- a/dns/dns_init.sh +++ b/dns/dns_init.sh @@ -29,6 +29,7 @@ cp /mnt/dns/epc_zone /etc/bind cp /mnt/dns/ims_zone /etc/bind cp /mnt/dns/pub_3gpp_zone /etc/bind +cp /mnt/dns/e164.arpa /etc/bind cp /mnt/dns/named.conf /etc/bind [ ${#MNC} == 3 ] && EPC_DOMAIN="epc.mnc${MNC}.mcc${MCC}.3gppnetwork.org" || EPC_DOMAIN="epc.mnc0${MNC}.mcc${MCC}.3gppnetwork.org" @@ -45,11 +46,15 @@ sed -i 's|PCSCF_IP|'$PCSCF_IP'|g' /etc/bind/ims_zone sed -i 's|ICSCF_IP|'$ICSCF_IP'|g' /etc/bind/ims_zone sed -i 's|SCSCF_IP|'$SCSCF_IP'|g' /etc/bind/ims_zone sed -i 's|FHOSS_IP|'$FHOSS_IP'|g' /etc/bind/ims_zone +sed -i 's|SMSC_IP|'$SMSC_IP'|g' /etc/bind/ims_zone sed -i 's|PUB_3GPP_DOMAIN|'$PUB_3GPP_DOMAIN'|g' /etc/bind/pub_3gpp_zone sed -i 's|DNS_IP|'$DNS_IP'|g' /etc/bind/pub_3gpp_zone sed -i 's|ENTITLEMENT_SERVER_IP|'$ENTITLEMENT_SERVER_IP'|g' /etc/bind/pub_3gpp_zone +sed -i 's|IMS_DOMAIN|'$IMS_DOMAIN'|g' /etc/bind/e164.arpa +sed -i 's|DNS_IP|'$DNS_IP'|g' /etc/bind/e164.arpa + sed -i 's|EPC_DOMAIN|'$EPC_DOMAIN'|g' /etc/bind/named.conf sed -i 's|IMS_DOMAIN|'$IMS_DOMAIN'|g' /etc/bind/named.conf sed -i 's|PUB_3GPP_DOMAIN|'$PUB_3GPP_DOMAIN'|g' /etc/bind/named.conf diff --git a/dns/e164.arpa b/dns/e164.arpa new file mode 100644 index 0000000..192d01a --- /dev/null +++ b/dns/e164.arpa @@ -0,0 +1,14 @@ +$TTL 1h +@ IN SOA ns.e164.arpa. root.e164.arpa. ( + 2009010918 ;serial + 3600 ;refresh + 3600 ;retry + 3600 ;expire + 3600 ;minimum TTL +) +@ IN NS e164.arpa. +@ IN A DNS_IP + +; Wildcard to match any tel:+xxxx and change to sip:xxxx@IMS_DOMAIN +* IN NAPTR 10 100 "u" "E2U+sip" "!(^.*$)!sip:\\1@IMS_DOMAIN!" . +* IN NAPTR 20 100 "u" "E2U+sip" "!(^.*$)!sip:+\\1@IMS_DOMAIN!" . diff --git a/dns/ims_zone b/dns/ims_zone index a7426b2..bd0fff4 100644 --- a/dns/ims_zone +++ b/dns/ims_zone @@ -23,3 +23,7 @@ _sip._udp.scscf 1D SRV 0 0 6060 scscf _sip._tcp.scscf 1D SRV 0 0 6060 scscf hss 1D IN A FHOSS_IP + +smsc 1D IN A SMSC_IP +_sip._udp.smsc 1D SRV 0 0 7060 smsc +_sip._tcp.smsc 1D SRV 0 0 7060 smsc diff --git a/dns/named.conf b/dns/named.conf index d9a5765..a6ed6ab 100644 --- a/dns/named.conf +++ b/dns/named.conf @@ -46,3 +46,8 @@ zone "PUB_3GPP_DOMAIN" { type master; file "/etc/bind/pub_3gpp_zone"; }; + +zone "e164.arpa" { + type master; + file "/etc/bind/e164.arpa"; +}; diff --git a/docker-compose.yaml b/docker-compose.yaml index 6d15dd5..d649f27 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -554,6 +554,27 @@ services: networks: default: ipv4_address: ${PCSCF_IP} + smsc: + image: docker_kamailio + container_name: smsc + dns: ${DNS_IP} + volumes: + - ./smsc:/mnt/smsc + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + env_file: + - .env + environment: + - COMPONENT_NAME=smsc-1 + depends_on: + - dns + - mysql + expose: + - "7060/udp" + - "7060/tcp" + networks: + default: + ipv4_address: ${SMSC_IP} osmomsc: build: ./osmomsc image: docker_osmomsc diff --git a/icscf/kamailio_icscf.cfg b/icscf/kamailio_icscf.cfg index cc20f01..3571128 100644 --- a/icscf/kamailio_icscf.cfg +++ b/icscf/kamailio_icscf.cfg @@ -252,6 +252,13 @@ route{ route(register); } + if (is_method("NOTIFY") && search("^(Event|o)([ \t]*):([ \t]*)reg")) { + if (!t_relay()) { + sl_reply_error(); + } + exit; + } + if (is_method("INVITE|SUBSCRIBE|MESSAGE|INFO|PUBLISH|CANCEL")) { route(initial_request); } else { diff --git a/ims_base/Dockerfile b/ims_base/Dockerfile index c578a0a..fdbafda 100644 --- a/ims_base/Dockerfile +++ b/ims_base/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get update && \ apt-get -y install mysql-server tcpdump screen tmux ntp ntpdate git-core dkms \ gcc flex bison libmysqlclient-dev make libssl-dev libcurl4-openssl-dev \ libxml2-dev libpcre3-dev bash-completion g++ autoconf libmnl-dev \ - libsctp-dev libradcli-dev libradcli4 iproute2 net-tools \ + libsctp-dev libradcli-dev libradcli4 libjson-c-dev pkg-config iproute2 net-tools \ iputils-ping # Fetch Kamailio code (branch 5.3) diff --git a/ims_base/kamailio_init.sh b/ims_base/kamailio_init.sh index a22464e..077e615 100755 --- a/ims_base/kamailio_init.sh +++ b/ims_base/kamailio_init.sh @@ -43,6 +43,11 @@ elif [[ "$COMPONENT_NAME" =~ ^(pcscf-[[:digit:]]+$) ]]; then /mnt/pcscf/pcscf_init.sh && \ mkdir -p /var/run/kamailio_pcscf && \ kamailio -f /etc/kamailio_pcscf/kamailio_pcscf.cfg -P /kamailio_pcscf.pid -DD -E -e +elif [[ "$COMPONENT_NAME" =~ ^(smsc-[[:digit:]]+$) ]]; then + echo "Deploying component: '$COMPONENT_NAME'" + /mnt/smsc/smsc_init.sh && \ + mkdir -p /var/run/kamailio_smsc && \ + kamailio -f /etc/kamailio_smsc/kamailio_smsc.cfg -P /kamailio_smsc.pid -DD -E -e else echo "Error: Invalid component name: '$COMPONENT_NAME'" -fi \ No newline at end of file +fi diff --git a/ims_base/modules.lst b/ims_base/modules.lst index 0e4925f..7765df1 100644 --- a/ims_base/modules.lst +++ b/ims_base/modules.lst @@ -7,7 +7,7 @@ modules_dirs:=modules cfg_group_include= # the list of extra modules to compile -include_modules= cdp cdp_avp db_mysql dialplan ims_auth ims_charging ims_dialog ims_diameter_server ims_icscf ims_ipsec_pcscf ims_isc ims_ocs ims_qos ims_registrar_pcscf ims_registrar_scscf ims_usrloc_pcscf ims_usrloc_scscf outbound presence presence_conference presence_dialoginfo presence_mwi presence_profile presence_reginfo presence_xml pua pua_bla pua_dialoginfo pua_reginfo pua_rpc pua_usrloc pua_xmpp sctp tls utils xcap_client xcap_server xmlops xmlrpc +include_modules= cdp cdp_avp db_mysql dialplan enum json http_client ims_auth ims_charging ims_dialog ims_diameter_server ims_icscf ims_ipsec_pcscf ims_isc ims_ocs ims_qos ims_registrar_pcscf ims_registrar_scscf ims_usrloc_pcscf ims_usrloc_scscf outbound presence presence_conference presence_dialoginfo presence_mwi presence_profile presence_reginfo presence_xml pua pua_bla pua_dialoginfo pua_reginfo pua_rpc pua_usrloc pua_xmpp sctp tls utils xcap_client xcap_server xmlops xmlrpc # the list of static modules static_modules= @@ -16,7 +16,7 @@ static_modules= skip_modules= # the list of modules to exclude from compile list -exclude_modules= acc_json acc_radius app_java app_lua app_lua_sr app_mono app_perl app_python app_python3 app_ruby auth_ephemeral auth_identity auth_radius cnxcc cplc crypto db2_ldap db_berkeley db_cassandra db_mongodb db_oracle db_perlvdb db_postgres db_redis db_sqlite db_unixodbc dnssec erlang evapi geoip geoip2 gzcompress h350 http_async_client http_client jansson janssonrpcc json jsonrpcc jwt kafka kazoo lcr ldap log_systemd lost lwsc memcached misc_radius ndb_cassandra mqtt ndb_mongodb ndb_redis nsq osp peering phonenum pua_json rabbitmq regex rls rtp_media_server secsipid secsipid_proc snmpstats stirshaken systemdops topos_redis uuid websocket xhttp_pi xmpp $(skip_modules) +exclude_modules= acc_json acc_radius app_java app_lua app_lua_sr app_mono app_perl app_python app_python3 app_ruby auth_ephemeral auth_identity auth_radius cnxcc cplc crypto db2_ldap db_berkeley db_cassandra db_mongodb db_oracle db_perlvdb db_postgres db_redis db_sqlite db_unixodbc dnssec erlang evapi geoip geoip2 gzcompress h350 http_async_client jansson janssonrpcc jsonrpcc jwt kafka kazoo lcr ldap log_systemd lost lwsc memcached misc_radius ndb_cassandra mqtt ndb_mongodb ndb_redis nsq osp peering phonenum pua_json rabbitmq regex rls rtp_media_server secsipid secsipid_proc snmpstats stirshaken systemdops topos_redis uuid websocket xhttp_pi xmpp $(skip_modules) modules_all= $(filter-out modules/CVS,$(wildcard modules/*)) modules_noinc= $(filter-out $(addprefix modules/, $(exclude_modules) $(static_modules)), $(modules_all)) diff --git a/smsc/kamailio_smsc.cfg b/smsc/kamailio_smsc.cfg new file mode 100644 index 0000000..3604cd2 --- /dev/null +++ b/smsc/kamailio_smsc.cfg @@ -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: . +# +# 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=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" +loadmodule "tmx" +loadmodule "smsops" +loadmodule "xlog" +loadmodule "maxfwd" +loadmodule "textops" +loadmodule "sl" +loadmodule "sanity" +loadmodule "siputils" +loadmodule "pv" +loadmodule "uac" +loadmodule "http_client" +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_DB_URL) +modparam("dialplan|pua", "db_url", DIALPLAN_PUA_DB_URL) +modparam("uac", "restore_mode", "none") + +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:SMSC_SERVER") +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) = "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); + # 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", "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"); +#!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:"+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"); +#!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);"); + } +} + diff --git a/smsc/smsc-create.sql b/smsc/smsc-create.sql new file mode 100644 index 0000000..00e306f --- /dev/null +++ b/smsc/smsc-create.sql @@ -0,0 +1,9 @@ +CREATE TABLE `messages` ( + `id` INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, + `caller` VARCHAR(255) NOT NULL, + `callee` VARCHAR(255) NOT NULL, + `text` VARCHAR(512), + `valid` datetime NOT NULL +); + +INSERT INTO version (table_name, table_version) values ('messages','1'); diff --git a/smsc/smsc.cfg b/smsc/smsc.cfg new file mode 100644 index 0000000..b7e58da --- /dev/null +++ b/smsc/smsc.cfg @@ -0,0 +1,17 @@ +listen=udp:SMSC_IP:7060 +listen=tcp:SMSC_IP:7060 + +#!define DOMAIN "IMS_DOMAIN" +#!subst "/DOMAIN/IMS_DOMAIN/" +#!define SMSC_SERVER "smsc.IMS_DOMAIN" +#!subst "/SMSC_SERVER/smsc.IMS_DOMAIN/" + +# Connection URL for the database: +#!define SMS_DB_URL "sms=>mysql://smsc:heslo@MYSQL_IP/smsc" +#!define DIALPLAN_PUA_DB_URL "mysql://smsc:heslo@MYSQL_IP/smsc" + +#!subst "/NEXMO_APIKEY/abcdef/" +#!subst "/NEXMO_APISECRET/xyz/" +#!subst "/SUBSCRIBE_EXPIRE/7200/" + +#!define WITH_DEBUG diff --git a/smsc/smsc_init.sh b/smsc/smsc_init.sh new file mode 100755 index 0000000..88eef15 --- /dev/null +++ b/smsc/smsc_init.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# BSD 2-Clause License + +# Copyright (c) 2020, Supreeth Herle +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. + +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +[ ${#MNC} == 3 ] && IMS_DOMAIN="ims.mnc${MNC}.mcc${MCC}.3gppnetwork.org" || IMS_DOMAIN="ims.mnc0${MNC}.mcc${MCC}.3gppnetwork.org" + +mkdir -p /etc/kamailio_smsc +cp /mnt/smsc/smsc.cfg /etc/kamailio_smsc +cp /mnt/smsc/kamailio_smsc.cfg /etc/kamailio_smsc + +while ! mysqladmin ping -h ${MYSQL_IP} --silent; do + sleep 5; +done + +# Sleep until permissions are set +sleep 10; + +# Create SMSC database, populate tables and grant privileges +if [[ -z "`mysql -u root -h ${MYSQL_IP} -qfsBe "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='smsc'" 2>&1`" ]]; +then + mysql -u root -h ${MYSQL_IP} -e "create database smsc;" + mysql -u root -h ${MYSQL_IP} smsc < /usr/local/src/kamailio/utils/kamctl/mysql/standard-create.sql + mysql -u root -h ${MYSQL_IP} smsc < /mnt/smsc/smsc-create.sql + mysql -u root -h ${MYSQL_IP} smsc < /usr/local/src/kamailio/utils/kamctl/mysql/dialplan-create.sql + mysql -u root -h ${MYSQL_IP} smsc < /usr/local/src/kamailio/utils/kamctl/mysql/presence-create.sql + + SMSC_USER_EXISTS=`mysql -u root -h ${MYSQL_IP} -s -N -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE User = 'smsc' AND Host = '%')"` + if [[ "$SMSC_USER_EXISTS" == 0 ]] + then + mysql -u root -h ${MYSQL_IP} -e "CREATE USER 'smsc'@'%' IDENTIFIED WITH mysql_native_password BY 'heslo'"; + mysql -u root -h ${MYSQL_IP} -e "CREATE USER 'smsc'@'$SMSC_IP' IDENTIFIED WITH mysql_native_password BY 'heslo'"; + mysql -u root -h ${MYSQL_IP} -e "GRANT ALL ON smsc.* TO 'smsc'@'%'"; + mysql -u root -h ${MYSQL_IP} -e "GRANT ALL ON smsc.* TO 'smsc'@'$SMSC_IP'"; + mysql -u root -h ${MYSQL_IP} -e "FLUSH PRIVILEGES;" + fi +fi + +sed -i 's|SMSC_IP|'$SMSC_IP'|g' /etc/kamailio_smsc/smsc.cfg +sed -i 's|IMS_DOMAIN|'$IMS_DOMAIN'|g' /etc/kamailio_smsc/smsc.cfg +sed -i 's|MYSQL_IP|'$MYSQL_IP'|g' /etc/kamailio_smsc/smsc.cfg + +# Sync docker time +#ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone