diff --git a/bumble/device.py b/bumble/device.py index 66178fb..ff3c349 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -182,6 +182,7 @@ from .core import ( BaseBumbleError, ConnectionParameterUpdateError, CommandTimeoutError, + ConnectionParameters, ConnectionPHY, InvalidArgumentError, InvalidOperationError, @@ -1304,6 +1305,7 @@ class Connection(CompositeEventEmitter): handle: int transport: int self_address: Address + self_resolvable_address: Optional[Address] peer_address: Address peer_resolvable_address: Optional[Address] peer_le_features: Optional[LeFeatureMask] @@ -1351,6 +1353,7 @@ class Connection(CompositeEventEmitter): handle, transport, self_address, + self_resolvable_address, peer_address, peer_resolvable_address, role, @@ -1362,6 +1365,7 @@ class Connection(CompositeEventEmitter): self.handle = handle self.transport = transport self.self_address = self_address + self.self_resolvable_address = self_resolvable_address self.peer_address = peer_address self.peer_resolvable_address = peer_resolvable_address self.peer_name = None # Classic only @@ -1395,6 +1399,7 @@ class Connection(CompositeEventEmitter): None, BT_BR_EDR_TRANSPORT, device.public_address, + None, peer_address, None, role, @@ -1553,7 +1558,9 @@ class Connection(CompositeEventEmitter): f'Connection(handle=0x{self.handle:04X}, ' f'role={self.role_name}, ' f'self_address={self.self_address}, ' - f'peer_address={self.peer_address})' + f'self_resolvable_address={self.self_resolvable_address}, ' + f'peer_address={self.peer_address}, ' + f'peer_resolvable_address={self.peer_resolvable_address})' ) @@ -1586,6 +1593,7 @@ class DeviceConfiguration: irk: bytes = bytes(16) # This really must be changed for any level of security keystore: Optional[str] = None address_resolution_offload: bool = False + address_generation_offload: bool = False cis_enabled: bool = False def __post_init__(self) -> None: @@ -1891,6 +1899,7 @@ class Device(CompositeEventEmitter): self.connectable = config.connectable self.classic_accept_any = config.classic_accept_any self.address_resolution_offload = config.address_resolution_offload + self.address_generation_offload = config.address_generation_offload # Extended advertising. self.extended_advertising_sets: Dict[int, AdvertisingSet] = {} @@ -2313,7 +2322,7 @@ class Device(CompositeEventEmitter): # Create a host-side address resolver self.address_resolver = smp.AddressResolver(resolving_keys) - if self.address_resolution_offload: + if self.address_resolution_offload or self.address_generation_offload: await self.send_command(HCI_LE_Clear_Resolving_List_Command()) # Add an empty entry for non-directed address generation. @@ -4158,12 +4167,14 @@ class Device(CompositeEventEmitter): @host_event_handler def on_connection( self, - connection_handle, - transport, - peer_address, - role, - connection_parameters, - ): + connection_handle: int, + transport: int, + peer_address: Address, + self_resolvable_address: Optional[Address], + peer_resolvable_address: Optional[Address], + role: int, + connection_parameters: ConnectionParameters, + ) -> None: logger.debug( f'*** Connection: [0x{connection_handle:04X}] ' f'{peer_address} {"" if role is None else HCI_Constant.role_name(role)}' @@ -4184,15 +4195,15 @@ class Device(CompositeEventEmitter): return - # Resolve the peer address if we can - peer_resolvable_address = None - if self.address_resolver: - if peer_address.is_resolvable: - resolved_address = self.address_resolver.resolve(peer_address) - if resolved_address is not None: - logger.debug(f'*** Address resolved as {resolved_address}') - peer_resolvable_address = peer_address - peer_address = resolved_address + if peer_resolvable_address is None: + # Resolve the peer address if we can + if self.address_resolver: + if peer_address.is_resolvable: + resolved_address = self.address_resolver.resolve(peer_address) + if resolved_address is not None: + logger.debug(f'*** Address resolved as {resolved_address}') + peer_resolvable_address = peer_address + peer_address = resolved_address self_address = None if role == HCI_CENTRAL_ROLE: @@ -4223,12 +4234,19 @@ class Device(CompositeEventEmitter): else self.random_address ) + # Convert all-zeros addresses into None. + if self_resolvable_address == Address.ANY_RANDOM: + self_resolvable_address = None + if peer_resolvable_address == Address.ANY_RANDOM: + peer_resolvable_address = None + # Create a connection. connection = Connection( self, connection_handle, transport, self_address, + self_resolvable_address, peer_address, peer_resolvable_address, role, @@ -4239,9 +4257,10 @@ class Device(CompositeEventEmitter): if role == HCI_PERIPHERAL_ROLE and self.legacy_advertiser: if self.legacy_advertiser.auto_restart: + advertiser = self.legacy_advertiser connection.once( 'disconnection', - lambda _: self.abort_on('flush', self.legacy_advertiser.start()), + lambda _: self.abort_on('flush', advertiser.start()), ) else: self.legacy_advertiser = None diff --git a/bumble/hci.py b/bumble/hci.py index ba92f03..7e83f2f 100644 --- a/bumble/hci.py +++ b/bumble/hci.py @@ -1839,6 +1839,12 @@ class Address: data, offset, Address.PUBLIC_DEVICE_ADDRESS ) + @staticmethod + def parse_random_address(data, offset): + return Address.parse_address_with_type( + data, offset, Address.RANDOM_DEVICE_ADDRESS + ) + @staticmethod def parse_address_with_type(data, offset, address_type): return offset + 6, Address(data[offset : offset + 6], address_type) @@ -1965,7 +1971,8 @@ class Address: def __eq__(self, other): return ( - self.address_bytes == other.address_bytes + isinstance(other, Address) + and self.address_bytes == other.address_bytes and self.is_public == other.is_public ) @@ -5178,8 +5185,8 @@ class HCI_LE_Data_Length_Change_Event(HCI_LE_Meta_Event): ), ('peer_address_type', Address.ADDRESS_TYPE_SPEC), ('peer_address', Address.parse_address_preceded_by_type), - ('local_resolvable_private_address', Address.parse_address), - ('peer_resolvable_private_address', Address.parse_address), + ('local_resolvable_private_address', Address.parse_random_address), + ('peer_resolvable_private_address', Address.parse_random_address), ('connection_interval', 2), ('peripheral_latency', 2), ('supervision_timeout', 2), diff --git a/bumble/host.py b/bumble/host.py index 9d43fce..8085d5c 100644 --- a/bumble/host.py +++ b/bumble/host.py @@ -772,6 +772,8 @@ class Host(AbortableEventEmitter): event.connection_handle, BT_LE_TRANSPORT, event.peer_address, + getattr(event, 'local_resolvable_private_address', None), + getattr(event, 'peer_resolvable_private_address', None), event.role, connection_parameters, ) @@ -817,6 +819,8 @@ class Host(AbortableEventEmitter): event.bd_addr, None, None, + None, + None, ) else: logger.debug(f'### BR/EDR CONNECTION FAILED: {event.status}') diff --git a/bumble/smp.py b/bumble/smp.py index f95ff1c..9eba42d 100644 --- a/bumble/smp.py +++ b/bumble/smp.py @@ -767,8 +767,11 @@ class Session: self.oob_data_flag = 0 if pairing_config.oob is None else 1 # Set up addresses - self_address = connection.self_address + self_address = connection.self_resolvable_address or connection.self_address peer_address = connection.peer_resolvable_address or connection.peer_address + logger.debug( + f"pairing with self_address={self_address}, peer_address={peer_address}" + ) if self.is_initiator: self.ia = bytes(self_address) self.iat = 1 if self_address.is_random else 0 diff --git a/tests/device_test.py b/tests/device_test.py index a15353e..3b30f60 100644 --- a/tests/device_test.py +++ b/tests/device_test.py @@ -290,6 +290,8 @@ async def test_legacy_advertising_disconnection(auto_restart): 0x0001, BT_LE_TRANSPORT, peer_address, + None, + None, BT_PERIPHERAL_ROLE, ConnectionParameters(0, 0, 0), ) @@ -339,6 +341,8 @@ async def test_extended_advertising_connection(own_address_type): 0x0001, BT_LE_TRANSPORT, peer_address, + None, + None, BT_PERIPHERAL_ROLE, ConnectionParameters(0, 0, 0), ) @@ -379,6 +383,8 @@ async def test_extended_advertising_connection_out_of_order(own_address_type): 0x0001, BT_LE_TRANSPORT, peer_address, + None, + None, BT_PERIPHERAL_ROLE, ConnectionParameters(0, 0, 0), )