working SMS generation, not correct though
This commit is contained in:
parent
73c5ec938f
commit
9690cdf98c
363
data_gateway-SAMPLE.cfg
Normal file
363
data_gateway-SAMPLE.cfg
Normal file
@ -0,0 +1,363 @@
|
||||
# PROGRAM-WIDE PARAMETERS GO HERE
|
||||
# PATH - working path for files, leave it alone unless you NEED to change it
|
||||
# PING_TIME - the interval that peers will ping the master, and re-try registraion
|
||||
# - how often the Master maintenance loop runs
|
||||
# MAX_MISSED - how many pings are missed before we give up and re-register
|
||||
# - number of times the master maintenance loop runs before de-registering a peer
|
||||
#
|
||||
# ACLs:
|
||||
#
|
||||
# Access Control Lists are a very powerful tool for administering your system.
|
||||
# But they consume packet processing time. Disable them if you are not using them.
|
||||
# But be aware that, as of now, the configuration stanzas still need the ACL
|
||||
# sections configured even if you're not using them.
|
||||
#
|
||||
# REGISTRATION ACLS ARE ALWAYS USED, ONLY SUBSCRIBER AND TGID MAY BE DISABLED!!!
|
||||
#
|
||||
# The 'action' May be PERMIT|DENY
|
||||
# Each entry may be a single radio id, or a hypenated range (e.g. 1-2999)
|
||||
# Format:
|
||||
# ACL = 'action:id|start-end|,id|start-end,....'
|
||||
# --for example--
|
||||
# SUB_ACL: DENY:1,1000-2000,4500-60000,17
|
||||
#
|
||||
# ACL Types:
|
||||
# REG_ACL: peer radio IDs for registration (only used on HBP master systems)
|
||||
# SUB_ACL: subscriber IDs for end-users
|
||||
# TGID_TS1_ACL: destination talkgroup IDs on Timeslot 1
|
||||
# TGID_TS2_ACL: destination talkgroup IDs on Timeslot 2
|
||||
#
|
||||
# ACLs may be repeated for individual systems if needed for granularity
|
||||
# Global ACLs will be processed BEFORE the system level ACLs
|
||||
# Packets will be matched against all ACLs, GLOBAL first. If a packet 'passes'
|
||||
# All elements, processing continues. Packets are discarded at the first
|
||||
# negative match, or 'reject' from an ACL element.
|
||||
#
|
||||
# If you do not wish to use ACLs, set them to 'PERMIT:ALL'
|
||||
# TGID_TS1_ACL in the global stanza is used for OPENBRIDGE systems, since all
|
||||
# traffic is passed as TS 1 between OpenBridges
|
||||
[GLOBAL]
|
||||
PATH: ./
|
||||
PING_TIME: 5
|
||||
MAX_MISSED: 3
|
||||
USE_ACL: True
|
||||
REG_ACL: PERMIT:ALL
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
|
||||
# NOT YET WORKING: NETWORK REPORTING CONFIGURATION
|
||||
# Enabling "REPORT" will configure a socket-based reporting
|
||||
# system that will send the configuration and other items
|
||||
# to a another process (local or remote) that may process
|
||||
# the information for some useful purpose, like a web dashboard.
|
||||
#
|
||||
# REPORT - True to enable, False to disable
|
||||
# REPORT_INTERVAL - Seconds between reports
|
||||
# REPORT_PORT - TCP port to listen on if "REPORT_NETWORKS" = NETWORK
|
||||
# REPORT_CLIENTS - comma separated list of IPs you will allow clients
|
||||
# to connect on. Entering a * will allow all.
|
||||
#
|
||||
# ****FOR NOW MUST BE TRUE - USE THE LOOPBACK IF YOU DON'T USE THIS!!!****
|
||||
[REPORTS]
|
||||
REPORT: True
|
||||
REPORT_INTERVAL: 60
|
||||
REPORT_PORT: 4329
|
||||
REPORT_CLIENTS: 127.0.0.1
|
||||
|
||||
|
||||
# SYSTEM LOGGER CONFIGURAITON
|
||||
# This allows the logger to be configured without chaning the individual
|
||||
# python logger stuff. LOG_FILE should be a complete path/filename for *your*
|
||||
# system -- use /dev/null for non-file handlers.
|
||||
# LOG_HANDLERS may be any of the following, please, no spaces in the
|
||||
# list if you use several:
|
||||
# null
|
||||
# console
|
||||
# console-timed
|
||||
# file
|
||||
# file-timed
|
||||
# syslog
|
||||
# LOG_LEVEL may be any of the standard syslog logging levels, though
|
||||
# as of now, DEBUG, INFO, WARNING and CRITICAL are the only ones
|
||||
# used.
|
||||
#
|
||||
[LOGGER]
|
||||
LOG_FILE: /tmp/hblink.log
|
||||
LOG_HANDLERS: console-timed
|
||||
LOG_LEVEL: DEBUG
|
||||
LOG_NAME: HBlink
|
||||
|
||||
# DOWNLOAD AND IMPORT SUBSCRIBER, PEER and TGID ALIASES
|
||||
# Ok, not the TGID, there's no master list I know of to download
|
||||
# This is intended as a facility for other applcations built on top of
|
||||
# HBlink to use, and will NOT be used in HBlink directly.
|
||||
# STALE_DAYS is the number of days since the last download before we
|
||||
# download again. Don't be an ass and change this to less than a few days.
|
||||
[ALIASES]
|
||||
TRY_DOWNLOAD: True
|
||||
PATH: ./
|
||||
PEER_FILE: peer_ids.json
|
||||
SUBSCRIBER_FILE: subscriber_ids.json
|
||||
TGID_FILE: talkgroup_ids.json
|
||||
PEER_URL: https://www.radioid.net/static/rptrs.json
|
||||
SUBSCRIBER_URL: https://www.radioid.net/static/users.json
|
||||
STALE_DAYS: 7
|
||||
|
||||
# USER MANAGER
|
||||
# This is where to configure the details for use with a user managment script
|
||||
[WEB_SERVICE]
|
||||
THIS_SERVER_NAME: DATA_GATEWAY
|
||||
REMOTE_CONFIG_ENABLED: False
|
||||
# URL of the user managment server
|
||||
URL: http://localhost:8080/svr
|
||||
# Integer appended to DMR ID during the generation of a passphrase
|
||||
APPEND_INT: 1
|
||||
EXTRA_INT_1: 5
|
||||
EXTRA_INT_2: 8
|
||||
EXTRA_1: TeSt
|
||||
EXTRA_2: DmR4
|
||||
# Secret used to authenticate with user managment server, before checking if user login is approved
|
||||
SHARED_SECRET: test
|
||||
# Shorten passphrases
|
||||
SHORTEN_PASSPHRASE: True
|
||||
SHORTEN_SAMPLE: 4
|
||||
SHORTEN_LENGTH: 4
|
||||
BURN_FILE: ./burn_ids.txt
|
||||
BURN_INT: 5
|
||||
|
||||
[DATA_CONFIG]
|
||||
DATA_DMR_ID: 9099
|
||||
CALL_TYPE: both
|
||||
UNIT_SMS_TS: 2
|
||||
|
||||
USER_APRS_SSID: 5
|
||||
USER_APRS_COMMENT: HBNet APRS Gateway
|
||||
APRS_SERVER: hbl.ink
|
||||
APRS_PORT: 14580
|
||||
APRS_LOGIN_CALL: N0CALL
|
||||
APRS_LOGIN_PASSCODE: 12345
|
||||
APRS_FILTER: r/47/-120/500 t/m
|
||||
|
||||
# The following settings are only applicable if you are using the gps_data_beacon_igate script.
|
||||
# They do not affect the operation gps_data itself.
|
||||
# Time in minutes.
|
||||
IGATE_BEACON_TIME = 45
|
||||
IGATE_BEACON_COMMENT = HBLink3 D-APRS Gateway
|
||||
IGATE_BEACON_ICON = /I
|
||||
IGATE_LATITUDE = 4730. N
|
||||
IGATE_LONGITUDE = 11930. W
|
||||
|
||||
# The following settings are for the static positions only, for hotspots or repeaters connected to MASTER stanzas.
|
||||
# Implementation by IU7IGU
|
||||
# REPORT_INTERVAL in Minute (ALLOW only > 3 Minutes)
|
||||
# MESSAGE: This message will print on APRS description together RX and TX Frequency
|
||||
APRS_STATIC_REPORT_INTERVAL: 15
|
||||
APRS_STATIC_MESSAGE:Connected to HBLink
|
||||
|
||||
# The options below are required for operation of the dashboard and will cause errors in gps_data.py
|
||||
# if configured wrong. Leave them as default unless you know what you are doing.
|
||||
# If you do change, you must use absolute paths.
|
||||
LOCATION_FILE: /tmp/gps_data_user_loc.txt
|
||||
BULLETIN_BOARD_FILE: /tmp/gps_data_user_bb.txt
|
||||
MAILBOX_FILE: /tmp/gps_data_user_mailbox.txt
|
||||
EMERGENCY_SOS_FILE: /tmp/gps_data_user_sos.txt
|
||||
SMS_FILE: /tmp/gps_data_user_sms.txt
|
||||
|
||||
# User settings file, MUST configure using absolute path.
|
||||
USER_SETTINGS_FILE: /tmp/user_settings.txt
|
||||
|
||||
# API settings
|
||||
# Authorized Apps file - data used for the dashboard API
|
||||
USE_API: True
|
||||
AUTHORIZED_APPS_FILE: /tmp/authorized_apps.txt
|
||||
AUTHORIZED_TOKENS_FILE: /tmp/hblink_auth_tokens.txt
|
||||
AUTHORIZED_USERS_FILE: /home/eric/Sync/hblink3_sms_dev/authorized_users.txt
|
||||
ACCESS_SYSTEMS_FILE: /home/eric/Sync/hblink3_sms_dev/access_systems.txt
|
||||
MY_SERVER_SHORTCUT: XYZ
|
||||
SERVER_NAME: Test HBLink Network
|
||||
USE_PUBLIC_APPS: True
|
||||
PUBLIC_APPS_LIST: https://raw.githubusercontent.com/kf7eel/hblink_sms_external_apps/main/public_systems.txt
|
||||
RULES_PATH: /home/eric/Sync/hblink3_sms_dev/rules.py
|
||||
|
||||
# The following options are used for the dashboard. The dashboard is optional.
|
||||
# Title of the Dashboard
|
||||
DASHBOARD_TITLE: HBNet D-APRS Dashboard
|
||||
# Used for API, RSS feed link, etc
|
||||
DASHBOARD_URL: http://localhost:8092
|
||||
|
||||
# Logo used on dashboard page
|
||||
LOGO: https://raw.githubusercontent.com/kf7eel/hblink3/gps/HBlink.png
|
||||
|
||||
# Port to run server
|
||||
DASH_PORT: 8092
|
||||
|
||||
# IP to run server on
|
||||
DASH_HOST: 127.0.0.1
|
||||
|
||||
#Description of dashboard to show on main page
|
||||
DESCRIPTION: Welcome to the dashboard.
|
||||
|
||||
# Gateway contact info displayed on about page.
|
||||
CONTACT_NAME: your name
|
||||
CONTACT_CALL: N0CALL
|
||||
CONTACT_EMAIL: email@example.org
|
||||
CONTACT_WEBSITE: https://hbl.ink
|
||||
|
||||
# Time format for display
|
||||
TIME_FORMAT: %%H:%%M:%%S - %%m/%%d/%%y
|
||||
|
||||
# Center dashboard map over these coordinates
|
||||
MAP_CENTER_LAT: 47.00
|
||||
MAP_CENTER_LON: -120.00
|
||||
ZOOM_LEVEL: 7
|
||||
|
||||
# List and preview of some map themes at http://leaflet-extras.github.io/leaflet-providers/preview/
|
||||
# The following are options for map themes and just work, you should use one of these: “OpenStreetMap”, “Stamen” (Terrain, Toner, and Watercolor),
|
||||
MAP_THEME: Stamen Toner
|
||||
|
||||
|
||||
# OPENBRIDGE INSTANCES - DUPLICATE SECTION FOR MULTIPLE CONNECTIONS
|
||||
# OpenBridge is a protocol originall created by DMR+ for connection between an
|
||||
# IPSC2 server and Brandmeister. It has been implemented here at the suggestion
|
||||
# of the Brandmeister team as a way to legitimately connect HBlink to the
|
||||
# Brandemiester network.
|
||||
# It is recommended to name the system the ID of the Brandmeister server that
|
||||
# it connects to, but is not necessary. TARGET_IP and TARGET_PORT are of the
|
||||
# Brandmeister or IPSC2 server you are connecting to. PASSPHRASE is the password
|
||||
# that must be agreed upon between you and the operator of the server you are
|
||||
# connecting to. NETWORK_ID is a number in the format of a DMR Radio ID that
|
||||
# will be sent to the other server to identify this connection.
|
||||
# other parameters follow the other system types.
|
||||
#
|
||||
# ACLs:
|
||||
# OpenBridge does not 'register', so registration ACL is meaningless.
|
||||
# Proper OpenBridge passes all traffic on TS1.
|
||||
# HBlink can extend OPB to use both slots for unit calls only.
|
||||
# Setting "BOTH_SLOTS" True ONLY affects unit traffic!
|
||||
# Otherwise ACLs work as described in the global stanza
|
||||
[OBP-1]
|
||||
MODE: OPENBRIDGE
|
||||
ENABLED: True
|
||||
IP:
|
||||
PORT: 62036
|
||||
NETWORK_ID: 1234
|
||||
PASSPHRASE: passw0rd
|
||||
TARGET_IP: 127.0.0.1
|
||||
TARGET_PORT: 62037
|
||||
BOTH_SLOTS: True
|
||||
USE_ACL: True
|
||||
SUB_ACL: DENY:1
|
||||
TGID_ACL: PERMIT:ALL
|
||||
USE_ENCRYPTION: False
|
||||
ENCRYPTION_KEY:
|
||||
|
||||
# MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS
|
||||
# HomeBrew Protocol Master instances go here.
|
||||
# IP may be left blank if there's one interface on your system.
|
||||
# Port should be the port you want this master to listen on. It must be unique
|
||||
# and unused by anything else.
|
||||
# Repeat - if True, the master repeats traffic to peers, False, it does nothing.
|
||||
#
|
||||
# MAX_PEERS -- maximun number of peers that may be connect to this master
|
||||
# at any given time. This is very handy if you're allowing hotspots to
|
||||
# connect, or using a limited computer like a Raspberry Pi.
|
||||
#
|
||||
# ACLs:
|
||||
# See comments in the GLOBAL stanza
|
||||
[MASTER-1]
|
||||
MODE: MASTER
|
||||
ENABLED: True
|
||||
|
||||
# Use the user manager? If False, MASTER instance will operate as normal.
|
||||
USE_USER_MAN: False
|
||||
|
||||
REPEAT: True
|
||||
MAX_PEERS: 3
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 62033
|
||||
PASSPHRASE: passw0rd
|
||||
GROUP_HANGTIME: 5
|
||||
USE_ACL: True
|
||||
REG_ACL: DENY:1
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
# PEER INSTANCES - DUPLICATE SECTION FOR MULTIPLE PEERS
|
||||
# There are a LOT of errors in the HB Protocol specifications on this one!
|
||||
# MOST of these items are just strings and will be properly dealt with by the program
|
||||
# The TX & RX Frequencies are 9-digit numbers, and are the frequency in Hz.
|
||||
# Latitude is an 8-digit unsigned floating point number.
|
||||
# Longitude is a 9-digit signed floating point number.
|
||||
# Height is in meters
|
||||
# Setting Loose to True relaxes the validation on packets received from the master.
|
||||
# This will allow HBlink to connect to a non-compliant system such as XLXD, DMR+ etc.
|
||||
#
|
||||
# ACLs:
|
||||
# See comments in the GLOBAL stanza
|
||||
[REPEATER-1]
|
||||
MODE: PEER
|
||||
ENABLED: False
|
||||
LOOSE: False
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 54001
|
||||
MASTER_IP: 172.16.1.1
|
||||
MASTER_PORT: 54000
|
||||
PASSPHRASE: homebrew
|
||||
CALLSIGN: W1ABC
|
||||
RADIO_ID: 312000
|
||||
RX_FREQ: 449000000
|
||||
TX_FREQ: 444000000
|
||||
TX_POWER: 25
|
||||
COLORCODE: 1
|
||||
SLOTS: 1
|
||||
LATITUDE: 38.0000
|
||||
LONGITUDE: -095.0000
|
||||
HEIGHT: 75
|
||||
LOCATION: Anywhere, USA
|
||||
DESCRIPTION: This is a cool repeater
|
||||
URL: www.w1abc.org
|
||||
SOFTWARE_ID: 20170620
|
||||
PACKAGE_ID: MMDVM_HBlink
|
||||
GROUP_HANGTIME: 5
|
||||
OPTIONS:
|
||||
USE_ACL: True
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
|
||||
[XLX-1]
|
||||
MODE: XLXPEER
|
||||
ENABLED: False
|
||||
LOOSE: True
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 54002
|
||||
MASTER_IP: 172.16.1.1
|
||||
MASTER_PORT: 62030
|
||||
PASSPHRASE: passw0rd
|
||||
CALLSIGN: W1ABC
|
||||
RADIO_ID: 312000
|
||||
RX_FREQ: 449000000
|
||||
TX_FREQ: 444000000
|
||||
TX_POWER: 25
|
||||
COLORCODE: 1
|
||||
SLOTS: 1
|
||||
LATITUDE: 38.0000
|
||||
LONGITUDE: -095.0000
|
||||
HEIGHT: 75
|
||||
LOCATION: Anywhere, USA
|
||||
DESCRIPTION: This is a cool repeater
|
||||
URL: www.w1abc.org
|
||||
SOFTWARE_ID: 20170620
|
||||
PACKAGE_ID: MMDVM_HBlink
|
||||
GROUP_HANGTIME: 5
|
||||
XLXMODULE: 4004
|
||||
USE_ACL: True
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
339
data_gateway.py
339
data_gateway.py
@ -38,7 +38,7 @@ from twisted.internet import reactor, task
|
||||
|
||||
# Things we import from the main hblink module
|
||||
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases, config_reports
|
||||
from dmr_utils3.utils import bytes_3, int_id, get_alias
|
||||
from dmr_utils3.utils import bytes_3, int_id, get_alias, bytes_4
|
||||
from dmr_utils3 import decode, bptc, const
|
||||
import data_gateway_config
|
||||
import log
|
||||
@ -89,6 +89,8 @@ import threading
|
||||
import libscrc
|
||||
import random
|
||||
from bitarray.util import hex2ba as hex2bits
|
||||
import traceback
|
||||
|
||||
#################################
|
||||
|
||||
|
||||
@ -567,22 +569,326 @@ def process_sms(_rf_src, sms, call_type):
|
||||
if call_type == 'vcsbk':
|
||||
send_sms(False, 9, 0000, 0000, 'group', 'APRS Messaging must be enabled. Send command "@APRS ON" or use dashboard to enable.')
|
||||
|
||||
try:
|
||||
if sms in cmd_list:
|
||||
logger.info('Executing command/script.')
|
||||
os.popen(cmd_list[sms]).read()
|
||||
packet_assembly = ''
|
||||
except Exception as error_exception:
|
||||
logger.info('Exception. Command possibly not in list, or other error.')
|
||||
logger.info(error_exception)
|
||||
logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
packet_assembly = ''
|
||||
## try:
|
||||
## if sms in cmd_list:
|
||||
## logger.info('Executing command/script.')
|
||||
## os.popen(cmd_list[sms]).read()
|
||||
## packet_assembly = ''
|
||||
## except Exception as error_exception:
|
||||
## logger.info('Exception. Command possibly not in list, or other error.')
|
||||
## logger.info(error_exception)
|
||||
## logger.info(str(traceback.extract_tb(error_exception.__traceback__)))
|
||||
## packet_assembly = ''
|
||||
else:
|
||||
pass
|
||||
|
||||
# Module gobal varaibles
|
||||
##### SMS encode #########
|
||||
############## SMS Que and functions ###########
|
||||
def create_crc16(fragment_input):
|
||||
crc16 = libscrc.gsm16(bytearray.fromhex(fragment_input))
|
||||
return fragment_input + re.sub('x', '0', str(hex(crc16 ^ 0xcccc))[-4:])
|
||||
|
||||
def create_crc32(fragment_input):
|
||||
# Create and append CRC32 to data
|
||||
# Create list of hex
|
||||
word_list = []
|
||||
count_index = 0
|
||||
while count_index < len(fragment_input):
|
||||
word_list.append((fragment_input[count_index:count_index + 2]))
|
||||
count_index = count_index + 2
|
||||
# Create string of rearranged word_list to match ETSI 102 361-1 pg 141
|
||||
lst_index = 0
|
||||
crc_string = ''
|
||||
for i in (word_list):
|
||||
#print(lst_index)
|
||||
if lst_index % 2 == 0:
|
||||
crc_string = crc_string + word_list[lst_index + 1]
|
||||
#print(crc_string)
|
||||
if lst_index % 2 == 1:
|
||||
crc_string = crc_string + word_list[lst_index - 1]
|
||||
#print(crc_string)
|
||||
lst_index = lst_index + 1
|
||||
# Create bytearray of word_list_string
|
||||
# print(crc_string)
|
||||
word_array = libscrc.posix(bytearray.fromhex(crc_string))
|
||||
# XOR to get almost final CRC
|
||||
pre_crc = str(hex(word_array ^ 0xffffffff))[2:]
|
||||
# Rearrange pre_crc for transmission
|
||||
crc = ''
|
||||
c = 8
|
||||
while c > 0:
|
||||
crc = crc + pre_crc[c-2:c]
|
||||
c = c - 2
|
||||
#crc = crc.zfill(8)
|
||||
crc = crc.ljust(8, '0')
|
||||
# Return original data and append CRC32
|
||||
print('Output: ' + fragment_input + crc)
|
||||
return fragment_input + crc
|
||||
|
||||
def create_crc16_csbk(fragment_input):
|
||||
crc16_csbk = libscrc.gsm16(bytearray.fromhex(fragment_input))
|
||||
return fragment_input + re.sub('x', '0', str(hex(crc16_csbk ^ 0xa5a5))[-4:])
|
||||
def csbk_gen(to_id, from_id):
|
||||
csbk_lst = ['BD00801a', 'BD008019', 'BD008018', 'BD008017', 'BD008016']
|
||||
|
||||
send_seq_list = ''
|
||||
for block in csbk_lst:
|
||||
block = block + to_id + from_id
|
||||
block = create_crc16_csbk(block)
|
||||
print(block)
|
||||
send_seq_list = send_seq_list + block
|
||||
print(send_seq_list)
|
||||
return send_seq_list
|
||||
|
||||
def mmdvm_encapsulate(dst_id, src_id, peer_id, _seq, _slot, _call_type, _dtype_vseq, _stream_id, _dmr_data):
|
||||
signature = 'DMRD'
|
||||
# needs to be in bytes
|
||||
frame_type = 0x10 #bytes_2(int(10))
|
||||
#print((frame_type))
|
||||
dest_id = bytes_3(int(dst_id, 16))
|
||||
#print(ahex(dest_id))
|
||||
source_id = bytes_3(int(src_id, 16))
|
||||
via_id = bytes_4(int(peer_id, 16))
|
||||
#print(ahex(via_id))
|
||||
seq = int(_seq).to_bytes(1, 'big')
|
||||
#print(ahex(seq))
|
||||
# Binary, 0 for 1, 1 for 2
|
||||
slot = bitarray(str(_slot))
|
||||
#print(slot)
|
||||
# binary, 0 for group, 1 for unit, bin(1)
|
||||
call_type = bitarray(str(_call_type))
|
||||
#print(call_type)
|
||||
#0x00 for voice, 0x01 for voice sync, 0x10 for data
|
||||
#frame_type = int(16).to_bytes(1, 'big')
|
||||
frame_type = bitarray('10')
|
||||
#print(frame_type)
|
||||
# Observed to be always 7, int. Will be 6 for header
|
||||
#dtype_vseq = hex(int(_dtype_vseq)).encode()
|
||||
if _dtype_vseq == 6:
|
||||
dtype_vseq = bitarray('0110')
|
||||
if _dtype_vseq == 7:
|
||||
dtype_vseq = bitarray('0111')
|
||||
if _dtype_vseq == 3:
|
||||
dtype_vseq = bitarray('0011')
|
||||
# 9 digit integer in hex
|
||||
stream_id = bytes_4(_stream_id)
|
||||
#print(ahex(stream_id))
|
||||
|
||||
middle_guts = slot + call_type + frame_type + dtype_vseq
|
||||
#print(middle_guts)
|
||||
dmr_data = str(_dmr_data)[2:-1] #str(re.sub("b'|'", '', str(_dmr_data)))
|
||||
complete_packet = signature.encode() + seq + dest_id + source_id + via_id + middle_guts.tobytes() + stream_id + bytes.fromhex((dmr_data)) + bitarray('0000000000101111').tobytes()#bytes.fromhex(dmr_data)
|
||||
#print('Complete: ' + str(ahex(complete_packet)))
|
||||
return complete_packet
|
||||
|
||||
|
||||
# Break long string into block sequence
|
||||
def block_sequence(input_string):
|
||||
seq_blocks = len(input_string)/24
|
||||
n = 0
|
||||
block_seq = []
|
||||
while n < seq_blocks:
|
||||
if n == 0:
|
||||
block_seq.append(bytes.fromhex(input_string[:24].ljust(24,'0')))
|
||||
n = n + 1
|
||||
else:
|
||||
block_seq.append(bytes.fromhex(input_string[n*24:n*24+24].ljust(24,'0')))
|
||||
n = n + 1
|
||||
return block_seq
|
||||
|
||||
# Takes list of DMR packets, 12 bytes, then encodes them
|
||||
def dmr_encode(packet_list, _slot):
|
||||
send_seq = []
|
||||
for i in packet_list:
|
||||
stitched_pkt = bptc.interleave_19696(bptc.encode_19696(i))
|
||||
l_slot = bitarray('0111011100')
|
||||
r_slot = bitarray('1101110001')
|
||||
#Mobile Station
|
||||
#sync_data = bitarray('110101011101011111110111011111111101011101010111')
|
||||
if _slot == 0:
|
||||
# TS1 - F7FDD5DDFD55
|
||||
sync_data = bitarray('111101111111110111010101110111011111110101010101')
|
||||
if _slot == 1:
|
||||
#TS2 - D7557F5FF7F5
|
||||
sync_data = bitarray('110101110101010101111111010111111111011111110101')
|
||||
|
||||
# Data sync? 110101011101011111110111011111111101011101010111 - D5D7F77FD757
|
||||
new_pkt = ahex(stitched_pkt[:98] + l_slot + sync_data + r_slot + stitched_pkt[98:])
|
||||
send_seq.append(new_pkt)
|
||||
return send_seq
|
||||
|
||||
|
||||
def create_sms_seq(dst_id, src_id, peer_id, _slot, _call_type, dmr_string):
|
||||
rand_seq = random.randint(1, 999999)
|
||||
block_seq = block_sequence(dmr_string)
|
||||
dmr_list = dmr_encode(block_seq, _slot)
|
||||
cap_in = 0
|
||||
mmdvm_send_seq = []
|
||||
for i in dmr_list:
|
||||
if use_csbk == True:
|
||||
if cap_in < 5:
|
||||
the_mmdvm_pkt = mmdvm_encapsulate(dst_id, src_id, peer_id, cap_in, _slot, _call_type, 3, rand_seq, i)
|
||||
#print(block_seq[cap_in])
|
||||
#print(3)
|
||||
if cap_in == 5:
|
||||
#print(block_seq[cap_in])
|
||||
#print(6)
|
||||
the_mmdvm_pkt = mmdvm_encapsulate(dst_id, src_id, peer_id, cap_in, _slot, _call_type, 6, rand_seq, i) #(bytes.fromhex(re.sub("b'|'", '', str(orig_cap[cap_in][20:-4])))))
|
||||
if cap_in > 5:
|
||||
#print(block_seq[cap_in])
|
||||
#print(7)
|
||||
the_mmdvm_pkt = mmdvm_encapsulate(dst_id, src_id, peer_id, cap_in, _slot, _call_type, 7, rand_seq, i)#(bytes.fromhex(re.sub("b'|'", '', str(orig_cap[cap_in][20:-4])))))
|
||||
mmdvm_send_seq.append(ahex(the_mmdvm_pkt))
|
||||
cap_in = cap_in + 1
|
||||
if use_csbk == False:
|
||||
if cap_in == 0:
|
||||
the_mmdvm_pkt = mmdvm_encapsulate(dst_id, src_id, peer_id, cap_in, _slot, _call_type, 6, rand_seq, i) #(bytes.fromhex(re.sub("b'|'", '', str(orig_cap[cap_in][20:-4])))))
|
||||
else:
|
||||
the_mmdvm_pkt = mmdvm_encapsulate(dst_id, src_id, peer_id, cap_in, _slot, _call_type, 7, rand_seq, i)#(bytes.fromhex(re.sub("b'|'", '', str(orig_cap[cap_in][20:-4])))))
|
||||
mmdvm_send_seq.append(ahex(the_mmdvm_pkt))
|
||||
cap_in = cap_in + 1
|
||||
print(ahex(the_mmdvm_pkt))
|
||||
systems['OBP-2'].send_system(the_mmdvm_pkt)
|
||||
|
||||
with open('/tmp/.hblink_data_que_' + str(CONFIG['DATA_CONFIG']['APRS_LOGIN_CALL']).upper() + '/' + str(random.randint(1000, 9999)) + '.mmdvm_seq', "w") as packet_write_file:
|
||||
packet_write_file.write(str(mmdvm_send_seq))
|
||||
|
||||
return mmdvm_send_seq
|
||||
|
||||
# Built for max length msg, will improve later
|
||||
def sms_headers(to_id, from_id):
|
||||
## #ETSI 102 361-2 uncompressed ipv4
|
||||
## # UDP header, src and dest ports are 4007, 0fa7
|
||||
## udp_ports = '0fa70fa7'
|
||||
## # Length, of what?
|
||||
## udp_length = '00da'
|
||||
## # Checksum
|
||||
## udp_checksum = '4b37'
|
||||
##
|
||||
## # IPV4
|
||||
## #IPV4 version and header length, always 45
|
||||
## ipv4_v_l = '45'
|
||||
## #Type of service, always 00
|
||||
## ipv4_svc = '00'
|
||||
## #length, always 00ee
|
||||
## ipv4_len = '00ee'
|
||||
## #ID always 000d
|
||||
## ipv4_id = '000d'
|
||||
## #Flags and offset always0
|
||||
## ipv4_flag_off = '0000'
|
||||
## #TTL and Protocol always 4011, no matter what
|
||||
## ipv4_ttl_proto = '4011'
|
||||
#ipv4 = '450000ee000d0000401100000c' + from_id + '0c' + to_id
|
||||
ipv4 = '450000ee00000000401100000c' + from_id + '0c' + to_id
|
||||
count_index = 0
|
||||
hdr_lst = []
|
||||
while count_index < len(ipv4):
|
||||
hdr_lst.append((ipv4[count_index:count_index + 4]))
|
||||
count_index = count_index + 4
|
||||
sum = 0
|
||||
for i in hdr_lst:
|
||||
sum = sum + int(i, 16)
|
||||
flipped = ''
|
||||
for i in str(bin(sum))[2:]:
|
||||
if i == '1':
|
||||
flipped = flipped + '0'
|
||||
if i == '0':
|
||||
flipped = flipped + '1'
|
||||
ipv4_chk_sum = str(hex(int(flipped, 2)))[2:]
|
||||
# UDP checksum is optional per ETSI, zero for now as Anytone is not affected.
|
||||
header = ipv4[:20] + ipv4_chk_sum + ipv4[24:] + '0fa70fa700da000000d0a00081040d000a'
|
||||
return header
|
||||
|
||||
def format_sms(msg, to_id, from_id):
|
||||
msg_bytes = str.encode(msg)
|
||||
encoded = "".join([str('00' + x) for x in re.findall('..',bytes.hex(msg_bytes))] )
|
||||
final = encoded
|
||||
while len(final) < 400:
|
||||
final = final + '002e'
|
||||
final = final + '0000000000000000000000'
|
||||
headers = sms_headers(to_id, from_id)
|
||||
return headers + final
|
||||
|
||||
def gen_header(to_id, from_id, call_type):
|
||||
if call_type == 1:
|
||||
seq_header = '024A' + to_id + from_id + '9550'
|
||||
if call_type == 0:
|
||||
seq_header = '824A' + to_id + from_id + '9550'
|
||||
return seq_header
|
||||
|
||||
def send_sms(csbk, to_id, from_id, peer_id, call_type, msg):
|
||||
global use_csbk
|
||||
use_csbk = csbk
|
||||
to_id = str(hex(to_id))[2:].zfill(6)
|
||||
from_id = str(hex(from_id))[2:].zfill(6)
|
||||
peer_id = str(hex(peer_id))[2:].zfill(8)
|
||||
if call_type == 'unit':
|
||||
call_type = 1
|
||||
# Try to find slot from UNIT_MAP
|
||||
try:
|
||||
#Slot 2
|
||||
if UNIT_MAP[bytes.fromhex(to_id)][2] == 2:
|
||||
slot = 1
|
||||
# Slot 1
|
||||
if UNIT_MAP[bytes.fromhex(to_id)][2] == 1:
|
||||
slot = 0
|
||||
except Exception as e:
|
||||
logger.info(e)
|
||||
# Change to config value later
|
||||
slot = 1
|
||||
if call_type == 'group':
|
||||
call_type = 0
|
||||
# Send all Group data to TS 2, need to fix later.
|
||||
slot = 1
|
||||
if csbk == 'yes':
|
||||
use_csbk = True
|
||||
create_sms_seq(to_id, from_id, peer_id, int(slot), new_call_type, csbk_gen(to_id, from_id) + create_crc16(gen_header(to_id, from_id, new_call_type)) + create_crc32(format_sms(msg, to_id, from_id)))
|
||||
else:
|
||||
create_sms_seq(to_id, from_id, peer_id, int(slot), call_type, create_crc16(gen_header(to_id, from_id, call_type)) + create_crc32(format_sms(str(msg), to_id, from_id)))
|
||||
|
||||
def data_que_check():
|
||||
l=task.LoopingCall(data_que_send)
|
||||
l.start(1)
|
||||
def data_que_send():
|
||||
#logger.info('Check SMS que')
|
||||
try:
|
||||
#logger.info(UNIT_MAP)
|
||||
for packet_file in os.listdir('/tmp/.hblink_data_que_' + str(CONFIG['DATA_CONFIG']['APRS_LOGIN_CALL']).upper() + '/'):
|
||||
logger.info('Sending SMS')
|
||||
logger.info(os.listdir('/tmp/.hblink_data_que_' + str(CONFIG['DATA_CONFIG']['APRS_LOGIN_CALL']).upper() + '/'))
|
||||
snd_seq = ast.literal_eval(os.popen('cat /tmp/.hblink_data_que_' + str(CONFIG['DATA_CONFIG']['APRS_LOGIN_CALL']).upper() + '/' + packet_file).read())
|
||||
for data in snd_seq:
|
||||
# Get dest id
|
||||
dst_id = bytes.fromhex(str(data[10:16])[2:-1])
|
||||
call_type = hex2bits(data)[121:122]
|
||||
# Handle UNIT calls
|
||||
if call_type[0] == True:
|
||||
# If destination ID in map, route call only there
|
||||
if dst_id in UNIT_MAP:
|
||||
data_target = UNIT_MAP[dst_id][0]
|
||||
reactor.callFromThread(systems[data_target].send_system,bytes.fromhex(re.sub("b'|'", '', str(data))))
|
||||
logger.info('Sending data to ' + str(data[10:16])[2:-1] + ' on system ' + data_target)
|
||||
# Flood all systems
|
||||
elif dst_id not in UNIT_MAP:
|
||||
for i in UNIT:
|
||||
reactor.callFromThread(systems[i].send_system,bytes.fromhex(re.sub("b'|'", '', str(data))))
|
||||
logger.info('Sending data to ' + str(data[10:16])[2:-1] + ' on system ' + i)
|
||||
# Handle group calls
|
||||
elif call_type[0] == False:
|
||||
for i in BRIDGES.items():
|
||||
for d in i[1]:
|
||||
if dst_id == d['TGID']:
|
||||
data_target = d['SYSTEM']
|
||||
reactor.callFromThread(systems[data_target].send_system,bytes.fromhex(re.sub("b'|'", '', str(data))))
|
||||
logger.info('Sending data to ' + str(data[10:16])[2:-1] + ' on system ' + data_target)
|
||||
|
||||
os.system('rm /tmp/.hblink_data_que_' + str(CONFIG['DATA_CONFIG']['APRS_LOGIN_CALL']).upper() + '/' + packet_file)
|
||||
|
||||
#routerHBP.send_peer('MASTER-2', bytes.fromhex(re.sub("b'|'", '', str(data))))
|
||||
## os.system('rm /tmp/.hblink_data_que/' + packet_file)
|
||||
except Exception as e:
|
||||
logger.info(e)
|
||||
|
||||
##class DATA():
|
||||
##### DMR data function ####
|
||||
def data_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
|
||||
# Capture data headers
|
||||
@ -951,7 +1257,12 @@ if __name__ == '__main__':
|
||||
with open(sms_file, 'w') as user_sms_file:
|
||||
user_sms_file.write("[]")
|
||||
user_sms_file.close()
|
||||
|
||||
try:
|
||||
Path('/tmp/.hblink_data_que_' + str(CONFIG['DATA_CONFIG']['APRS_LOGIN_CALL']).upper() + '/').mkdir(parents=True, exist_ok=True)
|
||||
logger.info('Created que directory')
|
||||
except:
|
||||
logger.info('Unable to create data que directory')
|
||||
pass
|
||||
|
||||
# Start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
|
409
data_gateway_config.py
Normal file
409
data_gateway_config.py
Normal file
@ -0,0 +1,409 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
#
|
||||
# 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
|
||||
###############################################################################
|
||||
|
||||
'''
|
||||
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.
|
||||
'''
|
||||
|
||||
import configparser
|
||||
import sys
|
||||
import const
|
||||
|
||||
from socket import gethostbyname
|
||||
|
||||
# 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'
|
||||
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)))
|
||||
|
||||
acl = [] #set()
|
||||
sections = _acl.split(':')
|
||||
|
||||
if sections[0] == 'PERMIT':
|
||||
action = True
|
||||
else:
|
||||
action = False
|
||||
|
||||
for entry in sections[1].split(','):
|
||||
if entry == 'ALL':
|
||||
acl.append((const.ID_MIN, _max))
|
||||
break
|
||||
|
||||
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.append((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.append((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)
|
||||
|
||||
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'] = {}
|
||||
CONFIG['LOGGER'] = {}
|
||||
CONFIG['ALIASES'] = {}
|
||||
CONFIG['WEB_SERVICE'] = {}
|
||||
CONFIG['DATA_CONFIG'] = {}
|
||||
CONFIG['SYSTEMS'] = {}
|
||||
|
||||
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')
|
||||
})
|
||||
|
||||
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(',')
|
||||
})
|
||||
|
||||
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')
|
||||
})
|
||||
if not CONFIG['LOGGER']['LOG_FILE']:
|
||||
CONFIG['LOGGER']['LOG_FILE'] = '/dev/null'
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
elif section == 'WEB_SERVICE':
|
||||
CONFIG['WEB_SERVICE'].update({
|
||||
'THIS_SERVER_NAME': config.get(section, 'THIS_SERVER_NAME'),
|
||||
'URL': config.get(section, 'URL'),
|
||||
'REMOTE_CONFIG_ENABLED': config.getboolean(section, 'REMOTE_CONFIG_ENABLED'),
|
||||
'APPEND_INT': config.getint(section, 'APPEND_INT'),
|
||||
'EXTRA_INT_1': config.getint(section, 'EXTRA_INT_1'),
|
||||
'EXTRA_INT_2': config.getint(section, 'EXTRA_INT_2'),
|
||||
'EXTRA_1': config.get(section, 'EXTRA_1'),
|
||||
'EXTRA_2': config.get(section, 'EXTRA_2'),
|
||||
'SHARED_SECRET': config.get(section, 'SHARED_SECRET'),
|
||||
'SHORTEN_PASSPHRASE': config.getboolean(section, 'SHORTEN_PASSPHRASE'),
|
||||
'SHORTEN_SAMPLE': config.get(section, 'SHORTEN_SAMPLE'),
|
||||
'SHORTEN_LENGTH': config.get(section, 'SHORTEN_LENGTH'),
|
||||
'BURN_FILE': config.get(section, 'BURN_FILE'),
|
||||
'BURN_INT': config.getint(section, 'BURN_INT'),
|
||||
|
||||
|
||||
})
|
||||
|
||||
elif section == 'DATA_CONFIG':
|
||||
CONFIG['DATA_CONFIG'].update({
|
||||
'DATA_DMR_ID': config.get(section, 'DATA_DMR_ID'),
|
||||
'USER_APRS_SSID': config.get(section, 'USER_APRS_SSID'),
|
||||
'CALL_TYPE': config.get(section, 'CALL_TYPE'),
|
||||
## 'UNIT_SMS_TS': config.get(section, 'UNIT_SMS_TS'),
|
||||
'USER_APRS_COMMENT': config.get(section, 'USER_APRS_COMMENT'),
|
||||
'APRS_LOGIN_CALL': config.get(section, 'APRS_LOGIN_CALL'),
|
||||
'APRS_LOGIN_PASSCODE': config.get(section, 'APRS_LOGIN_PASSCODE'),
|
||||
'APRS_SERVER': config.get(section, 'APRS_SERVER'),
|
||||
'APRS_PORT': config.get(section, 'APRS_PORT'),
|
||||
'APRS_FILTER': config.get(section, 'APRS_FILTER'),
|
||||
'IGATE_BEACON_TIME': config.get(section, 'IGATE_BEACON_TIME'),
|
||||
'IGATE_BEACON_ICON': config.get(section, 'IGATE_BEACON_ICON'),
|
||||
'IGATE_BEACON_COMMENT': config.get(section, 'IGATE_BEACON_COMMENT'),
|
||||
'IGATE_LATITUDE': config.get(section, 'IGATE_LATITUDE'),
|
||||
'IGATE_LONGITUDE': config.get(section, 'IGATE_LONGITUDE'),
|
||||
'APRS_STATIC_REPORT_INTERVAL': config.get(section, 'APRS_STATIC_REPORT_INTERVAL'),
|
||||
'APRS_STATIC_MESSAGE': config.get(section, 'APRS_STATIC_MESSAGE'),
|
||||
## 'EMAIL_SENDER': config.get(section, 'EMAIL_SENDER'),
|
||||
## 'EMAIL_PASSWORD': config.get(section, 'EMAIL_PASSWORD'),
|
||||
## 'SMTP_SERVER': config.get(section, 'SMTP_SERVER'),
|
||||
## 'SMTP_PORT': config.get(section, 'SMTP_PORT'),
|
||||
'LOCATION_FILE': config.get(section, 'LOCATION_FILE'),
|
||||
'BULLETIN_BOARD_FILE': config.get(section, 'BULLETIN_BOARD_FILE'),
|
||||
'MAILBOX_FILE': config.get(section, 'MAILBOX_FILE'),
|
||||
'SMS_FILE': config.get(section, 'SMS_FILE'),
|
||||
'EMERGENCY_SOS_FILE': config.get(section, 'EMERGENCY_SOS_FILE'),
|
||||
'USER_SETTINGS_FILE': config.get(section, 'USER_SETTINGS_FILE'),
|
||||
## 'USE_API': config.getboolean(section, 'USE_API'),
|
||||
## 'AUTHORIZED_TOKENS_FILE': config.get(section, 'AUTHORIZED_TOKENS_FILE'),
|
||||
## 'USE_PUBLIC_APPS': config.getboolean(section, 'USE_PUBLIC_APPS'),
|
||||
## 'PUBLIC_APPS_LIST': config.get(section, 'PUBLIC_APPS_LIST'),
|
||||
## 'MY_SERVER_SHORTCUT': config.get(section, 'MY_SERVER_SHORTCUT'),
|
||||
## 'DASHBOARD_URL': config.get(section, 'DASHBOARD_URL'),
|
||||
## 'SERVER_NAME': config.get(section, 'SERVER_NAME'),
|
||||
## 'RULES_PATH': config.get(section, 'RULES_PATH'),
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
elif config.getboolean(section, 'ENABLED'):
|
||||
if config.get(section, 'MODE') == 'PEER':
|
||||
CONFIG['SYSTEMS'].update({section: {
|
||||
'MODE': config.get(section, 'MODE'),
|
||||
'ENABLED': config.getboolean(section, 'ENABLED'),
|
||||
'LOOSE': config.getboolean(section, 'LOOSE'),
|
||||
'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')),
|
||||
'IP': gethostbyname(config.get(section, 'IP')),
|
||||
'PORT': config.getint(section, 'PORT'),
|
||||
'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')),
|
||||
'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')),
|
||||
'MASTER_PORT': config.getint(section, 'MASTER_PORT'),
|
||||
'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'),
|
||||
'CALLSIGN': bytes(config.get(section, 'CALLSIGN').ljust(8)[:8], 'utf-8'),
|
||||
'RADIO_ID': config.getint(section, 'RADIO_ID').to_bytes(4, 'big'),
|
||||
'RX_FREQ': bytes(config.get(section, 'RX_FREQ').ljust(9)[:9], 'utf-8'),
|
||||
'TX_FREQ': bytes(config.get(section, 'TX_FREQ').ljust(9)[:9], 'utf-8'),
|
||||
'TX_POWER': bytes(config.get(section, 'TX_POWER').rjust(2,'0'), 'utf-8'),
|
||||
'COLORCODE': bytes(config.get(section, 'COLORCODE').rjust(2,'0'), 'utf-8'),
|
||||
'LATITUDE': bytes(config.get(section, 'LATITUDE').ljust(8)[:8], 'utf-8'),
|
||||
'LONGITUDE': bytes(config.get(section, 'LONGITUDE').ljust(9)[:9], 'utf-8'),
|
||||
'HEIGHT': bytes(config.get(section, 'HEIGHT').rjust(3,'0'), 'utf-8'),
|
||||
'LOCATION': bytes(config.get(section, 'LOCATION').ljust(20)[:20], 'utf-8'),
|
||||
'DESCRIPTION': bytes(config.get(section, 'DESCRIPTION').ljust(19)[:19], 'utf-8'),
|
||||
'SLOTS': bytes(config.get(section, 'SLOTS'), 'utf-8'),
|
||||
'URL': bytes(config.get(section, 'URL').ljust(124)[:124], 'utf-8'),
|
||||
'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'),
|
||||
'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'),
|
||||
'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'),
|
||||
'OPTIONS': b''.join([b'Type=HBlink;', bytes(config.get(section, 'OPTIONS'), 'utf-8')]),
|
||||
'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')
|
||||
}})
|
||||
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,
|
||||
}})
|
||||
|
||||
if config.get(section, 'MODE') == 'XLXPEER':
|
||||
CONFIG['SYSTEMS'].update({section: {
|
||||
'MODE': config.get(section, 'MODE'),
|
||||
'ENABLED': config.getboolean(section, 'ENABLED'),
|
||||
'LOOSE': config.getboolean(section, 'LOOSE'),
|
||||
'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')),
|
||||
'IP': gethostbyname(config.get(section, 'IP')),
|
||||
'PORT': config.getint(section, 'PORT'),
|
||||
'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')),
|
||||
'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')),
|
||||
'MASTER_PORT': config.getint(section, 'MASTER_PORT'),
|
||||
'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'),
|
||||
'CALLSIGN': bytes(config.get(section, 'CALLSIGN').ljust(8)[:8], 'utf-8'),
|
||||
'RADIO_ID': config.getint(section, 'RADIO_ID').to_bytes(4, 'big'),
|
||||
'RX_FREQ': bytes(config.get(section, 'RX_FREQ').ljust(9)[:9], 'utf-8'),
|
||||
'TX_FREQ': bytes(config.get(section, 'TX_FREQ').ljust(9)[:9], 'utf-8'),
|
||||
'TX_POWER': bytes(config.get(section, 'TX_POWER').rjust(2,'0'), 'utf-8'),
|
||||
'COLORCODE': bytes(config.get(section, 'COLORCODE').rjust(2,'0'), 'utf-8'),
|
||||
'LATITUDE': bytes(config.get(section, 'LATITUDE').ljust(8)[:8], 'utf-8'),
|
||||
'LONGITUDE': bytes(config.get(section, 'LONGITUDE').ljust(9)[:9], 'utf-8'),
|
||||
'HEIGHT': bytes(config.get(section, 'HEIGHT').rjust(3,'0'), 'utf-8'),
|
||||
'LOCATION': bytes(config.get(section, 'LOCATION').ljust(20)[:20], 'utf-8'),
|
||||
'DESCRIPTION': bytes(config.get(section, 'DESCRIPTION').ljust(19)[:19], 'utf-8'),
|
||||
'SLOTS': bytes(config.get(section, 'SLOTS'), 'utf-8'),
|
||||
'URL': bytes(config.get(section, 'URL').ljust(124)[:124], 'utf-8'),
|
||||
'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'),
|
||||
'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'),
|
||||
'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'),
|
||||
'XLXMODULE': config.getint(section, 'XLXMODULE'),
|
||||
'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')
|
||||
}})
|
||||
CONFIG['SYSTEMS'][section].update({'XLXSTATS': {
|
||||
'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,
|
||||
}})
|
||||
|
||||
elif config.get(section, 'MODE') == 'MASTER':
|
||||
CONFIG['SYSTEMS'].update({section: {
|
||||
'MODE': config.get(section, 'MODE'),
|
||||
'ENABLED': config.getboolean(section, 'ENABLED'),
|
||||
'USE_USER_MAN': config.getboolean(section, 'USE_USER_MAN'),
|
||||
'REPEAT': config.getboolean(section, 'REPEAT'),
|
||||
'MAX_PEERS': config.getint(section, 'MAX_PEERS'),
|
||||
'IP': gethostbyname(config.get(section, 'IP')),
|
||||
'PORT': config.getint(section, 'PORT'),
|
||||
'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'),
|
||||
'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')
|
||||
}})
|
||||
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': config.getint(section, 'NETWORK_ID').to_bytes(4, 'big'),
|
||||
'IP': gethostbyname(config.get(section, 'IP')),
|
||||
'PORT': config.getint(section, 'PORT'),
|
||||
'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE').ljust(20,'\x00')[:20], 'utf-8'),
|
||||
'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'),
|
||||
'BOTH_SLOTS': config.getboolean(section, 'BOTH_SLOTS'),
|
||||
'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',
|
||||
'USE_ENCRYPTION': config.getboolean(section, 'USE_ENCRYPTION'),
|
||||
'ENCRYPTION_KEY': bytes(config.get(section, 'ENCRYPTION_KEY'), 'utf-8'),
|
||||
}})
|
||||
elif config.get(section, 'MODE') == 'PROXY':
|
||||
CONFIG['SYSTEMS'].update({section: {
|
||||
'MODE': config.get(section, 'MODE'),
|
||||
'ENABLED': config.getboolean(section, 'ENABLED'),
|
||||
'EXTERNAL_PROXY_SCRIPT': config.getboolean(section, 'EXTERNAL_PROXY_SCRIPT'),
|
||||
'STATIC_APRS_POSITION_ENABLED': config.getboolean(section, 'STATIC_APRS_POSITION_ENABLED'),
|
||||
'REPEAT': config.getboolean(section, 'REPEAT'),
|
||||
'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'),
|
||||
'EXTERNAL_PORT': config.getint(section, 'EXTERNAL_PORT'),
|
||||
'INTERNAL_PORT_START': config.getint(section, 'INTERNAL_PORT_START'),
|
||||
'INTERNAL_PORT_STOP': config.getint(section, 'INTERNAL_PORT_STOP'),
|
||||
'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, 'TG1_ACL'),
|
||||
'TG2_ACL': config.get(section, 'TG2_ACL'),
|
||||
}})
|
||||
CONFIG['SYSTEMS'][section].update({'PEERS': {}})
|
||||
|
||||
except configparser.Error as err:
|
||||
sys.exit('Error processing configuration file -- {}'.format(err))
|
||||
|
||||
process_acls(CONFIG)
|
||||
|
||||
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_utils3.utils import int_id
|
||||
|
||||
# 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)
|
||||
|
||||
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]
|
||||
|
||||
print(acl_check(b'\x00\x01\x37', CONFIG['GLOBAL']['TG1_ACL']))
|
@ -196,9 +196,12 @@ class OPENBRIDGE(DatagramProtocol):
|
||||
_data = _packet[:53]
|
||||
_hash = _packet[53:]
|
||||
_ckhs = hmac_new(self._config['PASSPHRASE'],_data,sha1).digest()
|
||||
print(ahex(_ckhs))
|
||||
print(ahex(_hash))
|
||||
|
||||
print(compare_digest(_hash, _ckhs))
|
||||
|
||||
if compare_digest(_hash, _ckhs) and _sockaddr == self._config['TARGET_SOCK']:
|
||||
print('good data')
|
||||
_peer_id = _data[11:15]
|
||||
_seq = _data[4]
|
||||
_rf_src = _data[5:8]
|
||||
|
Loading…
x
Reference in New Issue
Block a user