Beispiel #1
0
 def cri_from_knx(cri: bytes) -> int:
     """Parse CRI (Connection Request Information)."""
     if cri[0] != ConnectRequest.CRI_LENGTH:
         raise CouldNotParseKNXIP("CRI has wrong length")
     if len(cri) < ConnectRequest.CRI_LENGTH:
         raise CouldNotParseKNXIP("CRI data has wrong length")
     self.request_type = ConnectRequestType(cri[1])
     self.flags = cri[2]
     return 4
Beispiel #2
0
 def header_from_knx(header: bytes) -> int:
     """Parse header bytes."""
     if header[0] != TunnellingRequest.HEADER_LENGTH:
         raise CouldNotParseKNXIP("connection header wrong length")
     if len(header) < TunnellingRequest.HEADER_LENGTH:
         raise CouldNotParseKNXIP("connection header wrong length")
     self.communication_channel_id = header[1]
     self.sequence_counter = header[2]
     return TunnellingRequest.HEADER_LENGTH
Beispiel #3
0
 def crd_from_knx(crd):
     """Parse CRD (Connection Response Data Block)."""
     if crd[0] != ConnectResponse.CRD_LENGTH:
         raise CouldNotParseKNXIP("CRD has wrong length")
     if len(crd) < ConnectResponse.CRD_LENGTH:
         raise CouldNotParseKNXIP("CRD data has wrong length")
     self.request_type = ConnectRequestType(crd[1])
     self.identifier = crd[2] * 256 + crd[3]
     return 4
Beispiel #4
0
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if raw[0] != TunnellingAck.BODY_LENGTH:
         raise CouldNotParseKNXIP("connection header wrong length")
     if len(raw) != TunnellingAck.BODY_LENGTH:
         raise CouldNotParseKNXIP("connection header wrong length")
     self.communication_channel_id = raw[1]
     self.sequence_counter = raw[2]
     self.status_code = ErrorCode(raw[3])
     return TunnellingAck.BODY_LENGTH
Beispiel #5
0
 def ack_from_knx(header: bytes) -> int:
     """Parse ACK information."""
     if header[0] != TunnellingAck.BODY_LENGTH:
         raise CouldNotParseKNXIP("connection header wrong length")
     if len(header) < TunnellingAck.BODY_LENGTH:
         raise CouldNotParseKNXIP("connection header wrong length")
     self.communication_channel_id = header[1]
     self.sequence_counter = header[2]
     self.status_code = ErrorCode(header[3])
     return 4
Beispiel #6
0
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) != SessionStatus.LENGTH:
         raise CouldNotParseKNXIP("SessionStatus has wrong length")
     try:
         self.status = SecureSessionStatusCode(raw[0])
     except ValueError as err:
         raise CouldNotParseKNXIP(
             f"SessionStatus has unsupported status code: {raw[0]}"
         ) from err
     return SessionStatus.LENGTH
Beispiel #7
0
 def from_knx(self, raw):
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) < HPAI.LENGTH:
         raise CouldNotParseKNXIP("wrong HPAI length")
     if raw[0] != HPAI.LENGTH:
         raise CouldNotParseKNXIP("wrong HPAI length")
     if raw[1] != HPAI.TYPE_UDP:
         raise CouldNotParseKNXIP("wrong HPAI type")
     self.ip_addr = "{}.{}.{}.{}".format(raw[2], raw[3], raw[4], raw[5])
     self.port = raw[6] * 256 + raw[7]
     return HPAI.LENGTH
Beispiel #8
0
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) < HPAI.LENGTH:
         raise CouldNotParseKNXIP("wrong HPAI length")
     if raw[0] != HPAI.LENGTH:
         raise CouldNotParseKNXIP("wrong HPAI length")
     if raw[1] != HPAI.TYPE_UDP:
         raise CouldNotParseKNXIP("wrong HPAI type")
     self.ip_addr = f"{raw[2]}.{raw[3]}.{raw[4]}.{raw[5]}"
     self.port = raw[6] * 256 + raw[7]
     return HPAI.LENGTH
Beispiel #9
0
    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]))
Beispiel #10
0
Datei: hpai.py Projekt: XKNX/xknx
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) < HPAI.LENGTH:
         raise CouldNotParseKNXIP("wrong HPAI length")
     if raw[0] != HPAI.LENGTH:
         raise CouldNotParseKNXIP("wrong HPAI length")
     try:
         self.protocol = HostProtocol(raw[1])
     except ValueError as err:
         raise CouldNotParseKNXIP("unsupported host protocol code") from err
     self.ip_addr = socket.inet_ntoa(raw[2:6])
     self.port = raw[6] * 256 + raw[7]
     return HPAI.LENGTH
Beispiel #11
0
    def from_knx(self, raw):
        """Parse/deserialize from KNX/IP raw data."""
        if len(raw) < 2:
            raise CouldNotParseKNXIP("could not parse DIB header")

        dib_length = raw[0]
        if len(raw) < dib_length:
            raise CouldNotParseKNXIP("DIB wrong length")

        self.dtc = DIBTypeCode(raw[1])
        self.data = raw[:dib_length]

        return dib_length
Beispiel #12
0
Datei: srp.py Projekt: XKNX/xknx
    def from_knx(data: bytes) -> SRP:
        """Convert the bytes to a SRP object."""
        if len(data) < SRP.SRP_HEADER_SIZE:
            raise CouldNotParseKNXIP("Data too short for SRP object.")

        size: int = data[0]
        if size > len(data):
            raise CouldNotParseKNXIP("SRP is larger than actual data size.")

        return SRP(
            srp_type=SearchRequestParameterType(data[1] & 0x7F),
            mandatory=bool(data[1] >> SRP.MANDATORY_BIT_INDEX),
            data=data[2:size],
        )
Beispiel #13
0
    def from_knx(self, raw: bytes) -> int:
        """Parse/deserialize from KNX/IP raw data."""
        if len(raw) < 2:
            raise CouldNotParseKNXIP("DIB header too small")
        length = raw[0]
        if len(raw) < length:
            raise CouldNotParseKNXIP("DIB wrong size")
        if DIBTypeCode(raw[1]) != DIBTypeCode.SUPP_SVC_FAMILIES:
            raise CouldNotParseKNXIP("DIB is no device info")

        for i in range(0, int((length - 2) / 2)):
            name = DIBServiceFamily(raw[i * 2 + 2])
            version = raw[i * 2 + 3]
            self.families.append(DIBSuppSVCFamilies.Family(name, version))
        return length
Beispiel #14
0
    def from_knx(self, raw: bytes) -> int:
        """Parse/deserialize from KNX/IP raw data."""
        if len(raw) < 2:
            raise CouldNotParseKNXIP("could not parse DIB header")

        dib_length = raw[0]
        if len(raw) < dib_length:
            raise CouldNotParseKNXIP("DIB wrong length")
        try:
            self.dtc = DIBTypeCode(raw[1])
        except ValueError:
            self.dtc = raw[1]
        self.data = raw[:dib_length]

        return dib_length
Beispiel #15
0
    def from_knx(self, data):
        """Parse/deserialize from KNX/IP raw data."""
        if len(data) < KNXIPHeader.HEADERLENGTH:
            raise CouldNotParseKNXIP("wrong connection header length")
        if data[0] != KNXIPHeader.HEADERLENGTH:
            raise CouldNotParseKNXIP("wrong connection header length")
        if data[1] != KNXIPHeader.PROTOCOLVERSION:
            raise CouldNotParseKNXIP("wrong protocol version")

        self.header_length = data[0]
        self.protocol_version = data[1]
        self.service_type_ident = KNXIPServiceType(data[2] * 256 + data[3])
        self.b4_reserve = data[4]
        self.total_length = data[5]
        return KNXIPHeader.HEADERLENGTH
Beispiel #16
0
 def handle_knxipframe(self, knxipframe: KNXIPFrame, source: HPAI) -> None:
     """Handle KNXIP Frame and call all callbacks matching the service type ident."""
     # TODO: disallow unencrypted frames with exceptions for discovery etc. eg. DescriptionResponse
     if isinstance(knxipframe.body, SecureWrapper):
         if not self.initialized:
             raise CouldNotParseKNXIP(
                 "Received SecureWrapper while Secure session not initialized"
             )
         new_sequence_number = int.from_bytes(
             knxipframe.body.sequence_information, "big")
         if not new_sequence_number > self._sequence_number_received:
             knx_logger.warning(
                 "Discarding SecureWrapper with invalid sequence number: %s",
                 knxipframe,
             )
             return
         try:
             knxipframe = self.decrypt_frame(knxipframe)
         except KNXSecureValidationError as err:
             knx_logger.warning("Could not decrypt KNXIPFrame: %s", err)
             # Frame shall be discarded
             return
         except CouldNotParseKNXIP as couldnotparseknxip:
             knx_logger.debug(
                 "Unsupported encrypted KNXIPFrame: %s",
                 couldnotparseknxip.description,
             )
             return
         self._sequence_number_received = new_sequence_number
         knx_logger.debug("Decrypted frame: %s", knxipframe)
     super().handle_knxipframe(knxipframe, source)
Beispiel #17
0
Datei: dib.py Projekt: XKNX/xknx
    def from_knx(self, raw: bytes) -> int:
        """Parse/deserialize from KNX/IP raw data."""
        if len(raw) < 2:
            raise CouldNotParseKNXIP("DIB header too small")
        length = raw[0]
        if (len(raw) < length) or (length % 2):
            raise CouldNotParseKNXIP("DIB wrong size")
        if DIBTypeCode(raw[1]) != self.type_code:
            raise CouldNotParseKNXIP(
                f"DIB has wrong type code for {self.__class__.__name__}")

        for pos in range(2, length, 2):
            name = DIBServiceFamily(raw[pos])
            version = raw[pos + 1]
            self.families.append(DIBSuppSVCFamilies.Family(name, version))
        return length
Beispiel #18
0
 def info_from_knx(info):
     """Parse info bytes."""
     if len(info) < 2:
         raise CouldNotParseKNXIP("Disconnect info has wrong length")
     self.communication_channel_id = info[0]
     self.status_code = ErrorCode(info[1])
     return 2
Beispiel #19
0
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) != SessionAuthenticate.LENGTH:
         raise CouldNotParseKNXIP("SessionAuthenticate has wrong length")
     self.user_id = raw[1]
     self.message_authentication_code = raw[2:]
     return SessionAuthenticate.LENGTH
Beispiel #20
0
    def init(self, service_type_ident: KNXIPServiceType) -> KNXIPBody:
        """Init object by service_type_ident. Will instanciate a body object depending on service_type_ident."""
        self.header.service_type_ident = service_type_ident

        body: KNXIPBody
        if service_type_ident == KNXIPServiceType.ROUTING_INDICATION:
            body = RoutingIndication(self.xknx)
        elif service_type_ident == KNXIPServiceType.CONNECT_REQUEST:
            body = ConnectRequest(self.xknx)
        elif service_type_ident == KNXIPServiceType.CONNECT_RESPONSE:
            body = ConnectResponse(self.xknx)
        elif service_type_ident == KNXIPServiceType.TUNNELLING_REQUEST:
            body = TunnellingRequest(self.xknx)
        elif service_type_ident == KNXIPServiceType.TUNNELLING_ACK:
            body = TunnellingAck(self.xknx)
        elif service_type_ident == KNXIPServiceType.SEARCH_REQUEST:
            body = SearchRequest(self.xknx)
        elif service_type_ident == KNXIPServiceType.SEARCH_RESPONSE:
            body = SearchResponse(self.xknx)
        elif service_type_ident == KNXIPServiceType.DISCONNECT_REQUEST:
            body = DisconnectRequest(self.xknx)
        elif service_type_ident == KNXIPServiceType.DISCONNECT_RESPONSE:
            body = DisconnectResponse(self.xknx)
        elif service_type_ident == KNXIPServiceType.CONNECTIONSTATE_REQUEST:
            body = ConnectionStateRequest(self.xknx)
        elif service_type_ident == KNXIPServiceType.CONNECTIONSTATE_RESPONSE:
            body = ConnectionStateResponse(self.xknx)
        else:
            raise CouldNotParseKNXIP(
                f"KNXIPServiceType not implemented: {service_type_ident.name}")
        self.body = body
        return body
Beispiel #21
0
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) != SessionRequest.LENGTH:
         raise CouldNotParseKNXIP("SessionRequest has wrong length")
     pos = self.control_endpoint.from_knx(raw)
     self.ecdh_client_public_key = raw[pos:]
     return SessionRequest.LENGTH
Beispiel #22
0
 def info_from_knx(info):
     """Parse info bytes."""
     if len(info) < 2:
         raise CouldNotParseKNXIP("Disconnect info has wrong length")
     self.communication_channel_id = info[0]
     # info[1] is reserved
     return 2
Beispiel #23
0
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) < 2:
         raise CouldNotParseKNXIP("Disconnect info has wrong length")
     self.communication_channel_id = raw[0]
     # raw[1] is reserved
     return self.control_endpoint.from_knx(raw[2:]) + 2
Beispiel #24
0
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) < ConnectionStateResponse.LENGTH:
         raise CouldNotParseKNXIP(
             "ConnectionStateResponse info has wrong length")
     self.communication_channel_id = raw[0]
     self.status_code = ErrorCode(raw[1])
     return ConnectionStateResponse.LENGTH
Beispiel #25
0
 def to_knx(self) -> List[int]:
     """Serialize to KNX/IP raw data."""
     if self.body is None:
         raise CouldNotParseKNXIP("No body defined in KNXIPFrame.")
     data = []
     data.extend(self.header.to_knx())
     data.extend(self.body.to_knx())
     return data
Beispiel #26
0
 def from_knx(self, raw: bytes) -> int:
     """Parse/deserialize from KNX/IP raw data."""
     if len(raw) != SessionResponse.LENGTH:
         raise CouldNotParseKNXIP("SessionResponse has wrong length")
     self.secure_session_id = int.from_bytes(raw[:2], "big")
     self.ecdh_server_public_key = raw[2:34]
     self.message_authentication_code = raw[34:]
     return SessionResponse.LENGTH
Beispiel #27
0
    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")

        # AddIL (Additional Info Length), as specified within
        # KNX Chapter 3.6.3/4.1.4.3 "Additional information."
        # Additional information is not yet parsed.
        addil = cemi[1]

        self.flags = cemi[2 + addil] * 256 + cemi[3 + addil]

        self.src_addr = PhysicalAddress((cemi[4 + addil], cemi[5 + addil]))

        if self.flags & CEMIFlags.DESTINATION_GROUP_ADDRESS:
            self.dst_addr = GroupAddress((cemi[6 + addil], cemi[7 + addil]),
                                         levels=self.xknx.address_format)
        else:
            self.dst_addr = PhysicalAddress((cemi[6 + addil], cemi[7 + addil]))

        self.mpdu_len = cemi[8 + addil]

        # TPCI (transport layer control information)   -> First 14 bit
        # APCI (application layer control information) -> Last  10 bit

        tpci_apci = cemi[9 + addil] * 256 + cemi[10 + addil]

        try:
            self.cmd = APCICommand(tpci_apci & 0xFFC0)
        except ValueError:
            raise CouldNotParseKNXIP(
                "APCI not supported: {0:#012b}".format(tpci_apci & 0xFFC0))

        apdu = cemi[10 + addil:]
        if len(apdu) != self.mpdu_len:
            raise CouldNotParseKNXIP(
                "APDU LEN should be {} but is {}".format(
                    self.mpdu_len, len(apdu)))

        if len(apdu) == 1:
            apci = tpci_apci & DPTBinary.APCI_BITMASK
            self.payload = DPTBinary(apci)
        else:
            self.payload = DPTArray(cemi[11 + addil:])

        return 10 + addil + len(apdu)
Beispiel #28
0
    def from_knx(self, data: bytes) -> int:
        """Parse/deserialize from KNX/IP raw data."""
        if len(data) < KNXIPHeader.HEADERLENGTH:
            raise IncompleteKNXIPFrame("wrong connection header length")
        if data[0] != KNXIPHeader.HEADERLENGTH:
            raise CouldNotParseKNXIP("wrong connection header length")
        # set immediately, as we need it for tcp stream parsing before raising exception
        self.total_length = data[5]
        if data[1] != KNXIPHeader.PROTOCOLVERSION:
            raise CouldNotParseKNXIP("wrong protocol version")

        try:
            self.service_type_ident = KNXIPServiceType(data[2] * 256 + data[3])
        except ValueError:
            raise CouldNotParseKNXIP(
                f"KNXIPServiceType unknown: {hex(data[2] * 256 + data[3])}")
        self.b4_reserve = data[4]
        return KNXIPHeader.HEADERLENGTH
Beispiel #29
0
    def from_knx_data_link_layer(self, cemi: bytes) -> int:
        """Parse L_DATA_IND, CEMIMessageCode.L_DATA_REQ, CEMIMessageCode.L_DATA_CON."""
        if len(cemi) < 10:
            raise UnsupportedCEMIMessage(
                f"CEMI too small. Length: {len(cemi)}; CEMI: {cemi.hex()}")

        # AddIL (Additional Info Length), as specified within
        # KNX Chapter 3.6.3/4.1.4.3 "Additional information."
        # Additional information is not yet parsed.
        addil = cemi[1]
        # Control field 1 and Control field 2 - first 2 octets after Additional information
        self.flags = cemi[2 + addil] * 256 + cemi[3 + addil]

        self.src_addr = IndividualAddress((cemi[4 + addil], cemi[5 + addil]))

        dst_is_group_address = bool(self.flags
                                    & CEMIFlags.DESTINATION_GROUP_ADDRESS)
        dst_raw_address = (cemi[6 + addil], cemi[7 + addil])
        self.dst_addr = (GroupAddress(dst_raw_address) if dst_is_group_address
                         else IndividualAddress(dst_raw_address))

        npdu_len = cemi[8 + addil]

        apdu = cemi[9 + addil:]
        if len(apdu) != (npdu_len +
                         1):  # TCPI octet not included in NPDU length
            raise CouldNotParseKNXIP(
                f"APDU LEN should be {npdu_len} but is {len(apdu) - 1} in CEMI: {cemi.hex()}"
            )

        # TPCI (transport layer control information)
        # - with control bit set -> 8 bit; no APDU
        # - no control bit set (data) -> First 6 bit
        # APCI (application layer control information) -> Last  10 bit of TPCI/APCI
        try:
            self.tpci = TPCI.resolve(raw_tpci=cemi[9 + addil],
                                     dst_is_group_address=dst_is_group_address)
        except ConversionError as err:
            raise UnsupportedCEMIMessage(
                f"TPCI not supported: {cemi[9 + addil]:#10b}") from err

        if self.tpci.control:
            if npdu_len:
                raise UnsupportedCEMIMessage(
                    f"Invalid length for control TPDU {self.tpci}: {npdu_len}")
            return 10 + addil

        _apci = apdu[0] * 256 + apdu[1]
        try:
            self.payload = APCI.resolve_apci(_apci)
        except ConversionError as err:
            raise UnsupportedCEMIMessage(
                f"APCI not supported: {_apci:#012b}") from err

        self.payload.from_knx(apdu)

        return 10 + addil + npdu_len
Beispiel #30
0
    def from_knx(self, data: bytes) -> int:
        """Parse/deserialize from KNX/IP raw data."""
        if len(data) < KNXIPHeader.HEADERLENGTH:
            raise CouldNotParseKNXIP("wrong connection header length")
        if data[0] != KNXIPHeader.HEADERLENGTH:
            raise CouldNotParseKNXIP("wrong connection header length")
        if data[1] != KNXIPHeader.PROTOCOLVERSION:
            raise CouldNotParseKNXIP("wrong protocol version")

        self.header_length = data[0]
        self.protocol_version = data[1]
        try:
            self.service_type_ident = KNXIPServiceType(data[2] * 256 + data[3])
        except ValueError:
            raise CouldNotParseKNXIP("KNXIPServiceType unknown: {}".format(
                hex(data[2] * 256 + data[3])))
        self.b4_reserve = data[4]
        self.total_length = data[5]
        return KNXIPHeader.HEADERLENGTH