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,41 @@ 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" )
63+ aparse .add_argument ("-v" , "--verbose" , action = "store_true" , help = "Print messages" )
5864 args = aparse .parse_args ()
5965
66+ if args .zmq :
67+ import zmq
68+
69+ url = f"tcp://{ args .zmqsetting } "
70+
71+ context = zmq .Context ()
72+ socket = context .socket (zmq .XPUB )
73+ socket .setsockopt (zmq .XPUB_VERBOSE , True )
74+ socket .bind (url )
75+
76+ def zmq_thread (socket ):
77+ try :
78+ while True :
79+ event = socket .recv ()
80+ # Event is one byte 0=unsub or 1=sub, followed by topic
81+ if event [0 ] == 1 :
82+ log ("new subscriber for" , event [1 :])
83+ elif event [0 ] == 0 :
84+ log ("unsubscribed" , event [1 :])
85+ except zmq .error .ContextTerminated :
86+ pass
87+
88+ def log (* msg ):
89+ s = time .strftime ("%Y-%m-%d %H:%M:%S" , time .localtime ())
90+ print ("%s:" % s , * msg , end = "\n " , file = sys .stderr )
91+
92+ from threading import Thread
93+ zthread = Thread (target = zmq_thread , args = [socket ], daemon = True , name = 'zmq' )
94+ zthread .start ()
95+
6096 # Sanity check argument combinations
6197 targ_specs = bool (args .mac ) + bool (args .irk ) + bool (args .string )
6298 if args .hop and targ_specs < 1 :
@@ -70,7 +106,7 @@ def main():
70106 raise UsageError ("Don't specify an advertising channel if you want advertising channel hopping!" )
71107
72108 global hw
73- hw = make_sniffle_hw (args .serport )
109+ hw = make_sniffle_hw (serport = args .serport , baudrate = args . baudrate )
74110
75111 # if a channel was explicitly specified, don't hop
76112 hop3 = True if targ_specs else False
@@ -137,10 +173,19 @@ def main():
137173 while True :
138174 try :
139175 msg = hw .recv_and_decode ()
140- print_message (msg , args .quiet , args .decode )
176+ if args .zmq :
177+ smsg = msg .to_dict ()
178+ smsg = json .dumps (smsg )
179+ socket .send_string (smsg )
180+ if args .verbose :
181+ print_message (msg , args .quiet , args .decode )
182+ else :
183+ print_message (msg , args .quiet , args .decode )
141184 except SourceDone :
142185 break
143186 except KeyboardInterrupt :
187+ if args .zmq :
188+ socket .close ()
144189 hw .cancel_recv ()
145190 sys .stderr .write ("\r " )
146191 break
0 commit comments