mirror of https://github.com/kf7eel/hbnet.git
update hotspot_proxy, update map template
This commit is contained in:
parent
5dbca3aca7
commit
a5d9fb5d5c
84
bridge.py
84
bridge.py
|
|
@ -72,6 +72,9 @@ from datetime import datetime
|
||||||
import re
|
import re
|
||||||
from socket import gethostbyname
|
from socket import gethostbyname
|
||||||
|
|
||||||
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||||
|
|
@ -285,26 +288,94 @@ def download_config(L_CONFIG_FILE, cli_file):
|
||||||
|
|
||||||
# From hotspot_proxy2, FreeDMR
|
# From hotspot_proxy2, FreeDMR
|
||||||
def hotspot_proxy(listen_port, port_start, port_stop):
|
def hotspot_proxy(listen_port, port_start, port_stop):
|
||||||
|
## Master = "127.0.0.1"
|
||||||
|
## ListenPort = listen_port
|
||||||
|
## DestportStart = port_start
|
||||||
|
## DestPortEnd = port_stop
|
||||||
|
## Timeout = 30
|
||||||
|
## Stats = True
|
||||||
|
## Debug = False
|
||||||
|
## BlackList = [1234567]
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## CONNTRACK = {}
|
||||||
|
##
|
||||||
|
## for port in range(DestportStart,DestPortEnd+1,1):
|
||||||
|
## CONNTRACK[port] = False
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## reactor.listenUDP(ListenPort,Proxy(Master,ListenPort,CONNTRACK,BlackList,Timeout,Debug,DestportStart,DestPortEnd))
|
||||||
|
##
|
||||||
|
## def loopingErrHandle(failure):
|
||||||
|
## logger.error('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}'.format(failure))
|
||||||
|
## reactor.stop()
|
||||||
|
##
|
||||||
|
## def stats():
|
||||||
|
## count = 0
|
||||||
|
## nowtime = time()
|
||||||
|
## for port in CONNTRACK:
|
||||||
|
## if CONNTRACK[port]:
|
||||||
|
## count = count+1
|
||||||
|
##
|
||||||
|
## totalPorts = DestPortEnd - DestportStart
|
||||||
|
## freePorts = totalPorts - count
|
||||||
|
##
|
||||||
|
## logger.info("{} ports out of {} in use ({} free)".format(count,totalPorts,freePorts))
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## if Stats == True:
|
||||||
|
## stats_task = task.LoopingCall(stats)
|
||||||
|
## statsa = stats_task.start(30)
|
||||||
|
## statsa.addErrback(loopingErrHandle)
|
||||||
|
|
||||||
Master = "127.0.0.1"
|
Master = "127.0.0.1"
|
||||||
ListenPort = listen_port
|
ListenPort = listen_port
|
||||||
|
# '' = all IPv4, '::' = all IPv4 and IPv6 (Dual Stack)
|
||||||
|
ListenIP = ''
|
||||||
DestportStart = port_start
|
DestportStart = port_start
|
||||||
DestPortEnd = port_stop
|
DestPortEnd = port_stop
|
||||||
Timeout = 30
|
Timeout = 30
|
||||||
Stats = True
|
Stats = False
|
||||||
Debug = False
|
Debug = False
|
||||||
|
ClientInfo = True
|
||||||
BlackList = [1234567]
|
BlackList = [1234567]
|
||||||
|
|
||||||
|
#*******************
|
||||||
|
|
||||||
|
|
||||||
|
#Set process title early
|
||||||
|
setproctitle(__file__)
|
||||||
|
|
||||||
|
#If IPv6 is enabled by enivornment variable...
|
||||||
|
if ListenIP == '' and 'FDPROXY_IPV6' in os.environ and bool(os.environ['FDPROXY_IPV6']):
|
||||||
|
ListenIP = '::'
|
||||||
|
|
||||||
|
#Override static config from Environment
|
||||||
|
if 'FDPROXY_STATS' in os.environ:
|
||||||
|
Stats = bool(os.environ['FDPROXY_STATS'])
|
||||||
|
if 'FDPROXY_DEBUG' in os.environ:
|
||||||
|
Debug = bool(os.environ['FDPROXY_DEBUG'])
|
||||||
|
if 'FDPROXY_CLIENTINFO' in os.environ:
|
||||||
|
ClientInfo = bool(os.environ['FDPROXY_CLIENTINFO'])
|
||||||
|
if 'FDPROXY_LISTENPORT' in os.environ:
|
||||||
|
ListenPort = os.environ['FDPROXY_LISTENPORT']
|
||||||
|
|
||||||
|
|
||||||
CONNTRACK = {}
|
CONNTRACK = {}
|
||||||
|
|
||||||
for port in range(DestportStart,DestPortEnd+1,1):
|
for port in range(DestportStart,DestPortEnd+1,1):
|
||||||
CONNTRACK[port] = False
|
CONNTRACK[port] = False
|
||||||
|
|
||||||
|
#If we are listening IPv6 and Master is an IPv4 IPv4Address
|
||||||
|
#IPv6ify the address.
|
||||||
|
if ListenIP == '::' and IsIPv4Address(Master):
|
||||||
|
Master = '::ffff:' + Master
|
||||||
|
|
||||||
reactor.listenUDP(ListenPort,Proxy(Master,ListenPort,CONNTRACK,BlackList,Timeout,Debug,DestportStart,DestPortEnd))
|
reactor.listenUDP(ListenPort,Proxy(Master,ListenPort,CONNTRACK,BlackList,Timeout,Debug,ClientInfo,DestportStart,DestPortEnd),interface=ListenIP)
|
||||||
|
|
||||||
def loopingErrHandle(failure):
|
def loopingErrHandle(failure):
|
||||||
logger.error('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}'.format(failure))
|
print('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}'.format(failure))
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
|
|
||||||
def stats():
|
def stats():
|
||||||
|
|
@ -317,7 +388,7 @@ def hotspot_proxy(listen_port, port_start, port_stop):
|
||||||
totalPorts = DestPortEnd - DestportStart
|
totalPorts = DestPortEnd - DestportStart
|
||||||
freePorts = totalPorts - count
|
freePorts = totalPorts - count
|
||||||
|
|
||||||
logger.info("{} ports out of {} in use ({} free)".format(count,totalPorts,freePorts))
|
print("{} ports out of {} in use ({} free)".format(count,totalPorts,freePorts))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -326,6 +397,7 @@ def hotspot_proxy(listen_port, port_start, port_stop):
|
||||||
statsa = stats_task.start(30)
|
statsa = stats_task.start(30)
|
||||||
statsa.addErrback(loopingErrHandle)
|
statsa.addErrback(loopingErrHandle)
|
||||||
|
|
||||||
|
|
||||||
# Used to track if we have downloaded user custon rules
|
# Used to track if we have downloaded user custon rules
|
||||||
user_rules = {}
|
user_rules = {}
|
||||||
|
|
||||||
|
|
@ -745,7 +817,9 @@ class routerOBP(OPENBRIDGE):
|
||||||
pkt_time = time()
|
pkt_time = time()
|
||||||
dmrpkt = _data[20:53]
|
dmrpkt = _data[20:53]
|
||||||
_bits = _data[15]
|
_bits = _data[15]
|
||||||
print(int_id(_dst_id))
|
|
||||||
|
print('Src: ' + str(int_id(_rf_src)))
|
||||||
|
print('Dst: ' + str(int_id(_dst_id)))
|
||||||
|
|
||||||
# Make/update this unit in the UNIT_MAP cache
|
# Make/update this unit in the UNIT_MAP cache
|
||||||
UNIT_MAP[_rf_src] = (self.name, pkt_time)
|
UNIT_MAP[_rf_src] = (self.name, pkt_time)
|
||||||
|
|
|
||||||
|
|
@ -1058,7 +1058,7 @@ def mmdvm_encapsulate(dst_id, src_id, peer_id, _seq, _slot, _call_type, _dtype_v
|
||||||
middle_guts = slot + call_type + frame_type + dtype_vseq
|
middle_guts = slot + call_type + frame_type + dtype_vseq
|
||||||
#print(middle_guts)
|
#print(middle_guts)
|
||||||
dmr_data = str(_dmr_data)[2:-1] #str(re.sub("b'|'", '', str(_dmr_data)))
|
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)
|
complete_packet = signature.encode() + seq + source_id + dest_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)))
|
#print('Complete: ' + str(ahex(complete_packet)))
|
||||||
return complete_packet
|
return complete_packet
|
||||||
|
|
||||||
|
|
@ -1085,15 +1085,15 @@ def dmr_encode(packet_list, _slot):
|
||||||
l_slot = bitarray('0111011100')
|
l_slot = bitarray('0111011100')
|
||||||
r_slot = bitarray('1101110001')
|
r_slot = bitarray('1101110001')
|
||||||
#Mobile Station
|
#Mobile Station
|
||||||
# D5D7F77FD757
|
# D5D7F77FD757
|
||||||
#sync_data = bitarray('110101011101011111110111011111111101011101010111')
|
sync_data = bitarray('110101011101011111110111011111111101011101010111')
|
||||||
if _slot == 0:
|
## if _slot == 0:
|
||||||
# TS1 - F7FDD5DDFD55
|
## # TS1 - F7FDD5DDFD55
|
||||||
sync_data = bitarray('111101111111110111010101110111011111110101010101')
|
## sync_data = bitarray('111101111111110111010101110111011111110101010101')
|
||||||
if _slot == 1:
|
## if _slot == 1:
|
||||||
#TS2 - D7557F5FF7F5
|
## #TS2 - D7557F5FF7F5
|
||||||
sync_data = bitarray('110101110101010101111111010111111111011111110101')
|
## sync_data = bitarray('110101110101010101111111010111111111011111110101')
|
||||||
|
##
|
||||||
# Data sync? 110101011101011111110111011111111101011101010111 - D5D7F77FD757
|
# Data sync? 110101011101011111110111011111111101011101010111 - D5D7F77FD757
|
||||||
new_pkt = ahex(stitched_pkt[:98] + l_slot + sync_data + r_slot + stitched_pkt[98:])
|
new_pkt = ahex(stitched_pkt[:98] + l_slot + sync_data + r_slot + stitched_pkt[98:])
|
||||||
send_seq.append(new_pkt)
|
send_seq.append(new_pkt)
|
||||||
|
|
@ -1130,13 +1130,16 @@ def create_sms_seq(dst_id, src_id, peer_id, _slot, _call_type, dmr_string):
|
||||||
mmdvm_send_seq.append(ahex(the_mmdvm_pkt))
|
mmdvm_send_seq.append(ahex(the_mmdvm_pkt))
|
||||||
cap_in = cap_in + 1
|
cap_in = cap_in + 1
|
||||||
print(ahex(the_mmdvm_pkt))
|
print(ahex(the_mmdvm_pkt))
|
||||||
if bytes.fromhex(dst_id) in UNIT_MAP:
|
if bytes.fromhex(dst_id) in UNIT_MAP:
|
||||||
logger.info('Sending SMS packet to ' + str(UNIT_MAP[bytes.fromhex(dst_id)][0]))
|
logger.info('Sending SMS packet to ' + str(UNIT_MAP[bytes.fromhex(dst_id)][0]))
|
||||||
systems[UNIT_MAP[bytes.fromhex(dst_id)][0]].send_system(the_mmdvm_pkt)
|
systems[UNIT_MAP[bytes.fromhex(dst_id)][0]].send_system(the_mmdvm_pkt)
|
||||||
else:
|
else:
|
||||||
for s in CONFIG['SYSTEMS'].items():
|
for s in CONFIG['SYSTEMS'].items():
|
||||||
|
if 'FREEDMR' in s[1]['OTHER_OPTIONS']:
|
||||||
|
systems[s[0]].send_system(b'SVRDDATA' + the_mmdvm_pkt)
|
||||||
|
else:
|
||||||
systems[s[0]].send_system(the_mmdvm_pkt)
|
systems[s[0]].send_system(the_mmdvm_pkt)
|
||||||
logger.info('Sending SMS packet to ' + str(s[0]))
|
logger.info('Sending SMS packet to ' + str(s[0]))
|
||||||
if CONFIG['WEB_SERVICE']['REMOTE_CONFIG_ENABLED'] == False:
|
if CONFIG['WEB_SERVICE']['REMOTE_CONFIG_ENABLED'] == False:
|
||||||
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:
|
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))
|
packet_write_file.write(str(mmdvm_send_seq))
|
||||||
|
|
@ -1230,9 +1233,9 @@ def send_sms(csbk, to_id, from_id, peer_id, call_type, msg):
|
||||||
call_type = 0
|
call_type = 0
|
||||||
# Send all Group data to TS 2, need to fix later.
|
# Send all Group data to TS 2, need to fix later.
|
||||||
slot = 1
|
slot = 1
|
||||||
if csbk == 'yes':
|
if csbk == True:
|
||||||
use_csbk = True
|
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)))
|
create_sms_seq(to_id, from_id, peer_id, int(slot), call_type, csbk_gen(to_id, from_id) + create_crc16(gen_header(to_id, from_id, call_type)) + create_crc32(format_sms(msg, to_id, from_id)))
|
||||||
else:
|
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)))
|
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)))
|
||||||
|
|
||||||
|
|
@ -1476,6 +1479,7 @@ def data_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _fr
|
||||||
# Use block 0 as trigger. $GPRMC must also be in string to indicate NMEA.
|
# Use block 0 as trigger. $GPRMC must also be in string to indicate NMEA.
|
||||||
# This triggers the APRS upload
|
# This triggers the APRS upload
|
||||||
if btf == 0:
|
if btf == 0:
|
||||||
|
|
||||||
final_packet = str(bitarray(re.sub("\)|\(|bitarray|'", '', packet_assembly)).tobytes().decode('utf-8', 'ignore'))
|
final_packet = str(bitarray(re.sub("\)|\(|bitarray|'", '', packet_assembly)).tobytes().decode('utf-8', 'ignore'))
|
||||||
sms_hex = str(ba2hx(bitarray(re.sub("\)|\(|bitarray|'", '', packet_assembly))))
|
sms_hex = str(ba2hx(bitarray(re.sub("\)|\(|bitarray|'", '', packet_assembly))))
|
||||||
sms_hex_string = re.sub("b'|'", '', str(sms_hex))
|
sms_hex_string = re.sub("b'|'", '', str(sms_hex))
|
||||||
|
|
@ -1690,8 +1694,10 @@ class OBP(OPENBRIDGE):
|
||||||
if _mode == b'UNIT':
|
if _mode == b'UNIT':
|
||||||
UNIT_MAP[_data] = (self._system, time())
|
UNIT_MAP[_data] = (self._system, time())
|
||||||
print(UNIT_MAP)
|
print(UNIT_MAP)
|
||||||
if _mode == b'DATA' or _mode == b'MDATA':
|
if _mode == b'DATA' or _mode == b'MDAT':
|
||||||
|
## print(ahex(_data))
|
||||||
# DMR Data packet, sent via SVRD
|
# DMR Data packet, sent via SVRD
|
||||||
|
|
||||||
_peer_id = _data[11:15]
|
_peer_id = _data[11:15]
|
||||||
_seq = _data[4]
|
_seq = _data[4]
|
||||||
_rf_src = _data[5:8]
|
_rf_src = _data[5:8]
|
||||||
|
|
@ -1709,6 +1715,14 @@ class OBP(OPENBRIDGE):
|
||||||
_dtype_vseq = (_bits & 0xF) # data, 1=voice header, 2=voice terminator; voice, 0=burst A ... 5=burst F
|
_dtype_vseq = (_bits & 0xF) # data, 1=voice header, 2=voice terminator; voice, 0=burst A ... 5=burst F
|
||||||
_stream_id = _data[16:20]
|
_stream_id = _data[16:20]
|
||||||
|
|
||||||
|
print(int_id(_peer_id))
|
||||||
|
print(int_id(_rf_src))
|
||||||
|
print(int_id(_dst_id))
|
||||||
|
print((_dtype_vseq))
|
||||||
|
print(ahex(bptc_decode(_data)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## # Record last packet to prevent duplicates, think finger printing.
|
## # Record last packet to prevent duplicates, think finger printing.
|
||||||
## PACKET_MATCH[_rf_src] = [_data, time()]
|
## PACKET_MATCH[_rf_src] = [_data, time()]
|
||||||
|
|
||||||
|
|
@ -1724,9 +1738,8 @@ class HBP(HBSYSTEM):
|
||||||
|
|
||||||
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
|
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
|
||||||
UNIT_MAP[_rf_src] = (self._system, time())
|
UNIT_MAP[_rf_src] = (self._system, time())
|
||||||
print(_dtype_vseq)
|
## print(ahex(_data))
|
||||||
print('MMDVM RCVD')
|
print('MMDVM RCVD')
|
||||||
print(UNIT_MAP)
|
|
||||||
if _rf_src not in PACKET_MATCH:
|
if _rf_src not in PACKET_MATCH:
|
||||||
PACKET_MATCH[_rf_src] = [_data, time()]
|
PACKET_MATCH[_rf_src] = [_data, time()]
|
||||||
elif _data == PACKET_MATCH[_rf_src][0] and time() - 1 < PACKET_MATCH[_rf_src][1]:
|
elif _data == PACKET_MATCH[_rf_src][0] and time() - 1 < PACKET_MATCH[_rf_src][1]:
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,7 @@ class OPENBRIDGE(DatagramProtocol):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def datagramReceived(self, _packet, _sockaddr):
|
def datagramReceived(self, _packet, _sockaddr):
|
||||||
|
print(_sockaddr)
|
||||||
## print(_packet[:4])
|
## print(_packet[:4])
|
||||||
# Keep This Line Commented Unless HEAVILY Debugging!
|
# Keep This Line Commented Unless HEAVILY Debugging!
|
||||||
## logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_packet))
|
## logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_packet))
|
||||||
|
|
@ -299,6 +300,7 @@ class OPENBRIDGE(DatagramProtocol):
|
||||||
## else:
|
## else:
|
||||||
|
|
||||||
self.svrd_received(_d_pkt[4:8], _d_pkt[8:])
|
self.svrd_received(_d_pkt[4:8], _d_pkt[8:])
|
||||||
|
## print(ahex(_d_pkt[8:]))
|
||||||
|
|
||||||
#************************************************
|
#************************************************
|
||||||
# HB MASTER CLASS
|
# HB MASTER CLASS
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,63 @@
|
||||||
|
###############################################################################
|
||||||
|
# Copyright (C) 2020 Simon Adlem, G7RZU <g7rzu@gb7fr.org.uk>
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
from twisted.internet.protocol import DatagramProtocol
|
from twisted.internet.protocol import DatagramProtocol
|
||||||
from twisted.internet import reactor, task
|
from twisted.internet import reactor, task
|
||||||
from time import time
|
from time import time
|
||||||
from resettabletimer import ResettableTimer
|
|
||||||
from dmr_utils3.utils import int_id
|
from dmr_utils3.utils import int_id
|
||||||
import random
|
import random
|
||||||
|
import ipaddress
|
||||||
|
import os
|
||||||
|
from setproctitle import setproctitle
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||||
__author__ = 'Simon Adlem - G7RZU'
|
__author__ = 'Simon Adlem - G7RZU'
|
||||||
__copyright__ = 'Copyright (c) Simon Adlem, G7RZU 2020,2021'
|
__copyright__ = 'Copyright (c) Simon Adlem, G7RZU 2020,2021'
|
||||||
__credits__ = 'Jon Lee, G4TSN; Norman Williams, M6NBP'
|
__credits__ = 'Jon Lee, G4TSN; Norman Williams, M6NBP; Christian, OA4DOA'
|
||||||
__license__ = 'GNU GPLv3'
|
__license__ = 'GNU GPLv3'
|
||||||
__maintainer__ = 'Simon Adlem G7RZU'
|
__maintainer__ = 'Simon Adlem G7RZU'
|
||||||
__email__ = 'simon@gb7fr.org.uk'
|
__email__ = 'simon@gb7fr.org.uk'
|
||||||
|
|
||||||
|
def IsIPv4Address(ip):
|
||||||
|
try:
|
||||||
|
ipaddress.IPv4Address(ip)
|
||||||
|
return True
|
||||||
|
except ValueError as errorCode:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
def IsIPv6Address(ip):
|
||||||
|
try:
|
||||||
|
ipaddress.IPv6Address(ip)
|
||||||
|
return True
|
||||||
|
except ValueError as errorCode:
|
||||||
|
pass
|
||||||
|
|
||||||
class Proxy(DatagramProtocol):
|
class Proxy(DatagramProtocol):
|
||||||
|
|
||||||
def __init__(self,Master,ListenPort,connTrack,blackList,Timeout,Debug,DestportStart,DestPortEnd):
|
def __init__(self,Master,ListenPort,connTrack,blackList,Timeout,Debug,ClientInfo,DestportStart,DestPortEnd):
|
||||||
self.master = Master
|
self.master = Master
|
||||||
self.connTrack = connTrack
|
self.connTrack = connTrack
|
||||||
self.peerTrack = {}
|
self.peerTrack = {}
|
||||||
self.timeout = Timeout
|
self.timeout = Timeout
|
||||||
self.debug = Debug
|
self.debug = Debug
|
||||||
|
self.clientinfo = ClientInfo
|
||||||
self.blackList = blackList
|
self.blackList = blackList
|
||||||
self.destPortStart = DestportStart
|
self.destPortStart = DestportStart
|
||||||
self.destPortEnd = DestPortEnd
|
self.destPortEnd = DestPortEnd
|
||||||
|
|
@ -30,7 +67,9 @@ class Proxy(DatagramProtocol):
|
||||||
def reaper(self,_peer_id):
|
def reaper(self,_peer_id):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print("dead",_peer_id)
|
print("dead",_peer_id)
|
||||||
self.transport.write(b'RPTCL'+_peer_id, ('127.0.0.1',self.peerTrack[_peer_id]['dport']))
|
if self.clientinfo and _peer_id != b'\xff\xff\xff\xff':
|
||||||
|
print(f"{datetime.now().replace(microsecond=0)} Client: ID:{str(int_id(_peer_id)).rjust(9)} IP:{self.peerTrack[_peer_id]['shost'].rjust(15)} Port:{self.peerTrack[_peer_id]['sport']} Removed.")
|
||||||
|
self.transport.write(b'RPTCL'+_peer_id, (self.master,self.peerTrack[_peer_id]['dport']))
|
||||||
self.connTrack[self.peerTrack[_peer_id]['dport']] = False
|
self.connTrack[self.peerTrack[_peer_id]['dport']] = False
|
||||||
del self.peerTrack[_peer_id]
|
del self.peerTrack[_peer_id]
|
||||||
|
|
||||||
|
|
@ -57,6 +96,8 @@ class Proxy(DatagramProtocol):
|
||||||
RPTA = b'RPTA'
|
RPTA = b'RPTA'
|
||||||
RPTO = b'RPTO'
|
RPTO = b'RPTO'
|
||||||
|
|
||||||
|
_peer_id = False
|
||||||
|
|
||||||
host,port = addr
|
host,port = addr
|
||||||
|
|
||||||
nowtime = time()
|
nowtime = time()
|
||||||
|
|
@ -66,44 +107,31 @@ class Proxy(DatagramProtocol):
|
||||||
#If the packet comes from the master
|
#If the packet comes from the master
|
||||||
if host == self.master:
|
if host == self.master:
|
||||||
_command = data[:4]
|
_command = data[:4]
|
||||||
_lng_command = data[:6]
|
|
||||||
#### print(_lng_command)
|
|
||||||
|
|
||||||
|
|
||||||
if _command == DMRD:
|
if _command == DMRD:
|
||||||
_peer_id = data[11:15]
|
_peer_id = data[11:15]
|
||||||
## print(self.peerTrack[_peer_id]['timer'])
|
|
||||||
elif _command == RPTA:
|
elif _command == RPTA:
|
||||||
if data[6:10] in self.peerTrack:
|
if data[6:10] in self.peerTrack:
|
||||||
_peer_id = data[6:10]
|
_peer_id = data[6:10]
|
||||||
else:
|
else:
|
||||||
_peer_id = self.connTrack[port]
|
_peer_id = self.connTrack[port]
|
||||||
elif _lng_command == MSTNAK:
|
elif _command == MSTN:
|
||||||
_peer_id = data[6:10]
|
|
||||||
|
|
||||||
elif _command == MSTN and MSTNAK not in _lng_command:
|
|
||||||
_peer_id = data[6:10]
|
_peer_id = data[6:10]
|
||||||
|
|
||||||
self.peerTrack[_peer_id]['timer'].cancel()
|
|
||||||
self.reaper(_peer_id)
|
|
||||||
return
|
|
||||||
elif _command == MSTP:
|
elif _command == MSTP:
|
||||||
_peer_id = data[7:11]
|
_peer_id = data[7:11]
|
||||||
## print(self.peerTrack)
|
|
||||||
elif _command == MSTC:
|
elif _command == MSTC:
|
||||||
_peer_id = data[5:9]
|
_peer_id = data[5:9]
|
||||||
self.peerTrack[_peer_id]['timer'].cancel()
|
|
||||||
self.reaper(_peer_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
# _peer_id = self.connTrack[port]
|
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print(data)
|
print(data)
|
||||||
if _peer_id and _peer_id in self.peerTrack:
|
if _peer_id in self.peerTrack:
|
||||||
self.transport.write(data,(self.peerTrack[_peer_id]['shost'],self.peerTrack[_peer_id]['sport']))
|
self.transport.write(data,(self.peerTrack[_peer_id]['shost'],self.peerTrack[_peer_id]['sport']))
|
||||||
#self.peerTrack[_peer_id]['timer'].reset()
|
# Remove the client after send a MSTN or MSTC packet
|
||||||
return
|
if _command in (MSTN,MSTC):
|
||||||
|
# Give time to the client for a reply to prevent port reassignment
|
||||||
|
self.peerTrack[_peer_id]['timer'].reset(15)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
@ -112,16 +140,16 @@ class Proxy(DatagramProtocol):
|
||||||
if _command == DMRD: # DMRData -- encapsulated DMR data frame
|
if _command == DMRD: # DMRData -- encapsulated DMR data frame
|
||||||
_peer_id = data[11:15]
|
_peer_id = data[11:15]
|
||||||
elif _command == DMRA: # DMRAlias -- Talker Alias information
|
elif _command == DMRA: # DMRAlias -- Talker Alias information
|
||||||
_peer_id = _data[4:8]
|
_peer_id = data[4:8]
|
||||||
elif _command == RPTL: # RPTLogin -- a repeater wants to login
|
elif _command == RPTL: # RPTLogin -- a repeater wants to login
|
||||||
_peer_id = data[4:8]
|
_peer_id = data[4:8]
|
||||||
elif _command == RPTK: # Repeater has answered our login challenge
|
elif _command == RPTK: # Repeater has answered our login challenge
|
||||||
_peer_id = data[4:8]
|
_peer_id = data[4:8]
|
||||||
elif _command == RPTC: # Repeater is sending it's configuraiton OR disconnecting
|
elif _command == RPTC: # Repeater is sending it's configuraiton OR disconnecting
|
||||||
if data[:5] == RPTCL: # Disconnect command
|
if data[:5] == RPTCL: # Disconnect command
|
||||||
_peer_id = data[5:9]
|
_peer_id = data[5:9]
|
||||||
else:
|
else:
|
||||||
_peer_id = data[4:8] # Configure Command
|
_peer_id = data[4:8] # Configure Command
|
||||||
elif _command == RPTO: # options
|
elif _command == RPTO: # options
|
||||||
_peer_id = data[4:8]
|
_peer_id = data[4:8]
|
||||||
elif _command == RPTP: # RPTPing -- peer is pinging us
|
elif _command == RPTP: # RPTPing -- peer is pinging us
|
||||||
|
|
@ -133,33 +161,33 @@ class Proxy(DatagramProtocol):
|
||||||
_dport = self.peerTrack[_peer_id]['dport']
|
_dport = self.peerTrack[_peer_id]['dport']
|
||||||
self.peerTrack[_peer_id]['sport'] = port
|
self.peerTrack[_peer_id]['sport'] = port
|
||||||
self.peerTrack[_peer_id]['shost'] = host
|
self.peerTrack[_peer_id]['shost'] = host
|
||||||
self.transport.write(data, ('127.0.0.1',_dport))
|
self.transport.write(data, (self.master,_dport))
|
||||||
self.peerTrack[_peer_id]['timer'].reset()
|
self.peerTrack[_peer_id]['timer'].reset(self.timeout)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print(data)
|
print(data)
|
||||||
print(_dport)
|
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
|
|
||||||
|
else:
|
||||||
if int_id(_peer_id) in self.blackList:
|
if int_id(_peer_id) in self.blackList:
|
||||||
return
|
return
|
||||||
#for _dport in self.connTrack:
|
# Make a list with the available ports
|
||||||
while True:
|
_ports_avail = [port for port in self.connTrack if not self.connTrack[port]]
|
||||||
_dport = random.randint(1,(self.numPorts - 1))
|
if _ports_avail:
|
||||||
_dport = _dport + self.destPortStart
|
_dport = random.choice(_ports_avail)
|
||||||
if not self.connTrack[_dport]:
|
else:
|
||||||
break
|
return
|
||||||
self.connTrack[_dport] = _peer_id
|
self.connTrack[_dport] = _peer_id
|
||||||
self.peerTrack[_peer_id] = {}
|
self.peerTrack[_peer_id] = {}
|
||||||
self.peerTrack[_peer_id]['dport'] = _dport
|
self.peerTrack[_peer_id]['dport'] = _dport
|
||||||
self.peerTrack[_peer_id]['sport'] = port
|
self.peerTrack[_peer_id]['sport'] = port
|
||||||
self.peerTrack[_peer_id]['shost'] = host
|
self.peerTrack[_peer_id]['shost'] = host
|
||||||
self.peerTrack[_peer_id]['timer'] = ResettableTimer(self.timeout,self.reaper,[_peer_id])
|
self.peerTrack[_peer_id]['timer'] = reactor.callLater(self.timeout,self.reaper,_peer_id)
|
||||||
self.peerTrack[_peer_id]['timer'].start()
|
|
||||||
self.transport.write(data, (self.master,_dport))
|
self.transport.write(data, (self.master,_dport))
|
||||||
|
|
||||||
|
if self.clientinfo and _peer_id != b'\xff\xff\xff\xff':
|
||||||
|
print(f'{datetime.now().replace(microsecond=0)} New client: ID:{str(int_id(_peer_id)).rjust(9)} IP:{host.rjust(15)} Port:{port}, assigned to port:{_dport}.')
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print(data)
|
print(data)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -169,23 +197,48 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
Master = "127.0.0.1"
|
Master = "127.0.0.1"
|
||||||
ListenPort = 62031
|
ListenPort = 62031
|
||||||
DestportStart = 54100
|
# '' = all IPv4, '::' = all IPv4 and IPv6 (Dual Stack)
|
||||||
DestPortEnd = 54102
|
ListenIP = ''
|
||||||
|
DestportStart = 54000
|
||||||
|
DestPortEnd = 54100
|
||||||
Timeout = 30
|
Timeout = 30
|
||||||
Stats = True
|
Stats = False
|
||||||
Debug = True
|
Debug = False
|
||||||
|
ClientInfo = False
|
||||||
BlackList = [1234567]
|
BlackList = [1234567]
|
||||||
|
|
||||||
#*******************
|
#*******************
|
||||||
|
|
||||||
|
|
||||||
|
#Set process title early
|
||||||
|
setproctitle(__file__)
|
||||||
|
|
||||||
|
#If IPv6 is enabled by enivornment variable...
|
||||||
|
if ListenIP == '' and 'FDPROXY_IPV6' in os.environ and bool(os.environ['FDPROXY_IPV6']):
|
||||||
|
ListenIP = '::'
|
||||||
|
|
||||||
|
#Override static config from Environment
|
||||||
|
if 'FDPROXY_STATS' in os.environ:
|
||||||
|
Stats = bool(os.environ['FDPROXY_STATS'])
|
||||||
|
if 'FDPROXY_DEBUG' in os.environ:
|
||||||
|
Debug = bool(os.environ['FDPROXY_DEBUG'])
|
||||||
|
if 'FDPROXY_CLIENTINFO' in os.environ:
|
||||||
|
ClientInfo = bool(os.environ['FDPROXY_CLIENTINFO'])
|
||||||
|
if 'FDPROXY_LISTENPORT' in os.environ:
|
||||||
|
ListenPort = os.environ['FDPROXY_LISTENPORT']
|
||||||
|
|
||||||
|
|
||||||
CONNTRACK = {}
|
CONNTRACK = {}
|
||||||
|
|
||||||
for port in range(DestportStart,DestPortEnd+1,1):
|
for port in range(DestportStart,DestPortEnd+1,1):
|
||||||
CONNTRACK[port] = False
|
CONNTRACK[port] = False
|
||||||
|
|
||||||
|
#If we are listening IPv6 and Master is an IPv4 IPv4Address
|
||||||
|
#IPv6ify the address.
|
||||||
|
if ListenIP == '::' and IsIPv4Address(Master):
|
||||||
|
Master = '::ffff:' + Master
|
||||||
|
|
||||||
reactor.listenUDP(ListenPort,Proxy(Master,ListenPort,CONNTRACK,BlackList,Timeout,Debug,DestportStart,DestPortEnd))
|
reactor.listenUDP(ListenPort,Proxy(Master,ListenPort,CONNTRACK,BlackList,Timeout,Debug,ClientInfo,DestportStart,DestPortEnd),interface=ListenIP)
|
||||||
|
|
||||||
def loopingErrHandle(failure):
|
def loopingErrHandle(failure):
|
||||||
print('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}'.format(failure))
|
print('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}'.format(failure))
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td><span style="color: #ff0000;"><strong>Red</strong></span> = Peer location (no APRS)</td>
|
<td><span style="color: #ff0000;"><strong>Red</strong></span> = Peer location (no APRS)</td>
|
||||||
<td><span style="color: #0000ff;"><strong>Blue</strong></span> = GPS location (APRS)</td>
|
<td><span style="color: #0000ff;"><strong>Blue</strong></span> = GPS location (APRS)</td>
|
||||||
<td><strong><span style="color: #00ff00;">Green</span></strong> = Peer location (APRS)</td>
|
<!-- <td><strong><span style="color: #00ff00;">Green</span></strong> = Peer location (APRS)</td> -->
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue