def orb(x):
    # type: (Union[int, str, bytes]) -> int
    """Return ord(x) when not already an int."""
    if isinstance(x, int):
        return x
    return ord(x) class ByteEnumField(EnumField[int]):
    def __init__(self, name, default, enum):
        # type: (str, Optional[int], Dict[int, str]) -> None
        EnumField.__init__(self, name, default, enum, "B") class ConditionalField(_FieldContainer):
    __slots__ = ["fld", "cond"]

    def __init__(self,
                 fld,  # type: Field[Any, Any]
                 cond  # type: Callable[[Packet], bool]
                 ):
        # type: (...) -> None
        self.fld = fld
        self.cond = cond

    def _evalcond(self, pkt):
        # type: (Packet) -> bool
        return bool(self.cond(pkt))

    def any2i(self, pkt, x):
        # type: (Optional[Packet], Any) -> Any
        # BACKWARD COMPATIBILITY
        # Note: we shouldn't need this function. (it's not correct)
        # However, having i2h implemented (#2364), it changes the default
        # behavior and broke all packets that wrongly use two ConditionalField
        # with the same name. Those packets are the problem: they are wrongly
        # built (they should either be re-using the same conditional field, or
        # using a MultipleTypeField).
        # But I don't want to dive into fixing all of them just yet,
        # so for now, let's keep this this way, even though it's not correct.
        if type(self.fld) is Field:
            return x
        return self.fld.any2i(pkt, x)

    def i2h(self, pkt, val):
        # type: (Optional[Packet], Any) -> Any
        if pkt and not self._evalcond(pkt):
            return None
        return self.fld.i2h(pkt, val)

    def getfield(self, pkt, s):
        # type: (Packet, bytes) -> Tuple[bytes, Any]
        if self._evalcond(pkt):
            return self.fld.getfield(pkt, s)
        else:
            return s, None

    def addfield(self, pkt, s, val):
        # type: (Packet, bytes, Any) -> bytes
        if self._evalcond(pkt):
            return self.fld.addfield(pkt, s, val)
        else:
            return s

    def __getattr__(self, attr):
        # type: (str) -> Any
        return getattr(self.fld, attr) class FieldLenField(Field[int, int]):
    __slots__ = ["length_of", "count_of", "adjust"]

    def __init__(
            self,
            name,  # type: str
            default,  # type: Optional[Any]
            length_of=None,  # type: Optional[str]
            fmt="H",  # type: str
            count_of=None,  # type: Optional[str]
            adjust=lambda pkt, x: x,  # type: Callable[[Packet, int], int]
    ):
        # type: (...) -> None
        Field.__init__(self, name, default, fmt)
        self.length_of = length_of
        self.count_of = count_of
        self.adjust = adjust

    def i2m(self, pkt, x):
        # type: (Optional[Packet], Optional[int]) -> int
        if x is None and pkt is not None:
            if self.length_of is not None:
                fld, fval = pkt.getfield_and_val(self.length_of)
                f = fld.i2len(pkt, fval)
            elif self.count_of is not None:
                fld, fval = pkt.getfield_and_val(self.count_of)
                f = fld.i2count(pkt, fval)
            else:
                raise ValueError(
                    "Field should have either length_of or count_of"
                )
            x = self.adjust(pkt, f)
        elif x is None:
            x = 0
        return x class FieldListField(Field[List[Any], List[Any]]):
    __slots__ = ["field", "count_from", "length_from"]
    islist = 1

    def __init__(
            self,
            name,  # type: str
            default,  # type: Optional[List[AnyField]]
            field,  # type: AnyField
            length_from=None,  # type: Optional[Callable[[Packet], int]]
            count_from=None,  # type: Optional[Callable[[Packet], int]]
    ):
        # type: (...) -> None
        if default is None:
            default = []  # Create a new list for each instance
        self.field = field
        Field.__init__(self, name, default)
        self.count_from = count_from
        self.length_from = length_from

    def i2count(self, pkt, val):
        # type: (Optional[Packet], List[Any]) -> int
        if isinstance(val, list):
            return len(val)
        return 1

    def i2len(self, pkt, val):
        # type: (Packet, List[Any]) -> int
        return int(sum(self.field.i2len(pkt, v) for v in val))

    def any2i(self, pkt, x):
        # type: (Optional[Packet], List[Any]) -> List[Any]
        if not isinstance(x, list):
            return [self.field.any2i(pkt, x)]
        else:
            return [self.field.any2i(pkt, e) for e in x]

    def i2repr(self,
               pkt,  # type: Optional[Packet]
               x,  # type: List[Any]
               ):
        # type: (...) -> str
        return "[%s]" % ", ".join(self.field.i2repr(pkt, v) for v in x)

    def addfield(self,
                 pkt,  # type: Packet
                 s,  # type: bytes
                 val,  # type: Optional[List[Any]]
                 ):
        # type: (...) -> bytes
        val = self.i2m(pkt, val)
        for v in val:
            s = self.field.addfield(pkt, s, v)
        return s

    def getfield(self,
                 pkt,  # type: Packet
                 s,  # type: bytes
                 ):
        # type: (...) -> Any
        c = len_pkt = None
        if self.length_from is not None:
            len_pkt = self.length_from(pkt)
        elif self.count_from is not None:
            c = self.count_from(pkt)

        val = []
        ret = b""
        if len_pkt is not None:
            s, ret = s[:len_pkt], s[len_pkt:]

        while s:
            if c is not None:
                if c <= 0:
                    break
                c -= 1
            s, v = self.field.getfield(pkt, s)
            val.append(v)
        return s + ret, val class IPField(Field[Union[str, Net], bytes]):
    def __init__(self, name, default):
        # type: (str, Optional[str]) -> None
        Field.__init__(self, name, default, "4s")

    def h2i(self, pkt, x):
        # type: (Optional[Packet], Union[AnyStr, List[AnyStr]]) -> Any
        if isinstance(x, bytes):
            x = plain_str(x)  # type: ignore
        if isinstance(x, str):
            try:
                inet_aton(x)
            except socket.error:
                return Net(x)
        elif isinstance(x, list):
            return [self.h2i(pkt, n) for n in x]
        return x

    def i2h(self, pkt, x):
        # type: (Optional[Packet], Optional[Union[str, Net]]) -> str
        return cast(str, x)

    def resolve(self, x):
        # type: (str) -> str
        if self in conf.resolve:
            try:
                ret = socket.gethostbyaddr(x)[0]
            except Exception:
                pass
            else:
                if ret:
                    return ret
        return x

    def i2m(self, pkt, x):
        # type: (Optional[Packet], Optional[Union[str, Net]]) -> bytes
        if x is None:
            return b'\x00\x00\x00\x00'
        return inet_aton(plain_str(x))

    def m2i(self, pkt, x):
        # type: (Optional[Packet], bytes) -> str
        return inet_ntoa(x)

    def any2i(self, pkt, x):
        # type: (Optional[Packet], Any) -> Any
        return self.h2i(pkt, x)

    def i2repr(self, pkt, x):
        # type: (Optional[Packet], Union[str, Net]) -> str
        r = self.resolve(self.i2h(pkt, x))
        return r if isinstance(r, str) else repr(r)

    def randval(self):
        # type: () -> RandIP
        return RandIP() class LenField(Field[int, int]):
    """
    If None, will be filled with the size of the payload
    """
    __slots__ = ["adjust"]

    def __init__(self, name, default, fmt="H", adjust=lambda x: x):
        # type: (str, Optional[Any], str, Callable[[int], int]) -> None
        Field.__init__(self, name, default, fmt)
        self.adjust = adjust

    def i2m(self,
            pkt,  # type: Optional[Packet]
            x,  # type: Optional[int]
            ):
        # type: (...) -> int
        if x is None:
            x = 0
            if pkt is not None:
                x = self.adjust(len(pkt.payload))
        return x class MACField(Field[Optional[str], bytes]):
    def __init__(self, name, default):
        # type: (str, Optional[Any]) -> None
        Field.__init__(self, name, default, "6s")

    def i2m(self, pkt, x):
        # type: (Optional[Packet], Optional[str]) -> bytes
        if x is None:
            return b"\0\0\0\0\0\0"
        try:
            y = mac2str(x)
        except (struct.error, OverflowError):
            y = bytes_encode(x)
        return y

    def m2i(self, pkt, x):
        # type: (Optional[Packet], bytes) -> str
        return str2mac(x)

    def any2i(self, pkt, x):
        # type: (Optional[Packet], Any) -> str
        if isinstance(x, bytes) and len(x) == 6:
            return self.m2i(pkt, x)
        return cast(str, x)

    def i2repr(self, pkt, x):
        # type: (Optional[Packet], Optional[str]) -> str
        x = self.i2h(pkt, x)
        if x is None:
            return repr(x)
        if self in conf.resolve:
            x = conf.manufdb._resolve_MAC(x)
        return x

    def randval(self):
        # type: () -> RandMAC
        return RandMAC() class MultiEnumField(_MultiEnumField[int], EnumField[int]):
    __slots__ = ["depends_on", "i2s_multi", "s2i_multi", "s2i_all"] class MultipleTypeField(_FieldContainer):
    """MultipleTypeField are used for fields that can be implemented by
various Field subclasses, depending on conditions on the packet.

It is initialized with `flds` and `dflt`.

`dflt` is the default field type, to be used when none of the
conditions matched the current packet.

`flds` is a list of tuples (`fld`, `cond`), where `fld` if a field
type, and `cond` a "condition" to determine if `fld` is the field type
that should be used.

`cond` is either:

  - a callable `cond_pkt` that accepts one argument (the packet) and
    returns True if `fld` should be used, False otherwise.

  - a tuple (`cond_pkt`, `cond_pkt_val`), where `cond_pkt` is the same
    as in the previous case and `cond_pkt_val` is a callable that
    accepts two arguments (the packet, and the value to be set) and
    returns True if `fld` should be used, False otherwise.

See scapy.layers.l2.ARP (type "help(ARP)" in Scapy) for an example of
use.

    """

    __slots__ = ["flds", "dflt", "name", "default"]

    def __init__(self,
                 flds,  # type: List[Tuple[Field[Any, Any], Any]]
                 dflt  # type: Field[Any, Any]
                 ):
        # type: (...) -> None
        self.flds = flds
        self.dflt = dflt
        self.default = None  # So that we can detect changes in defaults
        self.name = self.dflt.name
        if any(x[0].name != self.name for x in self.flds):
            warnings.warn(
                "All fields should have the same name in a MultipleTypeField",
                SyntaxWarning
            )

    def _iterate_fields_cond(self, pkt, val, use_val):
        # type: (Optional[Packet], Any, bool) -> Field[Any, Any]
        """Internal function used by _find_fld_pkt & _find_fld_pkt_val"""
        # Iterate through the fields
        for fld, cond in self.flds:
            if isinstance(cond, tuple):
                if use_val:
                    if val is None:
                        val = self.dflt.default
                    if cond[1](pkt, val):
                        return fld
                    continue
                else:
                    cond = cond[0]
            if cond(pkt):
                return fld
        return self.dflt

    def _find_fld_pkt(self, pkt):
        # type: (Optional[Packet]) -> Field[Any, Any]
        """Given a Packet instance `pkt`, returns the Field subclass to be
used. If you know the value to be set (e.g., in .addfield()), use
._find_fld_pkt_val() instead.

        """
        return self._iterate_fields_cond(pkt, None, False)

    def _find_fld_pkt_val(self,
                          pkt,  # type: Optional[Packet]
                          val,  # type: Any
                          ):
        # type: (...) -> Tuple[Field[Any, Any], Any]
        """Given a Packet instance `pkt` and the value `val` to be set,
returns the Field subclass to be used, and the updated `val` if necessary.

        """
        fld = self._iterate_fields_cond(pkt, val, True)
        if val is None:
            val = fld.default
        return fld, val

    def _find_fld(self):
        # type: () -> Field[Any, Any]
        """Returns the Field subclass to be used, depending on the Packet
instance, or the default subclass.

DEV: since the Packet instance is not provided, we have to use a hack
to guess it. It should only be used if you cannot provide the current
Packet instance (for example, because of the current Scapy API).

If you have the current Packet instance, use ._find_fld_pkt_val() (if
the value to set is also known) of ._find_fld_pkt() instead.

        """
        # Hack to preserve current Scapy API
        # See https://stackoverflow.com/a/7272464/3223422
        frame = inspect.currentframe().f_back.f_back  # type: ignore
        while frame is not None:
            try:
                pkt = frame.f_locals['self']
            except KeyError:
                pass
            else:
                if isinstance(pkt, tuple(self.dflt.owners)):
                    if not pkt.default_fields:
                        # Packet not initialized
                        return self.dflt
                    return self._find_fld_pkt(pkt)
            frame = frame.f_back
        return self.dflt

    def getfield(self,
                 pkt,  # type: Packet
                 s,  # type: bytes
                 ):
        # type: (...) -> Tuple[bytes, Any]
        return self._find_fld_pkt(pkt).getfield(pkt, s)

    def addfield(self, pkt, s, val):
        # type: (Packet, bytes, Any) -> bytes
        fld, val = self._find_fld_pkt_val(pkt, val)
        return fld.addfield(pkt, s, val)

    def any2i(self, pkt, val):
        # type: (Optional[Packet], Any) -> Any
        fld, val = self._find_fld_pkt_val(pkt, val)
        return fld.any2i(pkt, val)

    def h2i(self, pkt, val):
        # type: (Optional[Packet], Any) -> Any
        fld, val = self._find_fld_pkt_val(pkt, val)
        return fld.h2i(pkt, val)

    def i2h(self,
            pkt,  # type: Packet
            val,  # type: Any
            ):
        # type: (...) -> Any
        fld, val = self._find_fld_pkt_val(pkt, val)
        return fld.i2h(pkt, val)

    def i2m(self, pkt, val):
        # type: (Optional[Packet], Optional[Any]) -> Any
        fld, val = self._find_fld_pkt_val(pkt, val)
        return fld.i2m(pkt, val)

    def i2len(self, pkt, val):
        # type: (Packet, Any) -> int
        fld, val = self._find_fld_pkt_val(pkt, val)
        return fld.i2len(pkt, val)

    def i2repr(self, pkt, val):
        # type: (Optional[Packet], Any) -> str
        fld, val = self._find_fld_pkt_val(pkt, val)
        return fld.i2repr(pkt, val)

    def register_owner(self, cls):
        # type: (Type[Packet]) -> None
        for fld, _ in self.flds:
            fld.owners.append(cls)
        self.dflt.owners.append(cls)

    @property
    def fld(self):
        # type: () -> Field[Any, Any]
        return self._find_fld() class PacketListField(_PacketField[List[BasePacket]]):
    """PacketListField represents a list containing a series of Packet instances
    that might occur right in the middle of another Packet field.
    This field type may also be used to indicate that a series of Packet
    instances have a sibling semantic instead of a parent/child relationship
    (i.e. a stack of layers).
    """
    __slots__ = ["count_from", "length_from", "next_cls_cb"]
    islist = 1

    def __init__(
            self,
            name,  # type: str
            default,  # type: Optional[List[BasePacket]]
            pkt_cls=None,  # type: Optional[Union[Callable[[bytes], Packet], Type[Packet]]]  # noqa: E501
            count_from=None,  # type: Optional[Callable[[Packet], int]]
            length_from=None,  # type: Optional[Callable[[Packet], int]]
            next_cls_cb=None,  # type: Optional[Callable[[Packet, List[BasePacket], Optional[Packet], bytes], Type[Packet]]]  # noqa: E501
    ):
        # type: (...) -> None
        """
        The number of Packet instances that are dissected by this field can
        be parametrized using one of three different mechanisms/parameters:

            * count_from: a callback that returns the number of Packet
              instances to dissect. The callback prototype is::

                count_from(pkt:Packet) -> int

            * length_from: a callback that returns the number of bytes that
              must be dissected by this field. The callback prototype is::

                length_from(pkt:Packet) -> int

            * next_cls_cb: a callback that enables a Scapy developer to
              dynamically discover if another Packet instance should be
              dissected or not. See below for this callback prototype.

        The bytes that are not consumed during the dissection of this field
        are passed to the next field of the current packet.

        For the serialization of such a field, the list of Packets that are
        contained in a PacketListField can be heterogeneous and is
        unrestricted.

        The type of the Packet instances that are dissected with this field is
        specified or discovered using one of the following mechanism:

            * the pkt_cls parameter may contain a callable that returns an
              instance of the dissected Packet. This may either be a
              reference of a Packet subclass (e.g. DNSRROPT in layers/dns.py)
              to generate an homogeneous PacketListField or a function
              deciding the type of the Packet instance
              (e.g. _CDPGuessAddrRecord in contrib/cdp.py)

            * the pkt_cls parameter may contain a class object with a defined
              ``dispatch_hook`` classmethod. That method must return a Packet
              instance. The ``dispatch_hook`` callmethod must implement the
                following prototype::

                dispatch_hook(cls,
                              _pkt:Optional[Packet],
                              *args, **kargs
                ) -> Type[Packet]

                The _pkt parameter may contain a reference to the packet
                instance containing the PacketListField that is being
                dissected.

            * the ``next_cls_cb`` parameter may contain a callable whose
              prototype is::

                cbk(pkt:Packet,
                    lst:List[Packet],
                    cur:Optional[Packet],
                    remain:str
                ) -> Optional[Type[Packet]]

              The pkt argument contains a reference to the Packet instance
              containing the PacketListField that is being dissected.
              The lst argument is the list of all Packet instances that were
              previously parsed during the current ``PacketListField``
              dissection, saved for the very last Packet instance.
              The cur argument contains a reference to that very last parsed
              ``Packet`` instance. The remain argument contains the bytes
              that may still be consumed by the current PacketListField
              dissection operation.

              This callback returns either the type of the next Packet to
              dissect or None to indicate that no more Packet are to be
              dissected.

              These four arguments allows a variety of dynamic discovery of
              the number of Packet to dissect and of the type of each one of
              these Packets, including: type determination based on current
              Packet instances or its underlayers, continuation based on the
              previously parsed Packet instances within that PacketListField,
              continuation based on a look-ahead on the bytes to be
              dissected...

        The pkt_cls and next_cls_cb parameters are semantically exclusive,
        although one could specify both. If both are specified, pkt_cls is
        silently ignored. The same is true for count_from and next_cls_cb.

        length_from and next_cls_cb are compatible and the dissection will
        end, whichever of the two stop conditions comes first.

        :param name: the name of the field
        :param default: the default value of this field; generally an empty
            Python list
        :param pkt_cls: either a callable returning a Packet instance or a
            class object defining a ``dispatch_hook`` class method
        :param count_from: a callback returning the number of Packet
            instances to dissect.
        :param length_from: a callback returning the number of bytes to dissect
        :param next_cls_cb: a callback returning either None or the type of
            the next Packet to dissect.
        """
        if default is None:
            default = []  # Create a new list for each instance
        super(PacketListField, self).__init__(
            name,
            default,
            pkt_cls  # type: ignore
        )
        self.count_from = count_from
        self.length_from = length_from
        self.next_cls_cb = next_cls_cb

    def any2i(self, pkt, x):
        # type: (Optional[Packet], Any) -> List[BasePacket]
        if not isinstance(x, list):
            return [x]
        else:
            return x

    def i2count(self,
                pkt,  # type: Optional[Packet]
                val,  # type: List[BasePacket]
                ):
        # type: (...) -> int
        if isinstance(val, list):
            return len(val)
        return 1

    def i2len(self,
              pkt,  # type: Optional[Packet]
              val,  # type: List[Packet]
              ):
        # type: (...) -> int
        return sum(len(p) for p in val)

    def getfield(self, pkt, s):
        # type: (Packet, bytes) -> Tuple[bytes, List[BasePacket]]
        c = len_pkt = cls = None
        if self.length_from is not None:
            len_pkt = self.length_from(pkt)
        elif self.count_from is not None:
            c = self.count_from(pkt)
        if self.next_cls_cb is not None:
            cls = self.next_cls_cb(pkt, [], None, s)
            c = 1
            if cls is None:
                c = 0

        lst = []  # type: List[BasePacket]
        ret = b""
        remain = s
        if len_pkt is not None:
            remain, ret = s[:len_pkt], s[len_pkt:]
        while remain:
            if c is not None:
                if c <= 0:
                    break
                c -= 1
            try:
                if cls is not None:
                    p = cls(remain)
                else:
                    p = self.m2i(pkt, remain)
            except Exception:
                if conf.debug_dissector:
                    raise
                p = conf.raw_layer(load=remain)
                remain = b""
            else:
                if conf.padding_layer in p:
                    pad = p[conf.padding_layer]
                    remain = pad.load
                    del(pad.underlayer.payload)
                    if self.next_cls_cb is not None:
                        cls = self.next_cls_cb(pkt, lst, p, remain)
                        if cls is not None:
                            c = 0 if c is None else c
                            c += 1
                else:
                    remain = b""
            lst.append(p)
        return remain + ret, lst

    def addfield(self, pkt, s, val):
        # type: (Packet, bytes, Any) -> bytes
        return s + b"".join(bytes_encode(v) for v in val) class PadField(_FieldContainer):
    """Add bytes after the proxified field so that it ends at the specified
       alignment from its beginning"""
    __slots__ = ["fld", "_align", "_padwith"]

    def __init__(self, fld, align, padwith=None):
        # type: (Field[Any, Any], int, Optional[bytes]) -> None
        self.fld = fld
        self._align = align
        self._padwith = padwith or b"\x00"

    def padlen(self, flen):
        # type: (int) -> int
        return -flen % self._align

    def getfield(self,
                 pkt,  # type: Packet
                 s,  # type: bytes
                 ):
        # type: (...) -> Tuple[bytes, Any]
        remain, val = self.fld.getfield(pkt, s)
        padlen = self.padlen(len(s) - len(remain))
        return remain[padlen:], val

    def addfield(self,
                 pkt,  # type: Packet
                 s,  # type: bytes
                 val,  # type: Any
                 ):
        # type: (...) -> bytes
        sval = self.fld.addfield(pkt, b"", val)
        return s + sval + struct.pack(
            "%is" % (
                self.padlen(len(sval))
            ),
            self._padwith
        ) class ShortEnumField(EnumField[int]):
    __slots__ = EnumField.__slots__

    def __init__(self,
                 name,  # type: str
                 default,  # type: int
                 enum,  # type: Union[Dict[int, str], Dict[str, int], Tuple[Callable[[int], str], Callable[[str], int]], DADict[int, str]]  # noqa: E501
                 ):
        # type: (...) -> None
        EnumField.__init__(self, name, default, enum, "H") class ShortField(Field[int, int]):
    def __init__(self, name, default):
        # type: (str, Optional[int]) -> None
        Field.__init__(self, name, default, "H") class StrLenField(StrField):
    __slots__ = ["length_from", "max_length"]

    def __init__(
            self,
            name,  # type: str
            default,  # type: bytes
            length_from=None,  # type: Optional[Callable[[Packet], int]]
            max_length=None,  # type: Optional[Any]
    ):
        # type: (...) -> None
        super(StrLenField, self).__init__(name, default)
        self.length_from = length_from
        self.max_length = max_length

    def getfield(self, pkt, s):
        # type: (Any, bytes) -> Tuple[bytes, bytes]
        len_pkt = (self.length_from or (lambda x: 0))(pkt)
        return s[len_pkt:], self.m2i(pkt, s[:len_pkt])

    def randval(self):
        # type: () -> RandBin
        return RandBin(RandNum(0, self.max_length or 1200)) class XByteField(ByteField):
    def i2repr(self, pkt, x):
        # type: (Optional[Packet], int) -> str
        return lhex(self.i2h(pkt, x)) class XIntField(IntField):
    def i2repr(self, pkt, x):
        # type: (Optional[Packet], int) -> str
        return lhex(self.i2h(pkt, x)) class XShortField(ShortField):
    def i2repr(self, pkt, x):
        # type: (Optional[Packet], int) -> str
        return lhex(self.i2h(pkt, x))
from scapy.compat import orb
from scapy.all import Packet, bind_layers, Padding
from scapy.fields import (
    ByteEnumField,
    ConditionalField,
    FieldLenField,
    FieldListField,
    IPField,
    LenField,
    MACField,
    MultiEnumField,
    MultipleTypeField,
    PacketListField,
    PadField,
    ShortEnumField,
    ShortField,
    StrLenField,
    XByteField,
    XIntField,
    XShortField,
)
        0x03: "Signal",
        0x04: "Response",
        0x05: "Reset Factory Settings",
        0x06: "Reset to Factory"
    },
    # device initiative
    0x06: {
        0x00: "Reserved",
        0x01: "Device Initiative"
    },
    0xff: {
        0xff: "ALL Selector"
    }
}

BLOCK_INFOS = {
    0x00: "Reserved",
}
BLOCK_INFOS.update({i: "reserved" for i in range(0x01, 0xff)})


IP_BLOCK_INFOS = {
    0x0000: "IP not set",
    0x0001: "IP set",
    0x0002: "IP set by DHCP",
    0x0080: "IP not set (address conflict detected)",
    0x0081: "IP set (address conflict detected)",
    0x0082: "IP set by DHCP (address conflict detected)",
}
IP_BLOCK_INFOS.update({i: "reserved" for i in range(0x0003, 0x007f)})

BLOCK_ERRORS = {
    0x00: "Ok",
    0x01: "Option unsupp.",
    0x02: "Suboption unsupp. or no DataSet avail.",
    0x03: "Suboption not set",
    0x04: "Resource Error",
    0x05: "SET not possible by local reasons",
    0x06: "In operation, SET not possible",
}

BLOCK_QUALIFIERS = {
    0x0000: "Use the value temporary",
    0x0001: "Save the value permanent",
}
BLOCK_QUALIFIERS.update({i: "reserved" for i in range(0x0002, 0x00ff)})


#####################################################
#                     DCP Blocks                    #
#####################################################

# GENERIC DCP BLOCK

# DCP RESPONSE BLOCKS

class DCPBaseBlock(Packet):
    """
        base class for all DCP Blocks
    """
    fields_desc = [
        ByteEnumField("option", 1, DCP_OPTIONS),
        MultiEnumField("sub_option", 2, DCP_SUBOPTIONS, fmt='B',
                       depends_on=lambda p: p.option),
        FieldLenField("dcp_block_length", None, length_of="data"),
        ShortEnumField("block_info", 0, BLOCK_INFOS),
        StrLenField("data", "", length_from=lambda x: x.dcp_block_length),
    ]

    def extract_padding(self, s):
        return '', s


# OPTION: IP

class DCPIPBlock(Packet):
    fields_desc = [
        ByteEnumField("option", 1, DCP_OPTIONS),
        MultiEnumField("sub_option", 2, DCP_SUBOPTIONS, fmt='B',
                       depends_on=lambda p: p.option),
        LenField("dcp_block_length", None),
        ShortEnumField("block_info", 1, IP_BLOCK_INFOS),
        IPField("ip", "192.168.0.2"),
        IPField("netmask", "255.255.255.0"),
        IPField("gateway", "192.168.0.1"),
        PadField(StrLenField("padding", b"\x00",
                             length_from=lambda p: p.dcp_block_length % 2), 1,
                 padwith=b"\x00")
    ]

    def extract_padding(self, s):
        return '', s


class DCPFullIPBlock(Packet):
    fields_desc = [
        ByteEnumField("option", 1, DCP_OPTIONS),
        MultiEnumField("sub_option", 3, DCP_SUBOPTIONS, fmt='B',
                       depends_on=lambda p: p.option),
        LenField("dcp_block_length", None),
        ShortEnumField("block_info", 1, IP_BLOCK_INFOS),
        IPField("ip", "192.168.0.2"),
        IPField("netmask", "255.255.255.0"),
        IPField("gateway", "192.168.0.1"),
        FieldListField("dnsaddr", [], IPField("", "0.0.0.0"),
                       count_from=lambda x: 4),
        PadField(StrLenField("padding", b"\x00",
                             length_from=lambda p: p.dcp_block_length % 2), 1,
                 padwith=b"\x00")
    ]

    def extract_padding(self, s):
        return '', s


class DCPMACBlock(Packet):
    fields_desc = [
        ByteEnumField("option", 1, DCP_OPTIONS),
        MultiEnumField("sub_option", 1, DCP_SUBOPTIONS, fmt='B',
                       depends_on=lambda p: p.option),

        FieldLenField("dcp_block_length", None),