简体   繁体   中英

Stuck with VarLenQField example from Scapy's documentation

I am going through the Scapy example of Adding new protocols and I'm stuck. The aged code as presented on their page throws an error in Python 3 because of string->bytes conversion, but that's a minor thing. I have written my own implementations of their vlenq2str() and str2vlenq() . I call them vlenq2m() and m2vlenq() respectively. vlenq2m converts integer values to bytes (to be used in the packet's raw data), and m2vlenq converts these bytes from the packet's raw data back to integer (as scapy-internal representation).

My actual problem is that at some point after calling the packet's show2 method, scapy throws a TypeError and I have no clue why.

Here is my minimal example (modernized version of the code from scapy's documentation):

#!/usr/bin/env python


from scapy.fields import Field, StrLenField
from scapy.packet import Packet, ls
from scapy.compat import raw


def vlenq2m(val: int) -> bytes:
    s = list()
    s.append(val & 0x7F)
    val = val >> 7
    while val:
        s.append(0x80 | (val & 0x7F))
        val = val >> 7
    s.reverse()
    return bytes(s)


def m2vlenq(m: bytes=b"") -> tuple[bytes, int]:
    i = l = 0
    for n in m:
        l = l << 7
        l = l + (n & 0x7F)
        i = i + 1
        if not n & 0x80:
            break
    return m[i + 1:], l


class VarLenQField(Field):
    """variable length quantities"""
    __slots__ = ["fld"]
    def __init__(self, name, default, fld):
        Field.__init__(self, name, default)
        self.fld = fld
    def i2m(self, pkt, x):
        if x is None:
            f = pkt.get_field(self.fld)
            x = f.i2len(pkt, pkt.getfieldval(self.fld))
            x = vlenq2m(x)
        return raw(x)
    def m2i(self, pkt, x):
        if s is None:
            return None, 0
        return m2vlenq(x)[1]
    def addfield(self, pkt, s, val):
        return s + self.i2m(pkt, val)
    def getfield(self, pkt, s):
        return m2vlenq(s)


class Foo(Packet):
    name = "Foo"
    fields_desc = [
            VarLenQField("len", None, "data"),
            StrLenField("data", "", "len"),
            ]


if __name__ == "__main__":
    f = Foo(data="test data")
    breakpoint()
    f.show2()

Here is the error stack:

$ python -i sominimal.py
Traceback (most recent call last):
  File "D:\Python\mlp-monitor\src\mlptc\sominimal.py", line 64, in <module>
    f.show2()
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\packet.py", line 1289, in show2
    return self.__class__(raw(self)).show(dump, indent, lvl, label_lvl)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py", line 266, in __call__
    i.__init__(*args, **kargs)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\packet.py", line 158, in __init__
    self.dissect(_pkt)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\packet.py", line 875, in dissect
    s = self.do_dissect(s)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\packet.py", line 839, in do_dissect
    s, fval = f.getfield(self, s)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\fields.py", line 1380, in getfield
    len_pkt = self.length_from(pkt)
TypeError: 'NoneType' object is not callable

Here is my careful stepping through the debugger up to the point where the error occurs.

$ python -i sominimal.py
> d:\python\mlp-monitor\src\mlptc\sominimal.py(65)<module>()
-> f.show2()
(Pdb) s
--Call--
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\packet.py(1277)show2()
-> def show2(self, dump=False, indent=3, lvl="", label_lvl=""):
(Pdb) n
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\packet.py(1289)show2()
-> return self.__class__(raw(self)).show(dump, indent, lvl, label_lvl)
(Pdb) s
--Call--
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\compat.py(50)raw()
-> def raw(x):
(Pdb) n
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\compat.py(53)raw()
-> return bytes(x)
(Pdb) n
--Return--
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\compat.py(53)raw()->b'\ttest data'
-> return bytes(x)
(Pdb) n
--Call--
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(256)__call__()
-> def __call__(cls, *args, **kargs):
(Pdb) n
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(257)__call__()
-> if "dispatch_hook" in cls.__dict__:
(Pdb) n
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(265)__call__()
-> i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
(Pdb) s
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(266)__call__()
-> i.__init__(*args, **kargs)
(Pdb) type(i)
<class '__main__.Foo'>
(Pdb) type(i.__init__)
<class 'method'>
(Pdb) n
TypeError: 'NoneType' object is not callable
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(266)__call__()
-> i.__init__(*args, **kargs)

You're super close. You only missed a small, weird quirk: use a function instead of a string in StrLenField

class Foo(Packet):
    name = "Foo"
    fields_desc = [
        VarLenQField("len", None, "data"),
        StrLenField("data", "", length_from=lambda pkt: pkt.len)
    ]

also wow there's a wrong example on the page that uses a string. I'll have that fixed

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM