Skip to content

Commit

Permalink
Merge pull request #296 from ikalchev/v3.1.0
Browse files Browse the repository at this point in the history
V3.1.0
  • Loading branch information
ikalchev authored Dec 13, 2020
2 parents d3c576c + cd31df8 commit 96fb1aa
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 15 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ Sections
### Developers
-->

## [3.1.0] - 2020-12-13

### Fixed
- Ensure an error response is generated on exception. [#292](https://github.com/ikalchev/HAP-python/pull/292)
- Improve error reporting during pairing. [#289](https://github.com/ikalchev/HAP-python/pull/289)
- Handle request for an empty read instead of throwing an exception. [#288](https://github.com/ikalchev/HAP-python/pull/288)
- Fix thread safety in get characteristics. [#287](https://github.com/ikalchev/HAP-python/pull/287)

## [3.0.0] - 2020-07-25

### Added
Expand Down
2 changes: 2 additions & 0 deletions pyhap/accessory_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,8 @@ def get_characteristics(self, char_ids):
available = True
else:
acc = self.accessory.accessories.get(aid)
if acc is None:
continue
available = acc.available
char = acc.iid_manager.get_obj(iid)

Expand Down
2 changes: 1 addition & 1 deletion pyhap/const.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""This module contains constants used by other modules."""
MAJOR_VERSION = 3
MINOR_VERSION = 0
MINOR_VERSION = 1
PATCH_VERSION = 0
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
Expand Down
43 changes: 29 additions & 14 deletions pyhap/hap_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class HAP_SERVER_STATUS:
OPERATION_TIMED_OUT = -70408
RESOURCE_DOES_NOT_EXIST = -70409
INVALID_VALUE_IN_REQUEST = -70410
INSUFFICIENT_AUTHORIZATION = -70411


# Error codes and the like, guessed by packet inspection
Expand Down Expand Up @@ -91,6 +92,10 @@ def hap_hkdf(key, salt, info):
return hkdf.derive(key)


class TimeoutException(Exception):
pass


class UnprivilegedRequestException(Exception):
pass

Expand Down Expand Up @@ -269,14 +274,20 @@ def dispatch(self):
try:
getattr(self, self.HANDLERS[self.command][path])()
except NotAllowedInStateException:
self.send_response(403)
self.end_response(b'')
self.send_response_with_status(403, HAP_SERVER_STATUS.INSUFFICIENT_AUTHORIZATION)
except UnprivilegedRequestException:
response = {"status": HAP_SERVER_STATUS.INSUFFICIENT_PRIVILEGES}
data = json.dumps(response).encode("utf-8")
self.send_response(401)
self.send_header("Content-Type", self.JSON_RESPONSE_TYPE)
self.end_response(data)
self.send_response_with_status(401, HAP_SERVER_STATUS.INSUFFICIENT_PRIVILEGES)
except TimeoutException:
self.send_response_with_status(500, HAP_SERVER_STATUS.OPERATION_TIMED_OUT)
except Exception: # pylint: disable=broad-except
logger.exception("Failed to process request for: %s", path)
self.send_response_with_status(500, HAP_SERVER_STATUS.SERVICE_COMMUNICATION_FAILURE)

def send_response_with_status(self, http_code, hap_server_status):
"""Send a generic HAP status response."""
self.send_response(http_code)
self.send_header("Content-Type", self.JSON_RESPONSE_TYPE)
self.end_response(json.dumps({"status": hap_server_status}).encode("utf-8"))

def handle_pairing(self):
"""Handles arbitrary step of the pairing process."""
Expand Down Expand Up @@ -430,8 +441,7 @@ def _pairing_five(self, client_username, client_ltpk, encryption_key):
should_confirm = self.accessory_handler.pair(client_uuid, client_ltpk)

if not should_confirm:
self.send_response(500)
self.end_response(b'')
self.send_response_with_status(500, HAP_SERVER_STATUS.INVALID_VALUE_IN_REQUEST)
return

tlv_data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x06',
Expand All @@ -456,7 +466,7 @@ def handle_pair_verify(self):
elif sequence == b'\x03':
self._pair_verify_two(tlv_objects)
else:
raise ValueError
raise ValueError("Unknown pairing sequence of %s during pair verify" % (sequence))

def _pair_verify_one(self, tlv_objects):
"""Generate new session key pair and send a proof to the client.
Expand Down Expand Up @@ -610,7 +620,7 @@ def handle_pairings(self):
elif request_type == 4:
self._handle_remove_pairing(tlv_objects)
else:
raise ValueError
raise ValueError("Unknown pairing request type of %s during pair verify" % (request_type))

def _handle_add_pairing(self, tlv_objects):
"""Update client information."""
Expand All @@ -621,8 +631,7 @@ def _handle_add_pairing(self, tlv_objects):
should_confirm = self.accessory_handler.pair(
client_uuid, client_public)
if not should_confirm:
self.send_response(500)
self.end_response(b'')
self.send_response_with_status(500, HAP_SERVER_STATUS.INVALID_VALUE_IN_REQUEST)
return

data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02")
Expand Down Expand Up @@ -765,7 +774,13 @@ def recv(self, buflen=1042, flags=0):
The received full cipher blocks are decrypted and returned and partial cipher
blocks are buffered locally.
"""
assert not flags and buflen
assert not flags

if buflen == 0:
# If the reads get aligned just right, it possible that we
# could be asked to read zero bytes. Since we do not want to block
# we return an empty bytes string.
return b""

result = self.curr_decrypted

Expand Down

0 comments on commit 96fb1aa

Please sign in to comment.