def detect_pairing_feature(self, paddr, patype, timeout:int=10): """ """ # TODO Mac OS 会弹窗,需要解决。 hci = HCI(self.hci) logger.info("Detecting SMP pairing feature of %s, using %s\n"%(blue(paddr), blue(self.hci))) pairing_req = SM_Hdr(sm_command=btsmp.CmdCode.PAIRING_REQUEST) / \ SM_Pairing_Request(iocap="NoInputNoOutput", oob='Not Present', authentication=(0b00 << AUTHREQ_RFU_POS) | (0 << CT2_POS) | \ (0 << KEYPRESS_POS) | (1 << SC_POS) | (0 << MITM_POS) | \ (BONDING << BONDING_FLAGS_POS), max_key_size=16, initiator_key_distribution=(0b0000 << INIT_RESP_KEY_DIST_RFU_POS) \ | (1 << LINKKEY_POS) | (1 << SIGNKEY_POS) | (1 << IDKEY_POS) \ | (1 << ENCKEY_POS), responder_key_distribution=(0b0000 << INIT_RESP_KEY_DIST_RFU_POS) \ | (1 << LINKKEY_POS) | (1 << SIGNKEY_POS) | (1 << IDKEY_POS) \ | (1 << ENCKEY_POS)) event_params = None spinner = Halo(text="Scanning", spinner={'interval': 200, 'frames': ['', '.', '.'*2, '.'*3]}, placement='right') hci = HCI(self.hci) logger.info('Scanning LE LL Features of %s, using %s\n'%(blue(paddr), blue(self.hci))) spinner.start() try: event_params = hci.le_create_connection(paddr, patype, timeout=timeout) logger.debug(event_params) result = btsmp.send_pairing_request(event_params['Connection_Handle'], pairing_req, self.hci) logger.debug("detect_pairing_feature(), result: {}".format(result)) rsp = btsmp.recv_pairing_response(timeout, self.hci) logger.debug("detect_pairing_feature(), rsp: {}".format(rsp)) spinner.stop() pp_smp_pkt(rsp) except RuntimeError as e: logger.error(str(e)) except TimeoutError as e: output = subprocess.check_output(' '.join(['hciconfig', self.hci, 'reset']), stderr=STDOUT, timeout=60, shell=True) event_params = None logger.info("Timeout") # logger.error("detect_pairing_feature(), TimeoutError {}".format(e)) if event_params != None: hci.disconnect(event_params['Connection_Handle'], ControllerErrorCodes.UNSUPPORTED_REMOTE_FEATURE) return
def scan(self, remote_bd_addr:str): hci = HCI(self.iface) event_params = hci.create_connection({ 'BD_ADDR': remote_bd_addr, 'Packet_Type': 0xcc18, 'Page_Scan_Repetition_Mode': 0x02, 'Reserved': 0x00, 'Clock_Offset': 0x0000, 'Allow_Role_Switch': 0x01 }) if event_params['Status'] != 0: print(ERROR, 'Failed to create ACL connection') sys.exit(1) event_params = hci.read_remote_version_information(cmd_params={ 'Connection_Handle': event_params['Connection_Handle'] }) if event_params['Status'] != 0: print(ERROR, 'Failed to read remote version') sys.exit(1) print(blue('Version')) print(' Version:') print(' '*8+lm_vers[event_params['Version']], '(LMP)') print(' '*8+ll_vers[event_params['Version']], '(LL)') print(' Manufacturer name:', event_params['Manufacturer_Name']) print(' Subversion:', event_params['Subversion'], '\n') event_params = hci.read_remote_supported_features({ 'Connection_Handle': event_params['Connection_Handle'] }) if event_params['Status'] != 0: print(ERROR, 'Failed to read remote supported features') else: print(blue('LMP features')) pp_lmp_features(event_params['LMP_Features']) print() if not True if (event_params['LMP_Features'][7] >> 7) & 0x01 else False: sys.exit(1) print(blue('Extended LMP features')) event_params = hci.read_remote_extended_features({ 'Connection_Handle': event_params['Connection_Handle'], 'Page_Number': 0x00 }) if event_params['Status'] != 0: print(ERROR, 'Failed to read remote extented features') else: pp_ext_lmp_features(event_params['Extended_LMP_Features'], 0) for i in range(1, event_params['Maximum_Page_Number']+1): event_params = hci.read_remote_extended_features({ 'Connection_Handle': event_params['Connection_Handle'], 'Page_Number': i}) if event_params['Status'] != 0: print(ERROR, 'Failed to read remote extented features, page', i) else: pp_ext_lmp_features(event_params['Extended_LMP_Features'], i)
def scan_ll_feature(self, paddr, patype, timeout: int=10): """LL feature scanning paddr - Peer addresss for scanning LL features. patype - Peer address type, public or random. timeout - sec """ spinner = Halo(text="Scanning", spinner={'interval': 200, 'frames': ['', '.', '.'*2, '.'*3]}, placement='right') hci = HCI(self.hci) logger.info('Scanning LE LL Features of %s, using %s\n'%(blue(paddr), blue(self.hci))) spinner.start() try: event_params = hci.le_create_connection(paddr, patype, timeout=timeout) logger.debug(event_params) except RuntimeError as e: logger.error(str(e)) return except TimeoutError as e: logger.info("Timeout") # logger.error("TimeoutError {}".format(e)) return event_params = hci.le_read_remote_features(event_params['Connection_Handle']) spinner.stop() logger.debug(event_params) print(blue('LE LL Features:')) pp_le_features(event_params['LE_Features']) event_params = hci.disconnect(event_params['Connection_Handle'], ControllerErrorCodes.REMOTE_USER_TERM_CONN) logger.debug(event_params) return
def scan_ll_feature(self, paddr, patype): """LL feature scanning paddr - Peer addresss for scanning LL features. patype - Peer address type, public or random. """ hci = HCI(self.hci) logger.info('Scanning LE LL Features of %s, using %s\n' % (blue(paddr), blue(self.hci))) try: event_params = hci.le_create_connection( HCI_Cmd_LE_Create_Connection(paddr=bytes.fromhex( paddr.replace(':', ''))[::-1], patype=patype)) logger.debug(event_params) except RuntimeError as e: logger.error(e) return event_params = hci.le_read_remote_features( HCI_Cmd_LE_Read_Remote_Features( handle=event_params['Connection_Handle'])) logger.debug(event_params) print(blue('LE LL Features:')) pp_le_features(event_params['LE_Features']) event_params = hci.disconnect({ 'Connection_Handle': event_params['Connection_Handle'], 'Reason': ERR_REMOTE_USER_TERMINATED_CONNECTION }) logger.debug(event_params) return
def __init__(self, iface='hci0'): self.iface = iface self.devid = HCI.hcistr2devid(self.iface) try: self.hci_bdaddr = HCI(iface).read_bdaddr()['BD_ADDR'].upper() except Exception as e: logger.error("{}".format(e)) exit(1)
def detect_pairing_feature(self, paddr, patype, timeout: int = 10): """ """ hci = HCI(self.hci) logger.info("Detecting SMP pairing feature of %s, using %s\n" % (blue(paddr), blue(self.hci))) pairing_req = SM_Hdr(sm_command=btsmp.CmdCode.PAIRING_REQUEST) / \ SM_Pairing_Request(iocap="NoInputNoOutput", oob='Not Present', authentication=(0b00 << AUTHREQ_RFU_POS) | (0 << CT2_POS) | \ (0 << KEYPRESS_POS) | (1 << SC_POS) | (0 << MITM_POS) | \ (BONDING << BONDING_FLAGS_POS), max_key_size=16, initiator_key_distribution=(0b0000 << INIT_RESP_KEY_DIST_RFU_POS) \ | (1 << LINKKEY_POS) | (1 << SIGNKEY_POS) | (1 << IDKEY_POS) \ | (1 << ENCKEY_POS), responder_key_distribution=(0b0000 << INIT_RESP_KEY_DIST_RFU_POS) \ | (1 << LINKKEY_POS) | (1 << SIGNKEY_POS) | (1 << IDKEY_POS) \ | (1 << ENCKEY_POS)) event_params = None try: event_params = hci.le_create_connection( HCI_Cmd_LE_Create_Connection(paddr=bytes.fromhex( paddr.replace(':', ''))[::-1], patype=patype), timeout) logger.debug(event_params) result = btsmp.send_pairing_request( event_params['Connection_Handle'], pairing_req, self.hci) logger.debug("detect_pairing_feature(), result: {}".format(result)) rsp = btsmp.recv_pairing_response(timeout, self.hci) logger.debug("detect_pairing_feature(), rsp: {}".format(rsp)) pp_smp_pkt(rsp) except RuntimeError as e: logger.error(e) except TimeoutError as e: output = subprocess.check_output(' '.join( ['hciconfig', self.hci, 'reset']), stderr=STDOUT, timeout=60, shell=True) event_params = None logger.info("Timeout") # logger.error("detect_pairing_feature(), TimeoutError {}".format(e)) if event_params != None: hci.disconnect({ 'Connection_Handle': event_params['Connection_Handle'], 'Reason': 0x1A }) return
def inquiry(self, inquiry_len=0x08): 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 hci = HCI(self.iface) def inquiry_result_handler(result: bytes): event_code = result[0] logger.debug("Entered inquiry(), inquiry_result_handler()\n" "{}".format(HciEventCodes[event_code].name)) if event_code == HCI_Inquiry_Result.evt_code: self.pp_inquiry_result(result[2:]) elif event_code == HCI_Inquiry_Result_with_RSSI.evt_code: self.pp_inquiry_result_with_rssi(result[2:]) elif event_code == HCI_Extended_Inquiry_Result.evt_code: self.pp_extended_inquiry_result(result[2:]) else: logger.warning('Unknow inquiry result: {}'.format(result)) try: hci.inquiry(inquiry_len=inquiry_len, inquiry_result_handler=inquiry_result_handler) logger.info('Inquiry completed\n') 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.remote_name_request({ 'BD_ADDR': bytes.fromhex(bd_addr.replace(':', '')), 'Page_Scan_Repetition_Mode': 0x01, 'Reserved': 0x00, 'Clock_Offset': 0x0000 })['Remote_Name'].decode().strip() except Exception as e: print(e) name = '' print(bd_addr + ':', blue(name)) except HciRuntimeError as e: logger.error("{}".format(e)) except KeyboardInterrupt as e: logger.info('BR/EDR devices scan canceled\n') hci.inquiry_cancel() hci.close()
def scan_lmp_feature(self, paddr): hci = HCI(self.iface) conn_complete_evt = hci.create_connection( paddr, page_scan_repetition_mode=0x02) if conn_complete_evt.status != 0: logger.error('Failed to create ACL connection') sys.exit(1) event_params = hci.read_remote_version_information( cmd_params={'Connection_Handle': conn_complete_evt.conn_handle}) if event_params['Status'] != 0: logger.error('Failed to read remote version') sys.exit(1) print(blue('Version')) print(' Version:') print(' ' * 8 + lmp_vers[event_params['Version']], '(LMP)') print(' ' * 8 + ll_vers[event_params['Version']], '(LL)') print(' Manufacturer name:', green(company_identfiers[event_params['Manufacturer_Name']])) print(' Subversion:', event_params['Subversion'], '\n') complete_evt = hci.read_remote_supported_features( event_params['Connection_Handle']) if complete_evt.status != ControllerErrorCodes.SUCCESS: logger.error('Failed to read remote supported features') else: print(blue('LMP features')) pp_lmp_features(complete_evt.lmp_features) print() if not True if (complete_evt.lmp_features[7] >> 7) & 0x01 else False: sys.exit(1) print(blue('Extended LMP features')) complete_evt = hci.read_remote_extended_features( event_params['Connection_Handle'], 0x00) if complete_evt.status != ControllerErrorCodes.SUCCESS: logger.error('Failed to read remote extented features') else: pp_ext_lmp_features(complete_evt.ext_lmp_features, 0) for i in range(1, complete_evt.max_page_num + 1): complete_evt = hci.read_remote_extended_features( event_params['Connection_Handle'], i) if complete_evt.status != ControllerErrorCodes.SUCCESS: logger.error( 'Failed to read remote extented features, page {}'. format(i)) else: pp_ext_lmp_features(complete_evt.ext_lmp_features, i)
def init_hci(iface: str = 'hci0'): # hciconfig <hci> up 的前提是 rfkill 先 unblock subprocess.check_output('rfkill unblock %d' % find_rfkill_devid(iface), stderr=STDOUT, timeout=5, shell=True) subprocess.check_output('hciconfig {} up'.format(iface), stderr=STDOUT, timeout=5, shell=True) subprocess.check_output('systemctl restart bluetooth.service', stderr=STDOUT, timeout=5, shell=True) hci = HCI(iface) # 下面在发送各种 HCI command 时,如果出现如下异常: # BlockingIOError: [Errno 11] Resource temporarily unavailable # 那么可能是 hci socket 被设为了 non-blocking mode。 hci.inquiry_cancel() hci.exit_periodic_inquiry_mode() hci.write_scan_enable() # No scan enabled event_params = hci.le_set_advertising_enable() # Advertising is disabled if event_params['Status'] != 0x00: #print(WARNING, 'Status of HCI_LE_Set_Advertising_Enable command: 0x%02x'%event_params['Status']) pass try: hci.le_set_scan_enable({ 'LE_Scan_Enable': 0x00, # Scanning disabled 'Filter_Duplicates': 0x01 # Ignored }) except RuntimeError as e: #print(WARNING, e) pass hci.set_event_filter({'Filter_Type': 0x00}) # Clear All Filters event_params = hci.read_bdaddr() if event_params['Status'] != 0: raise RuntimeError else: local_bd_addr = event_params['BD_ADDR'].upper() # Clear bluetoothd cache cache_path = PosixPath('/var/lib/bluetooth/') / local_bd_addr / 'cache' if cache_path.exists(): for file in cache_path.iterdir(): os.remove(file) hci.close()
def init_hci(iface='hci0'): hci = HCI(iface) exitcode, output = subprocess.getstatusoutput('rfkill unblock %d' % find_rfkill_devid(iface)) if exitcode != 0: logger.error('rfkill: ' + output) sys.exit(exitcode) exitcode, output = subprocess.getstatusoutput("hciconfig up " + iface) if exitcode != 0: logger.error("Failed to up " + iface) sys.exit(exitcode) else: time.sleep(0.5) # hci.reset() hci.inquiry_cancel() hci.exit_periodic_inquiry_mode() hci.write_scan_enable() # No scan enabled event_params = hci.le_set_advertising_enable() # Advertising is disabled if event_params['Status'] != 0x00: #print(WARNING, 'Status of HCI_LE_Set_Advertising_Enable command: 0x%02x'%event_params['Status']) pass try: hci.le_set_scan_enable({ 'LE_Scan_Enable': 0x00, # Scanning disabled 'Filter_Duplicates': 0x01 # Ignored }) except RuntimeError as e: #print(WARNING, e) pass hci.set_event_filter({'Filter_Type': 0x00}) # Clear All Filters event_params = hci.read_bdaddr() if event_params['Status'] != 0: raise RuntimeError else: local_bd_addr = event_params['BD_ADDR'].upper() # Clear bluetoothd cache cache_path = PosixPath('/var/lib/bluetooth/') / local_bd_addr / 'cache' if cache_path.exists(): for file in cache_path.iterdir(): os.remove(file)
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') self.scanned_dev = [] cmd_params = lap.to_bytes(3, 'little') + \ inquiry_len.to_bytes(1, 'little') + num_rsp.to_bytes(1, 'little') # If no filter is set, we can't receive any inquiry result. flt = hci_filter_new() hci_filter_set_ptype(flt, HCI_EVENT_PKT) hci_filter_all_events(flt) dd = hci_open_dev(self.devid) dd.setsockopt(SOL_HCI, HCI_FILTER, flt) hci_send_cmd(dd, OGF_LINK_CTL, OCF_INQUIRY, cmd_params) try: while True: data = dd.recv(300) if len(data) >= 4: event_code = data[1] if event_code == EVT_CMD_STATUS: # print(DEBUG, 'HCI_Command_Status') pass elif event_code == EVT_INQUIRY_RESULT: print(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') self.pp_inquiry_result_with_rssi(data[3:]) elif event_code == EVT_EXTENDED_INQUIRY_RESULT: # print(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') break else: print(DEBUG, "Unknow:", data) except KeyboardInterrupt as e: print(INFO, "BR/EDR devices scan canceled\n") HCI(self.iface).inquiry_cancel() hci_close_dev(dd.fileno())
def inquiry(self, lap=0x9e8b33, inquiry_len=0x08, num_rsp=0x00): 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 cmd_params = lap.to_bytes(3, 'little') + \ inquiry_len.to_bytes(1, 'little') + num_rsp.to_bytes(1, 'little') # If no filter is set, we can't receive any inquiry result. flt = hci_filter_new() hci_filter_set_ptype(flt, HCI_EVENT_PKT) hci_filter_all_events(flt) dd = hci_open_dev(self.devid) dd.setsockopt(SOL_HCI, HCI_FILTER, flt) hci_send_cmd(dd, OGF_LINK_CTL, OCF_INQUIRY, cmd_params) try: while True: data = dd.recv(300) if len(data) >= 4: event_code = data[1] if event_code == EVT_CMD_STATUS: logger.debug('HCI_Command_Status') pass elif event_code == EVT_INQUIRY_RESULT: logger.debug('HCI_Inquiry_Result') self.pp_inquiry_result(data[3:]) elif event_code == EVT_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: logger.debug('HCI_Extended_Inquiry_Result') self.pp_extended_inquiry_result(data[3:]) elif event_code == EVT_INQUIRY_COMPLETE: logger.debug('HCI_Inquiry_Complete') logger.info('Inquiry completed\n') 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( { 'BD_ADDR': bytes.fromhex( bd_addr.replace(':', '')), 'Page_Scan_Repetition_Mode': 0x01, 'Reserved': 0x00, 'Clock_Offset': 0x0000 })['Remote_Name'].decode().strip() except Exception as e: print(e) name = '' print(bd_addr + ':', blue(name)) break else: logger.debug('Unknow: {}'.format(data)) except KeyboardInterrupt as e: logger.info('BR/EDR devices scan canceled\n') HCI(self.iface).inquiry_cancel() hci_close_dev(dd.fileno())