HBLink/hb_config.py

276 lines
13 KiB
Python
Raw Normal View History

2016-09-13 17:23:14 -04:00
#!/usr/bin/env python
#
2016-11-21 20:13:32 -05:00
###############################################################################
# Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS <n0mjs@me.com>
2016-11-21 20:13:32 -05:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# 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 should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
###############################################################################
2016-09-13 17:23:14 -04:00
'''
This module generates the configuration data structure for hblink.py and
assoicated programs that use it. It has been seaparated into a different
module so as to keep hblink.py easeier to navigate. This file only needs
updated if the items in the main configuraiton file (usually hblink.cfg)
change.
'''
2016-07-20 22:25:47 -04:00
import ConfigParser
import sys
import hb_const as const
2016-07-20 22:25:47 -04:00
from socket import gethostbyname
2016-09-13 17:30:01 -04:00
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
__author__ = 'Cortney T. Buffington, N0MJS'
__copyright__ = 'Copyright (c) 2016-2018 Cortney T. Buffington, N0MJS and the K0USY Group'
2016-09-13 17:30:01 -04:00
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
2016-11-22 21:02:09 -05:00
__license__ = 'GNU GPLv3'
2016-09-13 17:30:01 -04:00
__maintainer__ = 'Cort Buffington, N0MJS'
__email__ = 'n0mjs@me.com'
2016-07-20 22:25:47 -04:00
# Processing of ALS goes here. It's separated from the acl_build function because this
# code is hblink config-file format specific, and acl_build is abstracted
def process_acls(_config):
# Global registration ACL
_config['GLOBAL']['REG_ACL'] = acl_build(_config['GLOBAL']['REG_ACL'], const.PEER_MAX)
2018-11-26 12:52:45 -05:00
# Global subscriber and TGID ACLs
for acl in ['SUB_ACL', 'TG1_ACL', 'TG2_ACL']:
_config['GLOBAL'][acl] = acl_build(_config['GLOBAL'][acl], const.ID_MAX)
2018-11-26 12:52:45 -05:00
# System level ACLs
for system in _config['SYSTEMS']:
# Registration ACLs (which make no sense for peer systems)
if _config['SYSTEMS'][system]['MODE'] == 'MASTER':
_config['SYSTEMS'][system]['REG_ACL'] = acl_build(_config['SYSTEMS'][system]['REG_ACL'], const.PEER_MAX)
2018-11-26 12:52:45 -05:00
# Subscriber and TGID ACLs (valid for all system types)
for acl in ['SUB_ACL', 'TG1_ACL', 'TG2_ACL']:
_config['SYSTEMS'][system][acl] = acl_build(_config['SYSTEMS'][system][acl], const.ID_MAX)
# Create an access control list that is programatically useable from human readable:
# ORIGINAL: 'DENY:1-5,3120101,3120124'
# PROCESSED: (False, set([(1, 5), (3120124, 3120124), (3120101, 3120101)]))
def acl_build(_acl, _max):
if not _acl:
return(True, set((const.ID_MIN, _max)))
2018-11-26 12:52:45 -05:00
acl = set()
sections = _acl.split(':')
2018-11-26 12:52:45 -05:00
if sections[0] == 'PERMIT':
action = True
else:
action = False
2018-11-26 12:52:45 -05:00
for entry in sections[1].split(','):
if entry == 'ALL':
acl.add((const.ID_MIN, _max))
break
2018-11-26 12:52:45 -05:00
elif '-' in entry:
start,end = entry.split('-')
start,end = int(start), int(end)
if (const.ID_MIN <= start <= _max) or (const.ID_MIN <= end <= _max):
acl.add((start, end))
else:
sys.exit('ACL CREATION ERROR, VALUE OUT OF RANGE (} - {})IN RANGE-BASED ENTRY: {}'.format(const.ID_MIN, _max, entry))
else:
id = int(entry)
if (const.ID_MIN <= id <= _max):
acl.add((id, id))
else:
sys.exit('ACL CREATION ERROR, VALUE OUT OF RANGE ({} - {}) IN SINGLE ID ENTRY: {}'.format(const.ID_MIN, _max, entry))
return (action, acl)
2016-07-20 22:25:47 -04:00
def build_config(_config_file):
config = ConfigParser.ConfigParser()
if not config.read(_config_file):
sys.exit('Configuration file \''+_config_file+'\' is not a valid configuration file! Exiting...')
CONFIG = {}
CONFIG['GLOBAL'] = {}
CONFIG['REPORTS'] = {}
2016-07-20 22:25:47 -04:00
CONFIG['LOGGER'] = {}
2016-09-12 12:26:41 -04:00
CONFIG['ALIASES'] = {}
2016-08-25 21:44:15 -04:00
CONFIG['SYSTEMS'] = {}
2016-07-20 22:25:47 -04:00
try:
for section in config.sections():
if section == 'GLOBAL':
CONFIG['GLOBAL'].update({
'PATH': config.get(section, 'PATH'),
'PING_TIME': config.getint(section, 'PING_TIME'),
'MAX_MISSED': config.getint(section, 'MAX_MISSED'),
'USE_ACL': config.get(section, 'USE_ACL'),
'REG_ACL': config.get(section, 'REG_ACL'),
'SUB_ACL': config.get(section, 'SUB_ACL'),
'TG1_ACL': config.get(section, 'TGID_TS1_ACL'),
'TG2_ACL': config.get(section, 'TGID_TS2_ACL')
2016-07-20 22:25:47 -04:00
})
elif section == 'REPORTS':
CONFIG['REPORTS'].update({
'REPORT': config.getboolean(section, 'REPORT'),
'REPORT_INTERVAL': config.getint(section, 'REPORT_INTERVAL'),
'REPORT_PORT': config.getint(section, 'REPORT_PORT'),
'REPORT_CLIENTS': config.get(section, 'REPORT_CLIENTS').split(',')
})
2016-07-20 22:25:47 -04:00
elif section == 'LOGGER':
CONFIG['LOGGER'].update({
'LOG_FILE': config.get(section, 'LOG_FILE'),
'LOG_HANDLERS': config.get(section, 'LOG_HANDLERS'),
'LOG_LEVEL': config.get(section, 'LOG_LEVEL'),
'LOG_NAME': config.get(section, 'LOG_NAME')
})
2016-09-12 12:26:41 -04:00
elif section == 'ALIASES':
CONFIG['ALIASES'].update({
'TRY_DOWNLOAD': config.getboolean(section, 'TRY_DOWNLOAD'),
'PATH': config.get(section, 'PATH'),
'PEER_FILE': config.get(section, 'PEER_FILE'),
'SUBSCRIBER_FILE': config.get(section, 'SUBSCRIBER_FILE'),
'TGID_FILE': config.get(section, 'TGID_FILE'),
'PEER_URL': config.get(section, 'PEER_URL'),
'SUBSCRIBER_URL': config.get(section, 'SUBSCRIBER_URL'),
'STALE_TIME': config.getint(section, 'STALE_DAYS') * 86400,
})
2016-07-20 22:25:47 -04:00
elif config.getboolean(section, 'ENABLED'):
if config.get(section, 'MODE') == 'PEER':
2016-08-25 21:44:15 -04:00
CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'),
2016-07-20 22:25:47 -04:00
'ENABLED': config.getboolean(section, 'ENABLED'),
2018-06-25 13:17:30 -04:00
'LOOSE': config.getboolean(section, 'LOOSE'),
'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')),
2016-07-20 22:25:47 -04:00
'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'),
'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')),
2016-07-20 22:25:47 -04:00
'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')),
'MASTER_PORT': config.getint(section, 'MASTER_PORT'),
'PASSPHRASE': config.get(section, 'PASSPHRASE'),
2016-11-18 09:27:57 -05:00
'CALLSIGN': config.get(section, 'CALLSIGN').ljust(8)[:8],
2016-07-20 22:25:47 -04:00
'RADIO_ID': hex(int(config.get(section, 'RADIO_ID')))[2:].rjust(8,'0').decode('hex'),
2016-11-18 09:27:57 -05:00
'RX_FREQ': config.get(section, 'RX_FREQ').ljust(9)[:9],
'TX_FREQ': config.get(section, 'TX_FREQ').ljust(9)[:9],
'TX_POWER': config.get(section, 'TX_POWER').rjust(2,'0'),
'COLORCODE': config.get(section, 'COLORCODE').rjust(2,'0'),
2016-11-18 09:27:57 -05:00
'LATITUDE': config.get(section, 'LATITUDE').ljust(8)[:8],
'LONGITUDE': config.get(section, 'LONGITUDE').ljust(9)[:9],
'HEIGHT': config.get(section, 'HEIGHT').rjust(3,'0'),
'LOCATION': config.get(section, 'LOCATION').ljust(20)[:20],
'DESCRIPTION': config.get(section, 'DESCRIPTION').ljust(19)[:19],
'SLOTS': config.get(section, 'SLOTS'),
'URL': config.get(section, 'URL').ljust(124)[:124],
'SOFTWARE_ID': config.get(section, 'SOFTWARE_ID').ljust(40)[:40],
2016-12-21 20:55:11 -05:00
'PACKAGE_ID': config.get(section, 'PACKAGE_ID').ljust(40)[:40],
'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'),
'OPTIONS': config.get(section, 'OPTIONS'),
'USE_ACL': config.getboolean(section, 'USE_ACL'),
'SUB_ACL': config.get(section, 'SUB_ACL'),
'TG1_ACL': config.get(section, 'TGID_TS1_ACL'),
'TG2_ACL': config.get(section, 'TGID_TS2_ACL')
2016-07-20 22:25:47 -04:00
}})
2016-08-25 21:44:15 -04:00
CONFIG['SYSTEMS'][section].update({'STATS': {
'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES
'CONNECTED': None,
'PINGS_SENT': 0,
'PINGS_ACKD': 0,
'NUM_OUTSTANDING': 0,
'PING_OUTSTANDING': False,
'LAST_PING_TX_TIME': 0,
'LAST_PING_ACK_TIME': 0,
}})
2016-07-20 22:25:47 -04:00
elif config.get(section, 'MODE') == 'MASTER':
2016-08-25 21:44:15 -04:00
CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'),
2016-07-20 22:25:47 -04:00
'ENABLED': config.getboolean(section, 'ENABLED'),
2016-07-27 19:07:01 -04:00
'REPEAT': config.getboolean(section, 'REPEAT'),
'MAX_PEERS': config.getint(section, 'MAX_PEERS'),
2016-07-20 22:25:47 -04:00
'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'),
2016-11-26 15:36:24 -05:00
'PASSPHRASE': config.get(section, 'PASSPHRASE'),
'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'),
'USE_ACL': config.getboolean(section, 'USE_ACL'),
'REG_ACL': config.get(section, 'REG_ACL'),
'SUB_ACL': config.get(section, 'SUB_ACL'),
'TG1_ACL': config.get(section, 'TGID_TS1_ACL'),
'TG2_ACL': config.get(section, 'TGID_TS2_ACL')
2016-07-20 22:25:47 -04:00
}})
CONFIG['SYSTEMS'][section].update({'PEERS': {}})
elif config.get(section, 'MODE') == 'OPENBRIDGE':
CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'),
'ENABLED': config.getboolean(section, 'ENABLED'),
'NETWORK_ID': hex(int(config.get(section, 'NETWORK_ID')))[2:].rjust(8,'0').decode('hex'),
'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'),
'PASSPHRASE': config.get(section, 'PASSPHRASE').ljust(20,'\x00')[:20],
2018-09-26 16:48:48 -04:00
'TARGET_SOCK': (gethostbyname(config.get(section, 'TARGET_IP')), config.getint(section, 'TARGET_PORT')),
'TARGET_IP': gethostbyname(config.get(section, 'TARGET_IP')),
'TARGET_PORT': config.getint(section, 'TARGET_PORT'),
'USE_ACL': config.getboolean(section, 'USE_ACL'),
'SUB_ACL': config.get(section, 'SUB_ACL'),
'TG1_ACL': config.get(section, 'TGID_ACL'),
'TG2_ACL': 'PERMIT:ALL'
}})
2016-07-20 22:25:47 -04:00
2016-07-25 14:03:01 -04:00
except ConfigParser.Error, err:
sys.exit('Error processing configuration file -- {}'.format(err))
2016-07-20 22:25:47 -04:00
process_acls(CONFIG)
2016-08-25 21:44:15 -04:00
return CONFIG
# Used to run this file direclty and print the config,
# which might be useful for debugging
if __name__ == '__main__':
import sys
import os
import argparse
from pprint import pprint
from dmr_utils.utils import int_id
2016-08-25 21:44:15 -04:00
# Change the current directory to the location of the application
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
# CLI argument parser - handles picking up the config file from the command line, and sending a "help" message
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually hblink.cfg)')
cli_args = parser.parse_args()
# Ensure we have a path for the config file, if one wasn't specified, then use the execution directory
if not cli_args.CONFIG_FILE:
cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/hblink.cfg'
CONFIG = build_config(cli_args.CONFIG_FILE)
pprint(CONFIG)
2016-08-25 21:44:15 -04:00
def acl_check(_id, _acl):
id = int_id(_id)
for entry in _acl[1]:
if entry[0] <= id <= entry[1]:
return _acl[0]
return not _acl[0]
2018-11-26 12:52:45 -05:00
print acl_check('\x00\x01\x37', CONFIG['GLOBAL']['TG1_ACL'])