cat fields.py ## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Fields: basic data structures that make up parts of packets. """ import struct,copy,socket from config import conf from volatile import * from data import * from utils import * from base_classes import BasePacket,Gen,Net ############ ## Fields ## ############ class Field(object): """For more informations on how this work, please refer to http://www.secdev.org/projects/scapy/files/scapydoc.pdf chapter ``Adding a New Field''""" islist=0 holds_packets=0 def __init__(self, name, default, fmt="H"): self.name = name if fmt[0] in "@=<>!": self.fmt = fmt else: self.fmt = "!"+fmt self.default = self.any2i(None,default) self.sz = struct.calcsize(self.fmt) self.owners = [] def register_owner(self, cls): self.owners.append(cls) def i2len(self, pkt, x): """Convert internal value to a length usable by a FieldLenField""" return self.sz def i2count(self, pkt, x): """Convert internal value to a number of elements usable by a FieldLenField. Always 1 except for list fields""" return 1 def h2i(self, pkt, x): """Convert human value to internal value""" return x def i2h(self, pkt, x): """Convert internal value to human value""" return x def m2i(self, pkt, x): """Convert machine value to internal value""" return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: x = 0 return x def any2i(self, pkt, x): """Try to understand the most input values possible and make an internal value from them""" return self.h2i(pkt, x) def i2repr(self, pkt, x): """Convert internal value to a nice representation""" return repr(self.i2h(pkt,x)) def addfield(self, pkt, s, val): """Add an internal value to a string""" return s+struct.pack(self.fmt, self.i2m(pkt,val)) def getfield(self, pkt, s): """Extract an internal value from a string""" return s[self.sz:], self.m2i(pkt, struct.unpack(self.fmt, s[:self.sz])[0]) def do_copy(self, x): if hasattr(x, "copy"): return x.copy() if type(x) is list: x = x[:] for i in xrange(len(x)): if isinstance(x[i], BasePacket): x[i] = x[i].copy() return x def __repr__(self): return "" % (",".join(x.__name__ for x in self.owners),self.name) def copy(self): return copy.deepcopy(self) def randval(self): """Return a volatile object whose value is both random and suitable for this field""" fmtt = self.fmt[-1] if fmtt in "BHIQ": return {"B":RandByte,"H":RandShort,"I":RandInt, "Q":RandLong}[fmtt]() elif fmtt == "s": if self.fmt[0] in "0123456789": l = int(self.fmt[:-1]) else: l = int(self.fmt[1:-1]) return RandBin(l) else: warning("no random class for [%s] (fmt=%s)." % (self.name, self.fmt)) class Emph(object): fld = "" def __init__(self, fld): self.fld = fld def __getattr__(self, attr): return getattr(self.fld,attr) def __hash__(self): return hash(self.fld) def __eq__(self, other): return self.fld == other class ActionField(object): _fld = None def __init__(self, fld, action_method, **kargs): self._fld = fld self._action_method = action_method self._privdata = kargs def any2i(self, pkt, val): getattr(pkt, self._action_method)(val, self._fld, **self._privdata) return getattr(self._fld, "any2i")(pkt, val) def __getattr__(self, attr): return getattr(self._fld,attr) class ConditionalField(object): fld = None def __init__(self, fld, cond): self.fld = fld self.cond = cond def _evalcond(self,pkt): return self.cond(pkt) def getfield(self, pkt, s): if self._evalcond(pkt): return self.fld.getfield(pkt,s) else: return s,None def addfield(self, pkt, s, val): if self._evalcond(pkt): return self.fld.addfield(pkt,s,val) else: return s def __getattr__(self, attr): return getattr(self.fld,attr) class PadField(object): """Add bytes after the proxified field so that it ends at the specified alignment from its begining""" _fld = None def __init__(self, fld, align, padwith=None): self._fld = fld self._align = align self._padwith = padwith or "" def padlen(self, flen): return -flen%self._align def getfield(self, pkt, s): remain,val = self._fld.getfield(pkt,s) padlen = self.padlen(len(s)-len(remain)) return remain[padlen:], val def addfield(self, pkt, s, val): sval = self._fld.addfield(pkt, "", val) return s+sval+struct.pack("%is" % (self.padlen(len(sval))), self._padwith) def __getattr__(self, attr): return getattr(self._fld,attr) class MACField(Field,object): def __init__(self, name, default): Field.__init__(self, name, default, "6s") def i2m(self, pkt, x): if x is None: return "\0\0\0\0\0\0" return mac2str(x) def m2i(self, pkt, x): return str2mac(x) def any2i(self, pkt, x): if type(x) is str and len(x) is 6: x = self.m2i(pkt, x) return x def i2repr(self, pkt, x): x = self.i2h(pkt, x) if self in conf.resolve: x = conf.manufdb._resolve_MAC(x) return x def randval(self): return RandMAC() class IPField(Field,object): def __init__(self, name, default): Field.__init__(self, name, default, "4s") def h2i(self, pkt, x): if type(x) is str: try: inet_aton(x) except socket.error: x = Net(x) elif type(x) is list: x = [self.h2i(pkt, n) for n in x] return x def resolve(self, x): if self in conf.resolve: try: ret = socket.gethostbyaddr(x)[0] except: pass else: if ret: return ret return x def i2m(self, pkt, x): return inet_aton(x) def m2i(self, pkt, x): return inet_ntoa(x) def any2i(self, pkt, x): return self.h2i(pkt,x) def i2repr(self, pkt, x): return self.resolve(self.i2h(pkt, x)) def randval(self): return RandIP() class SourceIPField(IPField,object): def __init__(self, name, dstname): IPField.__init__(self, name, None) self.dstname = dstname def i2m(self, pkt, x): if x is None: iff,x,gw = pkt.route() if x is None: x = "0.0.0.0" return IPField.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: dst=getattr(pkt,self.dstname) if isinstance(dst,Gen): r = map(conf.route.route, dst) r.sort() if r[0] != r[-1]: warning("More than one possible route for %s"%repr(dst)) iff,x,gw = r[0] else: iff,x,gw = conf.route.route(dst) return IPField.i2h(self, pkt, x) class ByteField(Field,object): def __init__(self, name, default): Field.__init__(self, name, default, "B") class XByteField(ByteField,object): def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class OByteField(ByteField, object): def i2repr(self, pkt, x): return "%03o"%self.i2h(pkt, x) class X3BytesField(XByteField, object): def __init__(self, name, default): Field.__init__(self, name, default, "!I") def addfield(self, pkt, s, val): return s+struct.pack(self.fmt, self.i2m(pkt,val))[1:4] def getfield(self, pkt, s): return s[3:], self.m2i(pkt, struct.unpack(self.fmt, "\x00"+s[:3])[0]) class ShortField(Field, object): def __init__(self, name, default): Field.__init__(self, name, default, "H") class LEShortField(Field, object): def __init__(self, name, default): Field.__init__(self, name, default, ">4))+chr(0x41+(ord(x)&0xf)), x)) x = " "+x return x def m2i(self, pkt, x): x = x.strip("\x00").strip(" ") return "".join(map(lambda x,y: chr((((ord(x)-1)&0xf)<<4)+((ord(y)-1)&0xf)), x[::2],x[1::2])) class StrLenField(StrField, object): def __init__(self, name, default, fld=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) class FieldListField(Field, object): islist=1 def __init__(self, name, default, field, length_from=None, count_from=None): if default is None: default = [] # Create a new list for each instance Field.__init__(self, name, default) self.count_from = count_from self.length_from = length_from self.field = field def i2count(self, pkt, val): if type(val) is list: return len(val) return 1 def i2len(self, pkt, val): return sum( self.field.i2len(pkt,v) for v in val ) def i2m(self, pkt, val): if val is None: val = [] return val def any2i(self, pkt, x): if type(x) is not list: return [x] else: return x def addfield(self, pkt, s, val): val = self.i2m(pkt, val) for v in val: s = self.field.addfield(pkt, s, v) return s def getfield(self, pkt, s): c = l = None if self.length_from is not None: l = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) val = [] ret="" if l is not None: s,ret = s[:l],s[l:] 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 FieldLenField(Field, object): def __init__(self, name, default, length_of=None, fmt = "H", count_of=None, adjust=lambda pkt,x:x, fld=None): Field.__init__(self, name, default, fmt) self.length_of=length_of self.count_of=count_of self.adjust=adjust if fld is not None: FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) self.length_of = fld def i2m(self, pkt, x): if x is None: if self.length_of is not None: fld,fval = pkt.getfield_and_val(self.length_of) f = fld.i2len(pkt, fval) else: fld,fval = pkt.getfield_and_val(self.count_of) f = fld.i2count(pkt, fval) x = self.adjust(pkt,f) return x class StrNullField(StrField, object): def addfield(self, pkt, s, val): return s+self.i2m(pkt, val)+"\x00" def getfield(self, pkt, s): l = s.find("\x00") if l < 0: #XXX \x00 not found return "",s return s[l+1:],self.m2i(pkt, s[:l]) def randval(self): return RandTermString(RandNum(0,1200),"\x00") class StrStopField(StrField, object): def __init__(self, name, default, stop, additionnal=0): Field.__init__(self, name, default) self.stop=stop self.additionnal=additionnal def getfield(self, pkt, s): l = s.find(self.stop) if l < 0: return "",s # raise Scapy_Exception,"StrStopField: stop value [%s] not found" %stop l += len(self.stop)+self.additionnal return s[l:],s[:l] def randval(self): return RandTermString(RandNum(0,1200),self.stop) class LenField(Field, object): def i2m(self, pkt, x): if x is None: x = len(pkt.payload) return x class BCDFloatField(Field,object): def i2m(self, pkt, x): return int(256*x) def m2i(self, pkt, x): return x/256.0 class BitField(Field, object): def __init__(self, name, default, size): Field.__init__(self, name, default) self.rev = size < 0 self.size = abs(size) def reverse(self, val): if self.size == 16: val = socket.ntohs(val) elif self.size == 32: val = socket.ntohl(val) return val def addfield(self, pkt, s, val): val = self.i2m(pkt, val) if type(s) is tuple: s,bitsdone,v = s else: bitsdone = 0 v = 0 if self.rev: val = self.reverse(val) v <<= self.size v |= val & ((1L<= 8: bitsdone -= 8 s = s+struct.pack("!B", v >> bitsdone) v &= (1L<> (nb_bytes*8 - self.size - bn) if self.rev: b = self.reverse(b) bn += self.size s = s[bn/8:] bn = bn%8 b = self.m2i(pkt, b) if bn: return (s,bn),b else: return s,b def randval(self): return RandNum(0,2**self.size-1) class BitFieldLenField(BitField, object): def __init__(self, name, default, size, length_of=None, count_of=None, adjust=lambda pkt,x:x): BitField.__init__(self, name, default, size) self.length_of=length_of self.count_of=count_of self.adjust=adjust def i2m(self, pkt, x): return FieldLenField.i2m.im_func(self, pkt, x) class XBitField(BitField, object): def i2repr(self, pkt, x): return lhex(self.i2h(pkt,x)) class EnumField(Field, object): def __init__(self, name, default, enum, fmt = "H"): i2s = self.i2s = {} s2i = self.s2i = {} if type(enum) is list: keys = xrange(len(enum)) else: keys = enum.keys() if filter(lambda x: type(x) is str, keys): i2s,s2i = s2i,i2s for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k Field.__init__(self, name, default, fmt) def any2i_one(self, pkt, x): if type(x) is str: x = self.s2i[x] return x def i2repr_one(self, pkt, x): if self not in conf.noenum and not isinstance(x,VolatileValue) and x in self.i2s: return self.i2s[x] return repr(x) def any2i(self, pkt, x): if type(x) is list: return map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x) else: return self.any2i_one(pkt,x) def i2repr(self, pkt, x): if type(x) is list: return map(lambda z,pkt=pkt:self.i2repr_one(pkt,z), x) else: return self.i2repr_one(pkt,x) class CharEnumField(EnumField, object): def __init__(self, name, default, enum, fmt = "1s"): EnumField.__init__(self, name, default, enum, fmt) k = self.i2s.keys() if k and len(k[0]) != 1: self.i2s,self.s2i = self.s2i,self.i2s def any2i_one(self, pkt, x): if len(x) != 1: x = self.s2i[x] return x class BitEnumField(BitField,EnumField, object): def __init__(self, name, default, size, enum): EnumField.__init__(self, name, default, enum) self.rev = size < 0 self.size = abs(size) def any2i(self, pkt, x): return EnumField.any2i(self, pkt, x) def i2repr(self, pkt, x): return EnumField.i2repr(self, pkt, x) class ShortEnumField(EnumField, object): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "H") class LEShortEnumField(EnumField, object): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, ">= 1 if self.multi: r = "+".join(r) return r class FixedPointField(BitField, object): def __init__(self, name, default, size, frac_bits=16): self.frac_bits = frac_bits BitField.__init__(self, name, default, size) def any2i(self, pkt, val): if val is None: return val ival = int(val) fract = int( (val-ival) * 2**self.frac_bits ) return (ival << self.frac_bits) | fract def i2h(self, pkt, val): int_part = val >> self.frac_bits frac_part = val & (1L << self.frac_bits) - 1 frac_part /= 2.0**self.frac_bits return int_part+frac_part def i2repr(self, pkt, val): return self.i2h(pkt, val)