From 6817b2c4cb1b39fb2d70600dce024edc25b50088 Mon Sep 17 00:00:00 2001 From: farmio Date: Mon, 23 Mar 2020 00:18:36 +0100 Subject: [PATCH] If we don't understand the CEMI Message ignore it and move on (eg. send TunnellingAck). Previously we would raise CuldNotParseKNXIP and loose the connection. If we don't understand the CEMI Message ignore it and move on (eg. send TunnellingAck). Previously we would raise CuldNotParseKNXIP and loose the connection. --- test/knxip_tests/cemi_frame_test.py | 16 +++++---------- xknx/exceptions/__init__.py | 3 ++- xknx/exceptions/exception.py | 16 ++++++++++++++- xknx/knxip/cemi_frame.py | 30 +++++++++++++++++------------ 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/test/knxip_tests/cemi_frame_test.py b/test/knxip_tests/cemi_frame_test.py index b305a3b30..711762ecf 100644 --- a/test/knxip_tests/cemi_frame_test.py +++ b/test/knxip_tests/cemi_frame_test.py @@ -5,7 +5,7 @@ from pytest import fixture, raises from xknx.dpt import DPTBinary -from xknx.exceptions import CouldNotParseKNXIP +from xknx.exceptions import CouldNotParseKNXIP, UnsupportedCEMIMessage from xknx.knxip.cemi_frame import CEMIFrame from xknx.knxip.knxip_enum import APCICommand, CEMIMessageCode from xknx.telegram import PhysicalAddress @@ -50,8 +50,8 @@ def test_valid_command(frame): def test_invalid_tpci_apci(frame): """Test for invalid APCICommand""" - with raises(CouldNotParseKNXIP, match=r".*APCI not supported: .*"): - frame.from_knx(get_data(0x29, 0, 0, 0, 0, 1, 0xFFC0, [])) + with raises(UnsupportedCEMIMessage, match=r".*APCI not supported: .*"): + frame.from_knx_data_link_layer(get_data(0x29, 0, 0, 0, 0, 1, 0xFFC0, [])) def test_invalid_apdu_len(frame): @@ -62,11 +62,5 @@ def test_invalid_apdu_len(frame): def test_invalid_invalid_len(frame): """Test for invalid cemi len""" - with raises(CouldNotParseKNXIP, match=r".*CEMI too small"): - frame.from_knx(get_data(0x29, 0, 0, 0, 0, 2, 0, [])[:5]) - - -def test_invalid_invalid_code(frame): - """Test for invalid cemi code""" - with raises(CouldNotParseKNXIP, match=r".*Could not understand CEMIMessageCode"): - frame.from_knx(get_data(0x0, 0, 0, 0, 0, 2, 0, [])) + with raises(UnsupportedCEMIMessage, match=r".*CEMI too small.*"): + frame.from_knx_data_link_layer(get_data(0x29, 0, 0, 0, 0, 2, 0, [])[:5]) diff --git a/xknx/exceptions/__init__.py b/xknx/exceptions/__init__.py index 0875fb6fd..95609fa09 100644 --- a/xknx/exceptions/__init__.py +++ b/xknx/exceptions/__init__.py @@ -2,4 +2,5 @@ # flake8: noqa from .exception import ( ConversionError, CouldNotParseAddress, CouldNotParseKNXIP, - CouldNotParseTelegram, DeviceIllegalValue, XKNXException) + CouldNotParseTelegram, DeviceIllegalValue, UnsupportedCEMIMessage, + XKNXException) diff --git a/xknx/exceptions/exception.py b/xknx/exceptions/exception.py index 626299bdf..187029529 100644 --- a/xknx/exceptions/exception.py +++ b/xknx/exceptions/exception.py @@ -39,7 +39,7 @@ class CouldNotParseKNXIP(XKNXException): """Exception class for wrong KNXIP data.""" def __init__(self, description=""): - """Initialize CouldNotParseTelegram class.""" + """Initialize CouldNotParseKNXIP class.""" super().__init__() self.description = description @@ -49,6 +49,20 @@ def __str__(self): .format(self.description) +class UnsupportedCEMIMessage(XKNXException): + """Exception class for unsupported CEMI Messages.""" + + def __init__(self, description=""): + """Initialize UnsupportedCEMIMessage class.""" + super().__init__() + self.description = description + + def __str__(self): + """Return object as readable string.""" + return '' \ + .format(self.description) + + class ConversionError(XKNXException): """Exception class for error while converting one type to another.""" diff --git a/xknx/knxip/cemi_frame.py b/xknx/knxip/cemi_frame.py index 1fc80eadd..dffa50642 100644 --- a/xknx/knxip/cemi_frame.py +++ b/xknx/knxip/cemi_frame.py @@ -12,7 +12,8 @@ File: AN117 v02.01 KNX IP Communication Medium DV.pdf """ from xknx.dpt import DPTArray, DPTBinary -from xknx.exceptions import ConversionError, CouldNotParseKNXIP +from xknx.exceptions import ( + ConversionError, CouldNotParseKNXIP, UnsupportedCEMIMessage) from xknx.telegram import GroupAddress, PhysicalAddress, Telegram, TelegramType from .body import KNXIPBody @@ -106,20 +107,25 @@ def calculated_length(self): def from_knx(self, raw): """Parse/deserialize from KNX/IP raw data.""" try: - self.code = CEMIMessageCode(raw[0]) - except ValueError: - raise CouldNotParseKNXIP("Could not understand CEMIMessageCode: {0} ".format(raw[0])) - - if self.code == CEMIMessageCode.L_DATA_IND or \ - self.code == CEMIMessageCode.L_Data_REQ or \ - self.code == CEMIMessageCode.L_DATA_CON: - return self.from_knx_data_link_layer(raw) - raise CouldNotParseKNXIP("Could not understand CEMIMessageCode: {0} / {1}".format(self.code, raw[0])) + try: + self.code = CEMIMessageCode(raw[0]) + except ValueError: + raise UnsupportedCEMIMessage("CEMIMessageCode not implemented: {0} ".format(raw[0])) + + if self.code == CEMIMessageCode.L_DATA_IND or \ + self.code == CEMIMessageCode.L_Data_REQ or \ + self.code == CEMIMessageCode.L_DATA_CON: + return self.from_knx_data_link_layer(raw) + raise UnsupportedCEMIMessage("Could not handle CEMIMessageCode: {0} / {1}".format(self.code, raw[0])) + except UnsupportedCEMIMessage as unsupported_cemi_err: + self.xknx.logger.warning("Ignoring not implemented CEMI: %s", unsupported_cemi_err) + return len(raw) def from_knx_data_link_layer(self, cemi): """Parse L_DATA_IND, CEMIMessageCode.L_Data_REQ, CEMIMessageCode.L_DATA_CON.""" if len(cemi) < 11: - raise CouldNotParseKNXIP("CEMI too small") + # eg. ETS Line-Scan issues L_DATA_IND with length 10 + raise UnsupportedCEMIMessage("CEMI too small. Length: {0}; CEMI: {1}".format(len(cemi), cemi)) # AddIL (Additional Info Length), as specified within # KNX Chapter 3.6.3/4.1.4.3 "Additional information." @@ -146,7 +152,7 @@ def from_knx_data_link_layer(self, cemi): try: self.cmd = APCICommand(tpci_apci & 0xFFC0) except ValueError: - raise CouldNotParseKNXIP( + raise UnsupportedCEMIMessage( "APCI not supported: {0:#012b}".format(tpci_apci & 0xFFC0)) apdu = cemi[10 + addil:]