2018-09-29 08:56:59 +00:00
|
|
|
#!/usr/bin/env python2
|
2015-12-01 18:07:30 +00:00
|
|
|
|
2015-12-01 23:23:09 +00:00
|
|
|
# Python interface to OsmoNITB MNCC (Mobile Network Call Control)
|
|
|
|
# interface
|
|
|
|
#
|
|
|
|
# (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
2018-09-29 14:37:11 +00:00
|
|
|
# (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
|
2015-12-01 23:23:09 +00:00
|
|
|
#
|
|
|
|
# Licensed under GNU General Public License, Version 2 or at your
|
|
|
|
# option, any later version.
|
|
|
|
|
2015-12-01 18:07:30 +00:00
|
|
|
import socket
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import mncc
|
|
|
|
import ctypes
|
|
|
|
|
2018-09-29 14:48:08 +00:00
|
|
|
class mncc_msg_common:
|
2015-12-01 18:07:30 +00:00
|
|
|
def send(self):
|
|
|
|
return buffer(self)[:]
|
|
|
|
def receive(self, bytes):
|
|
|
|
fit = min(len(bytes), ctypes.sizeof(self))
|
|
|
|
ctypes.memmove(ctypes.addressof(self), bytes, fit)
|
2018-09-29 14:48:08 +00:00
|
|
|
|
2018-09-29 15:17:33 +00:00
|
|
|
# Message type matching
|
|
|
|
def is_rtp(self):
|
|
|
|
return self.msg_type in (mncc.MNCC_RTP_CREATE,
|
|
|
|
mncc.MNCC_RTP_CONNECT, mncc.MNCC_RTP_FREE)
|
2018-09-29 15:32:57 +00:00
|
|
|
def is_frame(self):
|
|
|
|
return self.msg_type in (mncc.GSM_TCHF_FRAME,
|
|
|
|
mncc.GSM_TCHH_FRAME, mncc.GSM_TCHF_FRAME_EFR,
|
|
|
|
mncc.GSM_TCH_FRAME_AMR, mncc.GSM_BAD_FRAME)
|
2018-09-29 15:17:33 +00:00
|
|
|
|
2018-09-29 14:48:08 +00:00
|
|
|
class mncc_msg(mncc.gsm_mncc, mncc_msg_common):
|
2015-12-01 21:19:25 +00:00
|
|
|
def __str__(self):
|
|
|
|
return 'mncc_msg(type=0x%04x, callref=%u, fields=0x%04x)' % (self.msg_type, self.callref, self.fields)
|
|
|
|
def __unicode__(self):
|
|
|
|
return u'mncc_msg(type=0x%04x, callref=%u, fields=0x%04x)' % (self.msg_type, self.callref, self.fields)
|
2015-12-01 18:07:30 +00:00
|
|
|
|
2018-09-29 14:48:08 +00:00
|
|
|
class mncc_hello_msg(mncc.gsm_mncc_hello, mncc_msg_common):
|
2018-09-29 14:37:11 +00:00
|
|
|
def __str__(self):
|
|
|
|
return 'mncc_hello_msg(version=0x%04x)' % (self.version)
|
|
|
|
def __unicode__(self):
|
|
|
|
return u'mncc_hello_msg(version=0x%04x)' % (self.version)
|
|
|
|
|
2018-09-29 15:32:57 +00:00
|
|
|
class mncc_data_frame_msg(mncc.gsm_data_frame, mncc_msg_common):
|
|
|
|
def __str__(self):
|
|
|
|
return 'mncc_data_frame(type=0x%04x, codec=%s, callref=%u)' \
|
|
|
|
% (self.msg_type, self.codec_str(), self.callref)
|
|
|
|
def __unicode__(self):
|
|
|
|
return u'mncc_data_frame(type=0x%04x, codec=%s, callref=%u)' \
|
|
|
|
% (self.msg_type, self.codec_str(), self.callref)
|
|
|
|
|
|
|
|
def codec_str(self):
|
|
|
|
if self.msg_type == mncc.GSM_TCHF_FRAME:
|
|
|
|
return "FR"
|
|
|
|
elif self.msg_type == mncc.GSM_TCHH_FRAME:
|
|
|
|
return "HR"
|
|
|
|
elif self.msg_type == mncc.GSM_TCHF_FRAME_EFR:
|
|
|
|
return "EFR"
|
|
|
|
elif self.msg_type == mncc.GSM_TCH_FRAME_AMR:
|
|
|
|
return "AMR"
|
|
|
|
elif self.msg_type == mncc.GSM_BAD_FRAME:
|
|
|
|
return "(BFI)"
|
|
|
|
else:
|
|
|
|
return "(???)"
|
|
|
|
|
2018-09-29 14:48:08 +00:00
|
|
|
class mncc_rtp_msg(mncc.gsm_mncc_rtp, mncc_msg_common):
|
2015-12-01 22:04:20 +00:00
|
|
|
def __str__(self):
|
|
|
|
return 'mncc_rtp_msg(type=0x%04x, callref=%u, ip=%x, port=%u)' % (self.msg_type, self.callref, self.ip, self.port)
|
|
|
|
def __unicode__(self):
|
|
|
|
return u'mncc_rtp_msg(type=0x%04x, callref=%u, ip=%x, port=%u)' % (self.msg_type, self.callref, self.ip, self.port)
|
|
|
|
|
2018-09-29 14:48:08 +00:00
|
|
|
class mncc_bridge_msg(mncc.gsm_mncc_bridge, mncc_msg_common):
|
2015-12-03 12:55:19 +00:00
|
|
|
def __str__(self):
|
|
|
|
return 'mncc_bridge_msg(%u, %u)' % (self.callref[0], self.callref[1])
|
|
|
|
def __unicode__(self):
|
|
|
|
return u'mncc_bridge_msg(%u, %u)' % (self.callref[0], self.callref[1])
|
2015-12-01 22:04:20 +00:00
|
|
|
|
2015-12-01 18:07:30 +00:00
|
|
|
def mncc_number(number, num_type = 0, num_plan = 0, num_present = 1, num_screen = 0):
|
|
|
|
return mncc.gsm_mncc_number(number = number, type = num_type,
|
|
|
|
plan = num_plan, present = num_present,
|
|
|
|
screen = num_screen)
|
|
|
|
|
2017-06-05 04:04:56 +00:00
|
|
|
def mncc_bearer_cap(codecs_permitted):
|
|
|
|
speech_ver = ctypes.c_int * 8
|
|
|
|
speech_types = speech_ver()
|
|
|
|
index = 0
|
|
|
|
|
|
|
|
for codec in codecs_permitted:
|
|
|
|
speech_types[index] = codec
|
|
|
|
index = index + 1
|
|
|
|
|
|
|
|
speech_types[index] = -1
|
|
|
|
return mncc.gsm_mncc_bearer_cap(coding = 0, speech_ctm=0, radio = 1, speech_ver = speech_types, transfer = 0, mode = 0)
|
|
|
|
|
2017-04-29 12:12:31 +00:00
|
|
|
class MnccSocketBase(object):
|
2015-12-01 18:07:30 +00:00
|
|
|
def send(self, msg):
|
|
|
|
return self.sock.sendall(msg.send())
|
|
|
|
|
2017-04-29 12:12:31 +00:00
|
|
|
def send_msg(self, msg):
|
|
|
|
data = buffer(msg)[:]
|
|
|
|
return self.sock.sendall(data)
|
|
|
|
|
2015-12-01 18:07:30 +00:00
|
|
|
def recv(self):
|
|
|
|
data = self.sock.recv(1500)
|
|
|
|
ms = mncc_msg()
|
|
|
|
ms.receive(data)
|
2018-09-29 15:17:33 +00:00
|
|
|
if ms.is_rtp():
|
2015-12-01 22:04:20 +00:00
|
|
|
ms = mncc_rtp_msg()
|
|
|
|
ms.receive(data)
|
2018-09-29 15:32:57 +00:00
|
|
|
elif ms.is_frame():
|
|
|
|
ms = mncc_data_frame_msg()
|
|
|
|
ms.receive(data)
|
2018-09-29 14:37:11 +00:00
|
|
|
elif ms.msg_type == mncc.MNCC_SOCKET_HELLO:
|
|
|
|
ms = mncc_hello_msg()
|
|
|
|
ms.receive(data)
|
2015-12-01 18:07:30 +00:00
|
|
|
return ms
|
2017-04-29 12:12:31 +00:00
|
|
|
|
|
|
|
class MnccSocket(MnccSocketBase):
|
|
|
|
def __init__(self, address = '/tmp/bsc_mncc'):
|
|
|
|
super(MnccSocketBase, self).__init__()
|
|
|
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
|
|
|
|
print('connecting to %s' % address)
|
|
|
|
try:
|
|
|
|
self.sock.connect(address)
|
|
|
|
except socket.error as errmsg:
|
|
|
|
sys.stderr.write("%s\n" % errmsg)
|
|
|
|
sys.exit(1)
|
|
|
|
|
2018-09-29 14:37:11 +00:00
|
|
|
# Check the HELLO message
|
|
|
|
self.check_hello()
|
|
|
|
|
|
|
|
def check_hello(self):
|
|
|
|
print('Waiting for HELLO message...')
|
2017-04-29 12:12:31 +00:00
|
|
|
msg = self.recv()
|
|
|
|
|
2018-09-29 14:37:11 +00:00
|
|
|
# Match expected message type
|
|
|
|
if msg.msg_type != mncc.MNCC_SOCKET_HELLO:
|
|
|
|
sys.stderr.write('Received an unknown (!= MNCC_SOCKET_HELLO) '
|
|
|
|
'message: %s\n' % msg)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
# Match expected protocol version
|
|
|
|
if msg.version != mncc.MNCC_SOCK_VERSION:
|
|
|
|
sys.stderr.write('MNCC protocol version mismatch '
|
|
|
|
'(0x%04x vs 0x%04x)\n' % (msg.version, mncc.MNCC_SOCK_VERSION))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
# Match expected message sizes / offsets
|
|
|
|
if (msg.mncc_size != ctypes.sizeof(mncc.gsm_mncc) or
|
|
|
|
msg.data_frame_size != ctypes.sizeof(mncc.gsm_data_frame) or
|
|
|
|
msg.called_offset != mncc.gsm_mncc.called.offset or
|
|
|
|
msg.signal_offset != mncc.gsm_mncc.signal.offset or
|
|
|
|
msg.emergency_offset != mncc.gsm_mncc.emergency.offset or
|
|
|
|
msg.lchan_type_offset != mncc.gsm_mncc.lchan_type.offset):
|
|
|
|
sys.stderr.write('MNCC message alignment mismatch\n')
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
print('Received %s' % msg)
|
|
|
|
|
2017-04-29 12:12:31 +00:00
|
|
|
class MnccSocketServer(object):
|
|
|
|
def __init__(self, address = '/tmp/bsc_mncc'):
|
|
|
|
os.unlink(address)
|
|
|
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
|
|
|
|
self.sock.bind(address)
|
|
|
|
self.sock.listen(5)
|
|
|
|
|
|
|
|
def accept(self):
|
|
|
|
(fd,_) = self.sock.accept()
|
|
|
|
sock = MnccSocketBase()
|
|
|
|
sock.sock = fd
|
|
|
|
return sock
|