def __init__( self, files: Iterable[Union[str, Path]], checksum_module: str = None, skip_model_verification: bool = False, skip_message_verification: bool = False, split_disjunctions: bool = False, ): model = self._create_model([Path(f) for f in files], skip_model_verification, split_disjunctions) checksum_functions = self._parse_checksum_module(checksum_module) missing_checksum_definitions = { (str(message.identifier), str(field_identifier)) for message in model.messages for field_identifier in message.checksums } - {(str(message_name), field_name) for message_name, checksum_mapping in checksum_functions.items() for field_name in checksum_mapping} if len(missing_checksum_definitions) != 0: raise ValidationError( "missing checksum definition for " + ", ".join([ f'field "{field_name}" of "{message_name}"' for message_name, field_name in missing_checksum_definitions ])) try: self._pyrflx = PyRFLX(model, checksum_functions, skip_message_verification) except PyRFLXError as e: raise ValidationError(f"invalid checksum definition: {e}") from e
def test_tlv_message_with_not_operator_exhausting() -> None: message = Message( "TLV::Message_With_Not_Operator_Exhausting", [ Link(INITIAL, Field("Tag")), Link( Field("Tag"), Field("Length"), Not(Not(Not(NotEqual(Variable("Tag"), Variable("Msg_Data"))))), ), Link( Field("Tag"), FINAL, reduce( lambda acc, f: f(acc), [Not, Not] * 16, Not( Or( Not( Not( Equal(Variable("Tag"), Variable("Msg_Data")))), Not(Equal(Variable("Tag"), Variable("Msg_Error"))), )), ), ), Link(Field("Length"), Field("Value"), size=Mul(Variable("Length"), Number(8))), Link(Field("Value"), FINAL), ], { Field("Tag"): TLV_TAG, Field("Length"): TLV_LENGTH, Field("Value"): OPAQUE }, ) with pytest.raises( FatalError, match=re.escape( "failed to simplify complex expression `not (not (not (not " "(not (not (not (not (not (not (not (not (not (not (not (not " "(not (not (not (not (not (not (not (not (not (not (not (not " "(not (not (not (not (not (not (not (Tag = TLV::Msg_Data))\n" " " "or not (Tag = TLV::Msg_Error))))))))))))))))))))))))))))))))))` " "after `16` iterations, best effort: " "`not (not (not (not (not (not (not (not (not (not (not (not (not " "(not (not (not (not (Tag = TLV::Msg_Data\n" " or Tag /= TLV::Msg_Error)))))))))))))))))`"), ): model = PyRFLX(model=Model([TLV_TAG, TLV_LENGTH, message])) pkg = model.package("TLV") msg = pkg.new_message("Message_With_Not_Operator_Exhausting") test_bytes = b"\x01\x00\x04\x00\x00\x00\x00" msg.parse(test_bytes)
def test_no_verification_sequence_nested_messages( sequence_message_package: Package, message_sequence_value: MessageValue) -> None: sequence_message_one = sequence_message_package.new_message( "Sequence_Element") sequence_message_one.set("Byte", 5) sequence_message_two = sequence_message_package.new_message( "Sequence_Element") sequence_message_two.set("Byte", 6) sequence: List[TypeValue] = [sequence_message_one, sequence_message_two] message_sequence_value.set("Length", 2) message_sequence_value.set("Sequence_Field", sequence) assert message_sequence_value.valid_message pyrflx_ = PyRFLX.from_specs( [SPEC_DIR / "sequence_message.rflx"], skip_model_verification=True, skip_message_verification=True, ) sequence_message_package_unv = pyrflx_.package("Sequence_Message") sequence_message_unv = sequence_message_package_unv.new_message( "Message_Sequence") sequence_element_one_unv = sequence_message_package_unv.new_message( "Sequence_Element") sequence_element_one_unv.set("Byte", 5) sequence_element_two_unv = sequence_message_package_unv.new_message( "Sequence_Element") sequence_element_two_unv.set("Byte", 6) sequence_unv: List[TypeValue] = [ sequence_element_one_unv, sequence_element_two_unv ] sequence_message_unv.set("Length", 2) sequence_message_unv.set("Sequence_Field", sequence_unv) assert sequence_message_unv.valid_message assert sequence_message_unv.bytestring == message_sequence_value.bytestring
def __init__(self) -> None: pyrflx = PyRFLX(["specs/icmp.rflx"]) self.package_icmp = pyrflx["ICMP"] self.icmp_data = ( b"\x4a\xfc\x0d\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17" b"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" b"\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37" )
def test_tlv_message_with_not_operator() -> None: message = Message( "TLV::Message_With_Not_Operator", [ Link(INITIAL, Field("Tag")), Link( Field("Tag"), Field("Length"), Not(Not(Not(NotEqual(Variable("Tag"), Variable("Msg_Data"))))), ), Link( Field("Tag"), FINAL, Not( Not( Not( Or( Not( Not( Equal(Variable("Tag"), Variable("Msg_Data")))), Not( Equal(Variable("Tag"), Variable("Msg_Error"))), )))), ), Link(Field("Length"), Field("Value"), size=Mul(Variable("Length"), Number(8))), Link(Field("Value"), FINAL), ], { Field("Tag"): TLV_TAG, Field("Length"): TLV_LENGTH, Field("Value"): OPAQUE }, ) model = PyRFLX(model=Model([TLV_TAG, TLV_LENGTH, message])) pkg = model.package("TLV") msg = pkg.new_message("Message_With_Not_Operator") test_bytes = b"\x01\x00\x04\x00\x00\x00\x00" msg.parse(test_bytes) assert msg.valid_message assert msg.bytestring == test_bytes
def test_validate_message_parameterized_message() -> None: validator = Validator([], skip_model_verification=True) message = (PyRFLX.from_specs(["tests/data/specs/parameterized.rflx"], skip_model_verification=True).package( "Parameterized").new_message("Message")) validation_result = validator._validate_message( # pylint: disable = protected-access Path(TEST_DIR / "parameterized/message/valid/parameterized_message.raw"), valid_original_message=True, message_value=message, ) assert validation_result.validation_success
def __init__(self, specdir: Path) -> None: print("Loading...") start = perf_counter() self.__pyrflx = PyRFLX.from_specs( [str(specdir / "ipv4.rflx"), str(specdir / "icmp.rflx")], skip_model_verification=True, skip_message_verification=True, ) self.__ipv4 = self.__pyrflx.package("IPv4") self.__icmp = self.__pyrflx.package("ICMP") print(f"Loaded in {perf_counter() - start} seconds")
def test_validate_message_original_and_parsed_not_equal() -> None: validator = Validator([], skip_model_verification=True) ethernet_too_short_value = (PyRFLX.from_specs( [SPEC_DIR / "ethernet.rflx"], skip_model_verification=True).package("Ethernet").new_message("Frame")) validation_result = validator._validate_message( # pylint: disable = protected-access Path(TEST_DIR / "ethernet/frame/invalid/ethernet_invalid_too_long.raw"), valid_original_message=True, message_value=ethernet_too_short_value, ) assert (validation_result.parser_error == "message parsed by PyRFLX is shorter than the original message")
def fixture_pyrflx() -> PyRFLX: return PyRFLX( [ f"{SPECDIR}/ethernet.rflx", f"{SPECDIR}/icmp.rflx", f"{SPECDIR}/in_ethernet.rflx", f"{SPECDIR}/in_ipv4.rflx", f"{SPECDIR}/ipv4.rflx", f"{SPECDIR}/tls_alert.rflx", f"{SPECDIR}/tls_record.rflx", f"{SPECDIR}/tlv.rflx", f"{SPECDIR}/udp.rflx", f"{TESTDIR}/array_message.rflx", f"{TESTDIR}/array_type.rflx", f"{TESTDIR}/message_odd_length.rflx", f"{TESTDIR}/null_message.rflx", f"{TESTDIR}/tlv_with_checksum.rflx", ] )
def test_parameterized_message_excess_parameter() -> None: validator = Validator([], skip_model_verification=True) message = (PyRFLX.from_specs(["tests/data/specs/parameterized.rflx"], skip_model_verification=True).package( "Parameterized").new_message("Message")) with pytest.raises( ValidationError, match= (r"^" f"{TEST_DIR}/parameterized/message/invalid/parameterized_message_excess_parameter.raw:" r" pyrflx: error: unexpected parameter values: Excess" r"$"), ): validator._validate_message( # pylint: disable = protected-access Path( TEST_DIR / "parameterized/message/invalid/parameterized_message_excess_parameter.raw" ), valid_original_message=True, message_value=message, )
def test_no_verification_ethernet(ethernet_frame_value: MessageValue) -> None: payload = (b"\x45\x00\x00\x2e\x00\x01\x00\x00\x40\x11\x7c\xbc" b"\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x35\x00\x35" b"\x00\x1a\x01\x4e\x00\x00\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") ethernet_frame_value.set("Destination", int("FFFFFFFFFFFF", 16)) ethernet_frame_value.set("Source", int("0", 16)) ethernet_frame_value.set("Type_Length_TPID", int("0800", 16)) ethernet_frame_value.set("Type_Length", int("0800", 16)) ethernet_frame_value.set("Payload", payload) assert ethernet_frame_value.valid_message pyrflx_ = PyRFLX.from_specs( [SPEC_DIR / "ethernet.rflx"], skip_model_verification=True, skip_message_verification=True, ) frame_unv = pyrflx_.package("Ethernet").new_message("Frame") frame_unv.set("Destination", int("FFFFFFFFFFFF", 16)) frame_unv.set("Source", int("0", 16)) frame_unv.set("Type_Length_TPID", int("0800", 16)) frame_unv.set("Type_Length", int("0800", 16)) frame_unv.set("Payload", payload) assert frame_unv.valid_message assert frame_unv.bytestring == ethernet_frame_value.bytestring
def fixture_parameterized_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Parameterized")
def fixture_endianness_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Endianness")
def fixture_message_type_size(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Message_Type_Size_Condition")
def fixture_always_valid_aspect_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Always_Valid_Aspect")
def fixture_sequence_message_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Sequence_Message")
def fixture_message_size_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Message_Size")
def fixture_udp_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("UDP")
def test_file_not_found(tmp_path: Path) -> None: with pytest.raises(FileNotFoundError): PyRFLX([f"{tmp_path}/test.rflx"])
def fixture_tls_record_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("TLS_Record")
def fixture_tls_alert_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("TLS_Alert")
def test_imported_literals(tmp_path: Path) -> None: with open(tmp_path / "test.rflx", "x") as f: f.write( """ with Foo; package Test is type T is (E1 => 1, E2 => 2) with Size => 8; type Message is message A : Foo.T then null if A = Foo.E1; end message; end Test; """ ) with open(tmp_path / "foo.rflx", "x") as f: f.write( """ package Foo is type T is (E1 => 11, E2 => 12) with Size => 8; end Foo; """ ) pyrflx = PyRFLX([str(tmp_path / "test.rflx")]) m = pyrflx["Test"]["Message"] m.set("A", "E1") assert m.valid_message m.set("A", "Foo.E1") assert m.valid_message m.parse(b"\x0B") assert m.valid_message assert m.get("A") == "Foo.E1" with pytest.raises( ValueError, match=r"^none of the field conditions \['A = Foo.E1'\] for field A have been met" " by the assigned value: 00001100$", ): m.parse(b"\x0C") assert not m.valid_message with pytest.raises( ValueError, match=r"^none of the field conditions \['A = Foo.E1'\] for field A have been met" " by the assigned value: E2$", ): m.set("A", "E2") assert not m.valid_message with pytest.raises( ValueError, match=r"^none of the field conditions \['A = Foo.E1'\] for field A have been met" " by the assigned value: Foo.E2$", ): m.set("A", "Foo.E2") assert not m.valid_message
def fixture_ethernet_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Ethernet")
assert tag_first == 0 and checksum_first_minus_one == 15 second_arg = kwargs.get("Checksum'Last + 1 .. Message'Last") assert isinstance(second_arg, tuple) checksum_last_plus_one, data_last = second_arg assert checksum_last_plus_one == 32 and data_last == 511 checksum_size = kwargs.get("Checksum'Size") assert isinstance(checksum_size, int) assert checksum_size == 16 checksum_bytes = message[tag_first : (checksum_first_minus_one + 1) // 8] checksum_bytes += b"\x00" * (checksum_size // 8) checksum_bytes += message[(checksum_last_plus_one // 8) : (data_last + 1) // 8] return utils.internet_checksum(checksum_bytes) PYRFLX = PyRFLX.from_specs(["specs/ipv4.rflx"], skip_model_verification=True) ICMP = PYRFLX.package("ICMP") ICMP.set_checksum_functions({"Message": {"Checksum": icmp_checksum}}) IP = PYRFLX.package("IPv4") ICMP_DATA = bytes(list(range(0, 56))) def ping(target: str) -> None: target_ip = socket.gethostbyname(target) print(f"PING {target} ({target_ip})") sock_out = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) sock_in = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) fcntl.fcntl(sock_in, fcntl.F_SETFL, os.O_NONBLOCK)
def fixture_low_order_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Low_Order")
def fixture_tlv_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("TLV")
def fixture_aggregate_in_relation_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("Aggregate_In_Relation")
def test_attributes(pyrflx: PyRFLX) -> None: pyrflx = PyRFLX([f"{TESTDIR}/tlv_with_checksum.rflx"]) assert isinstance(pyrflx["TLV_With_Checksum"], Package) tlv_package = pyrflx["TLV_With_Checksum"] assert isinstance(tlv_package["Message"], MessageValue)
def fixture_icmp_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("ICMP")
def fixture_ipv4_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package: return pyrflx_.package("IPv4")