class SAPMSProperty(PacketNoPadded): """SAP Message Server Property packet. Packet containing information about properties. """ name = "SAP Message Server Property" fields_desc = [ StrNullFixedLenField("client", None, 39), IntEnumField("id", 0x00, ms_property_id_values), # MS_PROPERTY_VHOST ConditionalField(ShortEnumKeysField("logon", 0, ms_logon_type_values), lambda pkt:pkt.id in [0x02]), # MS_PROPERTY_IPADR ConditionalField(IPField("address", "0.0.0.0"), lambda pkt:pkt.id in [0x03]), ConditionalField(IP6Field("address6", "::"), lambda pkt:pkt.id in [0x03]), # MS_PROPERTY_PARAM ConditionalField(StrNullField("param", ""), lambda pkt:pkt.id in [0x04]), ConditionalField(StrNullField("value", ""), lambda pkt:pkt.id in [0x04]), # MS_PROPERTY_SERVICE ConditionalField(ShortField("service", 0), lambda pkt:pkt.id in [0x05]), # Release Information fields ConditionalField(StrNullFixedLenField("release", "720", length=9), lambda pkt:pkt.id in [0x07]), ConditionalField(IntField("patchno", 0), lambda pkt:pkt.id in [0x07]), ConditionalField(IntField("supplvl", 0), lambda pkt:pkt.id in [0x07]), ConditionalField(IntField("platform", 0), lambda pkt:pkt.id in [0x07]), ]
class SAPCARArchiveFilev201Format(PacketNoPadded): """SAP CAR file information format This is used to parse files inside a SAP CAR archive. """ name = "SAP CAR Archive File 2.01" fields_desc = [ StrFixedLenField("type", SAPCAR_TYPE_FILE, 2), LEIntField("perm_mode", 0), LEIntField("file_length", 0), LEIntField("unknown1", 0), LEIntField("unknown2", 0), LEIntField("timestamp", 0), StrFixedLenField("unknown3", None, 10), FieldLenField("filename_length", None, length_of="filename", fmt="<H"), StrNullFixedLenField("filename", None, length_from=lambda x: x.filename_length - 1), ConditionalField( StrFixedLenField("mark", "ED", 2), lambda x: x.type == SAPCAR_TYPE_FILE and x.file_length > 0), ConditionalField( PacketField("compressed", None, SAPCARCompressedFileFormat), lambda x: x.type == SAPCAR_TYPE_FILE and x.file_length > 0), ConditionalField( LESignedIntField("checksum", 0), lambda x: x.type == SAPCAR_TYPE_FILE and x.file_length > 0), ]
class SAPDiagError(PacketNoPadded): """SAP Diag Error packet This packet holds Diag error packets. """ name = "SAP Diag Error" # TODO: Need to figure out the meaning of the packets fields_desc = [ StrNullFixedLenField("msg", "**DPTMMSG**", length=12), StrField("padd", None), ]
class SAPEnqueueTracePattern(PacketNoPadded): """SAP Enqueue Server Admin Trace Pattern """ name = "SAP Enqueue Server Admin Trace Pattern" fields_desc = [ FieldLenField("len", None, length_of="pattern", fmt="B"), StrNullFixedLenField("pattern", "", length_from=lambda pkt: pkt.len, max_length=0xff), ]
class SAPRouterInfoClient(PacketNoPadded): """SAP Router Protocol Information Request Client info This packet is used to return the information of a connected client. """ name = "SAP Router Client Info" fields_desc = [ # 137 bytes length IntField("id", 1), BitField("flag_XXX1", 0, 1), BitField("flag_XXX2", 0, 1), BitField("flag_XXX3", 0, 1), BitField("flag_XXX4", 0, 1), BitField("flag_XXX5", 0, 1), BitField("flag_traced", 0, 1), BitField("flag_connected", 0, 1), BitField("flag_routed", 0, 1), LongField("connected_on", 0), StrNullFixedLenField("address", None, length=45), StrNullFixedLenField("partner", None, length=45), StrNullFixedLenField("service", None, length=27), StrFixedLenField("XXX3", None, length=4), ]
class SAPMS(Packet): """SAP Message Server packet This packet is used for the Message Server protocol. """ name = "SAP Message Server" fields_desc = [ StrFixedLenField("eyecatcher", "**MESSAGE**\x00", 12), ByteField("version", 0x04), ByteEnumKeysField("errorno", 0x00, ms_errorno_values), StrFixedLenField("toname", "-" + " " * 39, 40), FlagsField("msgtype", 0, 8, ["DIA", "UPD", "ENQ", "BTC", "SPO", "UP2", "ATP", "ICM"]), ByteField("reserved", 0x00), ByteEnumKeysField("domain", 0x00, ms_domain_values), ByteField("reserved", 0x00), StrFixedLenField("key", "\x00" * 8, 8), ByteEnumKeysField("flag", 0x01, ms_flag_values), ByteEnumKeysField("iflag", 0x01, ms_iflag_values), StrFixedLenField("fromname", "-" + " " * 39, 40), ShortField("padd", 0x0000), # OpCode fields ConditionalField(ByteEnumKeysField("opcode", 0x00, ms_opcode_values), lambda pkt: pkt.iflag in [0x00, 0x01]), ConditionalField( ByteEnumKeysField("opcode_error", 0x00, ms_opcode_error_values), lambda pkt: pkt.iflag in [0x00, 0x01]), ConditionalField(ByteField("opcode_version", 0x01), lambda pkt: pkt.iflag in [0x00, 0x01]), ConditionalField(ByteField("opcode_charset", 0x03), lambda pkt: pkt.iflag in [0x00, 0x01]), ConditionalField( StrField("opcode_value", ""), lambda pkt: pkt.iflag in [0x00, 0x01] and pkt.opcode not in [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x11, 0x1c, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2f, 0x43, 0x44, 0x45, 0x46, 0x47, 0x4a ]), # Adm OpCode fields ConditionalField( StrFixedLenField("adm_eyecatcher", "AD-EYECATCH\x00", 12), lambda pkt: pkt.iflag == 0x05), ConditionalField(ByteField("adm_version", 0x01), lambda pkt: pkt.iflag == 0x05), ConditionalField( ByteEnumKeysField("adm_type", 0x01, ms_adm_type_values), lambda pkt: pkt.iflag == 0x05), ConditionalField(IntToStrField("adm_recsize", 104, 11), lambda pkt: pkt.iflag == 0x05), ConditionalField(IntToStrField("adm_recno", 1, 11), lambda pkt: pkt.iflag == 0x05), ConditionalField(PacketListField("adm_records", None, SAPMSAdmRecord), lambda pkt: pkt.iflag == 0x05), # Server List fields ConditionalField( PacketListField("clients", None, SAPMSClient1), lambda pkt: pkt. opcode in [0x02, 0x03, 0x04, 0x05] and pkt.opcode_version == 0x01), ConditionalField( PacketListField("clients", None, SAPMSClient2), lambda pkt: pkt. opcode in [0x02, 0x03, 0x04, 0x05] and pkt.opcode_version == 0x02), ConditionalField( PacketListField("clients", None, SAPMSClient3), lambda pkt: pkt. opcode in [0x02, 0x03, 0x04, 0x05] and pkt.opcode_version == 0x03), ConditionalField( PacketListField("clients", None, SAPMSClient4), lambda pkt: pkt. opcode in [0x02, 0x03, 0x04, 0x05] and pkt.opcode_version == 0x04), # Change IP fields ConditionalField(IPField("change_ip_addressv4", "0.0.0.0"), lambda pkt: pkt.opcode == 0x06), ConditionalField( IP6Field("change_ip_addressv6", "::"), lambda pkt: pkt.opcode == 0x06 and pkt.opcode_version == 0x02), # Get/Set Text fields ConditionalField(StrFixedLenField("text_name", "", 40), lambda pkt: pkt.opcode in [0x22, 0x23]), ConditionalField( FieldLenField("text_length", None, length_of="text_value", fmt="!I"), lambda pkt: pkt.opcode in [0x22, 0x23]), ConditionalField( StrFixedLenField("text_value", "", length_from=lambda pkt: pkt.text_length or 80), lambda pkt: pkt.opcode in [0x22, 0x23]), # Counter fields ConditionalField( PacketField("counter", None, SAPMSCounter), lambda pkt: pkt.opcode in [0x24, 0x25, 0x26, 0x27, 0x28, 0x29]), ConditionalField(PacketListField("counters", None, SAPMSCounter), lambda pkt: pkt.opcode in [0x2a]), # Security Key 1 fields ConditionalField(StrFixedLenField("security_name", None, 40), lambda pkt: pkt.opcode in [0x07, 0x08]), ConditionalField(StrFixedLenField("security_key", None, 256), lambda pkt: pkt.opcode in [0x07, 0x08]), # Security Key 2 fields ConditionalField(IPField("security2_addressv4", "0.0.0.0"), lambda pkt: pkt.opcode == 0x09), ConditionalField(ShortField("security2_port", 0), lambda pkt: pkt.opcode == 0x09), ConditionalField(StrFixedLenField("security2_key", None, 256), lambda pkt: pkt.opcode == 0x09), ConditionalField(IP6Field("security2_addressv6", "::"), lambda pkt: pkt.opcode == 0x09), # Hardware ID field ConditionalField(StrNullFixedLenField("hwid", "", length=99), lambda pkt: pkt.opcode == 0x0a), # Statistics ConditionalField(PacketField("stats", None, SAPMSStat3), lambda pkt: pkt.opcode == 0x11 and pkt.flag == 0x03), # Codepage ConditionalField(IntField("codepage", 0), lambda pkt: pkt.opcode == 0x1c and pkt.flag == 0x03), # Dump Info Request fields ConditionalField(ByteField("dump_dest", 0x02), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), ConditionalField(StrFixedLenField("dump_filler", "\x00\x00\x00", 3), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), ConditionalField(ShortField("dump_index", 0x00), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), ConditionalField( ShortEnumKeysField("dump_command", 0x01, ms_dump_command_values), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), ConditionalField(StrFixedLenField("dump_name", "\x00" * 40, 40), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), # File Reload fields ConditionalField( ByteEnumKeysField("file_reload", 0, ms_file_reload_values), lambda pkt: pkt.opcode == 0x1f), ConditionalField(StrFixedLenField("file_filler", "\x00\x00\x00", 3), lambda pkt: pkt.opcode == 0x1f), # Get/Set/Del Logon fields ConditionalField(PacketField("logon", None, SAPMSLogon), lambda pkt: pkt.opcode in [0x2b, 0x2c, 0x2d]), # Server Disconnect/Shutdown fields ConditionalField(PacketField("shutdown_client", None, SAPMSClient3), lambda pkt: pkt.opcode in [0x2e, 0x2f, 0x30, 0x4a]), ConditionalField( FieldLenField("shutdown_reason_length", None, length_of="shutdown_reason", fmt="!H"), lambda pkt: pkt.opcode in [0x2e, 0x2f, 0x30, 0x4a]), ConditionalField( StrLenField("shutdown_reason", "", length_from=lambda pkt: pkt.shutdown_reason_length), lambda pkt: pkt.opcode in [0x2e, 0x2f, 0x30, 0x4a]), # Get/Set Property fields ConditionalField(PacketField("property", None, SAPMSProperty), lambda pkt: pkt.opcode in [0x43, 0x44, 0x45]), # IP/Port to name fields ConditionalField( IPField("ip_to_name_address4", "0.0.0.0"), lambda pkt: pkt.opcode == 0x46 and pkt.opcode_version == 0x01), ConditionalField( IP6Field("ip_to_name_address6", "::"), lambda pkt: pkt.opcode == 0x46 and pkt.opcode_version == 0x02), ConditionalField(ShortField("ip_to_name_port", 0), lambda pkt: pkt.opcode == 0x46), ConditionalField( FieldLenField("ip_to_name_length", None, length_of="ip_to_name", fmt="!I"), lambda pkt: pkt.opcode == 0x46), ConditionalField( StrLenField("ip_to_name", "", length_from=lambda pkt: pkt.logonname_length), lambda pkt: pkt.opcode == 0x46), # Check ACL fields ConditionalField(ShortField("error_code", 0), lambda pkt: pkt.opcode == 0x47), ConditionalField(StrFixedLenField("acl", "", 46), lambda pkt: pkt.opcode == 0x47), ]
class SAPRouter(Packet): """SAP Router packet This packet is used for general SAP Router packets. There are (at least) five types of SAP Router packets: 1. Route packets. For requesting the routing of a connection to a remote hosts. The packet contains some general information and a connection string with a list of routing hops (:class:`SAPRouterRouteHop`). 2. Administration packets. This packet is used for the SAP Router to send administrative commands. It's suppose to be used only from the hosts running the SAP Router or when an specific route is included in the routing table. Generally administration packets are not accepted from the external binding. 3. Error Information packets. Packets sent when an error occurred. 4. Control Message packets. Used to perform some control activities, like retrieving the current SAPRouter version or to perform the SNC handshake. They have the same structure that error information packets. 5. Route accepted packet. Used to acknowledge a route request ("NI_PONG"). Routed packets and some responses doesn't fill in these five packet types. For identifying those cases, you should check the type using the function :class:`router_is_known_type`. NI Versions found (unconfirmed): - 30: Release 40C - 36: Release <6.20 - 38: Release 7.00/7.10 - 39: Release 7.11 - 40: Release 7.20/7.21 """ # Constants for router types SAPROUTER_ROUTE = "NI_ROUTE" """ :cvar: Constant for route packets :type: C{string} """ SAPROUTER_ADMIN = "ROUTER_ADM" """ :cvar: Constant for administration packets :type: C{string} """ SAPROUTER_ERROR = "NI_RTERR" """ :cvar: Constant for error information packets :type: C{string} """ SAPROUTER_CONTROL = "NI_RTERR" """ :cvar: Constant for control messages packets :type: C{string} """ SAPROUTER_PONG = "NI_PONG" """ :cvar: Constant for route accepted packets :type: C{string} """ router_type_values = [ SAPROUTER_ADMIN, SAPROUTER_ERROR, SAPROUTER_CONTROL, SAPROUTER_ROUTE, SAPROUTER_PONG, ] """ :cvar: List of known packet types :type: ``list`` of C{string} """ name = "SAP Router" fields_desc = [ # General fields present in all SAP Router packets StrNullField("type", SAPROUTER_ROUTE), ConditionalField(ByteField("version", 0x02), lambda pkt:router_is_known_type(pkt) and not router_is_pong(pkt)), # Route packets ConditionalField(ByteField("route_ni_version", 0x28), router_is_route), ConditionalField(ByteField("route_entries", 0), router_is_route), ConditionalField(ByteEnumKeysField("route_talk_mode", 0, router_ni_talk_mode_values), router_is_route), ConditionalField(ShortField("route_padd", 0), router_is_route), ConditionalField(ByteField("route_rest_nodes", 0), router_is_route), ConditionalField(FieldLenField("route_length", 0, length_of="route_string", fmt="I"), router_is_route), ConditionalField(IntField("route_offset", 0), router_is_route), ConditionalField(PacketListField("route_string", None, SAPRouterRouteHop, length_from=lambda pkt:pkt.route_length), router_is_route), # Admin packets ConditionalField(ByteEnumKeysField("adm_command", 0x02, router_adm_commands), router_is_admin), ConditionalField(ShortField("adm_unused", 0x00), lambda pkt:router_is_admin(pkt) and pkt.adm_command not in [10, 11, 12, 13]), # Info Request fields ConditionalField(StrNullFixedLenField("adm_password", "", 19), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [2]), # Cancel Route fields ConditionalField(FieldLenField("adm_client_count", None, count_of="adm_client_ids", fmt="H"), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [6]), ConditionalField(FieldListField("adm_client_ids", [0x00], IntField("", 0), count_from=lambda pkt:pkt.adm_client_count), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [6]), # Trace Connection fields ConditionalField(FieldLenField("adm_client_count", None, count_of="adm_client_ids", fmt="I"), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [12, 13]), ConditionalField(FieldListField("adm_client_ids", [0x00], IntField("", 0), count_from=lambda pkt:pkt.adm_client_count), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [12, 13]), # Set/Clear Peer Trace fields # TODO: Check whether this field should be a IPv6 address or another proper field ConditionalField(StrFixedLenField("adm_address_mask", "", 32), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [10, 11]), # Error Information/Control Messages fields ConditionalField(ByteEnumKeysField("opcode", 0, router_control_opcodes), lambda pkt: router_is_error(pkt) or router_is_control(pkt)), ConditionalField(ByteField("opcode_padd", 0), lambda pkt: router_is_error(pkt) or router_is_control(pkt)), ConditionalField(SignedIntEnumField("return_code", 0, router_return_codes), lambda pkt: router_is_error(pkt) or router_is_control(pkt)), # Error Information fields ConditionalField(FieldLenField("err_text_length", None, length_of="err_text_value", fmt="!I"), lambda pkt: router_is_error(pkt) and pkt.opcode == 0), ConditionalField(PacketField("err_text_value", SAPRouterError(), SAPRouterError), lambda pkt: router_is_error(pkt) and pkt.opcode == 0), ConditionalField(IntField("err_text_unknown", 0), lambda pkt: router_is_error(pkt) and pkt.opcode == 0), # Control Message fields ConditionalField(IntField("control_text_length", 0), lambda pkt: router_is_control(pkt) and pkt.opcode != 0), ConditionalField(StrField("control_text_value", "*ERR"), lambda pkt: router_is_control(pkt) and pkt.opcode != 0), # SNC Frame fields ConditionalField(PacketField("snc_frame", None, SAPSNCFrame), lambda pkt: router_is_control(pkt) and pkt.opcode in [70, 71]) ]
class SAPEnqueue(PacketNoPadded): """SAP Enqueue Server packet This packet is used for general Enqueue packets. """ name = "SAP Enqueue" fields_desc = [ StrFixedLenField("magic_bytes", "\xab\xcd\xe1\x23", 4), IntField("id", 0), LenField("len", None, fmt="!I"), LenField("len_frag", None, fmt="!I"), ByteEnumKeysField("dest", 0x00, enqueue_dest_values), ByteEnumKeysField("opcode", 0x00, enqueue_conn_admin_opcode_values), ByteField("more_frags", 0), ByteEnumKeysField("type", 0x00, enqueue_type_values), # Server Admin fields ConditionalField(StrNullFixedLenField("adm_eyecatcher1", "ENC", 3), lambda pkt: pkt.dest == 3), ConditionalField(ByteField("adm_version", 1), lambda pkt: pkt.dest == 3), ConditionalField(ByteField("adm_padd1", 0), lambda pkt: pkt.dest == 3), ConditionalField(ByteField("adm_padd2", 0), lambda pkt: pkt.dest == 3), ConditionalField(ByteField("adm_padd3", 0), lambda pkt: pkt.dest == 3), ConditionalField(StrFixedLenField("adm_eyecatcher2", "#EAA", 4), lambda pkt: pkt.dest == 3), ConditionalField(ByteField("adm_1", 1), lambda pkt: pkt.dest == 3), ConditionalField(IntField("adm_len", 0), lambda pkt: pkt.dest == 3), ConditionalField( ByteEnumKeysField("adm_opcode", 0, enqueue_server_admin_opcode_values), lambda pkt: pkt.dest == 3), ConditionalField(ByteField("adm_flags", 0), lambda pkt: pkt.dest == 3), ConditionalField(IntField("adm_rc", 0), lambda pkt: pkt.dest == 3), ConditionalField(StrFixedLenField("adm_eyecatcher3", "#EAE", 4), lambda pkt: pkt.dest == 3), # Server Admin Trace fields ConditionalField( ByteField("adm_trace_protocol_version", 1), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( ByteEnumKeysField("adm_trace_action", 3, enqueue_server_admin_trace_action_values), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( ByteEnumKeysField("adm_trace_limit", 0, enqueue_server_admin_trace_limit_values), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( ByteEnumKeysField("adm_trace_thread", 0, enqueue_server_admin_trace_thread_values), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( IntField("adm_trace_unknown1", 0), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( IntField("adm_trace_level", 1), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( IntField("adm_trace_level1", 1), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( ByteField("adm_trace_logging", 0), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( IntField("adm_trace_max_file_size", 20 * 1024 * 1024), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( FieldLenField("adm_trace_nopatterns", 0, count_of="adm_trace_patterns", fmt="!I"), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( FieldLenField("adm_trace_nopatterns1", 0, count_of="adm_trace_patterns", fmt="!I"), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( IntField("adm_trace_unknown3", 37), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( StrFixedLenField("adm_trace_eyecatcher4", "#EAH", 4), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( PacketListField("adm_trace_patterns", None, SAPEnqueueTracePattern, count_from=lambda pkt: pkt.adm_trace_nopatterns), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), ConditionalField( StrFixedLenField("adm_trace_eyecatcher5", "#EAD", 4), lambda pkt: pkt.dest == 3 and pkt.adm_opcode in [0x06]), # Connection Admin fields ConditionalField( FieldLenField("params_count", None, count_of="params", fmt="!I"), lambda pkt: pkt.dest == 6 and pkt.opcode in [1, 2]), ConditionalField( PacketListField("params", None, SAPEnqueueParam, count_from=lambda pkt: pkt.params_count), lambda pkt: pkt.dest == 6 and pkt.opcode in [1, 2]), ] def post_build(self, pkt, pay): """Adjust the len and len_frags fields after the build of the whole packet. """ l = struct.pack("!I", len(pkt) + len(pay)) pkt = pkt[:8] + l + l + pkt[16:] return pkt + pay
class SAPCARArchiveFilev201Format(PacketNoPadded): """SAP CAR file information format This is used to parse files inside a SAP CAR archive. """ name = "SAP CAR Archive File 2.01" version = SAPCAR_VERSION_201 fields_desc = [ StrFixedLenField("type", SAPCAR_TYPE_FILE, 2), LEIntField("perm_mode", 0), LEIntField("file_length", 0), LEIntField("unknown1", 0), LEIntField("unknown2", 0), LEIntField("timestamp", 0), StrFixedLenField("unknown3", None, 10), FieldLenField("filename_length", None, length_of="filename", fmt="<H"), StrNullFixedLenField("filename", None, length_from=lambda x: x.filename_length - 1), ConditionalField(PacketListStopField("blocks", None, SAPCARCompressedBlockFormat, stop=sapcar_is_last_block), lambda x: x.type == SAPCAR_TYPE_FILE and x.file_length > 0), ] def extract(self, fd): """Extracts the archive file and writes the extracted file to the provided file object. Returns the checksum obtained from the archive. If blocks are uncompressed, the file is directly extracted. If the blocks are compressed, each block is decompressed independently. Checksum is obtained from the last block. :param fd: file-like object to write the extracted file to :type fd: file :return: checksum :rtype: int :raise DecompressError: If there's a decompression error :raise SAPCARInvalidFileException: If the file is invalid """ if self.file_length == 0: return 0 checksum = 0 remaining_length = self.file_length for block in self.blocks: # Process uncompressed block types if block.type in [SAPCAR_BLOCK_TYPE_UNCOMPRESSED, SAPCAR_BLOCK_TYPE_UNCOMPRESSED_LAST]: fd.write(block.compressed) remaining_length -= len(block.compressed) # Process compressed block types elif block.type in [SAPCAR_BLOCK_TYPE_COMPRESSED, SAPCAR_BLOCK_TYPE_COMPRESSED_LAST]: compressed = block.compressed exp_block_length = compressed.uncompress_length (_, block_length, block_buffer) = decompress(str(compressed)[4:], exp_block_length) if block_length != exp_block_length or not block_buffer: raise DecompressError("Error decompressing block") fd.write(block_buffer) remaining_length -= block_length else: raise SAPCARInvalidFileException("Invalid block type found") # Check last block if sapcar_is_last_block(block): checksum = block.checksum break return checksum