106 строки
3.3 KiB
Python
Исполняемый файл
106 строки
3.3 KiB
Python
Исполняемый файл
import argparse
|
|
import asyncio
|
|
import logging
|
|
import traceback
|
|
|
|
from dmrtools.udpproxy import AbstractUDPProxy
|
|
from dmrtools import DMRPPacketFactory
|
|
from dmrtools import hexdump
|
|
# from dmrtools.dmrproto import DMRPL2FullLC, DMRPL2VoiceBurst
|
|
from dmrtools.dmrproto import DMRPPacketData
|
|
from dmrtools.dmrproto import EmbLCAssembler, LCFactory
|
|
|
|
|
|
def setup_logger(log_file=None):
|
|
handlers = [logging.StreamHandler()]
|
|
if log_file:
|
|
handlers.append(logging.FileHandler(log_file))
|
|
|
|
logging.basicConfig(
|
|
# level=logging.INFO,
|
|
level=logging.DEBUG,
|
|
format='%(asctime)s [%(levelname)s] %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S',
|
|
handlers=handlers
|
|
)
|
|
|
|
|
|
elca: dict[int, EmbLCAssembler] = dict()
|
|
|
|
def get_packet_details(data):
|
|
try:
|
|
p = DMRPPacketFactory.fd(data)
|
|
if type(p) is DMRPPacketData:
|
|
emblcinfo = ""
|
|
if (full_lc := p.get_full_lc()) is not None:
|
|
lcdec = LCFactory.fd(full_lc)
|
|
emblcinfo = f"FullLC:{hexdump(full_lc)}\n{lcdec}\n"
|
|
return f"{p:l2}\n" + emblcinfo
|
|
if p.stream_id not in elca:
|
|
elca[p.stream_id] = EmbLCAssembler()
|
|
if elca[p.stream_id].process_voicedata(p):
|
|
emblcdata = elca[p.stream_id].decode()
|
|
lcdec = LCFactory.fd(emblcdata)
|
|
emblcinfo = f"EmbLC:{hexdump(emblcdata)}\n{lcdec}\n"
|
|
del elca[p.stream_id]
|
|
if p.is_voice_term:
|
|
del elca[p.stream_id]
|
|
return f"{p:l2}\n" + emblcinfo
|
|
return f"{p}\n"
|
|
except:
|
|
return f"Exception while decoding packet:\n{traceback.format_exc()}"
|
|
|
|
|
|
def log_packet(direction, data):
|
|
log_message = (
|
|
f"{direction} | {len(data)} bytes\n"
|
|
f"HD: {data.hex()}\n{hexdump(data)}\n"
|
|
f"{get_packet_details(data)}"
|
|
)
|
|
logging.info(log_message)
|
|
|
|
|
|
class UDPProxyLogger(AbstractUDPProxy):
|
|
def on_forward(self, data: bytes, to_server: bool) -> bytes:
|
|
direction = "OUT=>" if to_server else "<==IN"
|
|
log_packet(direction, data)
|
|
return data
|
|
|
|
|
|
async def start_udp_proxy(server_ip, server_port, listen_ip, listen_port):
|
|
logging.info(f"Proxy running: {listen_ip}:{listen_port} <=> {server_ip}:{server_port}")
|
|
|
|
proxy = UDPProxyLogger(server_ip, server_port, listen_ip, listen_port)
|
|
await proxy.start()
|
|
|
|
try:
|
|
await asyncio.Future() # Run forever
|
|
finally:
|
|
# await proxy.stop()
|
|
pass
|
|
|
|
|
|
def parse_arguments():
|
|
parser = argparse.ArgumentParser(description='Simple UDP Proxy with Hex Logging (Asyncio Version)')
|
|
parser.add_argument('listenip', type=str, help='IP address to listen on')
|
|
parser.add_argument('listenport', type=int, help='Port to listen on')
|
|
parser.add_argument('serverip', type=str, help='Server IP address to forward to')
|
|
parser.add_argument('serverport', type=int, help='Server port to forward to')
|
|
parser.add_argument('-l', '--log-file', type=str, help='Log filename')
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_arguments()
|
|
setup_logger(args.log_file)
|
|
|
|
try:
|
|
asyncio.run(start_udp_proxy(args.serverip, args.serverport,
|
|
args.listenip, args.listenport))
|
|
except KeyboardInterrupt:
|
|
logging.info("Shutting down.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|