#!/usr/bin/env python3 # -*- coding: UTF-8 -*- #/** # * Software Name : pycrate # * Version : 0.4 # * # * Copyright 2018. Benoit Michau. P1sec # * # * This program is free software: you can redistribute it and/or modify # * it under the terms of the GNU General Public License version 2 as published # * by the Free Software Foundation. # * # * This program 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 General Public License for more details. # * # * You will find a copy of the terms and conditions of the GNU General Public # * License version 2 in the "license.txt" file or # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # * # *-------------------------------------------------------- # * File Name : tools/pycrate_map_op_info.py # * Created : 2018-12-13 # * Authors : Benoit Michau # *-------------------------------------------------------- #*/ import sys import argparse import pprint pp = pprint.PrettyPrinter(indent=2) from pycrate_asn1rt.asnobj import ASN1Obj ASN1Obj._SILENT = True # from pycrate_asn1dir.TCAP_MAPv2v3 import * from pycrate_mobile.TS29002_MAPAppCtx import * def load_cap_mod(): from pycrate_asn1dir.TCAP_CAP import GLOBAL # this does not work perfectly, because get_construct_info() is not recursive # but call get_proto() for SEQUENCE / SET inner components PRINT_PROTO_EXTCONTAINER = False SPACE_MARGIN = ' ' SPACE_CONSTRUCT = ' ' TYPE_CONSTRUCT = { TYPE_SEQ, TYPE_SEQ_OF, TYPE_SET, TYPE_SET_OF, TYPE_CHOICE, TYPE_ENUM } def get_error_info(err, info, wext=True): info.append(' errorCode: (local, %.2i)' % err['errorCode'][1]) if 'ParameterType' in err: par = err['ParameterType'] try: info.append(' ParameterType: %s (%s)' % (par._typeref.called[1], par.TYPE)) except Exception: info.append(' ParameterType: %s (%s)' % (par._name, par.TYPE)) get_construct_info(par, info, wext=wext, space=SPACE_CONSTRUCT) info.append('') def get_construct_info(obj, info, wext=True, space=SPACE_CONSTRUCT): if PRINT_PROTO_EXTCONTAINER: bl = set() else: bl = set(('extensionContainer', )) # if obj.TYPE in (TYPE_SEQ, TYPE_SET): for c in obj._cont.values(): info.append(SPACE_MARGIN + '- %s (%s)' % (c._name, c.TYPE)) if wext and c.TYPE in TYPE_CONSTRUCT and \ (c._name != 'extensionContainer' or PRINT_PROTO_EXTCONTAINER): info.append( SPACE_MARGIN + space + pp.pformat( c.get_proto(w_open=wext, w_opt=wext, w_enum=wext, blacklist=bl)[1] ).replace('\n', '\n' + SPACE_MARGIN + space) ) info.append(space + 'mandatory : %s' % ', '.join(obj._root_mand)) # elif obj.TYPE in (TYPE_SEQ_OF, TYPE_SET_OF): c = obj._cont info.append(SPACE_MARGIN + '- %s (%s)' % (c._typeref.called[1], c.TYPE)) if wext and c.TYPE in TYPE_CONSTRUCT: info.append( SPACE_MARGIN + space + pp.pformat( c.get_proto(w_open=wext, w_opt=wext, w_enum=wext, blacklist=bl)[1] ).replace('\n', '\n' + SPACE_MARGIN + space) ) # elif obj.TYPE == TYPE_CHOICE: for c in obj._cont.values(): info.append(SPACE_MARGIN + '- %s (%s)' % (c._name, c.TYPE)) if wext and c.TYPE in TYPE_CONSTRUCT: info.append( SPACE_MARGIN + space + pp.pformat( c.get_proto(w_open=wext, w_opt=wext, w_enum=wext, blacklist=bl)[1] ).replace('\n', '\n' + SPACE_MARGIN + space) ) # elif obj.TYPE == TYPE_ENUM and wext: cont = obj._root[:] if obj._ext is not None: cont.append('...') cont.extend(obj._ext) info.append( SPACE_MARGIN + space + repr(cont) ) def show_infos(val, werr, wext, cap=False): print() vers = '' info = ['OPERATION content: %s\n' % ' - '.join(sorted(val.keys()))] if 'ArgumentType' in val: arg = val['ArgumentType'] vers = arg._typeref.called[0].split('-')[0] info.append(' ArgumentType: %s (%s)' % (arg._typeref.called[1], arg.TYPE)) get_construct_info(arg, info, wext) info.append('') if 'ResultType' in val: res = val['ResultType'] if res._typeref.called[0].split('-')[0] != vers: print('[ERR] version error') info.append(' ResultType: %s (%s)' % (res._typeref.called[1], res.TYPE)) get_construct_info(res, info, wext) info.append('') if werr and 'Errors' in val: err = val['Errors'] for e in err.getv(): get_error_info(e, info, wext) if cap: for name in GLOBAL.MOD['CAP-operationcodes']['_val_']: if GLOBAL.MOD['CAP-operationcodes'][name]._val == val['operationCode']: info.insert(0, 'CAP-operationcodes name: %s' % name) break else: if vers == 'MAPv2': info.insert(0, 'MAP version 1 and 2') elif vers: info.insert(0, 'MAP version 3 and over') else: info.insert(0, 'MAP version unknown') print('\n'.join(info)) def show_appctx(oc): aci = get_application_ctxs(oc, 'I') if aci: print() print('Initiator in MAP application context:') for acname, ac in aci.items(): print(' - %-40s (%s)' % (acname, ' '.join(map(str, ac['code'])))) print(' {%s} -> {%s}' % (', '.join(ac['InitiatorIn'].getv()), ', '.join(ac['ResponderIn'].getv()))) acr = get_application_ctxs(oc, 'R') if acr: print() print('Responder in MAP application context:') for acname, ac in acr.items(): print(' - %-40s (%s)' % (acname, ' '.join(map(str, ac['code'])))) ocini = [] if 'InitiatorConsumerOf' in ac: for opval in ac['InitiatorConsumerOf'].getv(): if 'Supplier' in opval: for oval in opval['Supplier'].getv(): ocini.append(oval['operationCode'][1]) print(' MAP opcode initiating: %s' % ', '.join(map(str, ocini))) print(' {%s} -> {%s}' % (', '.join(ac['InitiatorIn'].getv()), ', '.join(ac['ResponderIn'].getv()))) def print_operation_infos(args): if args.c: Op = GLOBAL.MOD['TCAP-CAP-Messages']['AllCAPInvokable'] prot = 'CAP' else: Op = GLOBAL.MOD['MAPv2v3-Protocol']['Supported-MAP-Operations'] prot = 'MAP' # if args.o == []: oc = list(range(0x100)) else: oc = args.o[:] for i in oc: vals = Op.get('operationCode', ('local', i)) if args.l: if vals[0] == 'U': if 'ArgumentType' in vals[1]: mod, name = vals[1]['ArgumentType']._typeref.called print('%s operation code (local, %.2i): %s.%s' % (prot, i, mod, name)) else: print('%s operation code (local, %.2i): no argument' % (prot, i)) elif vals[0] == 'M': for v in vals[1]: if 'ArgumentType' in v: mod, name = v['ArgumentType']._typeref.called print('%s operation code (local, %.2i): %s.%s' % (prot, i, mod, name)) else: print('%s operation code (local, %.2i): no argument' % (prot, i)) else: if vals[0] == 'U': print() print('-'*80) print('-'*23, ' %s operationCode: (local, %.2i) ' % (prot, i), '-'*23) print('-'*80) val = vals[1] show_infos(val, args.e, args.x, cap=args.c) elif vals[0] == 'M': print() print('-'*80) print('-'*23, ' %s operationCode: (local, %.2i) ' % (prot, i), '-'*23) print('-'*80) for val in vals[1]: show_infos(val, args.e, args.x) if not args.c: show_appctx(i) def main(): parser = argparse.ArgumentParser(description='print information related to MAP '\ 'procedures providing their operation code integral value') parser.add_argument('-o', type=int, nargs='+', help='MAP operation code', default=[]) parser.add_argument('-l', action='store_true', help='only list MAP argument name associated to operation codes') parser.add_argument('-e', action='store_true', help='add procedure-related errors') parser.add_argument('-x', action='store_true', help='add extended data structures') parser.add_argument('-c', action='store_true', help='switch to the CAP / Camel protocol instead of MAP') args = parser.parse_args() if args.c: load_cap_mod() # print_operation_infos(args) return 0 if __name__ == '__main__': sys.exit(main())