pycrate/pycrate_corenet/HdlrUESMS.py

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