161 lines
5.2 KiB
Python
161 lines
5.2 KiB
Python
# -*- coding: UTF-8 -*-
|
|
#/**
|
|
# * Software Name : pycrate
|
|
# * Version : 0.4
|
|
# *
|
|
# * Copyright 2017. Benoit Michau. ANSSI.
|
|
# *
|
|
# * This library is free software; you can redistribute it and/or
|
|
# * modify it under the terms of the GNU Lesser General Public
|
|
# * License as published by the Free Software Foundation; either
|
|
# * version 2.1 of the License, or (at your option) any later version.
|
|
# *
|
|
# * This library is distributed in the hope that it will be useful,
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# * Lesser General Public License for more details.
|
|
# *
|
|
# * You should have received a copy of the GNU Lesser General Public
|
|
# * License along with this library; if not, write to the Free Software
|
|
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# * MA 02110-1301 USA
|
|
# *
|
|
# *--------------------------------------------------------
|
|
# * File Name : pycrate_corenet/HdlrUESMS.py
|
|
# * Created : 2017-12-04
|
|
# * Authors : Benoit Michau
|
|
# *--------------------------------------------------------
|
|
#*/
|
|
|
|
from .utils import *
|
|
from .ProcProto import *
|
|
from .ProcCNSMS import *
|
|
|
|
|
|
class UESMSd(SigStack):
|
|
"""UE SMS handler within a UEIuCSd or UES1d instance
|
|
responsible for point-to-point Short Message Service procedures
|
|
"""
|
|
|
|
TRACK_PROC = True
|
|
|
|
# reference to the UEd
|
|
UE = None
|
|
# reference to the UEIuCSd / UES1d
|
|
RAN = None
|
|
|
|
# to bypass the process() server loop with a custom NAS PDU handler
|
|
RX_HOOK = None
|
|
|
|
# CP ack / err timer
|
|
TC1star = 2
|
|
|
|
# maximum Transaction Identifier value
|
|
# 0x7f correspond to using the extended TI structure
|
|
# otherwise set it to 0x06 to always use the basic TI structure
|
|
TI_MAX_VAL = 0x7f
|
|
|
|
|
|
def _log(self, logtype, msg):
|
|
self.RAN._log(logtype, '[SMS] %s' % msg)
|
|
|
|
def __init__(self, ued, rand):
|
|
self.UE = ued
|
|
self.SMSd = ued.Server.SMSd
|
|
self.set_ran(rand)
|
|
#
|
|
# dict of ongoing SMS procedures indexed transaction identifier:
|
|
# 0..127 : network-initiated, 128..255: UE-initiated
|
|
self.Proc = {}
|
|
# next TID to be used
|
|
self._tid = 0
|
|
# list of tracked procedures (requires TRACK_PROC = True)
|
|
self._proc = []
|
|
|
|
def set_ran(self, rand):
|
|
self.RAN = rand
|
|
|
|
def process(self, SMSRx):
|
|
"""process a SMS-CP message (SMSRx) sent by the UE,
|
|
and return a list (potentially empty) of SMS-CP messages back to the UE
|
|
"""
|
|
if self.RX_HOOK is not None:
|
|
return self.RX_HOOK(SMSRx)
|
|
#
|
|
if self.UE.TRACE_NAS_SMS:
|
|
self.RAN._log('TRACE_NAS_SMS_UL', '\n' + SMSRx.show())
|
|
#
|
|
if SMSRx._name == 'CP_DATA':
|
|
# new transaction
|
|
CPProc = CMSMSProcUE(self)
|
|
if self.TRACK_PROC:
|
|
self._proc.append(CPProc)
|
|
return CPProc.process(SMSRx)
|
|
else:
|
|
# completing transaction
|
|
tipd = SMSRx[0][0]
|
|
tif, ti = tipd[0].get_val(), tipd['TI'].get_val()
|
|
if tif:
|
|
# ti established by the CN
|
|
tid = ti
|
|
else:
|
|
# ti established by the UE
|
|
tid = 0x80 + ti
|
|
if tid not in self.Proc:
|
|
# error
|
|
CPErr = NAS.CP_ERROR(val=[{'TIPD': {'TIFlag': (1, 0)[tif],
|
|
'TI' : ti}},
|
|
81])
|
|
if self.UE.TRACE_NAS_SMS:
|
|
self._log('TRACE_NAS_SMS_DL', '\n' + CPErr.show())
|
|
return [CPErr]
|
|
else:
|
|
CPProc = self.Proc[tid]
|
|
return CPProc.process(SMSRx)
|
|
|
|
def init_cpdata(self, cpud, tid=None):
|
|
"""initialize a CN-initiated SMS procedure with `cpud' as CP user data
|
|
and return the procedure
|
|
"""
|
|
if tid is None:
|
|
#TIFlag = 0
|
|
tid = self._get_new_tid()
|
|
if tid is None:
|
|
# no transaction id available
|
|
self._log('WNG', 'no TID available for starting a new procedure')
|
|
return None
|
|
elif tid in self.Proc:
|
|
self._log('WNG', 'TID %i not available for starting a new procedure' % tid)
|
|
return None
|
|
CPProc = CMSMSProcCN(self, tid, cpud)
|
|
self.Proc[tid] = CPProc
|
|
if self.TRACK_PROC:
|
|
self._proc.append( CPProc )
|
|
return CPProc
|
|
|
|
def clear(self, tid=None):
|
|
"""abort all running procedures, eventually for a single transaction ID
|
|
"""
|
|
if tid is None:
|
|
for tid, Proc in self.Proc.items():
|
|
Proc.abort()
|
|
elif tid in self.Proc:
|
|
self.Proc[tid].abort()
|
|
|
|
def _get_new_tid(self):
|
|
tid, step = self._tid, 0
|
|
while tid in self.Proc:
|
|
tid += 1
|
|
step += 1
|
|
if step == self.TI_MAX_VAL:
|
|
# no TID available
|
|
return None
|
|
if tid > self.TI_MAX_VAL:
|
|
tid = 0
|
|
if tid == self.TI_MAX_VAL:
|
|
self._tid = 0
|
|
else:
|
|
self._tid = 1 + tid
|
|
return tid
|
|
|