Exemplo n.º 1
0
def SizedUTF16MultiSz(size_func):
    return ExprAdapter(
        FixedSized(size_func, GreedyRange(CString("UTF_16_le"))),
        lambda obj, ctx: list(
            obj),  # last element is already removed by GreedyRange
        lambda obj, ctx: obj + [''])
Exemplo n.º 2
0
from construct import Struct, Const, SymmetricAdapter, Int32ul, Int64ul, PaddedString, Bytes, Array, PrefixedArray, \
    Pointer, Tell, Switch, Error, Int16ul, Byte, Computed, ExprAdapter, Adapter, Rebuild, Pass, Enum
from procmon_parser.construct_helper import FixedNullTerminatedUTF16String, OriginalEnumAdapter, Filetime, \
    ListAdapter, PVoid, Duration, CheckCustom
from procmon_parser.logs_details_format import NetworkDetails, RegistryDetails, FilesystemDetails, ProcessDetails
from procmon_parser.consts import EventClass, ProcessOperation, RegistryOperation, NetworkOperation, ProfilingOperation, \
    FilesystemOperation
from procmon_parser.logs import Process, Event

EventClassType = OriginalEnumAdapter(Int32ul, EventClass)
ProcessOperationType = Enum(Int16ul, ProcessOperation)
RegistryOperationType = Enum(Int16ul, RegistryOperation)
NetworkOperationType = Enum(Int16ul, NetworkOperation)
ProfilingOperationType = Enum(Int16ul, ProfilingOperation)
FilesystemOperationType = Enum(Int16ul, FilesystemOperation)
StringIndex = ExprAdapter(Struct("string_index" / Int32ul), lambda obj, ctx: ctx._.strings_table[obj.string_index],
                          lambda obj, ctx: ctx._.strings_table.index(obj))
ProcessIndex = ExprAdapter(Struct("process_index" / Int32ul), lambda obj, ctx: ctx._.process_table[obj.process_index],
                           lambda obj, ctx: ctx._.process_table.index(obj))

SUPPORTED_VERSIONS = [9]


class PMLVersionNumberValidator(SymmetricAdapter):
    def _decode(self, obj, context, path):
        if obj not in SUPPORTED_VERSIONS:
            raise RuntimeError("PML is version {} while only versions {} are supported", obj, SUPPORTED_VERSIONS)


Header = """
The header of the PML file.
""" * Struct(
Exemplo n.º 3
0
         "_not_used2" / BitsInteger(6),
         "bell_output_overload_trouble" / Flag,
         "bell_output_disconnected_trouble" / Flag,
         "_not_used3" / BitsInteger(2),
         "computer_fail_to_communicate_trouble" / Flag,
         "voice_fail_to_communicate_trouble" / Flag,
         "pager_fail_to_communicate_trouble" / Flag,
         "central_2_reporting_ftc_indicator_trouble" / Flag,
         "central_1_reporting_ftc_indicator_trouble" / Flag,
         "telephone_line" / Flag,
     ),
     "system" / Struct(
         "date" / Struct("time" / DateAdapter(Bytes(6))),
         "power" / Struct(
             "vdc" / ExprAdapter(
                 Byte,
                 lambda obj, ctx: round(obj *
                                        (20.3 - 1.4) / 255.0 + 1.4, 1), 0),
             "battery" / ExprAdapter(
                 Byte, lambda obj, ctx: round(obj * 22.8 / 255.0, 1), 0),
             "dc" / ExprAdapter(
                 Byte, lambda obj, ctx: round(obj * 22.8 / 255.0, 1), 0),
         ),
         "rf" / Struct("noise_floor" / Int8ub, ),
     ),
     "zone_open" / StatusAdapter(Bytes(4)),
     "zone_tamper" / StatusAdapter(Bytes(4)),
     "pgm_tamper" / StatusAdapter(Bytes(2)),
     "module_tamper" / StatusAdapter(Bytes(2)),
     "zone_fire" / StatusAdapter(Bytes(4)),
     "_not_used0" / Int8ub,
 ),
Exemplo n.º 4
0
    def create_control_record_struct(self):
        little_endian = self.endianness == "little"
        min_mod = self.min_modulus
        control_len = self.control_len

        # Data types for integer values
        Int16s, Int16u, Int32s, Int32u = {
            "big": (Int16sb, Int16ub, Int32sb, Int32ub),
            "little": (Int16sl, Int16ul, Int32sl, Int32ul),
        }[self.endianness]

        # The 16 bytes that used to have the legacy MFTYPE field
        # has both the actual MFTYPE and the MSTXL,
        # and their order is affected by the endianness
        mftype = "mftype" / Default(Byte, 0)
        mstxl = "mstxl" / Rebuild(
            Byte,
            lambda this: this.mstxl
            if "mstxl" in this and this.mstxl is not None else this.get(
                "shift",
                this.modulus.bit_length() - 1
                if "modulus" in this else self.default_shift,
            ),
        )

        # MFCXX2 and MFCXX3 are used for locking the file on updating
        if self.lockable:
            mfcxx2 = "_mfcxx2" / Union(
                0,
                "mfcxx2" / Default(Int32s, 0),
                "delock_count" / Int32u,  # Data entry lock
            )
            mfcxx3 = "_mfcxx3" / Union(
                0,
                "mfcxx3" / Default(Int32s, 0),
                "ewlock" / ExprAdapter(
                    Int32u,  # Exclusive write lock
                    lambda obj, ctx: bool(obj),
                    lambda obj, ctx: int(obj)),
            )
        else:
            mfcxx2 = "mfcxx2" / Default(Int32s, 0)
            mfcxx3 = "mfcxx3" / Default(Int32s, 0)

        # Legacy shift replacement
        if self.shift4is3:
            shift = "shift" / Computed(lambda this: 3
                                       if this.mstxl == 4 else this.mstxl)
        else:
            shift = "shift" / Computed(lambda this: this.mstxl)

        # The control record struct for all cases
        nested_unpadded_struct = Struct(
            # First 4 fields, including information about the whole file
            "mfn" / Default(Int32s, 0),  # CTLMFN
            Check(lambda this: this.mfn == 0),
            "next_mfn" / Default(Int32s, 1),  # NXTMFN
            "next_block" / Default(Int32s, 1),  # NXTMFB
            "next_offset" / Default(Int16u, self.control_len),  # NXTMFP
            *([mftype, mstxl] if little_endian else [mstxl, mftype]),

            # Get the MST (modulus) and XRF (shift) alignment/shift values
            # from the MSTXL field, fixing the "legacy" replacements
            shift,
            "modulus" / Computed(lambda this: max(min_mod, 1 << this.shift)),
            Check(lambda this: control_len % this.modulus == 0),

            # Fields used for "statistics during backup/restore",
            # where the last two fields are also used for multi-user locking
            "reccnt" / Default(Int32s, 0),
            "mfcxx1" / Default(Int32s, 0),
            mfcxx2,
            mfcxx3,
        )
        return Padded(
            length=control_len,
            subcon=Unnest(["_mfcxx2", "_mfcxx3"], nested_unpadded_struct)
            if self.lockable else nested_unpadded_struct,
            pattern=self.control_filler,
        )
Exemplo n.º 5
0
    def __setup_constructors(self):
        '''Set endianness and create transport-specific constructors.'''
        # Set endianness of constructors before using them.
        self._set_endian('little')

        self.__Length = Int32ul
        self.__Type = Enum(
                Int16ul,
                default=Pass,
                Undefined=0x0000,
                Command=0x0001,
                Data=0x0002,
                Response=0x0003,
                Event=0x0004,
                )
        # This is just a convenience constructor to get the size of a header.
        self.__Code = Int16ul
        self.__Header = Struct(
                'Length' / self.__Length,
                'Type' / self.__Type,
                'Code' / self.__Code,
                'TransactionID' / self._TransactionID,
                )
        # These are the actual constructors for parsing and building.
        self.__CommandHeader = Struct(
                'Length' / self.__Length,
                'Type' / self.__Type,
                'OperationCode' / self._OperationCode,
                'TransactionID' / self._TransactionID,
                )
        self.__ResponseHeader = Struct(
                'Length' / self.__Length,
                'Type' / self.__Type,
                'ResponseCode' / self._ResponseCode,
                'TransactionID' / self._TransactionID,
                )
        self.__EventHeader = Struct(
                'Length' / self.__Length,
                'Type' / self.__Type,
                'EventCode' / self._EventCode,
                'TransactionID' / self._TransactionID,
                )
        # Apparently nobody uses the SessionID field. Even though it is
        # specified in ISO15740:2013(E), no device respects it and the session
        # number is implicit over USB.
        self.__Param = Range(0, 5, self._Parameter)
        self.__CommandTransactionBase = Struct(
                Embedded(self.__CommandHeader),
                'Payload' / Bytes(
                    lambda ctx, h=self.__Header: ctx.Length - h.sizeof()
                )
        )
        self.__CommandTransaction = ExprAdapter(
                self.__CommandTransactionBase,
                encoder=lambda obj, ctx, h=self.__Header: Container(
                    Length=len(obj.Payload) + h.sizeof(),
                    **obj
                    ),
                decoder=lambda obj, ctx: obj,
                )
        self.__ResponseTransactionBase = Struct(
                Embedded(self.__ResponseHeader),
                'Payload' / Bytes(
                    lambda ctx, h=self.__Header: ctx.Length - h.sizeof())
                )
        self.__ResponseTransaction = ExprAdapter(
                self.__ResponseTransactionBase,
                encoder=lambda obj, ctx, h=self.__Header: Container(
                    Length=len(obj.Payload) + h.sizeof(),
                    **obj
                    ),
                decoder=lambda obj, ctx: obj,
                )
Exemplo n.º 6
0
)
TaggedData = Struct(
    'tag'/Int16ul,
    'value'/Switch(this.tag, {
        3: Int32sl,
        5: Float64l,
        7: Int64ul,  # timestamp?
        8: VBString,
        9: SomeKindOfEnumMaybe,
        11: Const(b'\x00' * 2),  # null?
        35: Const(b'\x00' * 6),  # EOF
    })
)
# Specialization for loading float data faster
TaggedFloat64 = ExprAdapter(
    Struct(Const('tag'/Int16ul, 5), 'value'/Float64l),
    encoder=lambda obj, ctx: Container(tag=obj.tag, value=obj.value),
    decoder=lambda obj, ctx: obj.value)
DataList = Struct(
    'size'/Int64ul,
    OnDemand(Array(lambda ctx: ctx.size, TaggedFloat64)),
    Const('\xc0\xff\xee\x01')  # XXX: probably useful
)
# XXX: hacks
bad_strings = ('\xc0\xff\xee\x01\x00\x00', '\x01#Eg\x00\x00')
Property = Struct(
    'peek'/Peek(String(6)),
    Embedded(IfThenElse(
        this.peek in bad_strings,
        Padding(6),
        Struct('label'/VBString, 'TaggedData'/TaggedData)))
)
Exemplo n.º 7
0
    except ValueError:
        return -1


def PacketLength(subcons):
    return Rebuild(
        subcons,
        lambda x: x._root._subcons.fields.sizeof() + x._root._subcons.checksum.sizeof(),
    )


def PacketChecksum(subcons):
    return Checksum(subcons, lambda data: calculate_checksum(data), this.fields.data)


HexInt = ExprAdapter(Int8ub, _hex_to_int, lambda obj, path: int(str(obj), 16))

ProductIdEnum = Enum(
    Int8ub,
    DIGIPLEX_v13=0,
    DIGIPLEX_v2=1,
    DIGIPLEX_NE=2,
    DIGIPLEX_EVO_48=3,
    DIGIPLEX_EVO_96=4,
    DIGIPLEX_EVO_192=5,
    DIGIPLEX_EVO_HD=7,
    SPECTRA_SP5500=21,
    SPECTRA_SP6000=22,
    SPECTRA_SP7000=23,
    SPECTRA_SP4000=26,
    SPECTRA_SP65=27,
Exemplo n.º 8
0
    def __setup_constructors(self):
        '''Set endianness and create transport-specific constructors.'''
        # Set endianness of constructors before using them.
        self._set_endian('little')

        self.__Length = Int32ul
        self.__Type = Enum(
            Int32ul,
            Undefined=0x00000000,
            InitCommand=0x00000001,
            InitCommandAck=0x00000002,
            InitEvent=0x00000003,
            InitEventAck=0x00000004,
            InitFail=0x00000005,
            Command=0x00000006,
            Response=0x00000007,
            Event=0x00000008,
            StartData=0x00000009,
            Data=0x0000000A,
            Cancel=0x0000000B,
            EndData=0x0000000C,
            Ping=0x0000000D,
            Pong=0x0000000E,
        )
        self.__Header = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
        )
        self.__Param = Range(0, 5, self._Parameter)
        self.__EventParam = Range(0, 3, self._Parameter)
        self.__PacketBase = Struct(
            Embedded(self.__Header),
            'Payload' / Bytes(
                lambda ctx, h=self.__Header: ctx.Length - h.sizeof()),
        )
        self.__Packet = ExprAdapter(
            self.__PacketBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(),
                **obj
            ),
            decoder=lambda obj, ctx: obj,
        )
        # Yet another arbitrary string type. Max-length CString utf8-encoded
        self.__PTPIPString = ExprAdapter(
            RepeatUntil(
                lambda obj, ctx, lst:
                six.unichr(obj) in '\x00' or len(lst) == 40, Int16ul
            ),
            encoder=lambda obj, ctx:
            [] if len(obj) == 0 else[ord(c) for c in six.text_type(obj)]+[0],
            decoder=lambda obj, ctx:
            u''.join(
                [six.unichr(o) for o in obj]
            ).split('\x00')[0],
        )
        # PTP/IP packets
        # Command
        self.__ProtocolVersion = Struct(
            'Major' / Int16ul,
            'Minor' / Int16ul,
        )
        self.__InitCommand = Embedded(Struct(
            'InitiatorGUID' / Array(16, Int8ul),
            'InitiatorFriendlyName' / self.__PTPIPString,
            'InitiatorProtocolVersion' / self.__ProtocolVersion,
        ))
        self.__InitCommandACK = Embedded(Struct(
            'ConnectionNumber' / Int32ul,
            'ResponderGUID' / Array(16, Int8ul),
            'ResponderFriendlyName' / self.__PTPIPString,
            'ResponderProtocolVersion' / self.__ProtocolVersion,
        ))
        # Event
        self.__InitEvent = Embedded(Struct(
            'ConnectionNumber' / Int32ul,
        ))
        # Common to Events and Command requests
        self.__Reason = Enum(
            # TODO: Verify these codes...
            Int32ul,
            Undefined=0x0000,
            RejectedInitiator=0x0001,
            Busy=0x0002,
            Unspecified=0x0003,
        )
        self.__InitFail = Embedded(Struct(
            'Reason' / self.__Reason,
        ))

        self.__DataphaseInfo = Enum(
            Int32ul,
            Undefined=0x00000000,
            In=0x00000001,
            Out=0x00000002,
        )
        self.__Command = Embedded(Struct(
            'DataphaseInfo' / self.__DataphaseInfo,
            'OperationCode' / self._OperationCode,
            'TransactionID' / self._TransactionID,
            'Parameter' / self.__Param,
        ))
        self.__Response = Embedded(Struct(
            'ResponseCode' / self._ResponseCode,
            'TransactionID' / self._TransactionID,
            'Parameter' / self.__Param,
        ))
        self.__Event = Embedded(Struct(
            'EventCode' / self._EventCode,
            'TransactionID' / self._TransactionID,
            'Parameter' / self.__EventParam,
        ))
        self.__StartData = Embedded(Struct(
            'TransactionID' / self._TransactionID,
            'TotalDataLength' / Int64ul,
        ))
        # TODO: Fix packing and unpacking dataphase data
        self.__Data = Embedded(Struct(
            'TransactionID' / self._TransactionID,
            'Data' / Bytes(
                lambda ctx:
                ctx._.Length -
                self.__Header.sizeof() -
                self._TransactionID.sizeof()
            ),
        ))
        self.__EndData = Embedded(Struct(
            'TransactionID' / self._TransactionID,
            'Data' / Bytes(
                lambda ctx:
                ctx._.Length -
                self.__Header.sizeof() -
                self._TransactionID.sizeof()
            ),
        ))
        self.__Cancel = Embedded(Struct(
            'TransactionID' / self._TransactionID,
        ))
        # Convenience construct for parsing packets

        self.__PacketPayload = Debugger(Struct(
            'Header' / Embedded(self.__Header),
            'Payload' / Embedded(Switch(
                lambda ctx: ctx.Type,
                {
                    'InitCommand': self.__InitCommand,
                    'InitCommandAck': self.__InitCommandACK,
                    'InitEvent': self.__InitEvent,
                    'InitFail': self.__InitFail,
                    'Command': self.__Command,
                    'Response': self.__Response,
                    'Event': self.__Event,
                    'StartData': self.__StartData,
                    'Data': self.__Data,
                    'EndData': self.__EndData,
                },
                default=Pass,
            ))
        ))
Exemplo n.º 9
0
		Binary : 109,
		long : 111,
		Fun : 112,
		MFA : 113,
		map : 116,
		BitBinary : 77,
	}
	if obj == []:
		return 106
	return mapping[obj.__class__]

# Recurrent term
term_ = LazyBound(lambda: term)

atom_cache_ref = ExprAdapter(Int8ub,
		encoder=lambda obj, ctx: obj.index,
		decoder=lambda obj, ctx: AtomCacheReference(obj))
small_integer = Int8ub
integer = Int32sb
float_ = ExprAdapter(PaddedString(31, encoding="ascii"),
		encoder=lambda obj, ctx: u"{:.20e}    ".format(obj),
		decoder=lambda obj, ctx: float(obj))
atom = PascalString(lengthfield=Int16ub, encoding="latin1")
reference = ExprAdapter(Sequence("node" / term_,
		"id" / Int32ub,
		"creation" / Int8ub),
		encoder=lambda obj, ctx: (obj.node, obj.id, obj.creation),
		decoder=lambda obj, ctx: Reference(*obj))
port = ExprAdapter(Sequence("node" / term_,
		"id" / Int32ub,
		"creation" / Int8ub),
Exemplo n.º 10
0
        DISABLE=0,
        ENABLE=1,
    ),
    'swing' / Enum(
        Byte,
        SWING_50=0,
        SWING_55=1,
        SWING_57=2,
        SWING_59=3,
        SWING_61=4,
        SWING_64=5,
    ),
    'taps' / Byte,
    'tempo' / ExprAdapter(
        Int16ub,  # 7bit stuffed - each byte max 0x7F
        ((obj_ & 0x7f) + ((obj_ & 0x7f00) >> 1)),
        ((obj_ & 0x7f) + ((obj_ & 0x3f80) << 1)),
    ),
    'octaves' / Enum(
        Byte,
        OCT_1=0,
        OCT_2=1,
        OCT_3=2,
        OCT_4=3,
    ),
)

Joy = Struct(                       # Default values allow Mk1->Mk2 conversion
    'axis_x' / Default(Enum(
        Byte,
        PBEND=0,
Exemplo n.º 11
0
ProvisioningInvite = Struct("attention" / Int8ub, )

ProvisioningCapabilities = Struct(
    "num_elements" / Int8ub,
    "algorithms" / BitList(2, reversed=True),
    "public_key_type" / BitList(1, reversed=True),
    "static_oob_type" / BitList(1, reversed=True),
    "output_oob_size" / Int8ub,
    "output_oob_action" / BitList(2, reversed=True),
    "input_oob_size" / Int8ub,
    "input_oob_action" / BitList(2, reversed=True),
)

ProvisioningStart = Struct(
    "algorithm" / EnumAdapter(Int8ub, ProvisioningAlgorithm),
    "public_key" / ExprAdapter(Int8ub, lambda obj, ctx: bool(obj),
                               lambda obj, ctx: 1 if obj else 0),
    "authentication_method" /
    EnumAdapter(Int8ub, ProvisioningAuthenticationMethod),
    "authentication_action" / Switch(
        this.authentication_method,
        {
            ProvisioningAuthenticationMethod.NONE:
            Const(0, Int8ub),
            ProvisioningAuthenticationMethod.STATIC:
            Const(0, Int8ub),
            ProvisioningAuthenticationMethod.OUTPUT:
            EnumAdapter(Int8ub, ProvisioningOutputOOBAction),
            ProvisioningAuthenticationMethod.INPUT:
            EnumAdapter(Int8ub, ProvisioningInputOOBAction),
        },
    ),
Exemplo n.º 12
0
class USBTransport(object):
    '''Implement USB transport.'''
    def __init__(self, dev=None):
        '''Instantiate the first available PTP device over USB'''
        self.__setup_constructors()
        # If no device is specified, find all devices claiming to be Cameras
        # and get the USB endpoints for the first one that works.
        cameras = find_usb_cameras()
        devs = [dev] if (dev is not None) else cameras

        for dev in devs:
            if self.__setup_device(dev):
                break
        else:
            raise PTPError('No USB PTP device found.')

        if self.__dev.is_kernel_driver_active(self.__intf.bInterfaceNumber):
            try:
                self.__dev.detach_kernel_driver(self.__intf.bInterfaceNumber)
                usb.util.claim_interface(self.__dev, self.__intf)
            except usb.core.USBError:
                raise PTPError('Could not detach kernel driver.\n'
                               'Maybe the camera is mounted?')
        usb.util.claim_interface(self.__dev, self.__intf)
        self.__event_queue = Queue()
        self.__event_shutdown = Event()
        self.__event_proc = Thread(target=self.__poll_events)
        self.__event_proc.daemon = False
        atexit.register(self._shutdown)
        self.__event_proc.start()

    def _shutdown(self):
        self.__event_shutdown.set()
        # Free USB resource on shutdown.

        # Only join a running thread.
        if self.__event_proc.is_alive():
            self.__event_proc.join(2)

        usb.util.release_interface(self.__dev, self.__intf)

    # Helper methods.
    # ---------------------
    def __setup_device(self, dev):
        '''Get endpoints for a device. True on success.'''
        self.__inep = None
        self.__outep = None
        self.__intep = None
        self.__cfg = None
        self.__dev = None
        self.__intf = None
        # Attempt to find the USB in, out and interrupt endpoints for a PTP
        # interface.
        for cfg in dev:
            for intf in cfg:
                if intf.bInterfaceClass == PTP_USB_CLASS:
                    for ep in intf:
                        ep_type = endpoint_type(ep.bmAttributes)
                        ep_dir = endpoint_direction(ep.bEndpointAddress)
                        if ep_type == ENDPOINT_TYPE_BULK:
                            if ep_dir == ENDPOINT_IN:
                                self.__inep = ep
                            elif ep_dir == ENDPOINT_OUT:
                                self.__outep = ep
                        elif ((ep_type == ENDPOINT_TYPE_INTR)
                              and (ep_dir == ENDPOINT_IN)):
                            self.__intep = ep
                if not (self.__inep and self.__outep and self.__intep):
                    self.__inep = None
                    self.__outep = None
                    self.__intep = None
                else:
                    self.__cfg = cfg
                    self.__dev = dev
                    self.__intf = intf
                    return True
        return False

    def __setup_constructors(self):
        '''Set endianness and create transport-specific constructors.'''
        # Set endianness of constructors before using them.
        self._set_endian('little')

        self.__Length = Int32ul
        self.__Type = Enum(
            Int16ul,
            default=Pass,
            Undefined=0x0000,
            Command=0x0001,
            Data=0x0002,
            Response=0x0003,
            Event=0x0004,
        )
        # This is just a convenience constructor to get the size of a header.
        self.__Code = Int16ul
        self.__Header = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'Code' / self.__Code,
            'TransactionID' / self._TransactionID,
        )
        # These are the actual constructors for parsing and building.
        self.__CommandHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'OperationCode' / self._OperationCode,
            'TransactionID' / self._TransactionID,
        )
        self.__ResponseHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'ResponseCode' / self._ResponseCode,
            'TransactionID' / self._TransactionID,
        )
        self.__EventHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'EventCode' / self._EventCode,
            'TransactionID' / self._TransactionID,
        )
        # Apparently nobody uses the SessionID field. Even though it is
        # specified in ISO15740:2013(E), no device respects it and the session
        # number is implicit over USB.
        self.__Param = Range(0, 5, self._Parameter)
        self.__FullParam = Struct(Array(5, self._Parameter))
        self.__FullEventParam = Struct(Array(3, self._Parameter))
        self.__CommandTransactionBase = Struct(
            Embedded(self.__CommandHeader), 'Payload' /
            Bytes(lambda ctx, h=self.__Header: ctx.Length - h.sizeof()))
        self.__CommandTransaction = ExprAdapter(
            self.__CommandTransactionBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(), **obj),
            decoder=lambda obj, ctx: obj,
        )
        self.__ResponseTransactionBase = Struct(
            Embedded(self.__ResponseHeader), 'Payload' /
            Bytes(lambda ctx, h=self.__Header: ctx.Length - h.sizeof()))
        self.__ResponseTransaction = ExprAdapter(
            self.__ResponseTransactionBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(), **obj),
            decoder=lambda obj, ctx: obj,
        )
        self.__EventTransactionBase = Struct(
            Embedded(self.__EventHeader),
            'Payload' /
            Bytes(lambda ctx, h=self.__Header: ctx.Length - h.sizeof()),
        )
        self.__EventTransaction = ExprAdapter(
            self.__EventTransactionBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(), **obj),
            decoder=lambda obj, ctx: obj,
        )

    def __parse_response(self, usbdata):
        '''Helper method for parsing USB data.'''
        # Build up container with all PTP info.
        usbdata = bytearray(usbdata)
        transaction = self.__ResponseTransaction.parse(usbdata)
        response = Container(
            SessionID=self.session_id,
            TransactionID=transaction.TransactionID,
        )
        if transaction.Type == 'Response':
            response['ResponseCode'] = transaction.ResponseCode
            response['Parameter'] = self.__Param.parse(transaction.Payload)
        elif transaction.Type == 'Event':
            event = self.__EventHeader.parse(usbdata[0:self.__Header.sizeof()])
            response['EventCode'] = event.EventCode
            response['Parameter'] = self.__Param.parse(transaction.Payload)
        else:
            command = self.__CommandHeader.parse(
                usbdata[0:self.__Header.sizeof()])
            response['OperationCode'] = command.OperationCode
            response['Data'] = transaction.Payload
        return response

    def __recv(self, event=False, wait=False, raw=False):
        '''Helper method for receiving non-event data.'''
        # TODO: clear stalls automatically
        ep = self.__intep if event else self.__inep
        try:
            usbdata = ep.read(ep.wMaxPacketSize, timeout=0 if wait else 5)
        except usb.core.USBError as e:
            # Ignore timeout or busy device once.
            if e.errno == 110 or e.errno == 16:
                if event:
                    return None
                else:
                    usbdata = ep.read(ep.wMaxPacketSize, timeout=5000)
            else:
                raise e
        header = self.__ResponseHeader.parse(
            bytearray(usbdata[0:self.__Header.sizeof()]))
        if header.Type not in ['Response', 'Data', 'Event']:
            raise PTPError(
                'Unexpected USB transfer type.'
                'Expected Response, Event or Data but reveived {}'.format(
                    header.Type))
        while len(usbdata) < header.Length:
            usbdata += ep.read(ep.wMaxPacketSize, timeout=5000)
        if raw:
            return usbdata
        else:
            return self.__parse_response(usbdata)

    def __send(self, ptp_container, event=False):
        '''Helper method for sending data.'''
        ep = self.__intep if event else self.__outep
        transaction = self.__CommandTransaction.build(ptp_container)
        try:
            ep.write(transaction, timeout=1)
        except usb.core.USBError as e:
            # Ignore timeout or busy device once.
            if e.errno == 110 or e.errno == 16:
                ep.write(transaction, timeout=5000)

    def __send_request(self, ptp_container):
        '''Send PTP request without checking answer.'''
        # Don't modify original container to keep abstraction barrier.
        ptp = Container(**ptp_container)
        # Don't send unused parameters
        try:
            while not ptp.Parameter[-1]:
                ptp.Parameter.pop()
                if len(ptp.Parameter) == 0:
                    break
        except IndexError:
            # The Parameter list is already empty.
            pass

        # Send request
        ptp['Type'] = 'Command'
        ptp['Payload'] = self.__Param.build(ptp.Parameter)
        self.__send(ptp)

    def __send_data(self, ptp_container, data):
        '''Send data without checking answer.'''
        # Don't modify original container to keep abstraction barrier.
        ptp = Container(**ptp_container)
        # Send data
        ptp['Type'] = 'Data'
        ptp['Payload'] = data
        self.__send(ptp)

    # Actual implementation
    # ---------------------
    def send(self, ptp_container, data):
        '''Transfer operation with dataphase from initiator to responder'''
        self.__send_request(ptp_container)
        self.__send_data(ptp_container, data)
        # Get response and sneak in implicit SessionID and missing parameters.
        return self.__recv()

    def recv(self, ptp_container):
        '''Transfer operation with dataphase from responder to initiator.'''
        self.__send_request(ptp_container)
        dataphase = self.__recv()
        if hasattr(dataphase, 'Data'):
            response = self.__recv()
            if ((ptp_container.OperationCode != dataphase.OperationCode)
                    or (ptp_container.TransactionID != dataphase.TransactionID)
                    or (ptp_container.SessionID != dataphase.SessionID)
                    or (dataphase.TransactionID != response.TransactionID)
                    or (dataphase.SessionID != response.SessionID)):
                raise PTPError(
                    'Dataphase does not match with requested operation.')
            response['Data'] = dataphase.Data
            return response
        else:
            return dataphase

    def mesg(self, ptp_container):
        '''Transfer operation without dataphase.'''
        self.__send_request(ptp_container)
        # Get response and sneak in implicit SessionID and missing parameters
        # for FullResponse.
        return self.__recv()

    def event(self, wait=False):
        '''Check event.

        If `wait` this function is blocking. Otherwise it may return None.
        '''
        evt = None
        usbdata = None
        timeout = None if wait else 0.001
        if not self.__event_queue.empty():
            usbdata = self.__event_queue.get(block=not wait, timeout=timeout)
        if usbdata is not None:
            evt = self.__parse_response(usbdata)

        return evt

    def __poll_events(self):
        '''Poll events, adding them to a queue.'''
        while not self.__event_shutdown.is_set() and _main_thread_alive():
            evt = self.__recv(event=True, wait=False, raw=True)
            if evt is not None:
                self.__event_queue.put(evt)
Exemplo n.º 13
0
    Padding(8),
    "oem_id" / Int16ul,
    "oem_info" / Int16ul,
    Padding(20),
    "coff_header_pointer" / Int32ul,
    "_assembly_start" / Tell,
    "code" /
    OnDemand(HexDump(Bytes(this.coff_header_pointer - this._assembly_start))),
)

symbol_table = "symbol_table" / Struct(
    "name" / String(8, padchar=b"\x00"), "value" / Int32ul,
    "section_number" / Enum(
        ExprAdapter(
            Int16sl,
            encoder=lambda obj, ctx: obj + 1,
            decoder=lambda obj, ctx: obj - 1,
        ),
        UNDEFINED=-1,
        ABSOLUTE=-2,
        DEBUG=-3,
        _default_=Pass,
    ), "complex_type" / Enum(
        Int8ul,
        NULL=0,
        POINTER=1,
        FUNCTION=2,
        ARRAY=3,
    ), "base_type" / Enum(
        Int8ul,
        NULL=0,
Exemplo n.º 14
0
    def __setup_constructors(self):
        '''Set endianness and create transport-specific constructors.'''
        # Set endianness of constructors before using them.
        self._set_endian('little')

        self.__Length = Int32ul
        self.__Type = Enum(
            Int16ul,
            default=Pass,
            Undefined=0x0000,
            Command=0x0001,
            Data=0x0002,
            Response=0x0003,
            Event=0x0004,
        )
        # This is just a convenience constructor to get the size of a header.
        self.__Code = Int16ul
        self.__Header = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'Code' / self.__Code,
            'TransactionID' / self._TransactionID,
        )
        # These are the actual constructors for parsing and building.
        self.__CommandHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'OperationCode' / self._OperationCode,
            'TransactionID' / self._TransactionID,
        )
        self.__ResponseHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'ResponseCode' / self._ResponseCode,
            'TransactionID' / self._TransactionID,
        )
        self.__EventHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'EventCode' / self._EventCode,
            'TransactionID' / self._TransactionID,
        )
        # Apparently nobody uses the SessionID field. Even though it is
        # specified in ISO15740:2013(E), no device respects it and the session
        # number is implicit over USB.
        self.__Param = Range(0, 5, self._Parameter)
        self.__CommandTransactionBase = Struct(
            Embedded(self.__CommandHeader), 'Payload' /
            Bytes(lambda ctx, h=self.__Header: ctx.Length - h.sizeof()))
        self.__CommandTransaction = ExprAdapter(
            self.__CommandTransactionBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(), **obj),
            decoder=lambda obj, ctx: obj,
        )
        self.__ResponseTransactionBase = Struct(
            Embedded(self.__ResponseHeader), 'Payload' /
            Bytes(lambda ctx, h=self.__Header: ctx.Length - h.sizeof()))
        self.__ResponseTransaction = ExprAdapter(
            self.__ResponseTransactionBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(), **obj),
            decoder=lambda obj, ctx: obj,
        )
Exemplo n.º 15
0
class HostnameString(SymmetricAdapter):
    def __init__(self, is_ipv4_func):
        super(HostnameString, self).__init__(Bytes(16))
        self.is_ipv4_func = is_ipv4_func

    def _decode(self, obj, context, path):
        if obj in context.hosts_table:
            if context.hosts_table[obj]:
                return context.hosts_table[obj]
        if self.is_ipv4_func(context):
            return str(IPv4Address(obj[:4]))
        return str(IPv6Address(obj))


PortString = ExprAdapter(
    Struct("port_number" / Int16ul), lambda obj, ctx: ctx.ports_table.get(
        (obj.port_number, bool(ctx.flags.is_tcp)), str(obj.port_number)),
    lambda obj, ctx: ctx.ports_table.index(obj))

FilesystemQueryVolumeInformationOperationType = Enum(
    Int8ul, FilesystemQueryVolumeInformationOperation)
FilesystemSetVolumeInformationOperationType = Enum(
    Int8ul, FilesystemSetVolumeInformationOperation)
FilesystemQueryInformationOperationType = Enum(
    Int8ul, FilesystemQueryInformationOperation)
FilesystemDirectoryControlOperationType = Enum(
    Int8ul, FilesysemDirectoryControlOperation)
FilesystemSetInformationOperationType = Enum(
    Int8ul, FilesystemSetInformationOperation)
FilesystemPnpOperationType = Enum(Int8ul, FilesystemPnpOperation)
FilesystemLockUnlockOperationType = Enum(Int8ul, FilesystemLockUnlockOperation)
NetworkOperationFlags = FlagsEnum(Int16ul,
Exemplo n.º 16
0
class USBTransport(object):
    '''Implement USB transport.'''
    def __init__(self, *args, **kwargs):
        device = kwargs.get('device', None)
        '''Instantiate the first available PTP device over USB'''
        logger.debug('Init USB')
        self.__setup_constructors()
        # If no device is specified, find all devices claiming to be Cameras
        # and get the USB endpoints for the first one that works.
        if device is None:
            logger.debug('No device provided, probing all USB devices.')
        if isinstance(device, six.string_types):
            name = device
            logger.debug(
                'Device name provided, probing all USB devices for {}.'.format(
                    name))
            device = None
        else:
            name = None
        devs = ([device] if
                (device is not None) else find_usb_cameras(name=name))

        self.__acquire_camera(devs)

        self.__event_queue = Queue()
        self.__event_shutdown = Event()
        # Locks for different end points.
        self.__inep_lock = RLock()
        self.__intep_lock = RLock()
        self.__outep_lock = RLock()
        # Slightly redundant transaction lock to avoid catching other request's
        # response
        self.__transaction_lock = RLock()

        self.__event_proc = Thread(name='EvtPolling',
                                   target=self.__poll_events)
        self.__event_proc.daemon = False
        atexit.register(self._shutdown)
        self.__event_proc.start()

    def __available_cameras(self, devs):
        for dev in devs:
            if self.__setup_device(dev):
                logger.debug('Found USB PTP device {}'.format(dev))
                yield
        else:
            message = 'No USB PTP device found.'
            logger.error(message)
            raise PTPError(message)

    def __acquire_camera(self, devs):
        '''From the cameras given, get the first one that does not fail'''

        for _ in self.__available_cameras(devs):
            try:
                if self.__dev.is_kernel_driver_active(
                        self.__intf.bInterfaceNumber):
                    try:
                        self.__dev.detach_kernel_driver(
                            self.__intf.bInterfaceNumber)
                        usb.util.claim_interface(self.__dev, self.__intf)
                    except usb.core.USBError:
                        message = ('Could not detach kernel driver. '
                                   'Maybe the camera is mounted?')
                        logger.error(message)
                logger.debug('Claiming {}'.format(repr(self.__dev)))
                usb.util.claim_interface(self.__dev, self.__intf)
            except Exception as e:
                logger.debug('{}'.format(e))
                continue
            break
        else:
            message = ('Could acquire any camera.')
            logger.error(message)
            raise PTPError(message)

    def _shutdown(self):
        logger.debug('Shutdown request')
        self.__event_shutdown.set()
        # Free USB resource on shutdown.

        # Only join a running thread.
        if self.__event_proc.is_alive():
            self.__event_proc.join(2)

        logger.debug('Release {}'.format(repr(self.__dev)))
        usb.util.release_interface(self.__dev, self.__intf)

    # Helper methods.
    # ---------------------
    def __setup_device(self, dev):
        '''Get endpoints for a device. True on success.'''
        self.__inep = None
        self.__outep = None
        self.__intep = None
        self.__cfg = None
        self.__dev = None
        self.__intf = None
        # Attempt to find the USB in, out and interrupt endpoints for a PTP
        # interface.
        for cfg in dev:
            for intf in cfg:
                if intf.bInterfaceClass == PTP_USB_CLASS:
                    for ep in intf:
                        ep_type = endpoint_type(ep.bmAttributes)
                        ep_dir = endpoint_direction(ep.bEndpointAddress)
                        if ep_type == ENDPOINT_TYPE_BULK:
                            if ep_dir == ENDPOINT_IN:
                                self.__inep = ep
                            elif ep_dir == ENDPOINT_OUT:
                                self.__outep = ep
                        elif ((ep_type == ENDPOINT_TYPE_INTR)
                              and (ep_dir == ENDPOINT_IN)):
                            self.__intep = ep
                if not (self.__inep and self.__outep and self.__intep):
                    self.__inep = None
                    self.__outep = None
                    self.__intep = None
                else:
                    logger.debug('Found {}'.format(repr(self.__inep)))
                    logger.debug('Found {}'.format(repr(self.__outep)))
                    logger.debug('Found {}'.format(repr(self.__intep)))
                    self.__cfg = cfg
                    self.__dev = dev
                    self.__intf = intf
                    return True
        return False

    def __setup_constructors(self):
        '''Set endianness and create transport-specific constructors.'''
        # Set endianness of constructors before using them.
        self._set_endian('little')

        self.__Length = Int32ul
        self.__Type = Enum(
            Int16ul,
            default=Pass,
            Undefined=0x0000,
            Command=0x0001,
            Data=0x0002,
            Response=0x0003,
            Event=0x0004,
        )
        # This is just a convenience constructor to get the size of a header.
        self.__Code = Int16ul
        self.__Header = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'Code' / self.__Code,
            'TransactionID' / self._TransactionID,
        )
        # These are the actual constructors for parsing and building.
        self.__CommandHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'OperationCode' / self._OperationCode,
            'TransactionID' / self._TransactionID,
        )
        self.__ResponseHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'ResponseCode' / self._ResponseCode,
            'TransactionID' / self._TransactionID,
        )
        self.__EventHeader = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
            'EventCode' / self._EventCode,
            'TransactionID' / self._TransactionID,
        )
        # Apparently nobody uses the SessionID field. Even though it is
        # specified in ISO15740:2013(E), no device respects it and the session
        # number is implicit over USB.
        self.__Param = Range(0, 5, self._Parameter)
        self.__CommandTransactionBase = Struct(
            Embedded(self.__CommandHeader), 'Payload' /
            Bytes(lambda ctx, h=self.__Header: ctx.Length - h.sizeof()))
        self.__CommandTransaction = ExprAdapter(
            self.__CommandTransactionBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(), **obj),
            decoder=lambda obj, ctx: obj,
        )
        self.__ResponseTransactionBase = Struct(
            Embedded(self.__ResponseHeader), 'Payload' /
            Bytes(lambda ctx, h=self.__Header: ctx.Length - h.sizeof()))
        self.__ResponseTransaction = ExprAdapter(
            self.__ResponseTransactionBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(), **obj),
            decoder=lambda obj, ctx: obj,
        )

    def __parse_response(self, usbdata):
        '''Helper method for parsing USB data.'''
        # Build up container with all PTP info.
        logger.debug('Transaction:')
        usbdata = bytearray(usbdata)
        if logger.isEnabledFor(logging.DEBUG):
            for l in hexdump(six.binary_type(usbdata), result='generator'):
                logger.debug(l)
        transaction = self.__ResponseTransaction.parse(usbdata)
        response = Container(
            SessionID=self.session_id,
            TransactionID=transaction.TransactionID,
        )
        logger.debug('Interpreting {} transaction'.format(transaction.Type))
        if transaction.Type == 'Response':
            response['ResponseCode'] = transaction.ResponseCode
            response['Parameter'] = self.__Param.parse(transaction.Payload)
        elif transaction.Type == 'Event':
            event = self.__EventHeader.parse(usbdata[0:self.__Header.sizeof()])
            response['EventCode'] = event.EventCode
            response['Parameter'] = self.__Param.parse(transaction.Payload)
        else:
            command = self.__CommandHeader.parse(
                usbdata[0:self.__Header.sizeof()])
            response['OperationCode'] = command.OperationCode
            response['Data'] = transaction.Payload
        return response

    def __recv(self, event=False, wait=False, raw=False):
        '''Helper method for receiving data.'''
        # TODO: clear stalls automatically
        ep = self.__intep if event else self.__inep
        lock = self.__intep_lock if event else self.__inep_lock
        usbdata = array.array('B', [])
        with lock:
            try:
                usbdata += ep.read(ep.wMaxPacketSize, timeout=0 if wait else 5)
                if len(usbdata) < self.__Header.sizeof():
                    logger.debug('Initial read smaller than a header')
                    tries = 5
                    for _ in range(tries):
                        usbdata += ep.read(ep.wMaxPacketSize,
                                           timeout=5000 / tries)
            except usb.core.USBError as e:
                # Ignore timeout or busy device once.
                if e.errno == 110 or e.errno == 16:
                    if event:
                        return None
                    else:
                        usbdata += ep.read(ep.wMaxPacketSize, timeout=5000)
                else:
                    raise e

            if len(usbdata) == 0:
                if event:
                    return None
                else:
                    raise PTPError('Empty USB read')

            if (logger.isEnabledFor(logging.DEBUG)
                    and (len(usbdata) < self.__Header.sizeof())):
                logger.debug('Incomplete packet')
                for l in hexdump(six.binary_type(bytearray(usbdata)),
                                 result='generator'):
                    logger.debug(l)

            header = self.__ResponseHeader.parse(
                bytearray(usbdata[0:self.__Header.sizeof()]))
            if header.Type not in ['Response', 'Data', 'Event']:
                raise PTPError(
                    'Unexpected USB transfer type.'
                    'Expected Response, Event or Data but received {}'.format(
                        header.Type))
            while len(usbdata) < header.Length:
                usbdata += ep.read(ep.wMaxPacketSize, timeout=5000)
        if raw:
            return usbdata
        else:
            return self.__parse_response(usbdata)

    def __send(self, ptp_container, event=False):
        '''Helper method for sending data.'''
        ep = self.__intep if event else self.__outep
        lock = self.__intep_lock if event else self.__outep_lock
        transaction = self.__CommandTransaction.build(ptp_container)
        with lock:
            try:
                ep.write(transaction, timeout=1)
            except usb.core.USBError as e:
                # Ignore timeout or busy device once.
                if e.errno == 110 or e.errno == 16:
                    ep.write(transaction, timeout=5000)

    def __send_request(self, ptp_container):
        '''Send PTP request without checking answer.'''
        # Don't modify original container to keep abstraction barrier.
        ptp = Container(**ptp_container)
        # Don't send unused parameters
        try:
            while not ptp.Parameter[-1]:
                ptp.Parameter.pop()
                if len(ptp.Parameter) == 0:
                    break
        except IndexError:
            # The Parameter list is already empty.
            pass

        # Send request
        ptp['Type'] = 'Command'
        ptp['Payload'] = self.__Param.build(ptp.Parameter)
        self.__send(ptp)

    def __send_data(self, ptp_container, data):
        '''Send data without checking answer.'''
        # Don't modify original container to keep abstraction barrier.
        ptp = Container(**ptp_container)
        # Send data
        ptp['Type'] = 'Data'
        ptp['Payload'] = data
        self.__send(ptp)

    # Actual implementation
    # ---------------------
    def send(self, ptp_container, data):
        '''Transfer operation with dataphase from initiator to responder'''
        datalen = len(data)
        logger.debug('SEND {} {} bytes{}'.format(
            ptp_container.OperationCode,
            datalen,
            ' ' + str(list(map(hex, ptp_container.Parameter)))
            if ptp_container.Parameter else '',
        ))
        with self.__transaction_lock:
            self.__send_request(ptp_container)
            self.__send_data(ptp_container, data)
            # Get response and sneak in implicit SessionID and missing
            # parameters.
            response = self.__recv()
        logger.debug('SEND {} {} bytes {}{}'.format(
            ptp_container.OperationCode,
            datalen,
            response.ResponseCode,
            ' ' + str(list(map(hex, response.Parameter)))
            if ptp_container.Parameter else '',
        ))
        return response

    def recv(self, ptp_container):
        '''Transfer operation with dataphase from responder to initiator.'''
        logger.debug('RECV {}{}'.format(
            ptp_container.OperationCode,
            ' ' + str(list(map(hex, ptp_container.Parameter)))
            if ptp_container.Parameter else '',
        ))
        with self.__transaction_lock:
            self.__send_request(ptp_container)
            dataphase = self.__recv()
            if hasattr(dataphase, 'Data'):
                response = self.__recv()
                if ((ptp_container.OperationCode != dataphase.OperationCode) or
                    (ptp_container.TransactionID != dataphase.TransactionID)
                        or (ptp_container.SessionID != dataphase.SessionID)
                        or (dataphase.TransactionID != response.TransactionID)
                        or (dataphase.SessionID != response.SessionID)):
                    raise PTPError(
                        'Dataphase does not match with requested operation.')
                response['Data'] = dataphase.Data
            else:
                response = dataphase

        logger.debug('RECV {} {}{}{}'.format(
            ptp_container.OperationCode,
            response.ResponseCode,
            ' {} bytes'.format(len(response.Data)) if hasattr(
                response, 'Data') else '',
            ' ' + str(list(map(hex, response.Parameter)))
            if response.Parameter else '',
        ))
        return response

    def mesg(self, ptp_container):
        '''Transfer operation without dataphase.'''
        logger.debug('MESG {}{}'.format(
            ptp_container.OperationCode,
            ' ' + str(list(map(hex, ptp_container.Parameter)))
            if ptp_container.Parameter else '',
        ))
        with self.__transaction_lock:
            self.__send_request(ptp_container)
            # Get response and sneak in implicit SessionID and missing
            # parameters for FullResponse.
            response = self.__recv()
        logger.debug('MESG {} {}{}'.format(
            ptp_container.OperationCode,
            response.ResponseCode,
            ' ' + str(list(map(hex, response.Parameter)))
            if response.Parameter else '',
        ))
        return response

    def event(self, wait=False):
        '''Check event.

        If `wait` this function is blocking. Otherwise it may return None.
        '''
        evt = None
        usbdata = None
        timeout = None if wait else 0.001
        if not self.__event_queue.empty():
            usbdata = self.__event_queue.get(block=not wait, timeout=timeout)
        if usbdata is not None:
            evt = self.__parse_response(usbdata)

        return evt

    def __poll_events(self):
        '''Poll events, adding them to a queue.'''
        while not self.__event_shutdown.is_set() and _main_thread_alive():
            try:
                evt = self.__recv(event=True, wait=False, raw=True)
                if evt is not None:
                    logger.debug('Event queued')
                    self.__event_queue.put(evt)
            except Exception as e:
                logger.error(e)
Exemplo n.º 17
0
         "module_tlm_trouble" / Flag,
         "module_fail_to_com_trouble" / Flag,
         "module_printer_trouble" / Flag,
         "module_ac_trouble" / Flag,
         "module_battery_fail" / Flag,
         "module_aux_trouble" / Flag,
         "missing_keypad_trouble" / Flag,
         "missing_module_trouble" / Flag,
         "_future_use_4" / Flag,
         "_future_use_5" / Flag,
         "safety_mismatch_trouble" / Flag,
         "bus_global_fail" / Flag,
         "bus_overload_trouble" / Flag,
         "mdl_com_error" / Flag),
     "time" / DateAdapter(Bytes(7)),
     "vdc" / ExprAdapter(Byte,
                         obj_ * (20.3 - 1.4) / 255.0 + 1.4, 0),
     "battery" / ExprAdapter(Byte, obj_ * 22.8 / 255.0, 0),
     "dc" / ExprAdapter(Byte, obj_ * 22.8 / 255.0, 0),
     "zone_open" / StatusAdapter(Bytes(12)),
     "zone_tamper" / StatusAdapter(Bytes(12)),
     "zone_low_battery" / StatusAdapter(Bytes(12))),
 2:
 Struct("zone_status" / ZoneFlags(64)),
 3:
 Struct(
     "zone_status" / ZoneFlags(32, start_index_from=65),
     "partition_status" / PartitionStatus(Bytes(32)),
 ),
 4:
 Struct(
     "partition_status" / PartitionStatus(Bytes(16)), "_panel_status" /
Exemplo n.º 18
0
class IPTransport(object):
    '''Implement IP transport.'''
    def __init__(self, device=None):
        '''Instantiate the first available PTP device over IP'''
        self.__setup_constructors()
        logger.debug('Init IP')

        self.__dev = device
        if device is None:
            raise NotImplementedError(
                'IP discovery not implemented. Please provide a device.'
            )
        self.__device = device

        # Signal usable implicit session
        self.__implicit_session_open = Event()
        # Signal implicit session is shutting down
        self.__implicit_session_shutdown = Event()

        self.__check_session_lock = Lock()
        self.__transaction_lock = Lock()

        self.__event_queue = Queue()

        atexit.register(self._shutdown)

    def _shutdown(self):
        try:
            self.__close_implicit_session()
        except Exception as e:
            logger.error(e)

    @contextmanager
    def __implicit_session(self):
        '''Manage implicit sessions with responder'''
        # There is now an implicit session
        self.__check_session_lock.acquire()
        if not self.__implicit_session_open.is_set():
            try:
                self.__open_implicit_session()
                self.__check_session_lock.release()
                yield
            except Exception as e:
                logger.error(e)
                raise PTPError('Failed to open PTP/IP implicit session')
            finally:
                if self.__implicit_session_open.is_set():
                    self.__close_implicit_session()
                if self.__check_session_lock.locked():
                    self.__check_session_lock.release()
        else:
            self.__check_session_lock.release()
            yield

    def __open_implicit_session(self):
        '''Establish implicit session with responder'''

        self.__implicit_session_shutdown.clear()

        # Establish Command and Event connections
        if type(self.__device) is tuple:
            host, port = self.__device
            self.__setup_connection(host, port)
        else:
            self.__setup_connection(self.__device)

        self.__implicit_session_open.set()

        # Prepare Event and Probe threads
        self.__event_proc = Thread(
            name='EvtPolling',
            target=self.__poll_events
        )
        self.__event_proc.daemon = False

        self.__ping_pong_proc = Thread(
            name='PingPong',
            target=self.__ping_pong
        )
        self.__ping_pong_proc.daemon = False

        # Launch Event and Probe threads
        self.__event_proc.start()
        self.__ping_pong_proc.start()

    def __close_implicit_session(self):
        '''Terminate implicit session with responder'''
        self.__implicit_session_shutdown.set()

        if not self.__implicit_session_open.is_set():
            return

        # Only join running threads.
        if self.__event_proc.is_alive():
            self.__event_proc.join(2)
        if self.__ping_pong_proc.is_alive():
            self.__ping_pong_proc.join(2)

        logger.debug('Close connections for {}'.format(repr(self.__dev)))
        try:
            self.__evtcon.shutdown(socket.SHUT_RDWR)
        except socket.error as e:
            if e.errno == 107:
                pass
            else:
                raise e
        try:
            self.__cmdcon.shutdown(socket.SHUT_RDWR)
        except socket.error as e:
            if e.errno == 107:
                pass
            else:
                raise e
        self.__evtcon.close()
        self.__cmdcon.close()

        self.__implicit_session_open.clear()

    def __setup_connection(self, host=None, port=15740):
        '''Establish a PTP/IP session for a given host'''
        logger.debug(
            'Establishing PTP/IP connection with {}:{}'
            .format(host, port)
        )
        socket.setdefaulttimeout(5)
        hdrlen = self.__Header.sizeof()
        # Command Connection Establishment
        self.__cmdcon = create_connection((host, port))
        # Send InitCommand
        # TODO: Allow users to identify as an arbitrary initiator.
        init_cmd_req_payload = self.__InitCommand.build(
            Container(
                InitiatorGUID=16*[0xFF],
                InitiatorFriendlyName='PTPy',
                InitiatorProtocolVersion=Container(
                    Major=100,
                    Minor=000,
                ),
            ))
        init_cmd_req = self.__Packet.build(
            Container(
                Type='InitCommand',
                Payload=init_cmd_req_payload,
            )
        )
        actual_socket(self.__cmdcon).sendall(init_cmd_req)
        # Get ACK/NACK
        init_cmd_req_rsp = actual_socket(self.__cmdcon).recv(72)
        init_cmd_rsp_hdr = self.__Header.parse(
            init_cmd_req_rsp[0:hdrlen]
        )

        if init_cmd_rsp_hdr.Type == 'InitCommandAck':
            cmd_ack = self.__InitCommandACK.parse(init_cmd_req_rsp[hdrlen:])
            logger.debug(
                'Command connection ({}) established'
                .format(cmd_ack.ConnectionNumber)
            )
        elif init_cmd_rsp_hdr.Type == 'InitFail':
            cmd_nack = self.__InitFail.parse(init_cmd_req_rsp[hdrlen:])
            msg = 'InitCommand failed, Reason: {}'.format(
                cmd_nack
            )
            logger.error(msg)
            raise PTPError(msg)
        else:
            msg = 'Unexpected response Type to InitCommand : {}'.format(
                init_cmd_rsp_hdr.Type
            )
            logger.error(msg)
            raise PTPError(msg)

        # Event Connection Establishment
        self.__evtcon = create_connection((host, port))
        self.__evtcon.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        self.__evtcon.setsockopt(socket.IPPROTO_TCP, socket.SO_KEEPALIVE, 1)

        # Send InitEvent
        payload = self.__InitEvent.build(Container(
            ConnectionNumber=cmd_ack.ConnectionNumber,
        ))
        evt_req = self.__Packet.build(
            Container(
                Type='InitEvent',
                Payload=payload,
            )
        )
        actual_socket(self.__evtcon).sendall(evt_req)
        # Get ACK/NACK
        init_evt_req_rsp = actual_socket(self.__evtcon).recv(
            hdrlen + self.__InitFail.sizeof()
        )
        init_evt_rsp_hdr = self.__Header.parse(
            init_evt_req_rsp[0:hdrlen]
        )

        if init_evt_rsp_hdr.Type == 'InitEventAck':
            logger.debug(
                'Event connection ({}) established'
                .format(cmd_ack.ConnectionNumber)
            )
        elif init_evt_rsp_hdr.Type == 'InitFail':
            evt_nack = self.__InitFail.parse(init_evt_req_rsp[hdrlen:])
            msg = 'InitEvent failed, Reason: {}'.format(
                evt_nack
            )
            logger.error(msg)
            raise PTPError(msg)
        else:
            msg = 'Unexpected response Type to InitEvent : {}'.format(
                init_evt_rsp_hdr.Type
            )
            logger.error(msg)
            raise PTPError(msg)

    # Helper methods.
    # ---------------------
    def __setup_constructors(self):
        '''Set endianness and create transport-specific constructors.'''
        # Set endianness of constructors before using them.
        self._set_endian('little')

        self.__Length = Int32ul
        self.__Type = Enum(
            Int32ul,
            Undefined=0x00000000,
            InitCommand=0x00000001,
            InitCommandAck=0x00000002,
            InitEvent=0x00000003,
            InitEventAck=0x00000004,
            InitFail=0x00000005,
            Command=0x00000006,
            Response=0x00000007,
            Event=0x00000008,
            StartData=0x00000009,
            Data=0x0000000A,
            Cancel=0x0000000B,
            EndData=0x0000000C,
            Ping=0x0000000D,
            Pong=0x0000000E,
        )
        self.__Header = Struct(
            'Length' / self.__Length,
            'Type' / self.__Type,
        )
        self.__Param = Range(0, 5, self._Parameter)
        self.__EventParam = Range(0, 3, self._Parameter)
        self.__PacketBase = Struct(
            Embedded(self.__Header),
            'Payload' / Bytes(
                lambda ctx, h=self.__Header: ctx.Length - h.sizeof()),
        )
        self.__Packet = ExprAdapter(
            self.__PacketBase,
            encoder=lambda obj, ctx, h=self.__Header: Container(
                Length=len(obj.Payload) + h.sizeof(),
                **obj
            ),
            decoder=lambda obj, ctx: obj,
        )
        # Yet another arbitrary string type. Max-length CString utf8-encoded
        self.__PTPIPString = ExprAdapter(
            RepeatUntil(
                lambda obj, ctx, lst:
                six.unichr(obj) in '\x00' or len(lst) == 40, Int16ul
            ),
            encoder=lambda obj, ctx:
            [] if len(obj) == 0 else[ord(c) for c in six.text_type(obj)]+[0],
            decoder=lambda obj, ctx:
            u''.join(
                [six.unichr(o) for o in obj]
            ).split('\x00')[0],
        )
        # PTP/IP packets
        # Command
        self.__ProtocolVersion = Struct(
            'Major' / Int16ul,
            'Minor' / Int16ul,
        )
        self.__InitCommand = Embedded(Struct(
            'InitiatorGUID' / Array(16, Int8ul),
            'InitiatorFriendlyName' / self.__PTPIPString,
            'InitiatorProtocolVersion' / self.__ProtocolVersion,
        ))
        self.__InitCommandACK = Embedded(Struct(
            'ConnectionNumber' / Int32ul,
            'ResponderGUID' / Array(16, Int8ul),
            'ResponderFriendlyName' / self.__PTPIPString,
            'ResponderProtocolVersion' / self.__ProtocolVersion,
        ))
        # Event
        self.__InitEvent = Embedded(Struct(
            'ConnectionNumber' / Int32ul,
        ))
        # Common to Events and Command requests
        self.__Reason = Enum(
            # TODO: Verify these codes...
            Int32ul,
            Undefined=0x0000,
            RejectedInitiator=0x0001,
            Busy=0x0002,
            Unspecified=0x0003,
        )
        self.__InitFail = Embedded(Struct(
            'Reason' / self.__Reason,
        ))

        self.__DataphaseInfo = Enum(
            Int32ul,
            Undefined=0x00000000,
            In=0x00000001,
            Out=0x00000002,
        )
        self.__Command = Embedded(Struct(
            'DataphaseInfo' / self.__DataphaseInfo,
            'OperationCode' / self._OperationCode,
            'TransactionID' / self._TransactionID,
            'Parameter' / self.__Param,
        ))
        self.__Response = Embedded(Struct(
            'ResponseCode' / self._ResponseCode,
            'TransactionID' / self._TransactionID,
            'Parameter' / self.__Param,
        ))
        self.__Event = Embedded(Struct(
            'EventCode' / self._EventCode,
            'TransactionID' / self._TransactionID,
            'Parameter' / self.__EventParam,
        ))
        self.__StartData = Embedded(Struct(
            'TransactionID' / self._TransactionID,
            'TotalDataLength' / Int64ul,
        ))
        # TODO: Fix packing and unpacking dataphase data
        self.__Data = Embedded(Struct(
            'TransactionID' / self._TransactionID,
            'Data' / Bytes(
                lambda ctx:
                ctx._.Length -
                self.__Header.sizeof() -
                self._TransactionID.sizeof()
            ),
        ))
        self.__EndData = Embedded(Struct(
            'TransactionID' / self._TransactionID,
            'Data' / Bytes(
                lambda ctx:
                ctx._.Length -
                self.__Header.sizeof() -
                self._TransactionID.sizeof()
            ),
        ))
        self.__Cancel = Embedded(Struct(
            'TransactionID' / self._TransactionID,
        ))
        # Convenience construct for parsing packets

        self.__PacketPayload = Debugger(Struct(
            'Header' / Embedded(self.__Header),
            'Payload' / Embedded(Switch(
                lambda ctx: ctx.Type,
                {
                    'InitCommand': self.__InitCommand,
                    'InitCommandAck': self.__InitCommandACK,
                    'InitEvent': self.__InitEvent,
                    'InitFail': self.__InitFail,
                    'Command': self.__Command,
                    'Response': self.__Response,
                    'Event': self.__Event,
                    'StartData': self.__StartData,
                    'Data': self.__Data,
                    'EndData': self.__EndData,
                },
                default=Pass,
            ))
        ))

    def __parse_response(self, ipdata):
        '''Helper method for parsing data.'''
        # Build up container with all PTP info.
        response = self.__PacketPayload.parse(ipdata)
        # Sneak in an implicit Session ID
        response['SessionID'] = self.session_id
        return response

    def __recv(self, event=False, wait=False, raw=False):
        '''Helper method for receiving packets.'''
        hdrlen = self.__Header.sizeof()
        with self.__implicit_session():
            ip = (
                actual_socket(self.__evtcon)
                if event
                else actual_socket(self.__cmdcon)
            )
            data = bytes()
            while True:
                try:
                    ipdata = ip.recv(hdrlen)
                except socket.timeout:
                    if event:
                        return None
                    else:
                        ipdata = ip.recv(hdrlen)

                if len(ipdata) == 0 and not event:
                    raise PTPError('Command connection dropped')
                elif event:
                    return None

                # Read a single entire header
                while len(ipdata) < hdrlen:
                    ipdata += ip.recv(hdrlen - len(ipdata))
                header = self.__Header.parse(
                    ipdata[0:hdrlen]
                )
                # Read a single entire packet
                while len(ipdata) < header.Length:
                    ipdata += ip.recv(header.Length - len(ipdata))
                # Run sanity checks.
                if header.Type not in [
                        'Cancel',
                        'Data',
                        'Event',
                        'Response',
                        'StartData',
                        'EndData',
                ]:
                    raise PTPError(
                        'Unexpected PTP/IP packet type {}'
                        .format(header.Type)
                    )
                if header.Type not in ['StartData', 'Data', 'EndData']:
                    break
                else:
                    response = self.__parse_response(ipdata)

                if header.Type == 'StartData':
                    expected = response.TotalDataLength
                    current_transaction = response.TransactionID
                elif (
                        header.Type == 'Data' and
                        response.TransactionID == current_transaction
                ):
                    data += response.Data
                elif (
                        header.Type == 'EndData' and
                        response.TransactionID == current_transaction
                ):
                    data += response.Data
                    datalen = len(data)
                    if datalen != expected:
                        logger.warning(
                            '{} data than expected {}/{}'
                            .format(
                                'More' if datalen > expected else 'Less',
                                datalen,
                                expected
                            )
                        )
                    response['Data'] = data
                    response['Type'] = 'Data'
                    return response

        if raw:
            # TODO: Deal with raw Data packets??
            return ipdata
        else:
            return self.__parse_response(ipdata)

    def __send(self, ptp_container, event=False):
        '''Helper method for sending packets.'''
        packet = self.__Packet.build(ptp_container)
        ip = (
            actual_socket(self.__evtcon)
            if event
            else actual_socket(self.__cmdcon)
        )
        while ip.sendall(packet) is not None:
            logger.debug('Failed to send {} packet'.format(ptp_container.Type))

    def __send_request(self, ptp_container):
        '''Send PTP request without checking answer.'''
        # Don't modify original container to keep abstraction barrier.
        ptp = Container(**ptp_container)

        # Send unused parameters always
        ptp['Parameter'] += [0] * (5 - len(ptp.Parameter))

        # Send request
        ptp['Type'] = 'Command'
        ptp['DataphaseInfo'] = 'In'
        ptp['Payload'] = self.__Command.build(ptp)
        self.__send(ptp)

    def __send_data(self, ptp_container, data):
        '''Send data without checking answer.'''
        # Don't modify original container to keep abstraction barrier.
        ptp = Container(**ptp_container)
        # Send data
        ptp['Type'] = 'Data'
        ptp['DataphaseInfo'] = 'Out'
        ptp['Payload'] = data
        self.__send(ptp)

    # Actual implementation
    # ---------------------
    def send(self, ptp_container, data):
        '''Transfer operation with dataphase from initiator to responder'''
        logger.debug('SEND {}{}'.format(
            ptp_container.OperationCode,
            ' ' + str(list(map(hex, ptp_container.Parameter)))
            if ptp_container.Parameter else '',
        ))
        with self.__implicit_session():
            with self.__transaction_lock:
                self.__send_request(ptp_container)
                self.__send_data(ptp_container, data)
                # Get response and sneak in implicit SessionID and missing
                # parameters.
                return self.__recv()

    def recv(self, ptp_container):
        '''Transfer operation with dataphase from responder to initiator.'''
        logger.debug('RECV {}{}'.format(
            ptp_container.OperationCode,
            ' ' + str(list(map(hex, ptp_container.Parameter)))
            if ptp_container.Parameter else '',
        ))
        with self.__implicit_session():
            with self.__transaction_lock:
                self.__send_request(ptp_container)
                dataphase = self.__recv()
                if hasattr(dataphase, 'Data'):
                    response = self.__recv()
                    if (
                            (ptp_container.TransactionID != dataphase.TransactionID) or
                            (ptp_container.SessionID != dataphase.SessionID) or
                            (dataphase.TransactionID != response.TransactionID) or
                            (dataphase.SessionID != response.SessionID)
                    ):
                        raise PTPError(
                            'Dataphase does not match with requested operation'
                        )
                    response['Data'] = dataphase.Data
                    return response
                else:
                    return dataphase

    def mesg(self, ptp_container):
        '''Transfer operation without dataphase.'''
        op = ptp_container['OperationCode']
        if op == 'OpenSession':
            self.__open_implicit_session()

        with self.__implicit_session():
            with self.__transaction_lock:
                self.__send_request(ptp_container)
                # Get response and sneak in implicit SessionID and missing
                # parameters for FullResponse.
                response = self.__recv()

        rc = response['ResponseCode']
        if op == 'OpenSession':
            if rc != 'OK':
                self.__close_implicit_session()
        elif op == 'CloseSession':
            if rc == 'OK':
                self.__close_implicit_session()

        return response

    def event(self, wait=False):
        '''Check event.

        If `wait` this function is blocking. Otherwise it may return None.
        '''
        evt = None
        ipdata = None
        timeout = None if wait else 0.001
        if not self.__event_queue.empty():
            ipdata = self.__event_queue.get(block=not wait, timeout=timeout)
        if ipdata is not None:
            evt = self.__parse_response(ipdata)

        return evt

    def __poll_events(self):
        '''Poll events, adding them to a queue.'''
        logger.debug('Start')
        while (
                not self.__implicit_session_shutdown.is_set() and
                self.__implicit_session_open.is_set() and
                _main_thread_alive()
        ):
            try:
                evt = self.__recv(event=True, wait=False, raw=True)
            except OSError as e:
                if e.errno == 9 and not self.__implicit_session_open.is_set():
                    break
                else:
                    raise e
            if evt is not None:
                logger.debug('Event queued')
                self.__event_queue.put(evt)
            sleep(5e-3)
        logger.debug('Stop')

    def __ping_pong(self):
        '''Poll events, adding them to a queue.'''
        logger.debug('Start')
        last = time()
        while (
                not self.__implicit_session_shutdown.is_set() and
                self.__implicit_session_open.is_set() and
                _main_thread_alive()
        ):
            if time() - last > 10:
                logger.debug('PING')
                # TODO: implement Ping Pong
                last = time()
            sleep(0.10)
        logger.debug('Stop')
Exemplo n.º 19
0
DBField = Struct(
    "type" / DBFieldType,
    "value" / Switch(
        this.type,
        {
            "int8":
            Int8ub,
            "int16":
            Int16ub,
            "int32":
            Int32ub,
            "string":
            FocusedSeq(
                0,
                PascalString(ExprAdapter(Int32ub,
                                         encoder=lambda obj, ctx: obj // 2 + 1,
                                         decoder=lambda obj, ctx:
                                         (obj - 1) * 2),
                             encoding="utf-16-be"), Padding(2)),
            "binary":
            Prefixed(Int32ub, GreedyBytes)  # parses to byte string
        }))


class DBFieldFixedAdapter(Adapter):
    def __init__(self, subcon, ftype):
        self.ftype = ftype
        super().__init__(subcon)

    def _encode(self, obj, context):
        return {"type": self.ftype, "value": obj}
Exemplo n.º 20
0
class USBTransport(object):
    '''Implement USB transport.'''
    def __init__(self, *args, **kwargs):
        device = kwargs.get('device', None)
        '''Instantiate the first available PTP device over USB'''
        logger.debug('Init USB')
        self.__setup_constructors()
        # If no device is specified, find all devices claiming to be Cameras
        # and get the USB endpoints for the first one that works.
        if device is None:
            logger.debug('No device provided, probing all USB devices.')
        if isinstance(device, six.string_types):
            name = device
            logger.debug(
                'Device name provided, probing all USB devices for {}.'
                .format(name)
            )
            device = None
        else:
            name = None
        devs = (
            [device] if (device is not None)
            else find_usb_cameras(name=name)
        )
        self.__claimed = False
        self.__acquire_camera(devs)

        self.__event_queue = Queue()
        self.__event_shutdown = Event()
        # Locks for different end points.
        self.__inep_lock = RLock()
        self.__intep_lock = RLock()
        self.__outep_lock = RLock()
        # Slightly redundant transaction lock to avoid catching other request's
        # response
        self.__transaction_lock = RLock()

        self.__event_proc = Thread(
            name='EvtPolling',
            target=self.__poll_events
        )
        self.__event_proc.daemon = False
        atexit.register(self._shutdown)
        self.__event_proc.start()

    def __available_cameras(self, devs):
        for dev in devs:
            if self.__setup_device(dev):
                logger.debug('Found USB PTP device {}'.format(dev))
                yield
        else:
            message = 'No USB PTP device found.'
            logger.error(message)
            raise PTPError(message)

    def __acquire_camera(self, devs):
        '''From the cameras given, get the first one that does not fail'''

        for _ in self.__available_cameras(devs):
            # Stop system drivers
            try:
                if self.__dev.is_kernel_driver_active(
                        self.__intf.bInterfaceNumber):
                    try:
                        self.__dev.detach_kernel_driver(
                            self.__intf.bInterfaceNumber)
                    except usb.core.USBError:
                        message = (
                            'Could not detach kernel driver. '
                            'Maybe the camera is mounted?'
                        )
                        logger.error(message)
            except NotImplementedError as e:
                logger.debug('Ignoring unimplemented function: {}'.format(e))
            # Claim camera
            try:
                logger.debug('Claiming {}'.format(repr(self.__dev)))
                usb.util.claim_interface(self.__dev, self.__intf)
                self.__claimed = True
            except Exception as e:
                logger.warn('Failed to claim PTP device: {}'.format(e))
                continue
            self.__dev.reset()
            break
        else:
            message = (
                'Could not acquire any camera.'
            )
            logger.error(message)
            raise PTPError(message)

    def _shutdown(self):
        logger.debug('Shutdown request')
        self.__event_shutdown.set()
        # Free USB resource on shutdown.

        # Only join a running thread.
        if self.__event_proc.is_alive():
            self.__event_proc.join(2)

        try:
            if self.__claimed:
                logger.debug('Release {}'.format(repr(self.__dev)))
                usb.util.release_interface(self.__dev, self.__intf)
        except Exception as e:
            logger.warn(e)

    # Helper methods.
    # ---------------------
    def __setup_device(self, dev):
        '''Get endpoints for a device. True on success.'''
        self.__inep = None
        self.__outep = None
        self.__intep = None
        self.__cfg = None
        self.__dev = None
        self.__intf = None
        # Attempt to find the USB in, out and interrupt endpoints for a PTP
        # interface.
        for cfg in dev:
            for intf in cfg:
                if intf.bInterfaceClass == PTP_USB_CLASS:
                    for ep in intf:
                        ep_type = endpoint_type(ep.bmAttributes)
                        ep_dir = endpoint_direction(ep.bEndpointAddress)
                        if ep_type == ENDPOINT_TYPE_BULK:
                            if ep_dir == ENDPOINT_IN:
                                self.__inep = ep
                            elif ep_dir == ENDPOINT_OUT:
                                self.__outep = ep
                        elif ((ep_type == ENDPOINT_TYPE_INTR) and
                                (ep_dir == ENDPOINT_IN)):
                            self.__intep = ep
                if not (self.__inep and self.__outep and self.__intep):
                    self.__inep = None
                    self.__outep = None
                    self.__intep = None
                else:
                    logger.debug('Found {}'.format(repr(self.__inep)))
                    logger.debug('Found {}'.format(repr(self.__outep)))
                    logger.debug('Found {}'.format(repr(self.__intep)))
                    self.__cfg = cfg
                    self.__dev = dev
                    self.__intf = intf
                    return True
        return False

    def __setup_constructors(self):
        '''Set endianness and create transport-specific constructors.'''
        # Set endianness of constructors before using them.
        self._set_endian('little')

        self.__Length = Int32ul
        self.__Type = Enum(
                Int16ul,
                default=Pass,
                Undefined=0x0000,
                Command=0x0001,
                Data=0x0002,
                Response=0x0003,
                Event=0x0004,
                )
        # This is just a convenience constructor to get the size of a header.
        self.__Code = Int16ul
        self.__Header = Struct(
                'Length' / self.__Length,
                'Type' / self.__Type,
                'Code' / self.__Code,
                'TransactionID' / self._TransactionID,
                )
        # These are the actual constructors for parsing and building.
        self.__CommandHeader = Struct(
                'Length' / self.__Length,
                'Type' / self.__Type,
                'OperationCode' / self._OperationCode,
                'TransactionID' / self._TransactionID,
                )
        self.__ResponseHeader = Struct(
                'Length' / self.__Length,
                'Type' / self.__Type,
                'ResponseCode' / self._ResponseCode,
                'TransactionID' / self._TransactionID,
                )
        self.__EventHeader = Struct(
                'Length' / self.__Length,
                'Type' / self.__Type,
                'EventCode' / self._EventCode,
                'TransactionID' / self._TransactionID,
                )
        # Apparently nobody uses the SessionID field. Even though it is
        # specified in ISO15740:2013(E), no device respects it and the session
        # number is implicit over USB.
        self.__Param = Range(0, 5, self._Parameter)
        self.__CommandTransactionBase = Struct(
                Embedded(self.__CommandHeader),
                'Payload' / Bytes(
                    lambda ctx, h=self.__Header: ctx.Length - h.sizeof()
                )
        )
        self.__CommandTransaction = ExprAdapter(
                self.__CommandTransactionBase,
                encoder=lambda obj, ctx, h=self.__Header: Container(
                    Length=len(obj.Payload) + h.sizeof(),
                    **obj
                    ),
                decoder=lambda obj, ctx: obj,
                )
        self.__ResponseTransactionBase = Struct(
                Embedded(self.__ResponseHeader),
                'Payload' / Bytes(
                    lambda ctx, h=self.__Header: ctx.Length - h.sizeof())
                )
        self.__ResponseTransaction = ExprAdapter(
                self.__ResponseTransactionBase,
                encoder=lambda obj, ctx, h=self.__Header: Container(
                    Length=len(obj.Payload) + h.sizeof(),
                    **obj
                    ),
                decoder=lambda obj, ctx: obj,
                )

    def __parse_response(self, usbdata):
        '''Helper method for parsing USB data.'''
        # Build up container with all PTP info.
        logger.debug('Transaction:')
        usbdata = bytearray(usbdata)
        if logger.isEnabledFor(logging.DEBUG):
            for l in hexdump(
                    six.binary_type(usbdata[:512]),
                    result='generator'
            ):
                logger.debug(l)
        transaction = self.__ResponseTransaction.parse(usbdata)
        response = Container(
            SessionID=self.session_id,
            TransactionID=transaction.TransactionID,
        )
        logger.debug('Interpreting {} transaction'.format(transaction.Type))
        if transaction.Type == 'Response':
            response['ResponseCode'] = transaction.ResponseCode
            response['Parameter'] = self.__Param.parse(transaction.Payload)
        elif transaction.Type == 'Event':
            event = self.__EventHeader.parse(
                usbdata[0:self.__Header.sizeof()]
            )
            response['EventCode'] = event.EventCode
            response['Parameter'] = self.__Param.parse(transaction.Payload)
        else:
            command = self.__CommandHeader.parse(
                usbdata[0:self.__Header.sizeof()]
            )
            response['OperationCode'] = command.OperationCode
            response['Data'] = transaction.Payload
        return response

    def __recv(self, event=False, wait=False, raw=False):
        '''Helper method for receiving data.'''
        # TODO: clear stalls automatically
        ep = self.__intep if event else self.__inep
        lock = self.__intep_lock if event else self.__inep_lock
        usbdata = array.array('B', [])
        with lock:
            tries = 0
            # Attempt to read a header
            while len(usbdata) < self.__Header.sizeof() and tries < 5:
                if tries > 0:
                    logger.debug('Data smaller than a header')
                    logger.debug(
                        'Requesting {} bytes of data'
                        .format(ep.wMaxPacketSize)
                    )
                try:
                    usbdata += ep.read(
                        ep.wMaxPacketSize
                    )
                except usb.core.USBError as e:
                    # Return None on timeout or busy for events
                    if (
                            (e.errno is None and
                             ('timeout' in e.strerror.decode() or
                              'busy' in e.strerror.decode())) or
                            (e.errno == 110 or e.errno == 16 or e.errno == 5)
                    ):
                        if event:
                            return None
                        else:
                            logger.warning('Ignored exception: {}'.format(e))
                    else:
                        logger.error(e)
                        raise e
                tries += 1
            logger.debug('Read {} bytes of data'.format(len(usbdata)))

            if len(usbdata) == 0:
                if event:
                    return None
                else:
                    raise PTPError('Empty USB read')

            if (
                    logger.isEnabledFor(logging.DEBUG) and
                    len(usbdata) < self.__Header.sizeof()
            ):
                logger.debug('Incomplete header')
                for l in hexdump(
                        six.binary_type(bytearray(usbdata)),
                        result='generator'
                ):
                    logger.debug(l)

            header = self.__ResponseHeader.parse(
                bytearray(usbdata[0:self.__Header.sizeof()])
            )
            if header.Type not in ['Response', 'Data', 'Event']:
                raise PTPError(
                    'Unexpected USB transfer type. '
                    'Expected Response, Event or Data but received {}'
                    .format(header.Type)
                )
            while len(usbdata) < header.Length:
                usbdata += ep.read(
                    min(
                        header.Length - len(usbdata),
                        # Up to 64kB
                        64 * 2**10
                    )
                )
        if raw:
            return usbdata
        else:
            return self.__parse_response(usbdata)

    def __send(self, ptp_container, event=False):
        '''Helper method for sending data.'''
        ep = self.__intep if event else self.__outep
        lock = self.__intep_lock if event else self.__outep_lock
        transaction = self.__CommandTransaction.build(ptp_container)
        with lock:
            try:
                sent = 0
                while sent < len(transaction):
                    sent = ep.write(
                        # Up to 64kB
                        transaction[sent:(sent + 64*2**10)]
                    )
            except usb.core.USBError as e:
                # Ignore timeout or busy device once.
                if (
                        (e.errno is None and
                         ('timeout' in e.strerror.decode() or
                          'busy' in e.strerror.decode())) or
                        (e.errno == 110 or e.errno == 16 or e.errno == 5)
                ):
                    logger.warning('Ignored USBError {}'.format(e.errno))
                    ep.write(transaction)

    def __send_request(self, ptp_container):
        '''Send PTP request without checking answer.'''
        # Don't modify original container to keep abstraction barrier.
        ptp = Container(**ptp_container)
        # Don't send unused parameters
        try:
            while not ptp.Parameter[-1]:
                ptp.Parameter.pop()
                if len(ptp.Parameter) == 0:
                    break
        except IndexError:
            # The Parameter list is already empty.
            pass

        # Send request
        ptp['Type'] = 'Command'
        ptp['Payload'] = self.__Param.build(ptp.Parameter)
        self.__send(ptp)

    def __send_data(self, ptp_container, data):
        '''Send data without checking answer.'''
        # Don't modify original container to keep abstraction barrier.
        ptp = Container(**ptp_container)
        # Send data
        ptp['Type'] = 'Data'
        ptp['Payload'] = data
        self.__send(ptp)

    @property
    def _dev(self):
        return None if self.__event_shutdown.is_set() else self.__dev

    @_dev.setter
    def _dev(self, value):
        raise ValueError('Read-only property')

    # Actual implementation
    # ---------------------
    def send(self, ptp_container, data):
        '''Transfer operation with dataphase from initiator to responder'''
        datalen = len(data)
        logger.debug('SEND {} {} bytes{}'.format(
            ptp_container.OperationCode,
            datalen,
            ' ' + str(list(map(hex, ptp_container.Parameter)))
            if ptp_container.Parameter else '',
        ))
        with self.__transaction_lock:
            self.__send_request(ptp_container)
            self.__send_data(ptp_container, data)
            # Get response and sneak in implicit SessionID and missing
            # parameters.
            response = self.__recv()
        logger.debug('SEND {} {} bytes {}{}'.format(
            ptp_container.OperationCode,
            datalen,
            response.ResponseCode,
            ' ' + str(list(map(hex, response.Parameter)))
            if ptp_container.Parameter else '',
        ))
        return response

    def recv(self, ptp_container):
        '''Transfer operation with dataphase from responder to initiator.'''
        logger.debug('RECV {}{}'.format(
            ptp_container.OperationCode,
            ' ' + str(list(map(hex, ptp_container.Parameter)))
            if ptp_container.Parameter else '',
        ))
        with self.__transaction_lock:
            self.__send_request(ptp_container)
            dataphase = self.__recv()
            if hasattr(dataphase, 'Data'):
                response = self.__recv()
                if not (ptp_container.SessionID ==
                        dataphase.SessionID ==
                        response.SessionID):
                    self.__dev.reset()
                    raise PTPError(
                        'Dataphase session ID missmatch: {}, {}, {}.'
                        .format(
                            ptp_container.SessionID,
                            dataphase.SessionID,
                            response.SessionID
                        )
                    )
                if not (ptp_container.TransactionID ==
                        dataphase.TransactionID ==
                        response.TransactionID):
                    self.__dev.reset()
                    raise PTPError(
                        'Dataphase transaction ID missmatch: {}, {}, {}.'
                        .format(
                            ptp_container.TransactionID,
                            dataphase.TransactionID,
                            response.TransactionID
                        )
                    )
                if not (ptp_container.OperationCode ==
                        dataphase.OperationCode):
                    self.__dev.reset()
                    raise PTPError(
                        'Dataphase operation code missmatch: {}, {}.'.
                        format(
                            ptp_container.OperationCode,
                            dataphase.OperationCode
                        )
                    )

                response['Data'] = dataphase.Data
            else:
                response = dataphase

        logger.debug('RECV {} {}{}{}'.format(
            ptp_container.OperationCode,
            response.ResponseCode,
            ' {} bytes'.format(len(response.Data))
            if hasattr(response, 'Data') else '',
            ' ' + str(list(map(hex, response.Parameter)))
            if response.Parameter else '',
        ))
        return response

    def mesg(self, ptp_container):
        '''Transfer operation without dataphase.'''
        logger.debug('MESG {}{}'.format(
            ptp_container.OperationCode,
            ' ' + str(list(map(hex, ptp_container.Parameter)))
            if ptp_container.Parameter else '',
        ))
        with self.__transaction_lock:
            self.__send_request(ptp_container)
            # Get response and sneak in implicit SessionID and missing
            # parameters for FullResponse.
            response = self.__recv()
        logger.debug('MESG {} {}{}'.format(
            ptp_container.OperationCode,
            response.ResponseCode,
            ' ' + str(list(map(hex, response.Parameter)))
            if response.Parameter else '',
        ))
        return response

    def event(self, wait=False):
        '''Check event.

        If `wait` this function is blocking. Otherwise it may return None.
        '''
        evt = None
        usbdata = None
        if wait:
            usbdata = self.__event_queue.get(block=True)
        elif not self.__event_queue.empty():
            usbdata = self.__event_queue.get(block=False)

        if usbdata is not None:
            evt = self.__parse_response(usbdata)

        return evt

    def __poll_events(self):
        '''Poll events, adding them to a queue.'''
        while not self.__event_shutdown.is_set() and _main_thread_alive():
            try:
                evt = self.__recv(event=True, wait=False, raw=True)
                if evt is not None:
                    logger.debug('Event queued')
                    self.__event_queue.put(evt)
            except usb.core.USBError as e:
                logger.error(
                    '{} polling exception: {}'.format(repr(self.__dev), e)
                )
                # check if disconnected
                if e.errno == 19:
                    break
            except Exception as e:
                logger.error(
                    '{} polling exception: {}'.format(repr(self.__dev), e)
                )
Exemplo n.º 21
0
 def _ExposureBiasCompensation(self):
     return ExprAdapter(
         self._UInt16,
         encode=lambda x: x * 1000,
         decode=lambda x: x / 1000.,
     )
Exemplo n.º 22
0
def adapter(subcon: Construct, decoder: Decoder, encoder: Encoder) -> Adapter:
    return ExprAdapter(subcon, decoder, encoder)
Exemplo n.º 23
0
class DurationAdapter(Adapter):
    def _decode(self, obj, context, path):
        secs = obj // (10**7)
        nanosecs = (obj - secs * (10**7)) * 100
        return timedelta64(secs, 's') + timedelta64(nanosecs, 'ns')

    def _encode(self, obj, context, path):
        return int(uint64(obj.astype('O')))


Duration = DurationAdapter(Int64ul)
PVoid = IfThenElse(lambda ctx: ctx.is_64bit, Int64ul, Int32ul)

UTF16MultiSz = ExprAdapter(
    RepeatUntil(lambda x, lst, ctx: not x, CString("UTF_16_le")),
    lambda obj, ctx: list(obj[:-1]),  # last element is the null
    lambda obj, ctx: obj + [''])


def SizedUTF16MultiSz(size_func):
    return ExprAdapter(
        FixedSized(size_func, GreedyRange(CString("UTF_16_le"))),
        lambda obj, ctx: list(
            obj),  # last element is already removed by GreedyRange
        lambda obj, ctx: obj + [''])


class CheckCustom(Check):
    def __init__(self, func, exc_type, msg):
        super(CheckCustom, self).__init__(func)
        self.exc_type = exc_type
Exemplo n.º 24
0
    InliningStruct(
        'signature' / signature,
        '__raw_cert__' @ AttributeRawCopy(
            Aligned(
                0x40,
                InlineStruct(
                    'issuer' / PaddedString(0x40, 'ascii'),
                    'key_type' / EnumConvert(Int32ub, SignatureAlgorithm),
                    'name' / PaddedString(0x40, 'ascii'),
                    '_unk1' / Int32ub,  # might be a timestamp?
                    'key' / SwitchNoDefault(
                        this.key_type,
                        {t: t.key_construct
                         for t in SignatureAlgorithm}))))))

TitleID = ExprAdapter(Bytes(8), lambda data, _: ids.TitleID(data),
                      lambda title_id, _: title_id.to_bytes())


class PlatformSpecific:
    _3ds: Construct
    wiiu: Construct

    # expected signature: `func(is_wiiu: bool) -> Construct`
    def __init__(self, func: Callable[[bool], Construct]):
        self._3ds = func(False)
        self.wiiu = func(True)

    def get(self, platform: ids.AnyPlatform) -> Construct:
        return {
            ids.TitlePlatform._3DS: self._3ds,
            ids.ContentPlatform._3DS: self._3ds,
Exemplo n.º 25
0
            if not skip:
                print("%08x  *" % (i + st))
                skip = True
        else:
            print("%08x  %s" % (i + st, hexdump32(val, ' ')))
            last = val
            skip = False


def unhex(s):
    s = re.sub(r"/\*.*?\*/", "", s)
    return bytes.fromhex(s.replace(" ", "").replace("\n", ""))


FourCC = ExprAdapter(Int32ul,
                     lambda d, ctx: d.to_bytes(4, "big").decode("latin-1"),
                     lambda d, ctx: int.from_bytes(d.encode("latin-1"), "big"))


class SafeGreedyRange(GreedyRange):
    def __init__(self, subcon, discard=False):
        super().__init__(subcon)
        self.discard = discard

    def _parse(self, stream, context, path):
        discard = self.discard
        obj = ListContainer()
        try:
            for i in itertools.count():
                context._index = i
                e = self.subcon._parsereport(stream, context, path)