Change in osmo-hlr[master]: contrib/dgsm/ add example esme and dialplan

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Mon Jan 13 13:26:22 UTC 2020


laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-hlr/+/16204 )

Change subject: contrib/dgsm/ add example esme and dialplan
......................................................................

contrib/dgsm/ add example esme and dialplan

Add example scripts for the distributed GSM network:

esme_dgsm.py: connect to the SMPP port of OsmoMSC A and forward SMS to the SMPP
port of OsmoMSC B. The IP and port of OsmoMSC B is retrieved by the receiver's
MSISDN using osmo-mslookup-client.

contrib/dgsm/freeswitch_dialplan_dgsm.py: resolve the destination SIP servers
of calls with osmo-mslookup-client and bridge the calls accordingly.

For a detailed overview of the D-GSM and mslookup related files, please see the
elaborate comment at the top of mslookup.c (already added in an earlier patch).

Related: OS#4254
Related: OS#4255
Change-Id: I26e8dd8d9a08187fccb3e74ee91366bc24f6c608
---
M contrib/dgsm/Makefile.am
A contrib/dgsm/esme_dgsm.py
A contrib/dgsm/freeswitch_dialplan_dgsm.py
3 files changed, 237 insertions(+), 0 deletions(-)

Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved
  pespin: Looks good to me, but someone else must approve
  keith: Looks good to me, but someone else must approve



diff --git a/contrib/dgsm/Makefile.am b/contrib/dgsm/Makefile.am
index c759302..5392646 100644
--- a/contrib/dgsm/Makefile.am
+++ b/contrib/dgsm/Makefile.am
@@ -1,4 +1,6 @@
 EXTRA_DIST = \
+	esme_dgsm.py \
+	freeswitch_dialplan_dgsm.py \
 	osmo-mslookup-pipe.py \
 	osmo-mslookup-socket.py \
 	$(NULL)
diff --git a/contrib/dgsm/esme_dgsm.py b/contrib/dgsm/esme_dgsm.py
new file mode 100755
index 0000000..75cf93d
--- /dev/null
+++ b/contrib/dgsm/esme_dgsm.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python3
+"""
+SPDX-License-Identifier: MIT
+Copyright 2019 sysmocom s.f.m.c GmbH <info at sysmocom.de>
+
+WARNING: this is just a proof-of-concept implementation, it blocks for every
+received SMPP request and is not suitable for servicing more than one request
+at a time.
+
+Based on esme.py from RCCN (license changed with permission from author):
+https://github.com/Rhizomatica/rccn/blob/master/rccn/esme.py
+Copyright 2017 keith <keith at rhizomatica.org>
+
+Forward SMS to the receiver's SMSC, as determined with mslookup.
+Requires smpplip (pip3 install --user smpplib) and osmo-mslookup-client.
+
+Example SMPP configuration for osmo-msc.cfg:
+smpp
+ local-tcp-ip 127.0.0.1 2775
+ policy closed
+ smpp-first
+# outgoing to esme_dgsm.py
+ esme OSMPP
+  no alert-notifications
+  password foo
+  default-route
+# incoming from esme_dgsm.py
+ esme ISMPP
+  no alert-notifications
+  password foo
+"""
+import argparse
+import json
+import logging
+import smpplib
+import subprocess
+import time
+
+
+def can_handle_pdu(pdu):
+    if not isinstance(pdu, smpplib.command.DeliverSM):
+        logging.info('PDU is not a DeliverSM, ignoring')
+        return False
+
+    if int(pdu.dest_addr_ton) == smpplib.consts.SMPP_TON_INTL:
+        logging.info("Unable to handle SMS for %s: SMPP_TON_INTL" %
+                     (pdu.destination_addr))
+        return False
+
+    return True
+
+
+def query_mslookup(service_type, id, id_type='msisdn'):
+    query_str = '%s.%s.%s' % (service_type, id, id_type)
+    logging.info('mslookup: ' + query_str)
+
+    result_line = subprocess.check_output(['osmo-mslookup-client', query_str,
+                                           '-f', 'json'])
+    if isinstance(result_line, bytes):
+        result_line = result_line.decode('ascii')
+
+    logging.info('mslookup result: ' + result_line.rstrip())
+    return json.loads(result_line)
+
+
+def tx_sms(dst_host, dst_port, source, destination, registered_delivery,
+           unicode_text):
+    smpp_client = smpplib.client.Client(dst_host, dst_port, 90)
+    smpp_client.connect()
+    smpp_client.bind_transceiver(system_id=args.dst_id, password=args.dst_pass)
+    logging.info('Connected to destination SMSC (%s@%s:%s)' % (args.dst_id,
+                 dst_host, dst_port))
+
+    pdu = smpp_client.send_message(
+        source_addr_ton=smpplib.consts.SMPP_TON_ALNUM,
+        source_addr_npi=smpplib.consts.SMPP_NPI_UNK,
+        source_addr=source.decode(),
+        dest_addr_ton=smpplib.consts.SMPP_TON_SBSCR,
+        dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN,
+        destination_addr=destination.decode(),
+        short_message=unicode_text,
+        registered_delivery=registered_delivery,
+    )
+
+    smpp_client.unbind()
+    smpp_client.disconnect()
+    del pdu
+    del smpp_client
+
+
+def rx_deliver_sm(pdu):
+    if not can_handle_pdu(pdu):
+        return smpplib.consts.SMPP_ESME_RSYSERR
+
+    msisdn = pdu.destination_addr.decode()
+    logging.info("Incoming SMS for: " + msisdn)
+
+    if args.sleep:
+        logging.info("Sleeping for %i seconds" % (args.sleep))
+        time.sleep(args.sleep)
+        logging.info("Sleep done")
+
+    result = query_mslookup("smpp.sms", msisdn)
+    if 'v4' not in result or not result['v4']:
+        logging.info('No IPv4 result from mslookup! This example only'
+                     ' makes use of IPv4, dropping.')
+        return smpplib.consts.SMPP_ESME_RSYSERR
+
+    dst_host, dst_port = result['v4']
+    tx_sms(dst_host, dst_port, pdu.source_addr,
+           pdu.destination_addr, int(pdu.registered_delivery),
+           pdu.short_message)
+
+    return smpplib.consts.SMPP_ESME_ROK
+
+
+def smpp_bind():
+    client = smpplib.client.Client(args.src_host, args.src_port, 90)
+    client.set_message_received_handler(rx_deliver_sm)
+    client.connect()
+    client.bind_transceiver(system_id=args.src_id, password=args.src_pass)
+    logging.info('Connected to source SMSC (%s@%s:%s)' % (args.src_id,
+                 args.src_host, args.src_port))
+    logging.info('Waiting for SMS...')
+    client.listen()
+
+
+def main():
+    global args
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--src-host', default='127.0.0.1',
+                        help='source SMSC (OsmoMSC) host (default: 127.0.0.1)')
+    parser.add_argument('--src-port', default=2775, type=int,
+                        help='source SMSC (OsmoMSC) port (default: 2775)')
+    parser.add_argument('--src-id', default='OSMPP',
+                        help='source system id, as configured in osmo-msc.cfg'
+                             ' (default: OSMPP)')
+    parser.add_argument('--src-pass', default='foo',
+                        help='source system password, as configured in'
+                             ' osmo-msc.cfg (default: foo)')
+    parser.add_argument('--dst-id', default='ISMPP',
+                        help='destination system id, as configured in'
+                             ' osmo-msc.cfg (default: ISMPP)')
+    parser.add_argument('--dst-pass', default='foo',
+                        help='destination system password, as configured in'
+                             ' osmo-msc.cfg (default: foo)')
+    parser.add_argument('--sleep', default=0, type=float,
+                        help='sleep time in seconds before forwarding an SMS,'
+                             ' to test multithreading (default: 0)')
+    args = parser.parse_args()
+
+    logging.basicConfig(level=logging.INFO, format='[%(asctime)s]'
+                        ' (%(threadName)s) %(message)s', datefmt="%H:%M:%S")
+    smpp_bind()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/contrib/dgsm/freeswitch_dialplan_dgsm.py b/contrib/dgsm/freeswitch_dialplan_dgsm.py
new file mode 100755
index 0000000..502fa6e
--- /dev/null
+++ b/contrib/dgsm/freeswitch_dialplan_dgsm.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+"""
+SPDX-License-Identifier: MIT
+Copyright 2019 sysmocom s.f.m.c GmbH <info at sysmocom.de>
+
+This is a freeswitch dialplan implementation, see:
+https://freeswitch.org/confluence/display/FREESWITCH/mod_python
+
+Find the right SIP server with mslookup (depending on the destination number)
+and bridge calls accordingly.
+"""
+import json
+import subprocess
+
+
+def query_mslookup(service_type, id, id_type='msisdn'):
+    query_str = '%s.%s.%s' % (service_type, id, id_type)
+    print('[dialplan-dgsm] mslookup: ' + query_str)
+
+    result_line = subprocess.check_output([
+        'osmo-mslookup-client', query_str, '-f', 'json'])
+    if isinstance(result_line, bytes):
+        result_line = result_line.decode('ascii')
+
+    print('[dialplan-dgsm] mslookup result: ' + result_line)
+    return json.loads(result_line)
+
+
+def handler(session, args):
+    """ Handle calls: bridge to the SIP server found with mslookup. """
+    print('[dialplan-dgsm] call handler')
+    msisdn = session.getVariable('destination_number')
+
+    # Run osmo-mslookup-client binary. We have also tried to directly call the
+    # C functions with ctypes but this has lead to hard-to-debug segfaults.
+    try:
+        result = query_mslookup("sip.voice", msisdn)
+
+        # This example only makes use of IPv4
+        if not result['v4']:
+            print('[dialplan-dgsm] no IPv4 result from mslookup')
+            session.hangup('UNALLOCATED_NUMBER')
+            return
+
+        sip_ip, sip_port = result['v4']
+        dial_str = 'sofia/internal/sip:{}@{}:{}'.format(
+            msisdn, sip_ip, sip_port)
+        print('[dialplan-dgsm] dial_str: ' + str(dial_str))
+
+        session.execute('bridge', dial_str)
+    except:
+        print('[dialplan-dgsm]: exception during call handler')
+        session.hangup('UNALLOCATED_NUMBER')
+
+
+def fsapi(session, stream, env, args):
+    """ Freeswitch refuses to load the module without this. """
+    stream.write(env.serialize())
+
+
+def main():
+    import argparse
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument('id', type=int)
+    parser.add_argument('-i', '--id-type', default='msisdn',
+                        help='default: "msisdn"')
+    parser.add_argument('-s', '--service', default='sip.voice',
+                        help='default: "sip.voice"')
+    args = parser.parse_args()
+
+    result = query_mslookup(args.service, args.id, args.id_type)
+    print(json.dumps(result))
+
+
+if __name__ == '__main__':
+    main()

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-hlr/+/16204
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-hlr
Gerrit-Branch: master
Gerrit-Change-Id: I26e8dd8d9a08187fccb3e74ee91366bc24f6c608
Gerrit-Change-Number: 16204
Gerrit-PatchSet: 24
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: keith <keith at rhizomatica.org>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: osmith <osmith at sysmocom.de>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200113/f58149f3/attachment.htm>


More information about the gerrit-log mailing list