pycrate/pycrate_diameter/parse_iana_diameter_xml.py

236 lines
8.5 KiB
Python

# -*- coding: UTF-8 -*-
#/*
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2019. Benoit Michau. P1Sec.
# *
# * 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_diameter/parse_iana_diameter_xml.py
# * Created : 2019-07-30
# * Authors : Benoit Michau
# *--------------------------------------------------------
# */
import os
import sys
import re
import time
import pprint
from lxml import etree
# those are the xml files taken from the IANA web pages
# https://www.iana.org/assignments/aaa-parameters/aaa-parameters.xml
FILENAME_aaa_parameters = os.path.dirname(__file__) + '/aaa-parameters.xml'
#
# https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml
FILENAME_address_family_numbers = os.path.dirname(__file__) + '/address-family-numbers.xml'
#
# https://www.iana.org/assignments/radius-types/radius-types.xml
FILENAME_radius_types = os.path.dirname(__file__) + '/radius-types.xml'
#
# and this is a Python file with xml converted to dict
FILENAME_python_dicts = os.path.dirname(__file__) + '/iana_diameter_dicts.py'
'''
xml file structure for aaa-parameters.xml
AVP Codes:
AVP Code -> Attribute Name
AVP Specific Values:
Attribute Name, AVP Code:
AVP Value -> Value Name
...
AVP Flags Value
Bit -> Name
Application ID
ID Value -> Name
Command Codes
Code Value -> Name
xml file structure for address-family-numbers.xml
Address Family Numbers:
Number -> Description
'''
# some regexp
RE_INT = re.compile(r'^[1-9]{1}[0-9]{0,}$')
RE_HEX = re.compile(r'0x[0-9]{2,4,6,8,10,12,14,16}')
RE_SV_CODE = re.compile(r'\(code (%s)\)' % RE_INT.pattern[1:-1])
def build_dict_from_xml(filename=FILENAME_aaa_parameters):
if filename == FILENAME_aaa_parameters:
T = etree.parse(filename).getroot()
for child in T.getchildren():
subchild_list = child.getchildren()
if not subchild_list:
pass
elif subchild_list[0].text == 'AVP Codes':
dict_avp_codes = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
elif subchild_list[0].text == 'AVP Specific Values':
dict_avp_spec_val = build_dict_from_avp_spec_val(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}registry'])
elif subchild_list[0].text == 'Application IDs':
dict_app_id = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
elif subchild_list[0].text == 'Command Codes':
dict_cmd_codes = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
#
return (dict_app_id,
dict_cmd_codes,
dict_avp_codes,
dict_avp_spec_val)
#
elif filename == FILENAME_address_family_numbers:
T = etree.parse(filename).getroot()
for child in T.getchildren():
subchild_list = child.getchildren()
if not subchild_list:
pass
elif subchild_list[0].text == 'Address Family Numbers':
dict_addr_fam_nums = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
#
return dict_addr_fam_nums
#
elif filename == FILENAME_radius_types:
T = etree.parse(filename).getroot()
for child in T.getchildren():
subchild_list = child.getchildren()
if not subchild_list:
pass
elif subchild_list[0].text == 'RADIUS Attribute Types':
dict_radius_avp_codes = build_dict_from_val_name(
[i for i in subchild_list if i.tag == '{http://www.iana.org/assignments}record'])
#
return dict_radius_avp_codes
#
else:
print('error: invalid filename')
def build_dict_from_val_name(recordlist):
d = {}
for rec in recordlist:
code, name = rec.getchildren()[:2]
m = RE_INT.match(code.text)
if m:
d[int(m.group())] = re.sub('\s{1,}', ' ', name.text)
return d
def build_dict_from_avp_spec_val(registrylist):
d = {}
for reg in registrylist:
childlist = reg.getchildren()
m = RE_SV_CODE.search(childlist[0].text)
if m:
code = int(m.group(1))
if code not in d:
d[code] = {}
subd = d[code]
for rec in [i for i in childlist if i.tag == '{http://www.iana.org/assignments}record']:
val, name = rec.getchildren()[:2]
m = RE_INT.match(val.text)
if m:
subd[int(m.group())] = re.sub('\s{1,}', ' ', name.text)
# can also have flag values, e.g. 0x0000000000000020
else:
m = RE_HEX.match(val.text)
if m:
subd[int(m.group(), 16)] = re.sub('\s{1,}', ' ', name.text)
return d
# generated file header
_Header = '''# -*- coding: UTF-8 -*-
#/*
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2020. Benoit Michau. P1Sec.
# *
# * 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_diameter/iana_diameter_dicts.py
# * Created : %s
# * Authors : Benoit Michau
# *--------------------------------------------------------
# */
''' % time.strftime('%Y-%m-%d')
def gen_python_dict():
#
AVPRadCodes_dict = build_dict_from_xml(FILENAME_radius_types)
AppID_dict, Cmd_dict, AVPDiamCodes_dict, AVPSpecVal_dict = build_dict_from_xml(FILENAME_aaa_parameters)
AddrFamNums_dict = build_dict_from_xml(FILENAME_address_family_numbers)
AVPCodes_dict = dict(AVPRadCodes_dict)
AVPCodes_dict.update(AVPDiamCodes_dict)
#
# serialize those dicts into a new Python file
if os.path.exists(FILENAME_python_dicts):
print('WARN: %s already exists, please move it first' % FILENAME_python_dicts)
return 0
#
with open(FILENAME_python_dicts, 'w') as fd:
fd.write(_Header)
fd.write('\n')
#
# set a pretty formatter for the dicts
pf = pprint.PrettyPrinter(indent=2, sort_dicts=True)
fd.write('AVPRadCodes_dict = \\\n%s\n\n' % pf.pformat(AVPRadCodes_dict))
fd.write('AppID_dict = \\\n%s\n\n' % pf.pformat(AppID_dict))
fd.write('Cmd_dict = \\\n%s\n\n' % pf.pformat(Cmd_dict))
fd.write('AVPDiamCodes_dict = \\\n%s\n\n' % pf.pformat(AVPDiamCodes_dict))
fd.write('AVPSpecVal_dict = \\\n%s\n\n' % pf.pformat(AVPSpecVal_dict))
fd.write('AddrFamNums_dict = \\\n%s\n\n' % pf.pformat(AddrFamNums_dict))
fd.write('AVPCodes_dict = \\\n%s\n\n' % pf.pformat(AVPCodes_dict))
#
print('file generated: %s' % FILENAME_python_dicts)
#
return 0
if __name__ == '__main__':
sys.exit(gen_python_dict())