Introduce a cache mechanism to track changes in mutable field values

This is useful when a field value content is tampered without the
value itself being changed (this can happen with values of type list
and Packet, for example).

--HG--
branch : issue-5105
This commit is contained in:
Pierre LALET 2015-01-06 09:48:11 +01:00
parent ed71733fcc
commit 766b99b6c9
1 changed files with 49 additions and 2 deletions

View File

@ -49,6 +49,7 @@ class Packet(BasePacket):
show_indent=1
explicit = 0
raw_packet_cache = None
raw_packet_cache_fields = None
@classmethod
def from_hexcap(cls):
@ -153,6 +154,7 @@ class Packet(BasePacket):
clone.underlayer = self.underlayer
clone.explicit = self.explicit
clone.raw_packet_cache = self.raw_packet_cache
clone.raw_packet_cache_fields = self.clone_raw_packet_cache_fields()
clone.post_transforms = self.post_transforms[:]
clone.__dict__["payload"] = self.payload.copy()
clone.payload.add_underlayer(clone)
@ -194,6 +196,7 @@ class Packet(BasePacket):
self.fields[attr] = any2i(self, val)
self.explicit = 0
self.raw_packet_cache = None
self.raw_packet_cache_fields = None
elif attr == "payload":
self.remove_payload()
self.add_payload(val)
@ -215,6 +218,7 @@ class Packet(BasePacket):
del(self.fields[attr])
self.explicit = 0 # in case a default value must be explicited
self.raw_packet_cache = None
self.raw_packet_cache_fields = None
elif self.default_fields.has_key(attr):
pass
elif attr == "payload":
@ -295,9 +299,34 @@ class Packet(BasePacket):
return True
def __len__(self):
return len(self.__str__())
def clone_raw_packet_cache_fields(self):
if self.raw_packet_cache_fields is None:
return None
result = {}
for key, (value, islist,
holds_packets) in self.raw_packet_cache_fields.iteritems():
if islist:
if holds_packets:
result[key] = ([
p.copy() for p in value
], 1, 1)
else:
# XXX Not ideal since elements themselves can be
# mutable
result[key] = (value[:], 1, 0)
else:
# if holds_packets:
result[key] = (value, 0, 0)
return result
def self_build(self, field_pos_list=None):
if self.raw_packet_cache is not None:
return self.raw_packet_cache
for fname, (fval, _, _) in self.raw_packet_cache_fields.iteritems():
if self.getfieldval(fname) != fval:
self.raw_packet_cache = None
self.raw_packet_cache_fields = None
break
if self.raw_packet_cache is not None:
return self.raw_packet_cache
p=""
for f in self.fields_desc:
val = self.getfieldval(f.name)
@ -558,9 +587,26 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
flist = self.fields_desc[:]
flist.reverse()
raw = s
self.raw_packet_cache_fields = {}
while s and flist:
f = flist.pop()
s,fval = f.getfield(self, s)
s, fval = f.getfield(self, s)
# We need to track fields with mutable values to discard
# .raw_packet_cache when needed.
#
# The values in f.raw_packet_cache_fields are three
# elements tuples (value, f.islist, f.holds_packets)
if f.islist:
if f.holds_packets:
self.raw_packet_cache_fields[f.name] = ([
p.copy() for p in fval
], 1, 1)
else:
# XXX Not ideal since elements themselves can be
# mutable
self.raw_packet_cache_fields[f.name] = (fval[:], 1, 0)
elif f.holds_packets and fval is not None:
self.raw_packet_cache_fields[f.name] = (fval.copy(), 0, 1)
self.fields[f.name] = fval
assert(raw.endswith(s))
if s:
@ -635,6 +681,7 @@ Creates an EPS file describing a packet. If filename is not provided a temporary
pkt.overload_fields = self.overload_fields.copy()
pkt.post_transforms = self.post_transforms
pkt.raw_packet_cache = self.raw_packet_cache
pkt.raw_packet_cache_fields = self.clone_raw_packet_cache_fields()
if payload is not None:
pkt.add_payload(payload)
return pkt