116 lines
3.7 KiB
Python
116 lines
3.7 KiB
Python
import binascii
|
|
import typing
|
|
|
|
from pyshark.packet.common import Pickleable, SlotsPickleable
|
|
|
|
|
|
class LayerField(SlotsPickleable):
|
|
"""Holds all data about a field of a layer, both its actual value and its name and nice representation."""
|
|
|
|
# Note: We use this object with slots and not just a dict because
|
|
# it's much more memory-efficient (cuts about a third of the memory).
|
|
__slots__ = ['name', 'showname', 'raw_value', 'show', 'hide', 'pos', 'size', 'unmaskedvalue']
|
|
|
|
def __init__(self, name=None, showname=None, value=None, show=None, hide=None, pos=None, size=None, unmaskedvalue=None):
|
|
self.name = name
|
|
self.showname = showname
|
|
self.raw_value = value
|
|
self.show = show
|
|
self.pos = pos
|
|
self.size = size
|
|
self.unmaskedvalue = unmaskedvalue
|
|
|
|
if hide and hide == 'yes':
|
|
self.hide = True
|
|
else:
|
|
self.hide = False
|
|
|
|
def __repr__(self):
|
|
return f'<LayerField {self.name}: {self.get_default_value()}>'
|
|
|
|
def get_default_value(self) -> str:
|
|
"""Gets the best 'value' string this field has."""
|
|
val = self.show
|
|
if not val:
|
|
val = self.raw_value
|
|
if not val:
|
|
val = self.showname
|
|
return val
|
|
|
|
@property
|
|
def showname_value(self) -> typing.Union[str, None]:
|
|
"""The "pretty value" (as displayed by Wireshark) of the field."""
|
|
if self.showname and ': ' in self.showname:
|
|
return self.showname.split(': ', 1)[1]
|
|
return None
|
|
|
|
@property
|
|
def showname_key(self) -> typing.Union[str, None]:
|
|
"""The "pretty name" (as displayed by Wireshark) of the field."""
|
|
if self.showname and ': ' in self.showname:
|
|
return self.showname.split(': ', 1)[0]
|
|
return None
|
|
|
|
@property
|
|
def binary_value(self) -> bytes:
|
|
"""Converts this field to binary (assuming it's a binary string)"""
|
|
str_raw_value = str(self.raw_value)
|
|
if len(str_raw_value) % 2 == 1:
|
|
str_raw_value = '0' + str_raw_value
|
|
|
|
return binascii.unhexlify(str_raw_value)
|
|
|
|
@property
|
|
def int_value(self) -> int:
|
|
"""Returns the int value of this field (assuming it's represented as a decimal integer)."""
|
|
return int(self.raw_value)
|
|
|
|
@property
|
|
def hex_value(self) -> int:
|
|
"""Returns the int value of this field if it's in base 16
|
|
|
|
(either as a normal number or in a "0xFFFF"-style hex value)
|
|
"""
|
|
return int(self.raw_value, 16)
|
|
|
|
base16_value = hex_value
|
|
|
|
|
|
class LayerFieldsContainer(str, Pickleable):
|
|
"""An object which contains one or more fields (of the same name).
|
|
|
|
When accessing member, such as showname, raw_value, etc. the appropriate member of the main (first) field saved
|
|
in this container will be shown.
|
|
"""
|
|
|
|
def __new__(cls, main_field, *args, **kwargs):
|
|
if hasattr(main_field, 'get_default_value'):
|
|
obj = str.__new__(cls, main_field.get_default_value(), *args, **kwargs)
|
|
else:
|
|
obj = str.__new__(cls, main_field, *args, **kwargs)
|
|
obj.fields = [main_field]
|
|
return obj
|
|
|
|
def __dir__(self):
|
|
return dir(type(self)) + list(self.__dict__.keys()) + dir(self.main_field)
|
|
|
|
def add_field(self, field):
|
|
self.fields.append(field)
|
|
|
|
@property
|
|
def all_fields(self):
|
|
"""Returns all fields in a list, the main field followed by the alternate fields."""
|
|
return self.fields
|
|
|
|
@property
|
|
def main_field(self):
|
|
return self.fields[0]
|
|
|
|
@property
|
|
def alternate_fields(self):
|
|
"""Return the alternate values of this field containers (non-main ones)."""
|
|
return self.fields[1:]
|
|
|
|
def __getattr__(self, item):
|
|
return getattr(self.main_field, item)
|