Skip to content

Commit

Permalink
1. The case where there is no hci0 in the system but there are other …
Browse files Browse the repository at this point in the history
…hci devices is handled.

2. Used the new logging system.
3. Reparsed the manufacturer specific data in the GAP.
4. Fixed some bugs.
  • Loading branch information
x committed Oct 17, 2020
1 parent 73229e2 commit b314562
Show file tree
Hide file tree
Showing 14 changed files with 140 additions and 100 deletions.
4 changes: 2 additions & 2 deletions README-Chinese.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ sudo pip3 install bluescan

```txt
$ bluescan -h
bluescan v0.2.2
bluescan v0.2.3
A powerful Bluetooth scanner.
Expand All @@ -59,7 +59,7 @@ Arguments:
Options:
-h, --help Display this help.
-v, --version Show the version.
-i <hcix> HCI device for scan. [default: hci0]
-i <hcix> HCI device for scan. [default: The first HCI device]
-m <mode> Scan mode, support BR, LE, LMP, SDP, GATT and vuln.
--inquiry-len=<n> Inquiry_Length parameter of HCI_Inquiry command. [default: 8]
--timeout=<sec> Duration of LE scan. [default: 10]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ sudo pip3 install bluescan

```txt
$ bluescan -h
bluescan v0.2.2
bluescan v0.2.3
A powerful Bluetooth scanner.
Expand All @@ -61,7 +61,7 @@ Arguments:
Options:
-h, --help Display this help.
-v, --version Show the version.
-i <hcix> HCI device for scan. [default: hci0]
-i <hcix> HCI device for scan. [default: The first HCI device]
-m <mode> Scan mode, support BR, LE, LMP, SDP, GATT and vuln.
--inquiry-len=<n> Inquiry_Length parameter of HCI_Inquiry command. [default: 8]
--timeout=<sec> Duration of LE scan. [default: 10]
Expand Down
20 changes: 12 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

import os
import shutil
import logging
from pathlib import Path
from setuptools.command.install import install
from distutils.command.clean import clean
from setuptools import setup, find_packages

from pyclui import Logger

BLUESCAN_PATH = os.path.abspath(Path(__file__).parent)
logger = Logger(__name__, logging.INFO)

PROJECT_ROOT = os.path.abspath(Path(__file__).parent)


def read(fname):
Expand All @@ -18,7 +22,7 @@ def read(fname):
class MyInstall(install):
def run(self):
super().run()
print('[INFO] install bluescan_prompt.bash')
logger.info('install bluescan_prompt.bash')
shutil.copy(
'src/bluescan/bluescan_prompt.bash', '/etc/bash_completion.d'
)
Expand All @@ -28,10 +32,10 @@ class MyClean(clean):
def run(self):
super().run()
dirs = [
os.path.join(BLUESCAN_PATH, 'build'),
os.path.join(BLUESCAN_PATH, 'dist'),
os.path.join(BLUESCAN_PATH, 'src', 'bluescan.egg-info'),
os.path.join(BLUESCAN_PATH, 'src', 'bluescan', '__pycache__')
os.path.join(PROJECT_ROOT, 'build'),
os.path.join(PROJECT_ROOT, 'dist'),
os.path.join(PROJECT_ROOT, 'src', 'bluescan.egg-info'),
os.path.join(PROJECT_ROOT, 'src', 'bluescan', '__pycache__')
]

for d in dirs:
Expand All @@ -41,7 +45,7 @@ def run(self):
if __name__ == '__main__':
setup(
name='bluescan',
version='0.2.2',
version='0.2.3',
license = "GPL-3.0",
packages=find_packages('src'), # include all packages under src
package_dir={'':'src'}, # tell distutils packages are under src
Expand All @@ -56,7 +60,7 @@ def run(self):
#scripts=['src/bluescan/bluescan.py'],

install_requires=[
'bthci>=0.0.6', 'pyclui>=0.0.2',
'bthci>=0.0.7', 'pyclui>=0.0.3',
'pybluez>=0.23', 'bluepy>=1.3.0', 'docopt>=0.6.2'
],

Expand Down
6 changes: 3 additions & 3 deletions src/bluescan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from bthci import HCI



# https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/
# Table 2: Service Class Profile Identifiers
#
Expand Down Expand Up @@ -40,13 +39,14 @@
'Allowed Usage': items[2]
}


# EIR Data Type, Advertising Data Type (AD Type) and OOB Data Type Definitions
COMPLETE_16_BIT_SERVICE_CLS_UUID_LIST = 0x03
COMPLETE_32_BIT_SERVICE_CLS_UUID_LIST = 0x05
COMPLETE_128_BIT_SERVICE_CLS_UUID_LIST = 0x07
COMPLETE_LOCAL_NAME = 0X09
SHORTENED_LOCAL_NAME = 0X08
TX_POWER_LEVEL = 0x0a
MANUFACTURER_SPECIFIC_DATA = 0xff

gap_type_name_pairs = {
0x01: 'Flags',
Expand Down Expand Up @@ -99,4 +99,4 @@
class BlueScanner():
def __init__(self, iface='hci0'):
self.iface = iface
self.devid = HCI.hcix2devid(self.iface)
self.devid = HCI.hcistr2devid(self.iface)
34 changes: 20 additions & 14 deletions src/bluescan/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@

import os
import sys
import time
import logging
import traceback
import subprocess
from pathlib import PosixPath

from bthci import HCI
from pyclui import Logger
from bluepy.btle import BTLEException
from bluetooth.btcommon import BluetoothError

from bthci import HCI
from pyclui import blue, green, yellow, red, \
DEBUG, INFO, WARNING, ERROR

from .ui import parse_cmdline
from .helper import find_rfkill_devid

from .br_scan import BRScanner
from .le_scan import LEScanner
from .gatt_scan import GATTScanner
Expand All @@ -26,13 +23,16 @@
from .lmp_scan import LMPScanner


logger = Logger(__name__, logging.INFO)


def init(iface='hci0'):
hci = HCI(iface)

exitcode, output = subprocess.getstatusoutput(
'rfkill unblock %d' % find_rfkill_devid(iface))
if exitcode != 0:
print(ERROR, 'rfkill:', output)
logger.error('rfkill: ' + output)
sys.exit(exitcode)

# hci.reset()
Expand Down Expand Up @@ -73,6 +73,11 @@ def init(iface='hci0'):
def main():
try:
args = parse_cmdline()
logger.debug('args: {}'.format(args))

if args['-i'] == 'The first HCI device':
args['-i'] = HCI.get_default_hcistr() # May raise IndexError

init(args['-i'])

if args['-m'] == 'br':
Expand All @@ -93,22 +98,23 @@ def main():
elif args['-m'] == 'vuln':
VulnScanner(args['-i']).scan(args['BD_ADDR'], args['--addr-type'])
else:
print(ERROR, "invalid scan mode")
logger.error('Invalid scan mode')
except IndexError:
logger.error('There is no available HCI device')
except BluetoothError as e:
print(ERROR, e)
logger.error('{}'.format(e))
except RuntimeError as e:
print(ERROR, e)
logger.error('{}'.format(e))
except (BTLEException, ValueError) as e:
# print('__main__')
print(ERROR, e)
logger.error('{}'.format(e))
if 'le on' in str(e):
print(' No BLE adapter? or missing sudo ?')
except KeyboardInterrupt:
print(INFO, args['-m'].upper() + " scan canceled\n")
logger.info(args['-m'].upper() + " scan canceled\n")
# except Exception as e:
# #traceback.print_exc()
# print(ERROR, e)


if __name__ == "__main__":
if __name__ == '__main__':
main()
52 changes: 28 additions & 24 deletions src/bluescan/br_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import subprocess
import warnings
import struct
import logging

from bluetooth import DeviceDiscoverer

Expand All @@ -19,8 +20,8 @@
EVT_EXTENDED_INQUIRY_RESULT, EVT_INQUIRY_COMPLETE

from bthci import HCI
from pyclui import green, blue, yellow, red, \
DEBUG, INFO, WARNING, ERROR
from pyclui import Logger
from pyclui import green, blue, yellow, red

from . import BlueScanner
from . import service_cls_profile_ids
Expand All @@ -31,6 +32,9 @@
SHORTENED_LOCAL_NAME, TX_POWER_LEVEL


logger = Logger(__name__, logging.INFO)


major_dev_clses = {
0b00000: 'Miscellaneous',
0b00001: 'Computer',
Expand All @@ -47,8 +51,8 @@

class BRScanner(BlueScanner):
def inquiry(self, lap=0x9e8b33, inquiry_len=0x08, num_rsp=0x00):
print(INFO, "BR scanning on " + blue("hci%d"%self.devid) + \
" with timeout " + blue("%.2f sec\n"%(inquiry_len*1.28))+'\n')
logger.info('BR scanning on ' + blue("hci%d"%self.devid) + \
' with timeout ' + blue("%.2f sec\n"%(inquiry_len*1.28))+'\n')

self.scanned_dev = []
self.remote_name_req_flag = True
Expand All @@ -72,23 +76,23 @@ def inquiry(self, lap=0x9e8b33, inquiry_len=0x08, num_rsp=0x00):
if len(data) >= 4:
event_code = data[1]
if event_code == EVT_CMD_STATUS:
# print(DEBUG, 'HCI_Command_Status')
logger.debug('HCI_Command_Status')
pass
elif event_code == EVT_INQUIRY_RESULT:
print(DEBUG, 'HCI_Inquiry_Result')
logger.debug('HCI_Inquiry_Result')
self.pp_inquiry_result(data[3:])
elif event_code == EVT_INQUIRY_RESULT_WITH_RSSI:
# print(DEBUG, 'HCI_Inquiry_Result_with_RSSI')
logger.debug('HCI_Inquiry_Result_with_RSSI')
self.pp_inquiry_result_with_rssi(data[3:])
elif event_code == EVT_EXTENDED_INQUIRY_RESULT:
# print(DEBUG, 'HCI_Extended_Inquiry_Result')
logger.debug('HCI_Extended_Inquiry_Result')
self.pp_extended_inquiry_result(data[3:])
elif event_code == EVT_INQUIRY_COMPLETE:
# print(DEBUG, 'HCI_Inquiry_Complete')
print(INFO, 'Inquiry completed\n')
logger.debug('HCI_Inquiry_Complete')
logger.info('Inquiry completed\n')

if self.remote_name_req_flag:
print(INFO, 'Requesting the name of the scanned devices...')
if self.remote_name_req_flag and len(self.scanned_dev) != 0:
logger.info('Requesting the name of the scanned devices...')
for bd_addr in self.scanned_dev:
try:
name = HCI(self.iface).remote_name_request({
Expand All @@ -103,9 +107,9 @@ def inquiry(self, lap=0x9e8b33, inquiry_len=0x08, num_rsp=0x00):
print(bd_addr+':', blue(name))
break
else:
print(DEBUG, "Unknow:", data)
logger.debug('Unknow: {}'.format(data))
except KeyboardInterrupt as e:
print(INFO, "BR/EDR devices scan canceled\n")
logger.info('BR/EDR devices scan canceled\n')
HCI(self.iface).inquiry_cancel()

hci_close_dev(dd.fileno())
Expand All @@ -115,8 +119,8 @@ def pp_inquiry_result(self, params):
'''Parse and print HCI_Inquiry_Result.'''
num_rsp = params[0]
if num_rsp != 1:
print(INFO, 'Num_Responses in HCI_Inquiry_Result is %d.'%num_rsp)
print(DEBUG, 'HCI_Inquiry_Result:', params)
logger.info('Num_Responses in HCI_Inquiry_Result is %d.'%num_rsp)
logger.debug('HCI_Inquiry_Result: {}'.format(params))
return

bd_addr, page_scan_repetition_mode, reserved, cod, clk_offset = \
Expand Down Expand Up @@ -147,8 +151,8 @@ def pp_inquiry_result_with_rssi(self, params):
'''Parse and print HCI_Inquiry_Result_with_RSSI.'''
num_rsp = params[0]
if num_rsp != 1:
print(INFO, 'Num_Responses in HCI_Inquiry_Result_with_RSSI is %d.'%num_rsp)
print(DEBUG, 'HCI_Inquiry_Result_with_RSSI:', params)
logger.info('Num_Responses in HCI_Inquiry_Result_with_RSSI is %d.'%num_rsp)
logger.debug('HCI_Inquiry_Result_with_RSSI: {}'.format(params))
return

bd_addr, page_scan_repetition_mode, reserved, cod, clk_offset, rssi = \
Expand Down Expand Up @@ -179,8 +183,8 @@ def pp_extended_inquiry_result(self, params):
'''Parse and print HCI_Extended_Inquiry_Result'''
num_rsp = params[0]
if num_rsp != 1:
print(INFO, 'Num_Responses in HCI_Extended_Inquiry_Result is %d.'%num_rsp)
print(DEBUG, 'HCI_Extended_Inquiry_Result:', params)
logger.info('Num_Responses in HCI_Extended_Inquiry_Result is %d.'%num_rsp)
logger.debug('HCI_Extended_Inquiry_Result: {}'.format(params))
return

bd_addr, page_scan_repetition_mode, reserved, cod, \
Expand Down Expand Up @@ -223,9 +227,9 @@ def pp_page_scan_repetition_mode(val):

def pp_cod(cod:int):
'''Print and parse the Class of Device.'''
#print(DEBUG, 'br_scan.py pp_cod()')
logger.debug('Entered br_scan.py, pp_cod()')
if cod > 0xFFFFFF or cod & 0x000003 != 0:
print(WARNING, "CoD's Format Type is not format #1")
logger.warning('CoD\'s Format Type is not format #1')
return

print('\tService Class: %s' % bin(cod>>13))
Expand Down Expand Up @@ -315,7 +319,7 @@ def pp_ext_inquiry_rsp(ext_inq_rsp):
if length - 1 >= 4:
eir_data = data[1:]
if len(eir_data) % 4 != 0:
print('\t\t'+INFO, 'Invalid EIR data length: %d'%len(eir_data), eir_data)
logger.info('\t\tInvalid EIR data length: {} {}'.format(len(eir_data), eir_data))
continue
for i in range(0, len(eir_data), 4):
uuid = int.from_bytes(eir_data[i:i+4], byteorder='little')
Expand All @@ -327,7 +331,7 @@ def pp_ext_inquiry_rsp(ext_inq_rsp):
if length - 1 >= 16:
eir_data = data[1:]
if len(eir_data) % 16 != 0:
print('\t\t'+INFO, 'Invalid EIR data length: %d'%len(eir_data), eir_data)
logger.info('\t\tInvalid EIR data length: {} {}'.format(len(eir_data), eir_data))
continue
for i in range(0, len(eir_data), 16):
uuid = int.from_bytes(eir_data[i:i+16], byteorder='little')
Expand Down
8 changes: 3 additions & 5 deletions src/bluescan/gatt_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
import sys
import io

import pkg_resources
from bluepy.btle import Peripheral
from bluepy.btle import BTLEException
from pyclui import green, blue, yellow, red

from . import BlueScanner

import pkg_resources

from pyclui import green, blue, yellow, red, \
DEBUG, INFO, WARNING, ERROR

# char_uuid = pkg_resources.resource_string()
# service_uuid = pkg_resources.resource_string()
Expand Down Expand Up @@ -96,7 +94,7 @@ def scan(self, bdaddr, addr_type, include_descriptor:bool):
print(yellow('\tCharacteristic'), '(%s descriptors)' % len(descriptors))
#print('-'*8)
print('\t\tHandle: %#06x' % (characteristic.getHandle() - 1))
print('\t\tType: 0x2803 (tCharacteristic)')
print('\t\tType: 0x2803 (Characteristic)')
print('\t\tValue:')
print('\t\t\tCharacteristic properties:', green(characteristic.propertiesToString()))
print('\t\t\tCharacteristic value handle: %#06x' % characteristic.getHandle())
Expand Down
1 change: 0 additions & 1 deletion src/bluescan/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import subprocess

from pyclui import blue, green, yellow, red
from pyclui import DEBUG, ERROR, INFO, ERROR


def valid_bdaddr(addr:str) -> bool:
Expand Down
Loading

0 comments on commit b314562

Please sign in to comment.