11#!/usr/bin/env python3
22
33# Written by Sultan Qasim Khan
4+ # OpenDroneID mods (c) by B. Kerler
45# Copyright (c) 2018-2024, NCC Group plc
56# Released as open source under GPLv3
67
78import argparse , sys
9+ import json
10+ import time
811from binascii import unhexlify
9- from sniffle .constants import BLE_ADV_AA
1012from sniffle .pcap import PcapBleWriter
1113from sniffle .sniffle_hw import (make_sniffle_hw , PacketMessage , DebugMessage , StateMessage ,
1214 MeasurementMessage , SnifferMode , PhyMode )
1315from sniffle .packet_decoder import (AdvaMessage , AdvDirectIndMessage , AdvExtIndMessage ,
14- ScanRspMessage , DataMessage , str_mac )
16+ ScanRspMessage , DataMessage , str_mac , AdvIndMessage )
1517from sniffle .errors import UsageError , SourceDone
1618from sniffle .advdata .decoder import decode_adv_data
1719
2426def main ():
2527 aparse = argparse .ArgumentParser (description = "Host-side receiver for Sniffle BLE5 sniffer" )
2628 aparse .add_argument ("-s" , "--serport" , default = None , help = "Sniffer serial port name" )
29+ aparse .add_argument ("-b" , "--baudrate" , default = None , help = "Sniffer serial port baudrate" )
2730 aparse .add_argument ("-c" , "--advchan" , default = 40 , choices = [37 , 38 , 39 ], type = int ,
2831 help = "Advertising channel to listen on" )
2932 aparse .add_argument ("-p" , "--pause" , action = "store_true" ,
@@ -55,8 +58,40 @@ def main():
5558 aparse .add_argument ("-d" , "--decode" , action = "store_true" ,
5659 help = "Decode advertising data" )
5760 aparse .add_argument ("-o" , "--output" , default = None , help = "PCAP output file name" )
61+ aparse .add_argument ("-z" , "--zmq" , action = "store_true" , help = "Enable zmq" )
62+ aparse .add_argument ("--zmqsetting" , default = "127.0.0.1:4222" , help = "Define zmq server settings" )
5863 args = aparse .parse_args ()
5964
65+ if args .zmq :
66+ import zmq
67+
68+ url = f"tcp://{ args .zmqsetting } "
69+
70+ context = zmq .Context ()
71+ socket = context .socket (zmq .XPUB )
72+ socket .setsockopt (zmq .XPUB_VERBOSE , True )
73+ socket .bind (url )
74+
75+ def zmq_thread (socket ):
76+ try :
77+ while True :
78+ event = socket .recv ()
79+ # Event is one byte 0=unsub or 1=sub, followed by topic
80+ if event [0 ] == 1 :
81+ log ("new subscriber for" , event [1 :])
82+ elif event [0 ] == 0 :
83+ log ("unsubscribed" , event [1 :])
84+ except zmq .error .ContextTerminated :
85+ pass
86+
87+ def log (* msg ):
88+ s = time .strftime ("%Y-%m-%d %H:%M:%S" , time .localtime ())
89+ print ("%s:" % s , * msg , end = "\n " , file = sys .stderr )
90+
91+ from threading import Thread
92+ zthread = Thread (target = zmq_thread , args = [socket ], daemon = True , name = 'zmq' )
93+ zthread .start ()
94+
6095 # Sanity check argument combinations
6196 targ_specs = bool (args .mac ) + bool (args .irk ) + bool (args .string )
6297 if args .hop and targ_specs < 1 :
@@ -70,7 +105,7 @@ def main():
70105 raise UsageError ("Don't specify an advertising channel if you want advertising channel hopping!" )
71106
72107 global hw
73- hw = make_sniffle_hw (args .serport )
108+ hw = make_sniffle_hw (serport = args .serport , baudrate = args . baudrate )
74109
75110 # if a channel was explicitly specified, don't hop
76111 hop3 = True if targ_specs else False
@@ -137,10 +172,17 @@ def main():
137172 while True :
138173 try :
139174 msg = hw .recv_and_decode ()
140- print_message (msg , args .quiet , args .decode )
175+ if args .zmq :
176+ smsg = msg .to_dict ()
177+ smsg = json .dumps (smsg )
178+ socket .send_string (smsg )
179+ else :
180+ print_message (msg , args .quiet , args .decode )
141181 except SourceDone :
142182 break
143183 except KeyboardInterrupt :
184+ if args .zmq :
185+ socket .close ()
144186 hw .cancel_recv ()
145187 sys .stderr .write ("\r " )
146188 break
0 commit comments