Пример #1
0
    def __init__(self, *k, **kw):
        """Initialise a packet value.

		The function fills the content of the packet
		- either from a list of values (*k), in this case the values are expected
		  to have the same order as the
		- either by assigning the fields explicitely (**kw notation), in that case,
		  the fields are referenced by their name or alias. Unreferenced fields will
		  be left undefined.


		For example, using the ICMPv6 type definition above, the two following
		instantiations are equivalent:
			my_pkt = ICMPv6 (4, 0, 0x1234, b"   ")
			my_pkt = ICMPv6 (Type=4, Code=0, Checksum=0x1234, Body=b"   ")

		When using the explicit assignment notation, unreferenced fields are left
		undefined, thus the followings are equivalent:
			my_pkt2 = ICMPv6 (Type=4, Body=b"blah")
			my_pkt2 = ICMPv6 (4, None, None, b"blah")
			my_pkt2 = ICMPv6 (Type=4, Code=None, Checksum=None, Body=b"blah")
		"""

        Value.__init__(self)

        # TODO: copy constructor ?

        if len(k):
            if len(kw):
                raise Error('cannot mix positional and named arguments')

            missing = len(self) - len(k)
            if missing < 0:
                raise Error("to many parameters")

            self.__datas = [f.store_data(d) for d, f in zip(k, self.fields())]

            if missing:
                self.__datas.extend([None for v in range(0, missing)])
        else:
            # named parameters

            self.__datas = []
            for field in self.fields():
                for n in field.name, field.alias:
                    if n in kw:
                        v = kw.pop(n)
                        break
                else:
                    v = None

                # append the type to the internal list, but possibly convert it before
                self.__datas.append(field.store_data(v))

            if len(kw):
                raise Error('unknown fields in type %s: %s' %
                            (type(self).__name__, ' '.join(kw.keys())))
Пример #2
0
	def __init__ (self, *k):
		if len(k) != len(self.__types):
			raise Error("Expected %d parameters instead of %d" % (len(self.__types), len(k)))

		for i in range (0, len (k)):
			t = self.__types[i]

			if k[i] is None and self.__optional[i]:
				continue

			if not isinstance (k[i], t):
				raise Error("Param id %d is an instance of %s instead of %s" % (i, type(k[i]), t))

		self.__values = k

		self.__timestamp = clock.Clock.get_instance().time()
Пример #3
0
    def post_decode(self, ctx, value):

        value *= self.__unit

        if self.get_ref_id() is None:
            assert ctx.initial_slice.same_buffer_as(
                ctx.remaining_slice
            )  # we must still be working on the same slice

            right = ctx.initial_slice.get_left() + value
            if right != ctx.initial_slice.get_right():
                remaining_bits = right - ctx.remaining_slice.get_left()
                if remaining_bits < 0:
                    raise Error(
                        "InetLength: expected length is to small: %d (%d bits), %d bits decoded so far"
                        % (value / self.__unit, value,
                           ctx.remaining_slice.get_left() -
                           ctr.initial_slice.get_left()))

                else:
                    final_slice = ctx.initial_slice.shift_bits(value)
                    # TODO: update the initial_slice too ???
                    ctx.remaining_slice = ctx.remaining_slice.bit_slice(
                        0, remaining_bits)

                    def cleaner():
                        ctx.remaining_slice = final_slice

                    ctx.push_cleaner(cleaner)
        else:
            # set a handler -> a 'with' context ! to change the slice on the fly
            ctx.push_context(self.get_ref_id(),
                             self.__decode_context(ctx, value))
Пример #4
0
    def get_dissection(
        self,
        protocol: optional(is_protocol) = None,
    ) -> list_of(OrderedDict):
        """
        Function to get dissection of a capture as a list of frames represented as strings

        :param protocol: Protocol class for filtering purposes
        :type protocol: type
        :raises TypeError: If protocol is not a protocol class
        :raises ReaderError: If the reader couldn't process the file

        :return: A list of Frame represented as API's dict form
        :rtype: [OrderedDict]

        """
        # log.debug('Starting dissection.')
        # Check the protocol is one entered
        if all((protocol, not is_protocol(protocol))):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        fs = self.frames

        # For speeding up the process
        with Data.disable_name_resolution():

            # Filter the frames for the selected protocol
            if protocol:
                fs, _ = Frame.filter_frames(fs, protocol)

        if fs is None:
            raise Error('Empty capture cannot be dissected')

        # Then return the list of dictionary frame representation
        return [frame.dict() for frame in fs]
Пример #5
0
 def get_type_for_python_type(cls, python_type: type):
     try:
         return cls.__type_map[python_type]
     except KeyError:
         raise Error(
             "There is no know PrimitiveValue class to handle type '%s'" %
             python_type)
Пример #6
0
    def __new__(cls, value=None):
        if isinstance(value, str):
            mo = re.match(cls.__reg, value)
            if not mo:
                raise Error("malformed IEEE 802.15.4 short address")

            value = bytes(int(v, 16) for v in mo.groups())

        return super().__new__(cls, value)
Пример #7
0
    def new(cls, value):
        try:
            t = cls.__type_map[type(value)]
        except KeyError:
            raise Error(
                "Cannot build a PrimitiveValue from '%s' (not a known python primitive type)"
                % value)

        return t(value)
Пример #8
0
    def _flatten(cls, values: list_of(this_class)):

        # ensure that all the values are equal
        v0 = values[0]
        for v in values[1:]:
            if v != v0:
                raise Error("cannot flatten different values together " +
                            str(values))

        return v0
Пример #9
0
        def __new__(cls, value=None):
            if value is None:
                value = b"\0" * size
            # TODO: check that the resulting value is valid
            result = super().__new__(cls, value)

            if len(result) != size:
                raise Error("Invalid value length (%d instead of %d)" %
                            (len(value), size))

            return result
Пример #10
0
    def summary(self,
                protocol: optional(is_protocol) = None) -> list_of((int, str)):
        """
        The summaries function to get the summary of frames

        :param protocol: Protocol class for filtering purposes
        :type protocol: type

        :Example:

        from ttproto.core.lib.all import Ieee802154

        for s in dissector.summary(protocol = Ieee802154):

            print(s)

        :raises TypeError: If protocol is not a protocol class
        :raises ReaderError: If the reader couldn't process the file

        :return: Basic informations about frames like the underlying example
        :rtype: [(int, str)]

        :Example:

            [
                (13, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38515] GET /test'),

                (14, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38515] 2.05 Content'),

                (21, '[127.0.0.1 -> 127.0.0.1] CoAP [CON 38516] PUT /test'),

                (22, '[127.0.0.1 -> 127.0.0.1] CoAP [ACK 38516] 2.04 Changed')]
            ]

        .. note:: With the protocol option we can filter the response
        """

        if all((protocol, not is_protocol(protocol))):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        fs = self.frames

        # For speeding up the process
        with Data.disable_name_resolution():

            # Filter the frames for the selected protocol
            if protocol:
                fs, _ = Frame.filter_frames(fs, protocol)

        if fs is None:
            raise Error('Empty capture cannot be dissected')

        # Return list of frames summary
        return [frame.summary() for frame in fs]
Пример #11
0
    def compute(self, seq, values_bins):
        if self.get_ref_id() is None:
            l = 0
            for vb in values_bins:
                l += get_binary_length(vb[1])
        else:
            l = get_binary_length(values_bins[self.get_ref_id()][1])

        if l % self.__unit:
            raise Error("Invalid length: must be a multiple of %d bytes" %
                        (self.__unit // 8))
        return l // self.__unit
Пример #12
0
    def get_dissection_simple_format(
        self,
        protocol: optional(is_protocol) = None,
    ) -> list_of(str):
        """
        Function to get dissection of a capture as a list of frames represented as strings

        :param protocol: Protocol class for filtering purposes
        :type protocol: type
        :raises TypeError: If protocol is not a protocol class
        :raises ReaderError: If the reader couldn't process the file

        :return: A list of Frame represented as plain non-structured text
        :rtype: [str]

        """
        # log.debug('Starting dissection.')
        # Check the protocol is one entered
        if all((protocol, not is_protocol(protocol))):
            raise TypeError(protocol.__name__ + ' is not a protocol class')

        fs = self.frames

        # For speeding up the process
        with Data.disable_name_resolution():

            # Filter the frames for the selected protocol
            if protocol:
                fs, _ = Frame.filter_frames(fs, protocol)

        if fs is None:
            raise Error('Empty capture cannot be dissected')

        # fixme modify Message class from ttproto.data structure so I can get text display wihtout this patch
        class WritableObj(object):
            def __init__(self, text=''):
                self.val = text

            def __str__(self):
                return self.val

            def write(self, text):
                self.val += text

        frame_dissection_list = []

        for f in fs:
            text_output = WritableObj()
            f.message.display(output=text_output)
            frame_dissection_list.append(str(text_output))

        # Then return the list of frames,each as a simple text dissection
        return frame_dissection_list
Пример #13
0
    def decompress(
        cls, bin_slice: BinarySlice
    ) -> (is_flat_value, BinarySlice, is_binary, int):
        """returns:  (decoded header, new slice, decompressed header, next header value)"""

        assert cls == SixLowpanNHC  # decompress() must be reimplemented in derived classes

        nhc_id = bin_slice[0]
        try:
            new_class = sixlowpan_nhc_decompress_dict[nhc_id]
        except KeyError:
            # cannot decompress
            raise Error("Unable to decompress 6lowpan NHC id %d" % nhc_id)

        return new_class.decompress(bin_slice)
Пример #14
0
    def __decode_context(ctx, value):
        # generate a shorter slice
        #TODO: detect possible errors (slice too short)
        with ctx.replace_attr("remaining_slice",
                              ctx.remaining_slice.bit_slice(0, value)):
            yield

            # ensure that we decoded all the bits
            bl = ctx.remaining_slice.get_bit_length()
            if bl:
                raise Error(
                    "Did not decode all the content of the slice (%d bits remaining)"
                    % bl)

        # move forward n bits in the restored slice
        ctx.remaining_slice = ctx.remaining_slice.shift_bits(value)
Пример #15
0
    def post_decode(self, ctx, value):
        assert self.__bidict is not None  # must have been initialised

        expected_type = self.__bidict[value]

        if self.get_ref_id() is not None:
            # set a handler -> a 'with' context to change the slice on the fly
            ctx.push_context(self.get_ref_id(),
                             self.__decode_context(ctx, expected_type))
        else:
            # try to change the type on the fly

            # check that we can safely change the variant
            nb_decoded = len(ctx.values)
            v = expected_type
            prune = False
            while v != ctx.variant:
                if v.get_prune_id() < nb_decoded:
                    prune = True
                v = v.get_base_variant()

                if v is None:
                    raise Error(
                        "The new variant must be based on the current one"
                    )  # FIXME: this could be relaxed
            try:
                if not (expected_type._decode_message.__func__ is
                        ctx.variant._decode_message.__func__):
                    # the new variant has a different _decode_message() function
                    # -> we must restart decoding from scratch
                    raise _RestartDecodingAs(expected_type)
            except AttributeError:
                pass

            if prune:
                # we have already decode too many fields to be able to change the type on the fly
                # -> restart decoding from the beginning of the packet
                ctx.reset()
                ctx.remaining_slice = ctx.initial_slice
                ctx.values[:] = ()
                ctx.field_id = -1

            # change the variant
            ctx.variant = expected_type
Пример #16
0
        def compute_unicast(am, ac, inl, hw, ci):
            if am == 0:
                return IPV6_UNSPECIFIED_ADDRESS if ac else inl
            else:
                if ac == 0:
                    pfx = b"\xfe\x80\0\0\0\0\0\0"
                else:
                    if ci == Omit():
                        raise Error(
                            "6Lowpan decompression error: packet uses stateful compression whereas CID == 0"
                        )
                    pfx, length = cls.get_context(ci)
                    pfx = pfx[:8]

                if am == 1:
                    iid = inl
                elif am == 2:
                    iid = b"\0\0\0\xff\xfe\0" + inl
                else:
                    iid = hw

                return IPv6Address(pfx + iid)
Пример #17
0
    def _fill_default_values(self: is_flat_value) -> list_of(Value):
        """Fill the undefined fields with a default value.

		This function returns a list contaning for each field:
		- the value of the field (if defined)
		- the default provided by the field tag (otherwise)
		"""
        values = []

        for f, v in zip(self.fields(), self):

            # fill the field value (and use the default if not specified)

            if v is None:
                v = f.tag.get_default_value()

                if v is None:
                    raise Error("field '%s' in '%s' must have a value" %
                                (f.name, type(self).__name__))
                else:
                    v = f.store_data(v)
            values.append(v)

        return values
Пример #18
0
    def pack(self, data: is_data) -> this_class:
        """Encapsulate the given data into the present packet

		The given data is encapsulated into the "Payload" field (which
		must be present).

		If the current packet has no payload defined, this function is
		just returns self(Payload=data) (it generates a derived object
		and fills the Payload field with the given data.

		Otherwise the function will recursively call the pack() function
		on the existing payload. The result will be equal to:
			self(Payload=self["Payload"].pack(data))
		"""
        pid = self.get_payload_id()
        if pid is None:
            raise Error("Cannot pack a message into %s (no Payload field)" %
                        type(self).__name__)

        new_seq = self()

        payloads = list(new_seq.get_datas(pid))
        field = new_seq.get_field(pid)
        if len(payloads) == 0:
            new_seq.__datas[pid] = field.store_data(data)
        elif len(payloads) >= 1:
            # FIXME: this will create lots of duplicated objects when pack is called in cascade using the __div__ operator
            #	 we may want to avoid duplicating the parent object when it is not modified
            #	 (in this case: when it is a truly anonymous object)

            # FIXME: if a sequence has multiple payloads (because of inheritance), we will
            #	 implicitely pack the data at the end of the first known payload
            #	 (this may have side effects ?)
            new_seq.__datas[pid] = field.store_data(payloads[0].pack(data))

        return new_seq
Пример #19
0
    def _decode_message(cls, bin_slice, root_header=True):

        hwsrc, hwdst = cls.get_current_iid_context()

        def decoder_func():
            nonlocal bin_slice
            v = None
            for f in cls.fields():
                t = yield v
                v, bin_slice = f.tag.decode_message(t if t else f.type,
                                                    bin_slice, None)
                values.append(v)
            yield v

        values = cls.DataList()

        # prepare the decoder
        decoder = decoder_func()
        next(decoder)

        def decode():
            return next(decoder)

        def decode_if(cond):
            return decoder.send(None if cond else Omit)

        def decode_as(type):
            return decoder.send(type)

        # decode the base format
        dp = decode()
        tf = decode()
        nh = decode()
        hl = decode()
        cid = decode()
        sac = decode()
        sam = decode()
        m = decode()
        dac = decode()
        dam = decode()

        # CID extension
        sci = decode_if(cid)
        dci = decode_if(cid)

        # TF fields
        iecn = decode_if(tf != 3)
        idscp = decode_if(not (tf & 1))
        ipad = decode_as(Omit if tf & 2 else (UInt2 if tf else UInt4))
        ifl = decode_if(not (tf & 2))

        # NH field
        inh = decode_if(nh == 0)

        # HLIM field
        ihl = decode_if(hl == 0)

        # src & dst addr
        def decode_unicast(am, ac):
            if am == 0:
                return decode_as(Omit if ac else IPv6Address)
            else:
                return decode_as((None, Bytes8, Bytes2, Omit)[am])

        isrc = decode_unicast(sam, sac)
        if not m:
            idst = decode_unicast(dam, dac)
        else:
            # multicast
            if dac:
                idst = decode_as(Omit if dam else Bytes6)
            else:
                idst = decode_as((IPv6Address, Bytes6, Bytes4, Bytes1)[dam])

        # compute the addresses
        def compute_unicast(am, ac, inl, hw, ci):
            if am == 0:
                return IPV6_UNSPECIFIED_ADDRESS if ac else inl
            else:
                if ac == 0:
                    pfx = b"\xfe\x80\0\0\0\0\0\0"
                else:
                    if ci == Omit():
                        raise Error(
                            "6Lowpan decompression error: packet uses stateful compression whereas CID == 0"
                        )
                    pfx, length = cls.get_context(ci)
                    pfx = pfx[:8]

                if am == 1:
                    iid = inl
                elif am == 2:
                    iid = b"\0\0\0\xff\xfe\0" + inl
                else:
                    iid = hw

                return IPv6Address(pfx + iid)

        def compute_multicast():
            if dac:
                # Stateful
                if dam == 0:
                    pfx, length = cls.get_context(dci)
                    return IPv6Address(b"".join(
                        (b"\xff", idst[:2], bytes(
                            (length, )), pfx[:8], idst[2:])))
                else:
                    return IPV6_UNSPECIFIED_ADDRESS
            else:
                # Stateless
                if dam == 0:
                    return idst
                elif dam == 1:
                    return IPv6Address(b"".join(
                        (b"\xff", idst[:1], b"\0\0\0\0\0\0\0\0\0", idst[1:])))
                elif dam == 2:
                    return IPv6Address(b"".join(
                        (b"\xff", idst[:1], b"\0\0\0\0\0\0\0\0\0\0\0",
                         idst[1:])))
                else:
                    return IPv6Address(b"\xff\2\0\0\0\0\0\0\0\0\0\0\0\0\0" +
                                       idst)

        # decompress the addresses
        src = compute_unicast(sam, sac, isrc, hwsrc, sci)
        dst = compute_multicast() if m else compute_unicast(
            dam, dac, idst, hwdst, dci)

        # next compressed header
        if not nh:
            nhc = Omit()
            nhc_bin = b""
            next_header = inh
        else:
            # here were enter a pseudo_addr context because we may need to compute an IPv6 checksum
            # and enter and iid context because we may have compressed addresses
            with InetPacketValue.ipv6_pseudo_addresses_context((src, dst)), \
                 cls.encapsulating_iid_context(src, dst):

                nhc, bin_slice, nhc_bin, next_header = SixLowpanNHC.decompress(
                    bin_slice)
        values.append(nhc)

        def value(value, default_if_omit):
            return default_if_omit if isinstance(value, Omit) else value

        # Assemble the IPv6 header
        ipv6_bin = IPv6(
            tc=(value(iecn, 0) << 6) | value(idscp, 0),
            fl=value(ifl, 0),
            len=len(bin_slice) + len(nhc_bin),
            nh=next_header,
            hl=(ihl, 1, 64, 255)[hl],
            src=src,
            dst=dst,
            pl="",
        ).build_message()[1]

        if root_header:
            # Generate the new slice
            inner_slice = BinarySlice(
                concatenate((ipv6_bin, nhc_bin, bin_slice.as_binary())))

            # Decode the payload
            payload, inner_slice = IPv6.decode_message(inner_slice)
            values.append(payload)

            if inner_slice:
                raise Error("Buffer not fully decoded (%d remaining bits)" %
                            inner_slice.get_bit_length())

            return cls(*values), bin_slice.shift_bits(
                bin_slice.get_bit_length())
        else:
            values.append(Omit())
            return cls(*values), bin_slice, ipv6_bin + nhc_bin