diff --git a/scapy.py b/scapy.py index c9658e9eb4c3ef44ac335e8fd7c2249925a6742d..4eefb57eff0f7472ff82c98eb32e4371e7637bf7 100755 --- a/scapy.py +++ b/scapy.py @@ -1,14185 +1,5 @@ #! /usr/bin/env python -############################################################################# -## ## -## scapy.py --- Interactive packet manipulation tool ## -## see http://www.secdev.org/projects/scapy/ ## -## for more informations ## -## ## -## Copyright (C) 2003 Philippe Biondi <phil@secdev.org> ## -## ## -## This program is free software; you can redistribute it and/or modify it ## -## under the terms of the GNU General Public License version 2 as ## -## published by the Free Software Foundation; version 2. ## -## ## -## This program is distributed in the hope that it will be useful, but ## -## WITHOUT ANY WARRANTY; without even the implied warranty of ## -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## -## General Public License for more details. ## -## ## -############################################################################# +from scapy.main import interact - -from __future__ import generators -import os - -VERSION = "1.2.0.2" - -DEFAULT_CONFIG_FILE = os.path.join(os.environ["HOME"], ".scapy_startup.py") - -try: - os.stat(DEFAULT_CONFIG_FILE) -except OSError: - DEFAULT_CONFIG_FILE = None - -def usage(): - print """Usage: scapy.py [-s sessionfile] [-c new_startup_file] [-C] - -C: do not read startup file""" - sys.exit(0) - - -############################# -##### Logging subsystem ##### -############################# - -class Scapy_Exception(Exception): - pass - -import logging,traceback,time - -class ScapyFreqFilter(logging.Filter): - def __init__(self): - logging.Filter.__init__(self) - self.warning_table = {} - def filter(self, record): - wt = conf.warning_threshold - if wt > 0: - stk = traceback.extract_stack() - caller=None - for f,l,n,c in stk: - if n == 'warning': - break - caller = l - tm,nb = self.warning_table.get(caller, (0,0)) - ltm = time.time() - if ltm-tm > wt: - tm = ltm - nb = 0 - else: - if nb < 2: - nb += 1 - if nb == 2: - record.msg = "more "+record.msg - else: - return 0 - self.warning_table[caller] = (tm,nb) - return 1 - -log_scapy = logging.getLogger("scapy") -console_handler = logging.StreamHandler() -console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) -log_scapy.addHandler(console_handler) -log_runtime = logging.getLogger("scapy.runtime") # logs at runtime -log_runtime.addFilter(ScapyFreqFilter()) -log_interactive = logging.getLogger("scapy.interactive") # logs in interactive functions -log_loading = logging.getLogger("scapy.loading") # logs when loading scapy - -if __name__ == "__main__": - log_scapy.setLevel(1) - - -################## -##### Module ##### -################## - -import socket, sys, getopt, string, struct, random, code -import cPickle, copy, types, gzip, base64, re, zlib, array -from sets import Set -from select import select -from glob import glob -from fcntl import ioctl -import itertools -import fcntl -import warnings -warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__) - - -try: - import Gnuplot - GNUPLOT=1 -except ImportError: - log_loading.info("did not find python gnuplot wrapper . Won't be able to plot") - GNUPLOT=0 - -try: - import pyx - PYX=1 -except ImportError: - log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump()") - PYX=0 - - -LINUX=sys.platform.startswith("linux") -OPENBSD=sys.platform.startswith("openbsd") -FREEBSD=sys.platform.startswith("freebsd") -DARWIN=sys.platform.startswith("darwin") -BIG_ENDIAN= struct.pack("H",1) == "\x00\x01" -X86_64 = (os.uname()[4] == 'x86_64') -SOLARIS=sys.platform.startswith("sunos") - - -if LINUX: - DNET=PCAP=0 -else: - DNET=PCAP=1 - - -if PCAP: - try: - import pcap - PCAP = 1 - except ImportError: - if LINUX: - log_loading.warning("did not find pcap module. Fallback to linux primitives") - PCAP = 0 - else: - if __name__ == "__main__": - log_loading.error("did not find pcap module") - raise SystemExit - else: - raise - -if DNET: - try: - import dnet - DNET = 1 - except ImportError: - if LINUX: - log_loading.warning("did not find dnet module. Fallback to linux primitives") - DNET = 0 - else: - if __name__ == "__main__": - log_loading.error("did not find dnet module") - raise SystemExit - else: - raise - -if not PCAP: - f = os.popen("tcpdump -V 2> /dev/null") - if f.close() >> 8 == 0x7f: - log_loading.warning("Failed to execute tcpdump. Check it is installed and in the PATH") - TCPDUMP=0 - else: - TCPDUMP=1 - del(f) - - - -try: - from Crypto.Cipher import ARC4 -except ImportError: - log_loading.info("Can't find Crypto python lib. Won't be able to decrypt WEP") - - -# Workarround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470 -try: - socket.inet_aton("255.255.255.255") -except socket.error: - def inet_aton(x): - if x == "255.255.255.255": - return "\xff"*4 - else: - return socket.inet_aton(x) -else: - inet_aton = socket.inet_aton - -inet_ntoa = socket.inet_ntoa -try: - inet_ntop = socket.inet_ntop - inet_pton = socket.inet_pton -except AttributeError: - log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present") - - -if SOLARIS: - # GRE is missing on Solaris - socket.IPPROTO_GRE = 47 - -############################### -## Direct Access dictionnary ## -############################### - -def fixname(x): - if x and x[0] in "0123456789": - x = "n_"+x - return x.translate("________________________________________________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz_____________________________________________________________________________________________________________________________________") - - -class DADict_Exception(Scapy_Exception): - pass - -class DADict: - def __init__(self, _name="DADict", **kargs): - self._name=_name - self.__dict__.update(kargs) - def fixname(self,val): - return fixname(val) - def __contains__(self, val): - return val in self.__dict__ - def __getitem__(self, attr): - return getattr(self, attr) - def __setitem__(self, attr, val): - return setattr(self, self.fixname(attr), val) - def __iter__(self): - return iter(map(lambda (x,y):y,filter(lambda (x,y):x and x[0]!="_", self.__dict__.items()))) - def _show(self): - for k in self.__dict__.keys(): - if k and k[0] != "_": - print "%10s = %r" % (k,getattr(self,k)) - def __repr__(self): - return "<%s/ %s>" % (self._name," ".join(filter(lambda x:x and x[0]!="_",self.__dict__.keys()))) - - def _branch(self, br, uniq=0): - if uniq and br._name in self: - raise DADict_Exception("DADict: [%s] already branched in [%s]" % (br._name, self._name)) - self[br._name] = br - - def _my_find(self, *args, **kargs): - if args and self._name not in args: - return False - for k in kargs: - if k not in self or self[k] != kargs[k]: - return False - return True - - def _find(self, *args, **kargs): - return self._recurs_find((), *args, **kargs) - def _recurs_find(self, path, *args, **kargs): - if self in path: - return None - if self._my_find(*args, **kargs): - return self - for o in self: - if isinstance(o, DADict): - p = o._recurs_find(path+(self,), *args, **kargs) - if p is not None: - return p - return None - def _find_all(self, *args, **kargs): - return self._recurs_find_all((), *args, **kargs) - def _recurs_find_all(self, path, *args, **kargs): - r = [] - if self in path: - return r - if self._my_find(*args, **kargs): - r.append(self) - for o in self: - if isinstance(o, DADict): - p = o._recurs_find_all(path+(self,), *args, **kargs) - r += p - return r - def keys(self): - return filter(lambda x:x and x[0]!="_", self.__dict__.keys()) - - - -############ -## Consts ## -############ - -ETHER_ANY = "\x00"*6 -ETHER_BROADCAST = "\xff"*6 - -ETH_P_ALL = 3 -ETH_P_IP = 0x800 -ETH_P_ARP = 0x806 - -# From net/if_arp.h -ARPHDR_ETHER = 1 -ARPHDR_METRICOM = 23 -ARPHDR_PPP = 512 -ARPHDR_LOOPBACK = 772 -ARPHDR_TUN = 65534 - -# From bits/ioctls.h -SIOCGIFHWADDR = 0x8927 # Get hardware address -SIOCGIFADDR = 0x8915 # get PA address -SIOCGIFNETMASK = 0x891b # get network PA mask -SIOCGIFNAME = 0x8910 # get iface name -SIOCSIFLINK = 0x8911 # set iface channel -SIOCGIFCONF = 0x8912 # get iface list -SIOCGIFFLAGS = 0x8913 # get flags -SIOCSIFFLAGS = 0x8914 # set flags -SIOCGIFINDEX = 0x8933 # name -> if_index mapping -SIOCGIFCOUNT = 0x8938 # get number of devices -SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval) - - -# From if.h -IFF_UP = 0x1 # Interface is up. -IFF_BROADCAST = 0x2 # Broadcast address valid. -IFF_DEBUG = 0x4 # Turn on debugging. -IFF_LOOPBACK = 0x8 # Is a loopback net. -IFF_POINTOPOINT = 0x10 # Interface is point-to-point link. -IFF_NOTRAILERS = 0x20 # Avoid use of trailers. -IFF_RUNNING = 0x40 # Resources allocated. -IFF_NOARP = 0x80 # No address resolution protocol. -IFF_PROMISC = 0x100 # Receive all packets. - - - -# From netpacket/packet.h -PACKET_ADD_MEMBERSHIP = 1 -PACKET_DROP_MEMBERSHIP = 2 -PACKET_RECV_OUTPUT = 3 -PACKET_RX_RING = 5 -PACKET_STATISTICS = 6 -PACKET_MR_MULTICAST = 0 -PACKET_MR_PROMISC = 1 -PACKET_MR_ALLMULTI = 2 - - -# From bits/socket.h -SOL_PACKET = 263 -# From asm/socket.h -SO_ATTACH_FILTER = 26 -SOL_SOCKET = 1 - -# From net/route.h -RTF_UP = 0x0001 # Route usable -RTF_REJECT = 0x0200 - -# From BSD net/bpf.h -#BIOCIMMEDIATE=0x80044270 -BIOCIMMEDIATE=-2147204496 - -MTU = 1600 - - -# file parsing to get some values : - -def load_protocols(filename): - spaces = re.compile("[ \t]+|\n") - dct = DADict(_name=filename) - try: - for l in open(filename): - try: - shrp = l.find("#") - if shrp >= 0: - l = l[:shrp] - l = l.strip() - if not l: - continue - lt = tuple(re.split(spaces, l)) - if len(lt) < 2 or not lt[0]: - continue - dct[lt[0]] = int(lt[1]) - except Exception,e: - log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) - except IOError: - log_loading.info("Can't open /etc/protocols file") - return dct - -IP_PROTOS=load_protocols("/etc/protocols") - -def load_ethertypes(filename): - spaces = re.compile("[ \t]+|\n") - dct = DADict(_name=filename) - try: - f=open(filename) - for l in f: - try: - shrp = l.find("#") - if shrp >= 0: - l = l[:shrp] - l = l.strip() - if not l: - continue - lt = tuple(re.split(spaces, l)) - if len(lt) < 2 or not lt[0]: - continue - dct[lt[0]] = int(lt[1], 16) - except Exception,e: - log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) - f.close() - except IOError,msg: - pass - return dct - -ETHER_TYPES=load_ethertypes("/etc/ethertypes") - -def load_services(filename): - spaces = re.compile("[ \t]+|\n") - tdct=DADict(_name="%s-tcp"%filename) - udct=DADict(_name="%s-udp"%filename) - try: - f=open(filename) - for l in f: - try: - shrp = l.find("#") - if shrp >= 0: - l = l[:shrp] - l = l.strip() - if not l: - continue - lt = tuple(re.split(spaces, l)) - if len(lt) < 2 or not lt[0]: - continue - if lt[1].endswith("/tcp"): - tdct[lt[0]] = int(lt[1].split('/')[0]) - elif lt[1].endswith("/udp"): - udct[lt[0]] = int(lt[1].split('/')[0]) - except Exception,e: - log_loading.warning("Couldn't file [%s]: line [%r] (%s)" % (filename,l,e)) - f.close() - except IOError: - log_loading.info("Can't open /etc/services file") - return tdct,udct - -TCP_SERVICES,UDP_SERVICES=load_services("/etc/services") - -class ManufDA(DADict): - def fixname(self, val): - return val - def _get_manuf_couple(self, mac): - oui = ":".join(mac.split(":")[:3]).upper() - return self.__dict__.get(oui,(mac,mac)) - def _get_manuf(self, mac): - return self._get_manuf_couple(mac)[1] - def _get_short_manuf(self, mac): - return self._get_manuf_couple(mac)[0] - def _resolve_MAC(self, mac): - oui = ":".join(mac.split(":")[:3]).upper() - if oui in self: - return ":".join([self[oui][0]]+ mac.split(":")[3:]) - return mac - - - - -def load_manuf(filename): - try: - manufdb=ManufDA(_name=filename) - for l in open(filename): - try: - l = l.strip() - if not l or l.startswith("#"): - continue - oui,shrt=l.split()[:2] - i = l.find("#") - if i < 0: - lng=shrt - else: - lng = l[i+2:] - manufdb[oui] = shrt,lng - except Exception,e: - log_loading.warning("Couldn't parse one line from [%s] [%r] (%s)" % (filename, l, e)) - except IOError: - #log_loading.warning("Couldn't open [%s] file" % filename) - pass - return manufdb - -MANUFDB = load_manuf("/usr/share/wireshark/wireshark/manuf") - - - - -########### -## Tools ## -########### - -def sane_color(x): - r="" - for i in x: - j = ord(i) - if (j < 32) or (j >= 127): - r=r+conf.color_theme.not_printable(".") - else: - r=r+i - return r - -def sane(x): - r="" - for i in x: - j = ord(i) - if (j < 32) or (j >= 127): - r=r+"." - else: - r=r+i - return r - -def lhex(x): - if type(x) in (int,long): - return hex(x) - elif type(x) is tuple: - return "(%s)" % ", ".join(map(lhex, x)) - elif type(x) is list: - return "[%s]" % ", ".join(map(lhex, x)) - else: - return x - -def hexdump(x): - x=str(x) - l = len(x) - i = 0 - while i < l: - print "%04x " % i, - for j in range(16): - if i+j < l: - print "%02X" % ord(x[i+j]), - else: - print " ", - if j%16 == 7: - print "", - print " ", - print sane_color(x[i:i+16]) - i += 16 - -def linehexdump(x, onlyasc=0, onlyhex=0): - x = str(x) - l = len(x) - if not onlyasc: - for i in range(l): - print "%02X" % ord(x[i]), - print "", - if not onlyhex: - print sane_color(x) - -def chexdump(x): - x=str(x) - print ", ".join(map(lambda x: "%#04x"%ord(x), x)) - -def hexstr(x, onlyasc=0, onlyhex=0): - s = [] - if not onlyasc: - s.append(" ".join(map(lambda x:"%02x"%ord(x), x))) - if not onlyhex: - s.append(sane(x)) - return " ".join(s) - - -def hexdiff(x,y): - x=str(x)[::-1] - y=str(y)[::-1] - SUBST=1 - INSERT=1 - d={} - d[-1,-1] = 0,(-1,-1) - for j in range(len(y)): - d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1) - for i in range(len(x)): - d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1) - - for j in range(len(y)): - for i in range(len(x)): - d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ), - ( d[i-1,j][0]+INSERT, (i-1,j) ), - ( d[i,j-1][0]+INSERT, (i,j-1) ) ) - - - backtrackx = [] - backtracky = [] - i=len(x)-1 - j=len(y)-1 - while not (i == j == -1): - i2,j2 = d[i,j][1] - backtrackx.append(x[i2+1:i+1]) - backtracky.append(y[j2+1:j+1]) - i,j = i2,j2 - - - - x = y = i = 0 - colorize = { 0: lambda x:x, - -1: conf.color_theme.left, - 1: conf.color_theme.right } - - dox=1 - doy=0 - l = len(backtrackx) - while i < l: - separate=0 - linex = backtrackx[i:i+16] - liney = backtracky[i:i+16] - xx = sum(len(k) for k in linex) - yy = sum(len(k) for k in liney) - if dox and not xx: - dox = 0 - doy = 1 - if dox and linex == liney: - doy=1 - - if dox: - xd = y - j = 0 - while not linex[j]: - j += 1 - xd -= 1 - print colorize[doy-dox]("%04x" % xd), - x += xx - line=linex - else: - print " ", - if doy: - yd = y - j = 0 - while not liney[j]: - j += 1 - yd -= 1 - print colorize[doy-dox]("%04x" % yd), - y += yy - line=liney - else: - print " ", - - print " ", - - cl = "" - for j in range(16): - if i+j < l: - if line[j]: - col = colorize[(linex[j]!=liney[j])*(doy-dox)] - print col("%02X" % ord(line[j])), - if linex[j]==liney[j]: - cl += sane_color(line[j]) - else: - cl += col(sane(line[j])) - else: - print " ", - cl += " " - else: - print " ", - if j == 7: - print "", - - - print " ",cl - - if doy or not yy: - doy=0 - dox=1 - i += 16 - else: - if yy: - dox=0 - doy=1 - else: - i += 16 - - -crc32 = zlib.crc32 - -if BIG_ENDIAN: - def checksum(pkt): - if len(pkt) % 2 == 1: - pkt += "\0" - s = sum(array.array("H", pkt)) - s = (s >> 16) + (s & 0xffff) - s += s >> 16 - s = ~s - return s & 0xffff -else: - def checksum(pkt): - if len(pkt) % 2 == 1: - pkt += "\0" - s = sum(array.array("H", pkt)) - s = (s >> 16) + (s & 0xffff) - s += s >> 16 - s = ~s - return (((s>>8)&0xff)|s<<8) & 0xffff - -def warning(x): - log_runtime.warning(x) - -def mac2str(mac): - return "".join(map(lambda x: chr(int(x,16)), mac.split(":"))) - -def str2mac(s): - return ("%02x:"*6)[:-1] % tuple(map(ord, s)) - -def strxor(x,y): - return "".join(map(lambda x,y:chr(ord(x)^ord(y)),x,y)) - -def atol(x): - try: - ip = inet_aton(x) - except socket.error: - ip = inet_aton(socket.gethostbyname(x)) - return struct.unpack("!I", ip)[0] -def ltoa(x): - return inet_ntoa(struct.pack("!I", x)) - -def itom(x): - return (0xffffffff00000000L>>x)&0xffffffffL - -def do_graph(graph,prog=None,format="svg",target=None, type=None,string=None,options=None): - """do_graph(graph, prog=conf.prog.dot, format="svg", - target="| conf.prog.display", options=None, [string=1]): - string: if not None, simply return the graph string - graph: GraphViz graph description - format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option - target: filename or redirect. Defaults pipe to Imagemagick's display program - prog: which graphviz program to use - options: options to be passed to prog""" - - - if string: - return graph - if type is not None: - format=type - if prog is None: - prog = conf.prog.dot - if target is None: - target = "| %s" % conf.prog.display - if format is not None: - format = "-T %s" % format - w,r = os.popen2("%s %s %s %s" % (prog,options or "", format or "", target)) - w.write(graph) - w.close() - -_TEX_TR = { - "{":"{\\tt\\char123}", - "}":"{\\tt\\char125}", - "\\":"{\\tt\\char92}", - "^":"\\^{}", - "$":"\\$", - "#":"\\#", - "~":"\\~", - "_":"\\_", - "&":"\\&", - "%":"\\%", - "|":"{\\tt\\char124}", - "~":"{\\tt\\char126}", - "<":"{\\tt\\char60}", - ">":"{\\tt\\char62}", - } - -def tex_escape(x): - s = "" - for c in x: - s += _TEX_TR.get(c,c) - return s - -def colgen(*lstcol,**kargs): - """Returns a generator that mixes provided quantities forever - trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default""" - if len(lstcol) < 2: - lstcol *= 2 - trans = kargs.get("trans", lambda x,y,z: (x,y,z)) - while 1: - for i in range(len(lstcol)): - for j in range(len(lstcol)): - for k in range(len(lstcol)): - if i != j or j != k or k != i: - yield trans(lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)]) - -def incremental_label(label="tag%05i", start=0): - while True: - yield label % start - start += 1 - -######################### -#### Enum management #### -######################### - -class EnumElement: - _value=None - def __init__(self, key, value): - self._key = key - self._value = value - def __repr__(self): - return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value) - def __getattr__(self, attr): - return getattr(self._value, attr) - def __str__(self): - return self._key - def __eq__(self, other): - return self._value == int(other) - - -class Enum_metaclass(type): - element_class = EnumElement - def __new__(cls, name, bases, dct): - rdict={} - for k,v in dct.iteritems(): - if type(v) is int: - v = cls.element_class(k,v) - dct[k] = v - rdict[v] = k - dct["__rdict__"] = rdict - return super(Enum_metaclass, cls).__new__(cls, name, bases, dct) - def __getitem__(self, attr): - return self.__rdict__[attr] - def __contains__(self, val): - return val in self.__rdict__ - def get(self, attr, val=None): - return self._rdict__.get(attr, val) - def __repr__(self): - return "<%s>" % self.__dict__.get("name", self.__name__) - - - - -############################## -## Session saving/restoring ## -############################## - - -def save_session(fname, session=None, pickleProto=-1): - if session is None: - session = scapy_session - - to_be_saved = session.copy() - - if to_be_saved.has_key("__builtins__"): - del(to_be_saved["__builtins__"]) - - for k in to_be_saved.keys(): - if type(to_be_saved[k]) in [types.TypeType, types.ClassType, types.ModuleType]: - log_interactive.error("[%s] (%s) can't be saved." % (k, type(to_be_saved[k]))) - del(to_be_saved[k]) - - try: - os.rename(fname, fname+".bak") - except OSError: - pass - f=gzip.open(fname,"wb") - cPickle.dump(to_be_saved, f, pickleProto) - f.close() - -def load_session(fname): - try: - s = cPickle.load(gzip.open(fname,"rb")) - except IOError: - s = cPickle.load(open(fname,"rb")) - scapy_session.clear() - scapy_session.update(s) - -def update_session(fname): - try: - s = cPickle.load(gzip.open(fname,"rb")) - except IOError: - s = cPickle.load(open(fname,"rb")) - scapy_session.update(s) - - -def export_object(obj): - print base64.encodestring(gzip.zlib.compress(cPickle.dumps(obj,2),9)) - -def import_object(obj=None): - if obj is None: - obj = sys.stdin.read() - return cPickle.loads(gzip.zlib.decompress(base64.decodestring(obj.strip()))) - - -def save_object(fname, obj): - cPickle.dump(obj,gzip.open(fname,"wb")) - -def load_object(fname): - return cPickle.load(gzip.open(fname,"rb")) - - -###################### -## Extension system ## -###################### - - -def load_extension(filename): - import imp - paths = conf.extensions_paths - if type(paths) is not list: - paths = [paths] - - name = os.path.realpath(os.path.expanduser(filename)) - thepath = os.path.dirname(name) - thename = os.path.basename(name) - if thename.endswith(".py"): - thename = thename[:-3] - - paths.insert(0, thepath) - cwd=syspath=None - try: - cwd = os.getcwd() - os.chdir(thepath) - syspath = sys.path[:] - sys.path += paths - try: - extf = imp.find_module(thename, paths) - except ImportError: - log_runtime.error("Module [%s] not found. Check conf.extensions_paths ?" % filename) - else: - ext = imp.load_module(thename, *extf) - import __builtin__ - __builtin__.__dict__.update(ext.__dict__) - finally: - if syspath: - sys.path=syspath - if cwd: - os.chdir(cwd) - - - -################# -## Debug class ## -################# - -class debug: - recv=[] - sent=[] - match=[] - - -#################### -## IP Tools class ## -#################### - -class IPTools: - """Add more powers to a class that have a "src" attribute.""" - def whois(self): - os.system("whois %s" % self.src) - def ottl(self): - t = [32,64,128,255]+[self.ttl] - t.sort() - return t[t.index(self.ttl)+1] - def hops(self): - return self.ottl()-self.ttl-1 - - -############################## -## Routing/Interfaces stuff ## -############################## - -class Route: - def __init__(self): - self.resync() - self.s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.cache = {} - - def invalidate_cache(self): - self.cache = {} - - def resync(self): - self.invalidate_cache() - self.routes = read_routes() - - def __repr__(self): - rt = "Network Netmask Gateway Iface Output IP\n" - for net,msk,gw,iface,addr in self.routes: - rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net), - ltoa(msk), - gw, - iface, - addr) - return rt - - def make_route(self, host=None, net=None, gw=None, dev=None): - if host is not None: - thenet,msk = host,32 - elif net is not None: - thenet,msk = net.split("/") - msk = int(msk) - else: - raise Scapy_Exception("make_route: Incorrect parameters. You should specify a host or a net") - if gw is None: - gw="0.0.0.0" - if dev is None: - if gw: - nhop = gw - else: - nhop = thenet - dev,ifaddr,x = self.route(nhop) - else: - ifaddr = get_if_addr(dev) - return (atol(thenet), itom(msk), gw, dev, ifaddr) - - def add(self, *args, **kargs): - """Ex: - add(net="192.168.1.0/24",gw="1.2.3.4") - """ - self.invalidate_cache() - self.routes.append(self.make_route(*args,**kargs)) - - - def delt(self, *args, **kargs): - """delt(host|net, gw|dev)""" - self.invalidate_cache() - route = self.make_route(*args,**kargs) - try: - i=self.routes.index(route) - del(self.routes[i]) - except ValueError: - warning("no matching route found") - - def ifchange(self, iff, addr): - self.invalidate_cache() - the_addr,the_msk = (addr.split("/")+["32"])[:2] - the_msk = itom(int(the_msk)) - the_rawaddr = atol(the_addr) - the_net = the_rawaddr & the_msk - - - for i in range(len(self.routes)): - net,msk,gw,iface,addr = self.routes[i] - if iface != iff: - continue - if gw == '0.0.0.0': - self.routes[i] = (the_net,the_msk,gw,iface,the_addr) - else: - self.routes[i] = (net,msk,gw,iface,the_addr) - for i in arp_cache.keys(): - del(arp_cache[i]) - - - - def ifdel(self, iff): - self.invalidate_cache() - new_routes=[] - for rt in self.routes: - if rt[3] != iff: - new_routes.append(rt) - self.routes=new_routes - - def ifadd(self, iff, addr): - self.invalidate_cache() - the_addr,the_msk = (addr.split("/")+["32"])[:2] - the_msk = itom(int(the_msk)) - the_rawaddr = atol(the_addr) - the_net = the_rawaddr & the_msk - self.routes.append((the_net,the_msk,'0.0.0.0',iff,the_addr)) - - - def route(self,dest,verbose=None): - if dest in self.cache: - return self.cache[dest] - if verbose is None: - verbose=conf.verb - # Transform "192.168.*.1-5" to one IP of the set - dst = dest.split("/")[0] - dst = dst.replace("*","0") - while 1: - l = dst.find("-") - if l < 0: - break - m = (dst[l:]+".").find(".") - dst = dst[:l]+dst[l+m:] - - - dst = atol(dst) - pathes=[] - for d,m,gw,i,a in self.routes: - aa = atol(a) - if aa == dst: - pathes.append((0xffffffffL,("lo",a,"0.0.0.0"))) - if (dst & m) == (d & m): - pathes.append((m,(i,a,gw))) - if not pathes: - if verbose: - warning("No route found (no default route?)") - return "lo","0.0.0.0","0.0.0.0" #XXX linux specific! - # Choose the more specific route (greatest netmask). - # XXX: we don't care about metrics - pathes.sort() - ret = pathes[-1][1] - self.cache[dest] = ret - return ret - - def get_if_bcast(self, iff): - for net, msk, gw, iface, addr in self.routes: - if (iff == iface and net != 0L): - bcast = atol(addr)|(~msk&0xffffffffL); # FIXME: check error in atol() - return ltoa(bcast); - warning("No broadcast address found for iface %s\n" % iff); - -if DNET: - def get_if_raw_hwaddr(iff): - if iff[:2] == "lo": - return (772, '\x00'*6) - try: - l = dnet.intf().get(iff) - l = l["link_addr"] - except: - raise Scapy_Exception("Error in attempting to get hw address for interface [%s]" % iff) - return l.type,l.data - def get_if_raw_addr(ifname): - i = dnet.intf() - return i.get(ifname)["addr"].data -else: - def get_if_raw_hwaddr(iff): - return struct.unpack("16xh6s8x",get_if(iff,SIOCGIFHWADDR)) - - def get_if_raw_addr(iff): - try: - return get_if(iff, SIOCGIFADDR)[20:24] - except IOError: - return "\0\0\0\0" - - -if PCAP: - def get_if_list(): - # remove 'any' interface - return map(lambda x:x[0],filter(lambda x:x[1] is None,pcap.findalldevs())) - def get_working_if(): - try: - return pcap.lookupdev() - except Exception: - return 'lo' - - def attach_filter(s, filter): - warning("attach_filter() should not be called in PCAP mode") - def set_promisc(s,iff,val=1): - warning("set_promisc() should not be called in DNET/PCAP mode") - -else: - def get_if_list(): - f=open("/proc/net/dev","r") - lst = [] - f.readline() - f.readline() - for l in f: - lst.append(l.split(":")[0].strip()) - return lst - def get_working_if(): - for i in get_if_list(): - if i == 'lo': - continue - ifflags = struct.unpack("16xH14x",get_if(i,SIOCGIFFLAGS))[0] - if ifflags & IFF_UP: - return i - return "lo" - def attach_filter(s, filter): - # XXX We generate the filter on the interface conf.iface - # because tcpdump open the "any" interface and ppp interfaces - # in cooked mode. As we use them in raw mode, the filter will not - # work... one solution could be to use "any" interface and translate - # the filter from cooked mode to raw mode - # mode - if not TCPDUMP: - return - try: - f = os.popen("%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump,conf.iface,filter)) - except OSError,msg: - log_interactive.warning("Failed to execute tcpdump: (%s)") - return - lines = f.readlines() - if f.close(): - raise Scapy_Exception("Filter parse error") - nb = int(lines[0]) - bpf = "" - for l in lines[1:]: - bpf += struct.pack("HBBI",*map(long,l.split())) - - # XXX. Argl! We need to give the kernel a pointer on the BPF, - # python object header seems to be 20 bytes. 36 bytes for x86 64bits arch. - if X86_64: - bpfh = struct.pack("HL", nb, id(bpf)+36) - else: - bpfh = struct.pack("HI", nb, id(bpf)+20) - s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh) - - def set_promisc(s,iff,val=1): - mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, "") - if val: - cmd = PACKET_ADD_MEMBERSHIP - else: - cmd = PACKET_DROP_MEMBERSHIP - s.setsockopt(SOL_PACKET, cmd, mreq) - - -if not LINUX: - - def new_read_routes(): - - rtlst = [] - def addrt(rt,lst): - dst,gw = rt - lst.append(rt) - - r = dnet.route() - print r.loop(addrt, rtlst) - return rtlst - - def read_routes(): - if SOLARIS: - f=os.popen("netstat -rvn") # -f inet - elif FREEBSD: - f=os.popen("netstat -rnW") # -W to handle long interface names - else: - f=os.popen("netstat -rn") # -f inet - ok = 0 - mtu_present = False - routes = [] - for l in f.readlines(): - if not l: - break - l = l.strip() - if l.find("----") >= 0: # a separation line - continue - if l.find("Destination") >= 0: - ok = 1 - if l.find("Mtu") >= 0: - mtu_present = True - continue - if ok == 0: - continue - if not l: - break - if SOLARIS: - dest,mask,gw,netif,mxfrg,rtt,ref,flg = l.split()[:8] - else: - if mtu_present: - dest,gw,flg,ref,use,mtu,netif = l.split()[:7] - else: - dest,gw,flg,ref,use,netif = l.split()[:6] - if flg.find("Lc") >= 0: - continue - if dest == "default": - dest = 0L - netmask = 0L - else: - if SOLARIS: - netmask = atol(mask) - elif "/" in dest: - dest,netmask = dest.split("/") - netmask = itom(int(netmask)) - else: - netmask = itom((dest.count(".") + 1) * 8) - dest += ".0"*(3-dest.count(".")) - dest = atol(dest) - if not "G" in flg: - gw = '0.0.0.0' - ifaddr = get_if_addr(netif) - routes.append((dest,netmask,gw,netif,ifaddr)) - f.close() - return routes - - def read_interfaces(): - i = dnet.intf() - ifflist = {} - def addif(iff,lst): - if not iff.has_key("addr"): - return - if not iff.has_key("link_addr"): - return - rawip = iff["addr"].data - ip = inet_ntoa(rawip) - rawll = iff["link_addr"].data - ll = str2mac(rawll) - lst[iff["name"]] = (rawll,ll,rawip,ip) - i.loop(addif, ifflist) - return ifflist - - -else: - - def read_routes(): - f=open("/proc/net/route","r") - routes = [] - s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x","lo")) - addrfamily = struct.unpack("h",ifreq[16:18])[0] - if addrfamily == socket.AF_INET: - ifreq2 = ioctl(s, SIOCGIFNETMASK,struct.pack("16s16x","lo")) - msk = socket.ntohl(struct.unpack("I",ifreq2[20:24])[0]) - dst = socket.ntohl(struct.unpack("I",ifreq[20:24])[0]) & msk - ifaddr = inet_ntoa(ifreq[20:24]) - routes.append((dst, msk, "0.0.0.0", "lo", ifaddr)) - else: - warning("Interface lo: unkown address family (%i)"% addrfamily) - - for l in f.readlines()[1:]: - iff,dst,gw,flags,x,x,x,msk,x,x,x = l.split() - flags = int(flags,16) - if flags & RTF_UP == 0: - continue - if flags & RTF_REJECT: - continue - try: - ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",iff)) - except IOError: # interface is present in routing tables but does not have any assigned IP - ifaddr="0.0.0.0" - else: - addrfamily = struct.unpack("h",ifreq[16:18])[0] - if addrfamily == socket.AF_INET: - ifaddr = inet_ntoa(ifreq[20:24]) - else: - warning("Interface %s: unkown address family (%i)"%(iff, addrfamily)) - continue - routes.append((socket.htonl(long(dst,16))&0xffffffffL, - socket.htonl(long(msk,16))&0xffffffffL, - inet_ntoa(struct.pack("I",long(gw,16))), - iff, ifaddr)) - - f.close() - return routes - - def get_if(iff,cmd): - s=socket.socket() - ifreq = ioctl(s, cmd, struct.pack("16s16x",iff)) - s.close() - return ifreq - - - def get_if_index(iff): - return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0]) - - def get_last_packet_timestamp(sock): - ts = ioctl(sock, SIOCGSTAMP, "12345678") - s,us = struct.unpack("II",ts) - return s+us/1000000.0 - - -def get_if_addr(iff): - return inet_ntoa(get_if_raw_addr(iff)) - -def get_if_hwaddr(iff): - addrfamily, mac = get_if_raw_hwaddr(iff) - if addrfamily in [ARPHDR_ETHER,ARPHDR_LOOPBACK]: - return str2mac(mac) - else: - raise Scapy_Exception("Unsupported address family (%i) for interface [%s]" % (addrfamily,iff)) - - - -##################### -## ARP cache stuff ## -##################### - -ARPTIMEOUT=120 - -# XXX Fill arp_cache with /etc/ether and arp cache -arp_cache={} - -if 0 and DNET: ## XXX Can't use this because it does not resolve IPs not in cache - dnet_arp_object = dnet.arp() - def getmacbyip(ip, chainCC=0): - tmp = map(ord, inet_aton(ip)) - if (tmp[0] & 0xf0) == 0xe0: # mcast @ - return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) - iff,a,gw = conf.route.route(ip) - if iff == "lo": - return "ff:ff:ff:ff:ff:ff" - if gw != "0.0.0.0": - ip = gw - res = dnet_arp_object.get(dnet.addr(ip)) - if res is None: - return None - else: - return res.ntoa() -else: - def getmacbyip(ip, chainCC=0): - tmp = map(ord, inet_aton(ip)) - if (tmp[0] & 0xf0) == 0xe0: # mcast @ - return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) - iff,a,gw = conf.route.route(ip) - if ( (iff == "lo") or (ip == conf.route.get_if_bcast(iff)) ): - return "ff:ff:ff:ff:ff:ff" - if gw != "0.0.0.0": - ip = gw - - if arp_cache.has_key(ip): - mac, timeout = arp_cache[ip] - if not timeout or (time.time()-timeout < ARPTIMEOUT): - return mac - - res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), - type=ETH_P_ARP, - iface = iff, - timeout=2, - verbose=0, - chainCC=chainCC, - nofilter=1) - if res is not None: - mac = res.payload.hwsrc - arp_cache[ip] = (mac,time.time()) - return mac - return None - - -#################### -## Random numbers ## -#################### - -def randseq(inf, sup, seed=None, forever=1, renewkeys=0): - """iterate through a sequence in random order. - When all the values have been drawn, if forever=1, the drawing is done again. - If renewkeys=0, the draw will be in the same order, guaranteeing that the same - number will be drawn in not less than the number of integers of the sequence""" - rnd = random.Random(seed) - sbox_size = 256 - - top = sup-inf+1 - - n=0 - while (1<<n) < top: - n += 1 - - fs = min(3,(n+1)/2) - fsmask = 2**fs-1 - rounds = max(n,3) - turns = 0 - - while 1: - if turns == 0 or renewkeys: - sbox = [rnd.randint(0,fsmask) for k in xrange(sbox_size)] - turns += 1 - i = 0 - while i < 2**n: - ct = i - i += 1 - for k in range(rounds): # Unbalanced Feistel Network - lsb = ct & fsmask - ct >>= fs - lsb ^= sbox[ct%sbox_size] - ct |= lsb << (n-fs) - - if ct < top: - yield inf+ct - if not forever: - break - - -class VolatileValue: - def __repr__(self): - return "<%s>" % self.__class__.__name__ - def __getattr__(self, attr): - if attr == "__setstate__": - raise AttributeError(attr) - return getattr(self._fix(),attr) - def _fix(self): - return None - - -class RandField(VolatileValue): - pass - - -class RandNum(RandField): - min = 0 - max = 0 - def __init__(self, min, max): - self.seq = randseq(min,max) - def _fix(self): - return self.seq.next() - -class RandNumGamma(RandField): - def __init__(self, alpha, beta): - self.alpha = alpha - self.beta = beta - def _fix(self): - return int(round(random.gammavariate(self.alpha, self.beta))) - -class RandNumGauss(RandField): - def __init__(self, mu, sigma): - self.mu = mu - self.sigma = sigma - def _fix(self): - return int(round(random.gauss(self.mu, self.sigma))) - -class RandNumExpo(RandField): - def __init__(self, lambd): - self.lambd = lambd - def _fix(self): - return int(round(random.expovariate(self.lambd))) - -class RandByte(RandNum): - def __init__(self): - RandNum.__init__(self, 0, 2L**8-1) - -class RandShort(RandNum): - def __init__(self): - RandNum.__init__(self, 0, 2L**16-1) - -class RandInt(RandNum): - def __init__(self): - RandNum.__init__(self, 0, 2L**32-1) - -class RandSInt(RandNum): - def __init__(self): - RandNum.__init__(self, -2L**31, 2L**31-1) - -class RandLong(RandNum): - def __init__(self): - RandNum.__init__(self, 0, 2L**64-1) - -class RandSLong(RandNum): - def __init__(self): - RandNum.__init__(self, -2L**63, 2L**63-1) - -class RandChoice(RandField): - def __init__(self, *args): - if not args: - raise TypeError("RandChoice needs at least one choice") - self._choice = args - def _fix(self): - return random.choice(self._choice) - -class RandString(RandField): - def __init__(self, size, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"): - self.chars = chars - self.size = size - def _fix(self): - s = "" - for i in range(self.size): - s += random.choice(self.chars) - return s - -class RandBin(RandString): - def __init__(self, size): - RandString.__init__(self, size, "".join(map(chr,range(256)))) - - -class RandTermString(RandString): - def __init__(self, size, term): - RandString.__init__(self, size, "".join(map(chr,range(1,256)))) - self.term = term - def _fix(self): - return RandString._fix(self)+self.term - - - -class RandIP(RandString): - def __init__(self, iptemplate="0.0.0.0/0"): - self.ip = Net(iptemplate) - def _fix(self): - return self.ip.choice() - -class RandMAC(RandString): - def __init__(self, template="*"): - template += ":*:*:*:*:*" - template = template.split(":") - self.mac = () - for i in range(6): - if template[i] == "*": - v = RandByte() - elif "-" in template[i]: - x,y = template[i].split("-") - v = RandNum(int(x,16), int(y,16)) - else: - v = int(template[i],16) - self.mac += (v,) - def _fix(self): - return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac - - -class RandOID(RandString): - def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)): - self.ori_fmt = fmt - if fmt is not None: - fmt = fmt.split(".") - for i in range(len(fmt)): - if "-" in fmt[i]: - fmt[i] = tuple(map(int, fmt[i].split("-"))) - self.fmt = fmt - self.depth = depth - self.idnum = idnum - def __repr__(self): - if self.ori_fmt is None: - return "<%s>" % self.__class__.__name__ - else: - return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt) - def _fix(self): - if self.fmt is None: - return ".".join(map(str, [self.idnum for i in xrange(1+self.depth)])) - else: - oid = [] - for i in self.fmt: - if i == "*": - oid.append(str(self.idnum)) - elif i == "**": - oid += map(str, [self.idnum for i in xrange(1+self.depth)]) - elif type(i) is tuple: - oid.append(str(random.randrange(*i))) - else: - oid.append(i) - return ".".join(oid) - - - -class RandASN1Object(RandField): - def __init__(self, objlist=None): - if objlist is None: - objlist = map(lambda x:x._asn1_obj, - filter(lambda x:hasattr(x,"_asn1_obj"), ASN1_Class_UNIVERSAL.__rdict__.values())) - self.objlist = objlist - self.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - def _fix(self, n=0): - o = random.choice(self.objlist) - if issubclass(o, ASN1_INTEGER): - return o(int(random.gauss(0,1000))) - elif issubclass(o, ASN1_STRING): - z = int(random.expovariate(0.05)+1) - return o("".join([random.choice(self.chars) for i in range(z)])) - elif issubclass(o, ASN1_SEQUENCE) and (n < 10): - z = int(random.expovariate(0.08)+1) - return o(map(lambda x:x._fix(n+1), [self.__class__(objlist=self.objlist)]*z)) - return ASN1_INTEGER(int(random.gauss(0,1000))) - -class RandDHCPOptions(RandField): - def __init__(self, size=None, rndstr=None): - if size is None: - size = RandNumExpo(0.05) - self.size = size - if rndstr is None: - rndstr = RandBin(RandNum(0,255)) - self.rndstr=rndstr - self._opts = DHCPOptions.values() - self._opts.remove("pad") - self._opts.remove("end") - def _fix(self): - op = [] - for k in range(self.size): - o = random.choice(self._opts) - if type(o) is str: - op.append((o,self.rndstr*1)) - else: - op.append((o.name, o.randval()._fix())) - return op - - -# Automatic timestamp - -class AutoTime(VolatileValue): - def __init__(self, base=None): - if base == None: - self.diff = 0 - else: - self.diff = time.time()-base - def _fix(self): - return time.time()-self.diff - -class IntAutoTime(AutoTime): - def _fix(self): - return int(time.time()-self.diff) - - -class ZuluTime(AutoTime): - def __init__(self, diff=None): - self.diff=diff - def _fix(self): - return time.strftime("%y%m%d%H%M%SZ",time.gmtime(time.time()+self.diff)) - - -class DelayedEval(VolatileValue): - """ Exemple of usage: DelayedEval("time.time()") """ - def __init__(self, expr): - self.expr = expr - def _fix(self): - return eval(self.expr) - - -class IncrementalValue(VolatileValue): - def __init__(self, start=0, step=1, restart=-1): - self.start = self.val = start - self.step = step - self.restart = restart - def _fix(self): - v = self.val - if self.val == self.restart : - self.val = self.start - else: - self.val += self.step - return v - -def corrupt_bytes(s, p=0.01, n=None): - s = array.array("B",str(s)) - l = len(s) - if n is None: - n = max(1,int(l*p)) - for i in random.sample(xrange(l), n): - s[i] = random.randint(0,255) - return s.tostring() - -def corrupt_bits(s, p=0.01, n=None): - s = array.array("B",str(s)) - l = len(s)*8 - if n is None: - n = max(1,int(l*p)) - for i in random.sample(xrange(l), n): - s[i/8] ^= 1 << (i%8) - return s.tostring() - - -class CorruptedBytes(VolatileValue): - def __init__(self, s, p=0.01, n=None): - self.s = s - self.p = p - self.n = n - def _fix(self): - return corrupt_bytes(self.s, self.p, self.n) - -class CorruptedBits(CorruptedBytes): - def _fix(self): - return corrupt_bits(self.s, self.p, self.n) - -############## -#### ASN1 #### -############## - -class ASN1_Error(Exception): - pass - -class ASN1_Encoding_Error(ASN1_Error): - pass - -class ASN1_Decoding_Error(ASN1_Error): - pass - -class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error): - pass - - - -class ASN1Codec(EnumElement): - def register_stem(cls, stem): - cls._stem = stem - def dec(cls, s, context=None): - return cls._stem.dec(s, context=context) - def safedec(cls, s, context=None): - return cls._stem.safedec(s, context=context) - def get_stem(cls): - return cls.stem - - -class ASN1_Codecs_metaclass(Enum_metaclass): - element_class = ASN1Codec - -class ASN1_Codecs: - __metaclass__ = ASN1_Codecs_metaclass - BER = 1 - DER = 2 - PER = 3 - CER = 4 - LWER = 5 - BACnet = 6 - OER = 7 - SER = 8 - XER = 9 - -class ASN1Tag(EnumElement): - def __init__(self, key, value, context=None, codec=None): - EnumElement.__init__(self, key, value) - self._context = context - if codec == None: - codec = {} - self._codec = codec - def clone(self): # /!\ not a real deep copy. self.codec is shared - return self.__class__(self._key, self._value, self._context, self._codec) - def register_asn1_object(self, asn1obj): - self._asn1_obj = asn1obj - def asn1_object(self, val): - if hasattr(self,"_asn1_obj"): - return self._asn1_obj(val) - raise ASN1_Error("%r does not have any assigned ASN1 object" % self) - def register(self, codecnum, codec): - self._codec[codecnum] = codec - def get_codec(self, codec): - try: - c = self._codec[codec] - except KeyError,msg: - raise ASN1_Error("Codec %r not found for tag %r" % (codec, self)) - return c - -class ASN1_Class_metaclass(Enum_metaclass): - element_class = ASN1Tag - def __new__(cls, name, bases, dct): # XXX factorise a bit with Enum_metaclass.__new__() - for b in bases: - for k,v in b.__dict__.iteritems(): - if k not in dct and isinstance(v,ASN1Tag): - dct[k] = v.clone() - - rdict = {} - for k,v in dct.iteritems(): - if type(v) is int: - v = ASN1Tag(k,v) - dct[k] = v - rdict[v] = v - elif isinstance(v, ASN1Tag): - rdict[v] = v - dct["__rdict__"] = rdict - - cls = type.__new__(cls, name, bases, dct) - for v in cls.__dict__.values(): - if isinstance(v, ASN1Tag): - v.context = cls # overwrite ASN1Tag contexts, even cloned ones - return cls - - -class ASN1_Class: - __metaclass__ = ASN1_Class_metaclass - -class ASN1_Class_UNIVERSAL(ASN1_Class): - name = "UNIVERSAL" - ERROR = -3 - RAW = -2 - NONE = -1 - ANY = 0 - BOOLEAN = 1 - INTEGER = 2 - BIT_STRING = 3 - STRING = 4 - NULL = 5 - OID = 6 - OBJECT_DESCRIPTOR = 7 - EXTERNAL = 8 - REAL = 9 - ENUMERATED = 10 - EMBEDDED_PDF = 11 - UTF8_STRING = 12 - RELATIVE_OID = 13 - SEQUENCE = 0x30#XXX 16 ?? - SET = 0x31 #XXX 17 ?? - NUMERIC_STRING = 18 - PRINTABLE_STRING = 19 - T61_STRING = 20 - VIDEOTEX_STRING = 21 - IA5_STRING = 22 - UTC_TIME = 23 - GENERALIZED_TIME = 24 - GRAPHIC_STRING = 25 - ISO646_STRING = 26 - GENERAL_STRING = 27 - UNIVERSAL_STRING = 28 - CHAR_STRING = 29 - BMP_STRING = 30 - COUNTER32 = 0x41 - TIME_TICKS = 0x43 - -class ASN1_Object_metaclass(type): - def __new__(cls, name, bases, dct): - c = super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct) - try: - c.tag.register_asn1_object(c) - except: - warning("Error registering %r for %r" % (c.tag, c.codec)) - return c - - -class ASN1_Object: - __metaclass__ = ASN1_Object_metaclass - tag = ASN1_Class_UNIVERSAL.ANY - def __init__(self, val): - self.val = val - def enc(self, codec): - return self.tag.get_codec(codec).enc(self.val) - def __repr__(self): - return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) - def __str__(self): - return self.enc(conf.ASN1_default_codec) - def strshow(self, lvl=0): - return (" "*lvl)+repr(self)+"\n" - def show(self, lvl=0): - print self.strshow(lvl) - def __eq__(self, other): - return self.val == other - def __cmp__(self, other): - return cmp(self.val, other) - -class ASN1_DECODING_ERROR(ASN1_Object): - tag = ASN1_Class_UNIVERSAL.ERROR - def __init__(self, val, exc=None): - ASN1_Object.__init__(self, val) - self.exc = exc - def __repr__(self): - return "<%s[%r]{{%s}}>" % (self.__dict__.get("name", self.__class__.__name__), - self.val, self.exc.args[0]) - def enc(self, codec): - if isinstance(self.val, ASN1_Object): - return self.val.enc(codec) - return self.val - -class ASN1_force(ASN1_Object): - tag = ASN1_Class_UNIVERSAL.RAW - def enc(self, codec): - if isinstance(self.val, ASN1_Object): - return self.val.enc(codec) - return self.val - -class ASN1_BADTAG(ASN1_force): - pass - -class ASN1_INTEGER(ASN1_Object): - tag = ASN1_Class_UNIVERSAL.INTEGER - -class ASN1_STRING(ASN1_Object): - tag = ASN1_Class_UNIVERSAL.STRING - -class ASN1_BIT_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.BIT_STRING - -class ASN1_PRINTABLE_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING - -class ASN1_T61_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.T61_STRING - -class ASN1_IA5_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.IA5_STRING - -class ASN1_NUMERIC_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING - -class ASN1_VIDEOTEX_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING - -class ASN1_UTC_TIME(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.UTC_TIME - -class ASN1_TIME_TICKS(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.TIME_TICKS - -class ASN1_BOOLEAN(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.BOOLEAN - -class ASN1_NULL(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.NULL - -class ASN1_COUNTER32(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.COUNTER32 - -class ASN1_SEQUENCE(ASN1_Object): - tag = ASN1_Class_UNIVERSAL.SEQUENCE - def strshow(self, lvl=0): - s = (" "*lvl)+("# %s:" % self.__class__.__name__)+"\n" - for o in self.val: - s += o.strshow(lvl=lvl+1) - return s - -class ASN1_SET(ASN1_SEQUENCE): - tag = ASN1_Class_UNIVERSAL.SET - -class ASN1_OID(ASN1_Object): - tag = ASN1_Class_UNIVERSAL.OID - def __init__(self, val): - val = conf.mib._oid(val) - ASN1_Object.__init__(self, val) - def __repr__(self): - return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), conf.mib._oidname(self.val)) - - - -################## -## BER encoding ## -################## - - - -#####[ BER tools ]##### - - -class BER_Exception(Exception): - pass - -class BER_Decoding_Error(ASN1_Decoding_Error): - def __init__(self, msg, decoded=None, remaining=None): - Exception.__init__(self, msg) - self.remaining = remaining - self.decoded = decoded - def __str__(self): - s = Exception.__str__(self) - if isinstance(self.decoded, BERcodec_Object): - s+="\n### Already decoded ###\n%s" % self.decoded.strshow() - else: - s+="\n### Already decoded ###\n%r" % self.decoded - s+="\n### Remaining ###\n%r" % self.remaining - return s - -class BER_BadTag_Decoding_Error(BER_Decoding_Error, ASN1_BadTag_Decoding_Error): - pass - -def BER_len_enc(l, size=0): - if l <= 127 and size==0: - return chr(l) - s = "" - while l or size>0: - s = chr(l&0xff)+s - l >>= 8L - size -= 1 - if len(s) > 127: - raise BER_Exception("BER_len_enc: Length too long (%i) to be encoded [%r]" % (len(s),s)) - return chr(len(s)|0x80)+s -def BER_len_dec(s): - l = ord(s[0]) - if not l & 0x80: - return l,s[1:] - l &= 0x7f - if len(s) <= l: - raise BER_Decoding_Error("BER_len_dec: Got %i bytes while expecting %i" % (len(s)-1, l),remaining=s) - ll = 0L - for c in s[1:l+1]: - ll <<= 8L - ll |= ord(c) - return ll,s[l+1:] - -def BER_num_enc(l, size=1): - x=[] - while l or size>0: - x.insert(0, l & 0x7f) - if len(x) > 1: - x[0] |= 0x80 - l >>= 7 - size -= 1 - return "".join([chr(k) for k in x]) -def BER_num_dec(s): - x = 0 - for i in range(len(s)): - c = ord(s[i]) - x <<= 7 - x |= c&0x7f - if not c&0x80: - break - if c&0x80: - raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s) - return x, s[i+1:] - -#####[ BER classes ]##### - -class BERcodec_metaclass(type): - def __new__(cls, name, bases, dct): - c = super(BERcodec_metaclass, cls).__new__(cls, name, bases, dct) - try: - c.tag.register(c.codec, c) - except: - warning("Error registering %r for %r" % (c.tag, c.codec)) - return c - - -class BERcodec_Object: - __metaclass__ = BERcodec_metaclass - codec = ASN1_Codecs.BER - tag = ASN1_Class_UNIVERSAL.ANY - - @classmethod - def asn1_object(cls, val): - return cls.tag.asn1_object(val) - - @classmethod - def check_string(cls, s): - if not s: - raise BER_Decoding_Error("%s: Got empty object while expecting tag %r" % - (cls.__name__,cls.tag), remaining=s) - @classmethod - def check_type(cls, s): - cls.check_string(s) - if cls.tag != ord(s[0]): - raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" % - (cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s) - return s[1:] - @classmethod - def check_type_get_len(cls, s): - s2 = cls.check_type(s) - if not s2: - raise BER_Decoding_Error("%s: No bytes while expecting a length" % - cls.__name__, remaining=s) - return BER_len_dec(s2) - @classmethod - def check_type_check_len(cls, s): - l,s3 = cls.check_type_get_len(s) - if len(s3) < l: - raise BER_Decoding_Error("%s: Got %i bytes while expecting %i" % - (cls.__name__, len(s3), l), remaining=s) - return l,s3[:l],s3[l:] - - @classmethod - def do_dec(cls, s, context=None, safe=False): - if context is None: - context = cls.tag.context - cls.check_string(s) - p = ord(s[0]) - if p not in context: - t = s - if len(t) > 18: - t = t[:15]+"..." - raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s) - codec = context[p].get_codec(ASN1_Codecs.BER) - return codec.dec(s,context,safe) - - @classmethod - def dec(cls, s, context=None, safe=False): - if not safe: - return cls.do_dec(s, context, safe) - try: - return cls.do_dec(s, context, safe) - except BER_BadTag_Decoding_Error,e: - o,remain = BERcodec_Object.dec(e.remaining, context, safe) - return ASN1_BADTAG(o),remain - except BER_Decoding_Error, e: - return ASN1_DECODING_ERROR(s, exc=e),"" - except ASN1_Error, e: - return ASN1_DECODING_ERROR(s, exc=e),"" - - @classmethod - def safedec(cls, s, context=None): - return cls.dec(s, context, safe=True) - - - @classmethod - def enc(cls, s): - if type(s) is str: - return BERcodec_STRING.enc(s) - else: - return BERcodec_INTEGER.enc(int(s)) - - - -ASN1_Codecs.BER.register_stem(BERcodec_Object) - - -class BERcodec_INTEGER(BERcodec_Object): - tag = ASN1_Class_UNIVERSAL.INTEGER - @classmethod - def enc(cls, i): - s = [] - while 1: - s.append(i&0xff) - if -127 <= i < 0: - break - if 128 <= i <= 255: - s.append(0) - i >>= 8 - if not i: - break - s = map(chr, s) - s.append(BER_len_enc(len(s))) - s.append(chr(cls.tag)) - s.reverse() - return "".join(s) - @classmethod - def do_dec(cls, s, context=None, safe=False): - l,s,t = cls.check_type_check_len(s) - x = 0L - if s: - if ord(s[0])&0x80: # negative int - x = -1L - for c in s: - x <<= 8 - x |= ord(c) - return cls.asn1_object(x),t - - -class BERcodec_BOOLEAN(BERcodec_INTEGER): - tag = ASN1_Class_UNIVERSAL.BOOLEAN - -class BERcodec_NULL(BERcodec_INTEGER): - tag = ASN1_Class_UNIVERSAL.NULL - @classmethod - def enc(cls, i): - if i == 0: - return chr(cls.tag)+"\0" - else: - return super(cls,cls).enc(i) - -class BERcodec_STRING(BERcodec_Object): - tag = ASN1_Class_UNIVERSAL.STRING - @classmethod - def enc(cls,s): - return chr(cls.tag)+BER_len_enc(len(s))+s - @classmethod - def do_dec(cls, s, context=None, safe=False): - l,s,t = cls.check_type_check_len(s) - return cls.tag.asn1_object(s),t - -class BERcodec_BIT_STRING(BERcodec_STRING): - tag = ASN1_Class_UNIVERSAL.BIT_STRING - -class BERcodec_PRINTABLE_STRING(BERcodec_STRING): - tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING - -class BERcodec_T61_STRING (BERcodec_STRING): - tag = ASN1_Class_UNIVERSAL.T61_STRING - -class BERcodec_IA5_STRING(BERcodec_STRING): - tag = ASN1_Class_UNIVERSAL.IA5_STRING - -class BERcodec_UTC_TIME(BERcodec_STRING): - tag = ASN1_Class_UNIVERSAL.UTC_TIME - -class BERcodec_TIME_TICKS(BERcodec_INTEGER): - tag = ASN1_Class_UNIVERSAL.TIME_TICKS - -class BERcodec_COUNTER32(BERcodec_INTEGER): - tag = ASN1_Class_UNIVERSAL.COUNTER32 - -class BERcodec_SEQUENCE(BERcodec_Object): - tag = ASN1_Class_UNIVERSAL.SEQUENCE - @classmethod - def enc(cls, l): - if type(l) is not str: - l = "".join(map(lambda x: x.enc(cls.codec), l)) - return chr(cls.tag)+BER_len_enc(len(l))+l - @classmethod - def do_dec(cls, s, context=None, safe=False): - if context is None: - context = cls.tag.context - l,st = cls.check_type_get_len(s) # we may have len(s) < l - s,t = st[:l],st[l:] - obj = [] - while s: - try: - o,s = BERcodec_Object.dec(s, context, safe) - except BER_Decoding_Error, err: - err.remaining += t - if err.decoded is not None: - obj.append(err.decoded) - err.decoded = obj - raise - obj.append(o) - if len(st) < l: - raise BER_Decoding_Error("Not enough bytes to decode sequence", decoded=obj) - return cls.asn1_object(obj),t - -class BERcodec_SET(BERcodec_SEQUENCE): - tag = ASN1_Class_UNIVERSAL.SET - - -class BERcodec_OID(BERcodec_Object): - tag = ASN1_Class_UNIVERSAL.OID - - @classmethod - def enc(cls, oid): - lst = [int(x) for x in oid.strip(".").split(".")] - if len(lst) >= 2: - lst[1] += 40*lst[0] - del(lst[0]) - s = "".join([BER_num_enc(k) for k in lst]) - return chr(cls.tag)+BER_len_enc(len(s))+s - @classmethod - def do_dec(cls, s, context=None, safe=False): - l,s,t = cls.check_type_check_len(s) - lst = [] - while s: - l,s = BER_num_dec(s) - lst.append(l) - if (len(lst) > 0): - lst.insert(0,lst[0]/40) - lst[1] %= 40 - return cls.asn1_object(".".join([str(k) for k in lst])), t - - -################# -## MIB parsing ## -################# - -_mib_re_integer = re.compile("^[0-9]+$") -_mib_re_both = re.compile("^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$") -_mib_re_oiddecl = re.compile("$\s*([a-zA-Z0-9_-]+)\s+OBJECT[^:]+::=\s*\{([^\}]+)\}",re.M) -_mib_re_strings = re.compile('"[^"]*"') -_mib_re_comments = re.compile('--.*(\r|\n)') - -class MIBDict(DADict): - def _findroot(self, x): - if x.startswith("."): - x = x[1:] - if not x.endswith("."): - x += "." - max=0 - root="." - for k in self.keys(): - if x.startswith(self[k]+"."): - if max < len(self[k]): - max = len(self[k]) - root = k - return root, x[max:-1] - def _oidname(self, x): - root,remainder = self._findroot(x) - return root+remainder - def _oid(self, x): - xl = x.strip(".").split(".") - p = len(xl)-1 - while p >= 0 and _mib_re_integer.match(xl[p]): - p -= 1 - if p != 0 or xl[p] not in self: - return x - xl[p] = self[xl[p]] - return ".".join(xl[p:]) - def _make_graph(self, other_keys=[], **kargs): - nodes = [(k,self[k]) for k in self.keys()] - oids = [self[k] for k in self.keys()] - for k in other_keys: - if k not in oids: - nodes.append(self.oidname(k),k) - s = 'digraph "mib" {\n\trankdir=LR;\n\n' - for k,o in nodes: - s += '\t"%s" [ label="%s" ];\n' % (o,k) - s += "\n" - for k,o in nodes: - parent,remainder = self._findroot(o[:-1]) - remainder = remainder[1:]+o[-1] - if parent != ".": - parent = self[parent] - s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o,remainder) - s += "}\n" - do_graph(s, **kargs) - - -def mib_register(ident, value, the_mib, unresolved): - if ident in the_mib or ident in unresolved: - return ident in the_mib - resval = [] - not_resolved = 0 - for v in value: - if _mib_re_integer.match(v): - resval.append(v) - else: - v = fixname(v) - if v not in the_mib: - not_resolved = 1 - if v in the_mib: - v = the_mib[v] - elif v in unresolved: - v = unresolved[v] - if type(v) is list: - resval += v - else: - resval.append(v) - if not_resolved: - unresolved[ident] = resval - return False - else: - the_mib[ident] = resval - keys = unresolved.keys() - i = 0 - while i < len(keys): - k = keys[i] - if mib_register(k,unresolved[k], the_mib, {}): - del(unresolved[k]) - del(keys[i]) - i = 0 - else: - i += 1 - - return True - - -def load_mib(filenames): - the_mib = {'iso': ['1']} - unresolved = {} - for k in conf.mib.keys(): - mib_register(k, conf.mib[k].split("."), the_mib, unresolved) - - if type(filenames) is str: - filenames = [filenames] - for fnames in filenames: - for fname in glob(fnames): - f = open(fname) - text = f.read() - cleantext = " ".join(_mib_re_strings.split(" ".join(_mib_re_comments.split(text)))) - for m in _mib_re_oiddecl.finditer(cleantext): - ident,oid = m.groups() - ident=fixname(ident) - oid = oid.split() - for i in range(len(oid)): - m = _mib_re_both.match(oid[i]) - if m: - oid[i] = m.groups()[1] - mib_register(ident, oid, the_mib, unresolved) - - newmib = MIBDict(_name="MIB") - for k,o in the_mib.iteritems(): - newmib[k]=".".join(o) - for k,o in unresolved.iteritems(): - newmib[k]=".".join(o) - - conf.mib=newmib - - - -################ -## Generators ## -################ - -class Gen(object): - def __iter__(self): - return iter([]) - -class SetGen(Gen): - def __init__(self, set, _iterpacket=1): - self._iterpacket=_iterpacket - if type(set) is list: - self.set = set - elif isinstance(set, PacketList): - self.set = list(set) - else: - self.set = [set] - def transf(self, element): - return element - def __iter__(self): - for i in self.set: - if (type(i) is tuple) and (len(i) == 2) and type(i[0]) is int and type(i[1]) is int: - if (i[0] <= i[1]): - j=i[0] - while j <= i[1]: - yield j - j += 1 - elif isinstance(i, Gen) and (self._iterpacket or not isinstance(i,Packet)): - for j in i: - yield j - else: - yield i - def __repr__(self): - return "<SetGen %s>" % self.set.__repr__() - -class Net(Gen): - """Generate a list of IPs from a network address or a name""" - name = "ip" - ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") - def __init__(self, net): - self.repr=net - - tmp=net.split('/')+["32"] - if not self.ipaddress.match(net): - tmp[0]=socket.gethostbyname(tmp[0]) - netmask = int(tmp[1]) - - def parse_digit(a,netmask): - netmask = min(8,max(netmask,0)) - if a == "*": - a = (0,256) - elif a.find("-") >= 0: - x,y = map(int,a.split("-")) - if x > y: - y = x - a = (x & (0xffL<<netmask) , max(y, (x | (0xffL>>(8-netmask))))+1) - else: - a = (int(a) & (0xffL<<netmask),(int(a) | (0xffL>>(8-netmask)))+1) - return a - - self.parsed = map(lambda x,y: parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))) - - def __iter__(self): - for d in xrange(*self.parsed[3]): - for c in xrange(*self.parsed[2]): - for b in xrange(*self.parsed[1]): - for a in xrange(*self.parsed[0]): - yield "%i.%i.%i.%i" % (a,b,c,d) - def choice(self): - ip = [] - for v in self.parsed: - ip.append(str(random.randint(v[0],v[1]-1))) - return ".".join(ip) - - def __repr__(self): - return "Net(%r)" % self.repr - -class OID(Gen): - name = "OID" - def __init__(self, oid): - self.oid = oid - self.cmpt = [] - fmt = [] - for i in oid.split("."): - if "-" in i: - fmt.append("%i") - self.cmpt.append(tuple(map(int, i.split("-")))) - else: - fmt.append(i) - self.fmt = ".".join(fmt) - def __repr__(self): - return "OID(%r)" % self.oid - def __iter__(self): - ii = [k[0] for k in self.cmpt] - while 1: - yield self.fmt % tuple(ii) - i = 0 - while 1: - if i >= len(ii): - raise StopIteration - if ii[i] < self.cmpt[i][1]: - ii[i]+=1 - break - else: - ii[i] = self.cmpt[i][0] - i += 1 - - -############# -## Results ## -############# - -class PacketList: - res = [] - def __init__(self, res=None, name="PacketList", stats=None): - """create a packet list from a list of packets - res: the list of packets - stats: a list of classes that will appear in the stats (defaults to [TCP,UDP,ICMP])""" - if stats is None: - stats = [ TCP,UDP,ICMP ] - self.stats = stats - if res is None: - res = [] - if isinstance(res, PacketList): - res = res.res - self.res = res - self.listname = name - def _elt2pkt(self, elt): - return elt - def _elt2sum(self, elt): - return elt.summary() - def _elt2show(self, elt): - return self._elt2sum(elt) - def __repr__(self): -# stats=dict.fromkeys(self.stats,0) ## needs python >= 2.3 :( - stats = dict(map(lambda x: (x,0), self.stats)) - other = 0 - for r in self.res: - f = 0 - for p in stats: - if self._elt2pkt(r).haslayer(p): - stats[p] += 1 - f = 1 - break - if not f: - other += 1 - s = "" - ct = conf.color_theme - for p in self.stats: - s += " %s%s%s" % (ct.packetlist_proto(p.name), - ct.punct(":"), - ct.packetlist_value(stats[p])) - s += " %s%s%s" % (ct.packetlist_proto("Other"), - ct.punct(":"), - ct.packetlist_value(other)) - return "%s%s%s%s%s" % (ct.punct("<"), - ct.packetlist_name(self.listname), - ct.punct(":"), - s, - ct.punct(">")) - def __getattr__(self, attr): - return getattr(self.res, attr) - def __getitem__(self, item): - if isinstance(item,type) and issubclass(item,Packet): - return self.__class__(filter(lambda x: item in self._elt2pkt(x),self.res), - name="%s from %s"%(item.__name__,self.listname)) - if type(item) is slice: - return self.__class__(self.res.__getitem__(item), - name = "mod %s" % self.listname) - return self.res.__getitem__(item) - def __getslice__(self, *args, **kargs): - return self.__class__(self.res.__getslice__(*args, **kargs), - name="mod %s"%self.listname) - def __add__(self, other): - return self.__class__(self.res+other.res, - name="%s+%s"%(self.listname,other.listname)) - def summary(self, prn=None, lfilter=None): - """prints a summary of each packet -prn: function to apply to each packet instead of lambda x:x.summary() -lfilter: truth function to apply to each packet to decide whether it will be displayed""" - for r in self.res: - if lfilter is not None: - if not lfilter(r): - continue - if prn is None: - print self._elt2sum(r) - else: - print prn(r) - def nsummary(self,prn=None, lfilter=None): - """prints a summary of each packet with the packet's number -prn: function to apply to each packet instead of lambda x:x.summary() -lfilter: truth function to apply to each packet to decide whether it will be displayed""" - for i in range(len(self.res)): - if lfilter is not None: - if not lfilter(self.res[i]): - continue - print conf.color_theme.id(i,"%04i"), - if prn is None: - print self._elt2sum(self.res[i]) - else: - print prn(self.res[i]) - def display(self): # Deprecated. Use show() - """deprecated. is show()""" - self.show() - def show(self, *args, **kargs): - """Best way to display the packet list. Defaults to nsummary() method""" - return self.nsummary(*args, **kargs) - - def filter(self, func): - """Returns a packet list filtered by a truth function""" - return self.__class__(filter(func,self.res), - name="filtered %s"%self.listname) - def make_table(self, *args, **kargs): - """Prints a table using a function that returs for each packet its head column value, head row value and displayed value - ex: p.make_table(lambda x:(x[IP].dst, x[TCP].dport, x[TCP].sprintf("%flags%")) """ - return make_table(self.res, *args, **kargs) - def make_lined_table(self, *args, **kargs): - """Same as make_table, but print a table with lines""" - return make_lined_table(self.res, *args, **kargs) - def make_tex_table(self, *args, **kargs): - """Same as make_table, but print a table with LaTeX syntax""" - return make_tex_table(self.res, *args, **kargs) - - def plot(self, f, lfilter=None,**kargs): - """Applies a function to each packet to get a value that will be plotted with GnuPlot. A gnuplot object is returned - lfilter: a truth function that decides whether a packet must be ploted""" - g=Gnuplot.Gnuplot() - l = self.res - if lfilter is not None: - l = filter(lfilter, l) - l = map(f,l) - g.plot(Gnuplot.Data(l, **kargs)) - return g - - def diffplot(self, f, delay=1, lfilter=None, **kargs): - """diffplot(f, delay=1, lfilter=None) - Applies a function to couples (l[i],l[i+delay])""" - g = Gnuplot.Gnuplot() - l = self.res - if lfilter is not None: - l = filter(lfilter, l) - l = map(f,l[:-delay],l[delay:]) - g.plot(Gnuplot.Data(l, **kargs)) - return g - - def multiplot(self, f, lfilter=None, **kargs): - """Uses a function that returns a label and a value for this label, then plots all the values label by label""" - g=Gnuplot.Gnuplot() - l = self.res - if lfilter is not None: - l = filter(lfilter, l) - - d={} - for e in l: - k,v = f(e) - if k in d: - d[k].append(v) - else: - d[k] = [v] - data=[] - for k in d: - data.append(Gnuplot.Data(d[k], title=k, **kargs)) - - g.plot(*data) - return g - - - def rawhexdump(self): - """Prints an hexadecimal dump of each packet in the list""" - for p in self: - hexdump(self._elt2pkt(p)) - - def hexraw(self, lfilter=None): - """Same as nsummary(), except that if a packet has a Raw layer, it will be hexdumped - lfilter: a truth function that decides whether a packet must be displayed""" - for i in range(len(self.res)): - p = self._elt2pkt(self.res[i]) - if lfilter is not None and not lfilter(p): - continue - print "%s %s %s" % (conf.color_theme.id(i,"%04i"), - p.sprintf("%.time%"), - self._elt2sum(self.res[i])) - if p.haslayer(Raw): - hexdump(p.getlayer(Raw).load) - - def hexdump(self, lfilter=None): - """Same as nsummary(), except that packets are also hexdumped - lfilter: a truth function that decides whether a packet must be displayed""" - for i in range(len(self.res)): - p = self._elt2pkt(self.res[i]) - if lfilter is not None and not lfilter(p): - continue - print "%s %s %s" % (conf.color_theme.id(i,"%04i"), - p.sprintf("%.time%"), - self._elt2sum(self.res[i])) - hexdump(p) - - def padding(self, lfilter=None): - """Same as hexraw(), for Padding layer""" - for i in range(len(self.res)): - p = self._elt2pkt(self.res[i]) - if p.haslayer(Padding): - if lfilter is None or lfilter(p): - print "%s %s %s" % (conf.color_theme.id(i,"%04i"), - p.sprintf("%.time%"), - self._elt2sum(self.res[i])) - hexdump(p.getlayer(Padding).load) - - def nzpadding(self, lfilter=None): - """Same as padding() but only non null padding""" - for i in range(len(self.res)): - p = self._elt2pkt(self.res[i]) - if p.haslayer(Padding): - pad = p.getlayer(Padding).load - if pad == pad[0]*len(pad): - continue - if lfilter is None or lfilter(p): - print "%s %s %s" % (conf.color_theme.id(i,"%04i"), - p.sprintf("%.time%"), - self._elt2sum(self.res[i])) - hexdump(p.getlayer(Padding).load) - - - def conversations(self, getsrcdst=None,**kargs): - """Graphes a conversations between sources and destinations and display it - (using graphviz and imagemagick) - getsrcdst: a function that takes an element of the list and return the source and dest - by defaults, return source and destination IP - type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option - target: filename or redirect. Defaults pipe to Imagemagick's display program - prog: which graphviz program to use""" - if getsrcdst is None: - getsrcdst = lambda x:(x[IP].src, x[IP].dst) - conv = {} - for p in self.res: - p = self._elt2pkt(p) - try: - c = getsrcdst(p) - except: - #XXX warning() - continue - conv[c] = conv.get(c,0)+1 - gr = 'digraph "conv" {\n' - for s,d in conv: - gr += '\t "%s" -> "%s"\n' % (s,d) - gr += "}\n" - return do_graph(gr, **kargs) - - def afterglow(self, src=None, event=None, dst=None, **kargs): - """Experimental clone attempt of http://sourceforge.net/projects/afterglow - each datum is reduced as src -> event -> dst and the data are graphed. - by default we have IP.src -> IP.dport -> IP.dst""" - if src is None: - src = lambda x: x[IP].src - if event is None: - event = lambda x: x[IP].dport - if dst is None: - dst = lambda x: x[IP].dst - sl = {} - el = {} - dl = {} - for i in self.res: - try: - s,e,d = src(i),event(i),dst(i) - if s in sl: - n,l = sl[s] - n += 1 - if e not in l: - l.append(e) - sl[s] = (n,l) - else: - sl[s] = (1,[e]) - if e in el: - n,l = el[e] - n+=1 - if d not in l: - l.append(d) - el[e] = (n,l) - else: - el[e] = (1,[d]) - dl[d] = dl.get(d,0)+1 - except: - continue - - import math - def normalize(n): - return 2+math.log(n)/4.0 - - def minmax(x): - m,M = min(x),max(x) - if m == M: - m = 0 - if M == 0: - M = 1 - return m,M - - mins,maxs = minmax(map(lambda (x,y): x, sl.values())) - mine,maxe = minmax(map(lambda (x,y): x, el.values())) - mind,maxd = minmax(dl.values()) - - gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n' - - gr += "# src nodes\n" - for s in sl: - n,l = sl[s]; n = 1+float(n-mins)/(maxs-mins) - gr += '"src.%s" [label = "%s", shape=box, fillcolor="#FF0000", style=filled, fixedsize=1, height=%.2f,width=%.2f];\n' % (`s`,`s`,n,n) - gr += "# event nodes\n" - for e in el: - n,l = el[e]; n = n = 1+float(n-mine)/(maxe-mine) - gr += '"evt.%s" [label = "%s", shape=circle, fillcolor="#00FFFF", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`e`,`e`,n,n) - for d in dl: - n = dl[d]; n = n = 1+float(n-mind)/(maxd-mind) - gr += '"dst.%s" [label = "%s", shape=triangle, fillcolor="#0000ff", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`d`,`d`,n,n) - - gr += "###\n" - for s in sl: - n,l = sl[s] - for e in l: - gr += ' "src.%s" -> "evt.%s";\n' % (`s`,`e`) - for e in el: - n,l = el[e] - for d in l: - gr += ' "evt.%s" -> "dst.%s";\n' % (`e`,`d`) - - gr += "}" - open("/tmp/aze","w").write(gr) - return do_graph(gr, **kargs) - - - - def timeskew_graph(self, ip, **kargs): - """Tries to graph the timeskew between the timestamps and real time for a given ip""" - res = map(lambda x: self._elt2pkt(x), self.res) - b = filter(lambda x:x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), res) - c = [] - for p in b: - opts = p.getlayer(TCP).options - for o in opts: - if o[0] == "Timestamp": - c.append((p.time,o[1][0])) - if not c: - warning("No timestamps found in packet list") - return - d = map(lambda (x,y): (x%2000,((x-c[0][0])-((y-c[0][1])/1000.0))),c) - g = Gnuplot.Gnuplot() - g.plot(Gnuplot.Data(d,**kargs)) - return g - - def _dump_document(self, **kargs): - d = pyx.document.document() - l = len(self.res) - for i in range(len(self.res)): - elt = self.res[i] - c = self._elt2pkt(elt).canvas_dump(**kargs) - cbb = c.bbox() - c.text(cbb.left(),cbb.top()+1,r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i,l),[pyx.text.size.LARGE]) - if conf.verb >= 2: - os.write(1,".") - d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4, - margin=1*pyx.unit.t_cm, - fittosize=1)) - return d - - - - def psdump(self, filename = None, **kargs): - """Creates a multipage poscript file with a psdump of every packet - filename: name of the file to write to. If empty, a temporary file is used and - conf.prog.psreader is called""" - d = self._dump_document(**kargs) - if filename is None: - filename = "/tmp/scapy.psd.%i" % os.getpid() - d.writePSfile(filename) - os.system("%s %s.ps &" % (conf.prog.psreader,filename)) - else: - d.writePSfile(filename) - print - - def pdfdump(self, filename = None, **kargs): - """Creates a PDF file with a psdump of every packet - filename: name of the file to write to. If empty, a temporary file is used and - conf.prog.pdfreader is called""" - d = self._dump_document(**kargs) - if filename is None: - filename = "/tmp/scapy.psd.%i" % os.getpid() - d.writePDFfile(filename) - os.system("%s %s.pdf &" % (conf.prog.pdfreader,filename)) - else: - d.writePDFfile(filename) - print - - def sr(self,multi=0): - """sr([multi=1]) -> (SndRcvList, PacketList) - Matches packets in the list and return ( (matched couples), (unmatched packets) )""" - remain = self.res[:] - sr = [] - i = 0 - while i < len(remain): - s = remain[i] - j = i - while j < len(remain)-1: - j += 1 - r = remain[j] - if r.answers(s): - sr.append((s,r)) - if multi: - remain[i]._answered=1 - remain[j]._answered=2 - continue - del(remain[j]) - del(remain[i]) - i -= 1 - break - i += 1 - if multi: - remain = filter(lambda x:not hasattr(x,"_answered"), remain) - return SndRcvList(sr),PacketList(remain) - - - - - - -class Dot11PacketList(PacketList): - def __init__(self, res=None, name="Dot11List", stats=None): - if stats is None: - stats = [Dot11WEP, Dot11Beacon, UDP, ICMP, TCP] - - PacketList.__init__(self, res, name, stats) - def toEthernet(self): - data = map(lambda x:x.getlayer(Dot11), filter(lambda x : x.haslayer(Dot11) and x.type == 2, self.res)) - r2 = [] - for p in data: - q = p.copy() - q.unwep() - r2.append(Ether()/q.payload.payload.payload) #Dot11/LLC/SNAP/IP - return PacketList(r2,name="Ether from %s"%self.listname) - - - -class SndRcvList(PacketList): - def __init__(self, res=None, name="Results", stats=None): - PacketList.__init__(self, res, name, stats) - def _elt2pkt(self, elt): - return elt[1] - def _elt2sum(self, elt): - return "%s ==> %s" % (elt[0].summary(),elt[1].summary()) - - -class ARPingResult(SndRcvList): - def __init__(self, res=None, name="ARPing", stats=None): - PacketList.__init__(self, res, name, stats) - - def show(self): - for s,r in self.res: - print r.sprintf("%Ether.src% %ARP.psrc%") - - -class AS_resolver: - server = None - options = "-k" - def __init__(self, server=None, port=43, options=None): - if server is not None: - self.server = server - self.port = port - if options is not None: - self.options = options - - def _start(self): - self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.s.connect((self.server,self.port)) - if self.options: - self.s.send(self.options+"\n") - self.s.recv(8192) - def _stop(self): - self.s.close() - - def _parse_whois(self, txt): - asn,desc = None,"" - for l in txt.splitlines(): - if not asn and l.startswith("origin:"): - asn = l[7:].strip() - if l.startswith("descr:"): - if desc: - desc += r"\n" - desc += l[6:].strip() - if asn is not None and desc: - break - return asn,desc.strip() - - def _resolve_one(self, ip): - self.s.send("%s\n" % ip) - x = "" - while not ("%" in x or "source" in x): - x += self.s.recv(8192) - asn, desc = self._parse_whois(x) - return ip,asn,desc - def resolve(self, *ips): - self._start() - ret = [] - for ip in ips: - ip,asn,desc = self._resolve_one(ip) - if asn is not None: - ret.append((ip,asn,desc)) - self._stop() - return ret - -class AS_resolver_riswhois(AS_resolver): - server = "riswhois.ripe.net" - options = "-k -M -1" - - -class AS_resolver_radb(AS_resolver): - server = "whois.ra.net" - options = "-k -M" - - -class AS_resolver_cymru(AS_resolver): - server = "whois.cymru.com" - options = None - def resolve(self, *ips): - ASNlist = [] - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((self.server,self.port)) - s.send("begin\r\n"+"\r\n".join(ips)+"\r\nend\r\n") - r = "" - while 1: - l = s.recv(8192) - if l == "": - break - r += l - s.close() - for l in r.splitlines()[1:]: - if "|" not in l: - continue - asn,ip,desc = map(str.strip, l.split("|")) - if asn == "NA": - continue - asn = int(asn) - ASNlist.append((ip,asn,desc)) - return ASNlist - -class AS_resolver_multi(AS_resolver): - resolvers_list = ( AS_resolver_cymru(),AS_resolver_riswhois(),AS_resolver_radb() ) - def __init__(self, *reslist): - if reslist: - self.resolvers_list = reslist - def resolve(self, *ips): - todo = ips - ret = [] - for ASres in self.resolvers_list: - res = ASres.resolve(*todo) - resolved = [ ip for ip,asn,desc in res ] - todo = [ ip for ip in todo if ip not in resolved ] - ret += res - return ret - - - -class TracerouteResult(SndRcvList): - def __init__(self, res=None, name="Traceroute", stats=None): - PacketList.__init__(self, res, name, stats) - self.graphdef = None - self.graphASres = 0 - self.padding = 0 - self.hloc = None - self.nloc = None - - def show(self): - return self.make_table(lambda (s,r): (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), - s.ttl, - r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) - - - def get_trace(self): - trace = {} - for s,r in self.res: - if IP not in s: - continue - d = s[IP].dst - if d not in trace: - trace[d] = {} - trace[d][s[IP].ttl] = r[IP].src, ICMP not in r - for k in trace.values(): - m = filter(lambda x:k[x][1], k.keys()) - if not m: - continue - m = min(m) - for l in k.keys(): - if l > m: - del(k[l]) - return trace - - def trace3D(self): - """Give a 3D representation of the traceroute. - right button: rotate the scene - middle button: zoom - left button: move the scene - left button on a ball: toggle IP displaying - ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" - trace = self.get_trace() - import visual - - class IPsphere(visual.sphere): - def __init__(self, ip, **kargs): - visual.sphere.__init__(self, **kargs) - self.ip=ip - self.label=None - self.setlabel(self.ip) - def setlabel(self, txt,visible=None): - if self.label is not None: - if visible is None: - visible = self.label.visible - self.label.visible = 0 - elif visible is None: - visible=0 - self.label=visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) - def action(self): - self.label.visible ^= 1 - - visual.scene = visual.display() - visual.scene.exit_on_close(0) - start = visual.box() - rings={} - tr3d = {} - for i in trace: - tr = trace[i] - tr3d[i] = [] - ttl = tr.keys() - for t in range(1,max(ttl)+1): - if t not in rings: - rings[t] = [] - if t in tr: - if tr[t] not in rings[t]: - rings[t].append(tr[t]) - tr3d[i].append(rings[t].index(tr[t])) - else: - rings[t].append(("unk",-1)) - tr3d[i].append(len(rings[t])-1) - for t in rings: - r = rings[t] - l = len(r) - for i in range(l): - if r[i][1] == -1: - col = (0.75,0.75,0.75) - elif r[i][1]: - col = visual.color.green - else: - col = visual.color.blue - - s = IPsphere(pos=((l-1)*visual.cos(2*i*visual.pi/l),(l-1)*visual.sin(2*i*visual.pi/l),2*t), - ip = r[i][0], - color = col) - for trlst in tr3d.values(): - if t <= len(trlst): - if trlst[t-1] == i: - trlst[t-1] = s - forecol = colgen(0.625, 0.4375, 0.25, 0.125) - for trlst in tr3d.values(): - col = forecol.next() - start = (0,0,0) - for ip in trlst: - visual.cylinder(pos=start,axis=ip.pos-start,color=col,radius=0.2) - start = ip.pos - - movcenter=None - while 1: - if visual.scene.kb.keys: - k = visual.scene.kb.getkey() - if k == "esc": - break - if visual.scene.mouse.events: - ev = visual.scene.mouse.getevent() - if ev.press == "left": - o = ev.pick - if o: - if ev.ctrl: - if o.ip == "unk": - continue - savcolor = o.color - o.color = (1,0,0) - a,b=sr(IP(dst=o.ip)/TCP(dport=[21,22,23,25,80,443]),timeout=2) - o.color = savcolor - if len(a) == 0: - txt = "%s:\nno results" % o.ip - else: - txt = "%s:\n" % o.ip - for s,r in a: - txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") - o.setlabel(txt, visible=1) - else: - if hasattr(o, "action"): - o.action() - elif ev.drag == "left": - movcenter = ev.pos - elif ev.drop == "left": - movcenter = None - if movcenter: - visual.scene.center -= visual.scene.mouse.pos-movcenter - movcenter = visual.scene.mouse.pos - - - def world_trace(self): - ips = {} - rt = {} - ports_done = {} - for s,r in self.res: - ips[r.src] = None - if s.haslayer(TCP) or s.haslayer(UDP): - trace_id = (s.src,s.dst,s.proto,s.dport) - elif s.haslayer(ICMP): - trace_id = (s.src,s.dst,s.proto,s.type) - else: - trace_id = (s.src,s.dst,s.proto,0) - trace = rt.get(trace_id,{}) - if not r.haslayer(ICMP) or r.type != 11: - if ports_done.has_key(trace_id): - continue - ports_done[trace_id] = None - trace[s.ttl] = r.src - rt[trace_id] = trace - - trt = {} - for trace_id in rt: - trace = rt[trace_id] - loctrace = [] - for i in range(max(trace.keys())): - ip = trace.get(i,None) - if ip is None: - continue - loc = locate_ip(ip) - if loc is None: - continue -# loctrace.append((ip,loc)) # no labels yet - loctrace.append(loc) - if loctrace: - trt[trace_id] = loctrace - - tr = map(lambda x: Gnuplot.Data(x,with="lines"), trt.values()) - g = Gnuplot.Gnuplot() - world = Gnuplot.File(conf.gnuplot_world,with="lines") - g.plot(world,*tr) - return g - - def make_graph(self,ASres=None,padding=0): - if ASres is None: - ASres = conf.AS_resolver - self.graphASres = ASres - self.graphpadding = padding - ips = {} - rt = {} - ports = {} - ports_done = {} - for s,r in self.res: - r = r[IP] or r[IPv6] or r - s = s[IP] or s[IPv6] or s - ips[r.src] = None - if TCP in s: - trace_id = (s.src,s.dst,6,s.dport) - elif UDP in s: - trace_id = (s.src,s.dst,17,s.dport) - elif ICMP in s: - trace_id = (s.src,s.dst,1,s.type) - else: - trace_id = (s.src,s.dst,s.proto,0) - trace = rt.get(trace_id,{}) - ttl = IPv6 in s and s.hlim or s.ttl - if not (ICMP in r and r[ICMP].type == 11) and not (IPv6 in r and ICMPv6TimeExceeded in r): - if trace_id in ports_done: - continue - ports_done[trace_id] = None - p = ports.get(r.src,[]) - if TCP in r: - p.append(r.sprintf("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%")) - trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') - elif UDP in r: - p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%")) - trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') - elif ICMP in r: - p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%")) - trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') - else: - p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> IPv6 %nh%}")) - trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') - ports[r.src] = p - else: - trace[ttl] = r.sprintf('"%r,src%"') - rt[trace_id] = trace - - # Fill holes with unk%i nodes - unknown_label = incremental_label("unk%i") - blackholes = [] - bhip = {} - for rtk in rt: - trace = rt[rtk] - k = trace.keys() - for n in range(min(k), max(k)): - if not trace.has_key(n): - trace[n] = unknown_label.next() - if not ports_done.has_key(rtk): - if rtk[2] == 1: #ICMP - bh = "%s %i/icmp" % (rtk[1],rtk[3]) - elif rtk[2] == 6: #TCP - bh = "%s %i/tcp" % (rtk[1],rtk[3]) - elif rtk[2] == 17: #UDP - bh = '%s %i/udp' % (rtk[1],rtk[3]) - else: - bh = '%s %i/proto' % (rtk[1],rtk[2]) - ips[bh] = None - bhip[rtk[1]] = bh - bh = '"%s"' % bh - trace[max(k)+1] = bh - blackholes.append(bh) - - # Find AS numbers - ASN_query_list = dict.fromkeys(map(lambda x:x.rsplit(" ",1)[0],ips)).keys() - if ASres is None: - ASNlist = [] - else: - ASNlist = ASres.resolve(*ASN_query_list) - - ASNs = {} - ASDs = {} - for ip,asn,desc, in ASNlist: - if asn is None: - continue - iplist = ASNs.get(asn,[]) - if ip in bhip: - if ip in ports: - iplist.append(ip) - iplist.append(bhip[ip]) - else: - iplist.append(ip) - ASNs[asn] = iplist - ASDs[asn] = desc - - - backcolorlist=colgen("60","86","ba","ff") - forecolorlist=colgen("a0","70","40","20") - - s = "digraph trace {\n" - - s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" - - s += "\n#ASN clustering\n" - for asn in ASNs: - s += '\tsubgraph cluster_%s {\n' % asn - col = backcolorlist.next() - s += '\t\tcolor="#%s%s%s";' % col - s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col - s += '\t\tfontsize = 10;' - s += '\t\tlabel = "%s\\n[%s]"\n' % (asn,ASDs[asn]) - for ip in ASNs[asn]: - - s += '\t\t"%s";\n'%ip - s += "\t}\n" - - - - - s += "#endpoints\n" - for p in ports: - s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p,p,"|".join(ports[p])) - - s += "\n#Blackholes\n" - for bh in blackholes: - s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh - - if padding: - s += "\n#Padding\n" - pad={} - for snd,rcv in self.res: - if rcv.src not in ports and rcv.haslayer(Padding): - p = rcv.getlayer(Padding).load - if p != "\x00"*len(p): - pad[rcv.src]=None - for rcv in pad: - s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv - - - - s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" - - - for rtk in rt: - s += "#---[%s\n" % `rtk` - s += '\t\tedge [color="#%s%s%s"];\n' % forecolorlist.next() - trace = rt[rtk] - k = trace.keys() - for n in range(min(k), max(k)): - s += '\t%s ->\n' % trace[n] - s += '\t%s;\n' % trace[max(k)] - - s += "}\n"; - self.graphdef = s - - def graph(self, ASres=None, padding=0, **kargs): - """x.graph(ASres=conf.AS_resolver, other args): - ASres=None : no AS resolver => no clustering - ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) - ASres=AS_resolver_cymru(): use whois.cymru.com whois database - ASres=AS_resolver(server="whois.ra.net") - type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option - target: filename or redirect. Defaults pipe to Imagemagick's display program - prog: which graphviz program to use""" - if ASres is None: - ASres = conf.AS_resolver - if (self.graphdef is None or - self.graphASres != ASres or - self.graphpadding != padding): - self.make_graph(ASres,padding) - - return do_graph(self.graphdef, **kargs) - - - - -############ -## Fields ## -############ - -class Field: - """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""" - if x is None: - x = 0 - 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], Packet): - x[i] = x[i].copy() - return x - def __repr__(self): - return "<Field (%s).%s>" % (",".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: - 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: - _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: - 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: - """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 addfield(self, pkt, s, val): - sval = self._fld.addfield(pkt, "", val) - return s+sval+struct.pack("%is" % (-len(sval)%self._align), self._padwith) - - def __getattr__(self, attr): - return getattr(self._fld,attr) - - -class MACField(Field): - 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 DestMACField(MACField): - def __init__(self, name): - MACField.__init__(self, name, None) - def i2h(self, pkt, x): - if x is None: - dstip = None - if isinstance(pkt.payload, IPv6): - dstip = pkt.payload.dst - elif isinstance(pkt.payload, IP): - dstip = pkt.payload.dst - elif isinstance(pkt.payload, ARP): - dstip = pkt.payload.pdst - if isinstance(dstip, Gen): - dstip = dstip.__iter__().next() - if dstip is not None: - if isinstance(pkt.payload, IPv6): - x = getmacbyip6(dstip, chainCC=1) - else: - x = getmacbyip(dstip, chainCC=1) - if x is None: - x = "ff:ff:ff:ff:ff:ff" - warning("Mac address to reach %s not found\n"%dstip) - return MACField.i2h(self, pkt, x) - def i2m(self, pkt, x): - return MACField.i2m(self, pkt, self.i2h(pkt, x)) - -class SourceMACField(MACField): - def __init__(self, name): - MACField.__init__(self, name, None) - def i2h(self, pkt, x): - if x is None: - dstip = None - if isinstance(pkt.payload, IPv6): - dstip = pkt.payload.dst - elif isinstance(pkt.payload, IP): - dstip = pkt.payload.dst - elif isinstance(pkt.payload, ARP): - dstip = pkt.payload.pdst - if isinstance(dstip, Gen): - dstip = dstip.__iter__().next() - if dstip is not None: - if isinstance(pkt.payload, IPv6): - iff,a,nh = conf.route6.route(dstip) - else: - iff,a,gw = conf.route.route(dstip) - try: - x = get_if_hwaddr(iff) - except: - pass - if x is None: - x = "00:00:00:00:00:00" - return MACField.i2h(self, pkt, x) - def i2m(self, pkt, x): - return MACField.i2m(self, pkt, self.i2h(pkt, x)) - -class ARPSourceMACField(MACField): - def __init__(self, name): - MACField.__init__(self, name, None) - def i2h(self, pkt, x): - if x is None: - dstip = pkt.pdst - if isinstance(dstip, Gen): - dstip = dstip.__iter__().next() - if dstip is not None: - iff,a,gw = conf.route.route(dstip) - try: - x = get_if_hwaddr(iff) - except: - pass - if x is None: - x = "00:00:00:00:00:00" - return MACField.i2h(self, pkt, x) - def i2m(self, pkt, x): - return MACField.i2m(self, pkt, self.i2h(pkt, x)) - -class Dot11AddrMACField(MACField): - def is_applicable(self, pkt): - return 1 - def addfield(self, pkt, s, val): - if self.is_applicable(pkt): - return MACField.addfield(self, pkt, s, val) - else: - return s - def getfield(self, pkt, s): - if self.is_applicable(pkt): - return MACField.getfield(self, pkt, s) - else: - return s,None - -class Dot11Addr2MACField(Dot11AddrMACField): - def is_applicable(self, pkt): - if pkt.type == 1: - return pkt.subtype in [ 0xb, 0xa, 0xe, 0xf] # RTS, PS-Poll, CF-End, CF-End+CF-Ack - return 1 - -class Dot11Addr3MACField(Dot11AddrMACField): - def is_applicable(self, pkt): - if pkt.type in [0,2]: - return 1 - return 0 - -class Dot11Addr4MACField(Dot11AddrMACField): - def is_applicable(self, pkt): - if pkt.type == 2: - if pkt.FCfield & 0x3 == 0x3: # To-DS and From-DS are set - return 1 - return 0 - -class IPField(Field): - 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): - 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 = conf.route.route(getattr(pkt,self.dstname)) - 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]: - x=r[0][1] - else: - warning("More than one possible route for %s"%repr(dst)) - return None - else: - iff,x,gw = conf.route.route(dst) - return IPField.i2h(self, pkt, x) - - - - -class ByteField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "B") - -class XByteField(ByteField): - def i2repr(self, pkt, x): - if x is None: - x = 0 - return lhex(self.i2h(pkt, x)) - -class X3BytesField(XByteField): - 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): - def __init__(self, name, default): - Field.__init__(self, name, default, "H") - -class LEShortField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "<H") - -class XShortField(ShortField): - def i2repr(self, pkt, x): - if x is None: - x = 0 - return lhex(self.i2h(pkt, x)) - - -class IntField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "I") - -class SignedIntField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "i") - def randval(self): - return RandSInt() - -class LEIntField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "<I") - -class LESignedIntField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "<i") - def randval(self): - return RandSInt() - -class XIntField(IntField): - def i2repr(self, pkt, x): - if x is None: - x = 0 - return lhex(self.i2h(pkt, x)) - - -class LongField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "Q") - -class XLongField(LongField): - def i2repr(self, pkt, x): - if x is None: - x = 0 - return lhex(self.i2h(pkt, x)) - -class IEEEFloatField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "f") - -class IEEEDoubleField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "d") - - -def FIELD_LENGTH_MANAGEMENT_DEPRECATION(x): - try: - for tb in traceback.extract_stack()+[("??",-1,None,"")]: - f,l,_,line = tb - if line.startswith("fields_desc"): - break - except: - f,l="??",-1 - log_loading.warning("Deprecated use of %s (%s l. %i). See http://trac.secdev.org/scapy/wiki/LengthFields" % (x,f,l)) - -class StrField(Field): - def __init__(self, name, default, fmt="H", remain=0, shift=0): - Field.__init__(self,name,default,fmt) - self.remain = remain - self.shift = shift - if shift != 0: - FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) - def i2len(self, pkt, i): - return len(i)+self.shift - def i2m(self, pkt, x): - if x is None: - x = "" - return x - def addfield(self, pkt, s, val): - return s+self.i2m(pkt, val) - def getfield(self, pkt, s): - if self.remain == 0: - return "",self.m2i(pkt, s) - else: - return s[-self.remain:],self.m2i(pkt, s[:-self.remain]) - def randval(self): - return RandBin(RandNum(0,1200)) - -class PacketField(StrField): - holds_packets=1 - def __init__(self, name, default, cls, remain=0, shift=0): - StrField.__init__(self, name, default, remain=remain, shift=shift) - self.cls = cls - def i2m(self, pkt, i): - return str(i) - def m2i(self, pkt, m): - return self.cls(m) - def getfield(self, pkt, s): - i = self.m2i(pkt, s) - remain = "" - if i.haslayer(Padding): - r = i.getlayer(Padding) - del(r.underlayer.payload) - remain = r.load - return remain,i - -class PacketLenField(PacketField): - holds_packets=1 - def __init__(self, name, default, cls, fld=None, length_from=None, shift=0): - PacketField.__init__(self, name, default, cls, shift=shift) - self.length_from = length_from - if fld is not None or shift != 0: - FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) - self.count_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift - def getfield(self, pkt, s): - l = self.length_from(pkt) - i = self.m2i(pkt, s[:l]) - return s[l:],i - - -class PacketListField(PacketField): - islist = 1 - holds_packets=1 - def __init__(self, name, default, cls, fld=None, count_from=None, length_from=None, shift=0): - if default is None: - default = [] # Create a new list for each instance - PacketField.__init__(self, name, default, cls, shift=shift) - self.count_from = count_from - self.length_from = length_from - - if fld is not None or shift != 0: - FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) - if fld is not None: - self.count_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift - - def any2i(self, pkt, x): - if type(x) is not list: - return [x] - else: - return x - def i2count(self, pkt, val): - if type(val) is list: - return len(val) - return 1 - def i2len(self, pkt, val): - return sum( len(p) for p in val ) - def do_copy(self, x): - return map(lambda p:p.copy(), x) - 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) - - lst = [] - ret = "" - remain = s - if l is not None: - remain,ret = s[:l],s[l:] - while remain: - if c is not None: - if c <= 0: - break - c -= 1 - p = self.m2i(pkt,remain) - if Padding in p: - pad = p[Padding] - remain = pad.load - del(pad.underlayer.payload) - else: - remain = "" - lst.append(p) - return remain+ret,lst - def addfield(self, pkt, s, val): - return s+"".join(map(str, val)) - - -class StrFixedLenField(StrField): - def __init__(self, name, default, length=None, length_from=None, shift=0): - StrField.__init__(self, name, default, shift=shift) - self.length_from = length_from - if length is not None: - self.length_from = lambda pkt,length=length: length - def getfield(self, pkt, s): - l = self.length_from(pkt) - return s[l:], self.m2i(pkt,s[:l]) - def addfield(self, pkt, s, val): - l = self.length_from(pkt) - return s+struct.pack("%is"%l,self.i2m(pkt, val)) - def randval(self): - try: - l = self.length_from(None) - except: - l = RandNum(0,200) - return RandBin(l) - -class NetBIOSNameField(StrFixedLenField): - def __init__(self, name, default, length=31, shift=0): - StrFixedLenField.__init__(self, name, default, length, shift=shift) - def i2m(self, pkt, x): - l = self.length_from(pkt)/2 - if x is None: - x = "" - x += " "*(l) - x = x[:l] - x = "".join(map(lambda x: chr(0x41+(ord(x)>>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): - def __init__(self, name, default, fld=None, length_from=None, shift=0): - StrField.__init__(self, name, default, shift=shift) - self.length_from = length_from - if fld is not None or shift != 0: - FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) - self.length_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift - def getfield(self, pkt, s): - l = self.length_from(pkt) - return s[l:], self.m2i(pkt,s[:l]) - -class FieldListField(Field): - islist=1 - def __init__(self, name, default, field, fld=None, shift=0, 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 - if fld is not None or shift != 0: - FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) - self.count_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift - - - 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): - 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 - -# see http://www.iana.org/assignments/ipsec-registry for details -ISAKMPAttributeTypes= { "Encryption": (1, { "DES-CBC" : 1, - "IDEA-CBC" : 2, - "Blowfish-CBC" : 3, - "RC5-R16-B64-CBC" : 4, - "3DES-CBC" : 5, - "CAST-CBC" : 6, - "AES-CBC" : 7, - "CAMELLIA-CBC" : 8, }, 0), - "Hash": (2, { "MD5": 1, - "SHA": 2, - "Tiger": 3, - "SHA2-256": 4, - "SHA2-384": 5, - "SHA2-512": 6,}, 0), - "Authentication":(3, { "PSK": 1, - "DSS": 2, - "RSA Sig": 3, - "RSA Encryption": 4, - "RSA Encryption Revised": 5, - "ElGamal Encryption": 6, - "ElGamal Encryption Revised": 7, - "ECDSA Sig": 8, - "HybridInitRSA": 64221, - "HybridRespRSA": 64222, - "HybridInitDSS": 64223, - "HybridRespDSS": 64224, - "XAUTHInitPreShared": 65001, - "XAUTHRespPreShared": 65002, - "XAUTHInitDSS": 65003, - "XAUTHRespDSS": 65004, - "XAUTHInitRSA": 65005, - "XAUTHRespRSA": 65006, - "XAUTHInitRSAEncryption": 65007, - "XAUTHRespRSAEncryption": 65008, - "XAUTHInitRSARevisedEncryption": 65009, - "XAUTHRespRSARevisedEncryptio": 65010, }, 0), - "GroupDesc": (4, { "768MODPgr" : 1, - "1024MODPgr" : 2, - "EC2Ngr155" : 3, - "EC2Ngr185" : 4, - "1536MODPgr" : 5, - "2048MODPgr" : 14, - "3072MODPgr" : 15, - "4096MODPgr" : 16, - "6144MODPgr" : 17, - "8192MODPgr" : 18, }, 0), - "GroupType": (5, {"MODP": 1, - "ECP": 2, - "EC2N": 3}, 0), - "GroupPrime": (6, {}, 1), - "GroupGenerator1":(7, {}, 1), - "GroupGenerator2":(8, {}, 1), - "GroupCurveA": (9, {}, 1), - "GroupCurveB": (10, {}, 1), - "LifeType": (11, {"Seconds": 1, - "Kilobytes": 2, }, 0), - "LifeDuration": (12, {}, 1), - "PRF": (13, {}, 0), - "KeyLength": (14, {}, 0), - "FieldSize": (15, {}, 0), - "GroupOrder": (16, {}, 1), - } - -# the name 'ISAKMPTransformTypes' is actually a misnomer (since the table -# holds info for all ISAKMP Attribute types, not just transforms, but we'll -# keep it for backwards compatibility... for now at least -ISAKMPTransformTypes = ISAKMPAttributeTypes - -ISAKMPTransformNum = {} -for n in ISAKMPTransformTypes: - val = ISAKMPTransformTypes[n] - tmp = {} - for e in val[1]: - tmp[val[1][e]] = e - ISAKMPTransformNum[val[0]] = (n,tmp, val[2]) -del(n) -del(e) -del(tmp) -del(val) - - -class ISAKMPTransformSetField(StrLenField): - islist=1 - def type2num(self, (typ,val)): - type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typ, (typ,{},0)) - val = enc_dict.get(val, val) - s = "" - if (val & ~0xffff): - if not tlv: - warning("%r should not be TLV but is too big => using TLV encoding" % typ) - n = 0 - while val: - s = chr(val&0xff)+s - val >>= 8 - n += 1 - val = n - else: - type_val |= 0x8000 - return struct.pack("!HH",type_val, val)+s - def num2type(self, typ, enc): - val = ISAKMPTransformNum.get(typ,(typ,{})) - enc = val[1].get(enc,enc) - return (val[0],enc) - def i2m(self, pkt, i): - if i is None: - return "" - i = map(self.type2num, i) - return "".join(i) - def m2i(self, pkt, m): - # I try to ensure that we don't read off the end of our packet based - # on bad length fields we're provided in the packet. There are still - # conditions where struct.unpack() may not get enough packet data, but - # worst case that should result in broken attributes (which would - # be expected). (wam) - lst = [] - while len(m) >= 4: - trans_type, = struct.unpack("!H", m[:2]) - is_tlv = not (trans_type & 0x8000) - if is_tlv: - # We should probably check to make sure the attribute type we - # are looking at is allowed to have a TLV format and issue a - # warning if we're given an TLV on a basic attribute. - value_len, = struct.unpack("!H", m[2:4]) - if value_len+4 > len(m): - warning("Bad length for ISAKMP tranform type=%#6x" % trans_type) - value = m[4:4+value_len] - value = reduce(lambda x,y: (x<<8L)|y, struct.unpack("!%s" % ("B"*len(value),), value),0) - else: - trans_type &= 0x7fff - value_len=0 - value, = struct.unpack("!H", m[2:4]) - m=m[4+value_len:] - lst.append(self.num2type(trans_type, value)) - if len(m) > 0: - warning("Extra bytes after ISAKMP transform dissection [%r]" % m) - return lst - -class StrNullField(StrField): - 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): - 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): - def i2m(self, pkt, x): - if x is None: - x = len(pkt.payload) - return x - -class BCDFloatField(Field): - def i2m(self, pkt, x): - return int(256*x) - def m2i(self, pkt, x): - return x/256.0 - -class BitField(Field): - 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<<self.size) - 1) - bitsdone += self.size - while bitsdone >= 8: - bitsdone -= 8 - s = s+struct.pack("!B", v >> bitsdone) - v &= (1L<<bitsdone)-1 - if bitsdone: - return s,bitsdone,v - else: - return s - def getfield(self, pkt, s): - if type(s) is tuple: - s,bn = s - else: - bn = 0 - # we don't want to process all the string - nb_bytes = (self.size+bn-1)/8 + 1 - w = s[:nb_bytes] - - # split the substring byte by byte - bytes = struct.unpack('!%dB' % nb_bytes , w) - - b = 0L - for c in range(nb_bytes): - b |= long(bytes[c]) << (nb_bytes-c-1)*8 - - # get rid of high order bits - b &= (1L << (nb_bytes*8-bn)) - 1 - - # remove low order bits - b = b >> (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): - 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): - def i2repr(self, pkt, x): - return lhex(self.i2h(pkt,x)) - - -class EnumField(Field): - 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): - 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): - 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): - def __init__(self, name, default, enum): - EnumField.__init__(self, name, default, enum, "H") - -class LEShortEnumField(EnumField): - def __init__(self, name, default, enum): - EnumField.__init__(self, name, default, enum, "<H") - -class ByteEnumField(EnumField): - def __init__(self, name, default, enum): - EnumField.__init__(self, name, default, enum, "B") - -class IntEnumField(EnumField): - def __init__(self, name, default, enum): - EnumField.__init__(self, name, default, enum, "I") - -class SignedIntEnumField(EnumField): - def __init__(self, name, default, enum): - EnumField.__init__(self, name, default, enum, "i") - def randval(self): - return RandSInt() - -class LEIntEnumField(EnumField): - def __init__(self, name, default, enum): - EnumField.__init__(self, name, default, enum, "<I") - -class XShortEnumField(ShortEnumField): - 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 lhex(x) - -# Little endian long field -class LELongField(Field): - def __init__(self, name, default): - Field.__init__(self, name, default, "<Q") - -# Little endian fixed length field -class LEFieldLenField(FieldLenField): - def __init__(self, name, default, length_of=None, fmt = "<H", count_of=None, adjust=lambda pkt,x:x, fld=None): - FieldLenField.__init__(self, name, default, length_of=length_of, fmt=fmt, fld=fld, adjust=adjust) - - -class FlagsField(BitField): - def __init__(self, name, default, size, names): - BitField.__init__(self, name, default, size) - self.multi = type(names) is list - if self.multi: - self.names = map(lambda x:[x], names) - else: - self.names = names - def any2i(self, pkt, x): - if type(x) is str: - if self.multi: - x = map(lambda y:[y], x.split("+")) - y = 0 - for i in x: - y |= 1 << self.names.index(i) - x = y - return x - def i2repr(self, pkt, x): - if type(x) is list or type(x) is tuple: - return repr(x) - if self.multi: - r = [] - else: - r = "" - i=0 - while x: - if x & 1: - r += self.names[i] - i += 1 - x >>= 1 - if self.multi: - r = "+".join(r) - return r - - - - - -class IPoptionsField(StrField): - def i2m(self, pkt, x): - return x+"\x00"*(3-((len(x)+3)%4)) - def getfield(self, pkt, s): - opsz = (pkt.ihl-5)*4 - if opsz < 0: - warning("bad ihl (%i). Assuming ihl=5"%pkt.ihl) - opsz = 0 - return s[opsz:],s[:opsz] - def randval(self): - return RandBin(RandNum(0,39)) - - -TCPOptions = ( - { 0 : ("EOL",None), - 1 : ("NOP",None), - 2 : ("MSS","!H"), - 3 : ("WScale","!B"), - 4 : ("SAckOK",None), - 5 : ("SAck","!"), - 8 : ("Timestamp","!II"), - 14 : ("AltChkSum","!BH"), - 15 : ("AltChkSumOpt",None) - }, - { "EOL":0, - "NOP":1, - "MSS":2, - "WScale":3, - "SAckOK":4, - "SAck":5, - "Timestamp":8, - "AltChkSum":14, - "AltChkSumOpt":15, - } ) - -class TCPOptionsField(StrField): - islist=1 - def getfield(self, pkt, s): - opsz = (pkt.dataofs-5)*4 - if opsz < 0: - warning("bad dataofs (%i). Assuming dataofs=5"%pkt.dataofs) - opsz = 0 - return s[opsz:],self.m2i(pkt,s[:opsz]) - def m2i(self, pkt, x): - opt = [] - while x: - onum = ord(x[0]) - if onum == 0: - opt.append(("EOL",None)) - x=x[1:] - break - if onum == 1: - opt.append(("NOP",None)) - x=x[1:] - continue - olen = ord(x[1]) - if olen < 2: - warning("Malformed TCP option (announced length is %i)" % olen) - olen = 2 - oval = x[2:olen] - if TCPOptions[0].has_key(onum): - oname, ofmt = TCPOptions[0][onum] - if onum == 5: #SAck - ofmt += "%iI" % (len(oval)/4) - if ofmt and struct.calcsize(ofmt) == len(oval): - oval = struct.unpack(ofmt, oval) - if len(oval) == 1: - oval = oval[0] - opt.append((oname, oval)) - else: - opt.append((onum, oval)) - x = x[olen:] - return opt - - def i2m(self, pkt, x): - opt = "" - for oname,oval in x: - if type(oname) is str: - if oname == "NOP": - opt += "\x01" - continue - elif oname == "EOL": - opt += "\x00" - continue - elif TCPOptions[1].has_key(oname): - onum = TCPOptions[1][oname] - ofmt = TCPOptions[0][onum][1] - if onum == 5: #SAck - ofmt += "%iI" % len(oval) - if ofmt is not None and (type(oval) is not str or "s" in ofmt): - if type(oval) is not tuple: - oval = (oval,) - oval = struct.pack(ofmt, *oval) - else: - warning("option [%s] unknown. Skipped."%oname) - continue - else: - onum = oname - if type(oval) is not str: - warning("option [%i] is not string."%onum) - continue - opt += chr(onum)+chr(2+len(oval))+oval - return opt+"\x00"*(3-((len(opt)+3)%4)) - def randval(self): - return [] # XXX - - -class DNSStrField(StrField): - def i2m(self, pkt, x): - x = [k[:63] for k in x.split(".")] # Truncate chunks that cannont be encoded (more than 63 bytes..) - x = map(lambda y: chr(len(y))+y, x) - x = "".join(x) - if x[-1] != "\x00": - x += "\x00" - return x - def getfield(self, pkt, s): - n = "" - while 1: - l = ord(s[0]) - s = s[1:] - if not l: - break - if l & 0xc0: - raise Scapy_Exception("DNS message can't be compressed at this point!") - else: - n += s[:l]+"." - s = s[l:] - return s, n - - -class DNSRRCountField(ShortField): - holds_packets=1 - def __init__(self, name, default, rr): - ShortField.__init__(self, name, default) - self.rr = rr - def _countRR(self, pkt): - x = getattr(pkt,self.rr) - i = 0 - while isinstance(x, DNSRR) or isinstance(x, DNSQR): - x = x.payload - i += 1 - return i - - def i2m(self, pkt, x): - if x is None: - x = self._countRR(pkt) - return x - def i2h(self, pkt, x): - if x is None: - x = self._countRR(pkt) - return x - - -def DNSgetstr(s,p): - name = "" - q = 0 - jpath = [p] - while 1: - if p >= len(s): - warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s))) - break - l = ord(s[p]) - p += 1 - if l & 0xc0: - if not q: - q = p+1 - if p >= len(s): - warning("DNS incomplete jump token at (ofs=%i)" % p) - break - p = ((l & 0x3f) << 8) + ord(s[p]) - 12 - if p in jpath: - warning("DNS decompression loop detected") - break - jpath.append(p) - continue - elif l > 0: - name += s[p:p+l]+"." - p += l - continue - break - if q: - p = q - return name,p - - -class DNSRRField(StrField): - holds_packets=1 - def __init__(self, name, countfld, passon=1): - StrField.__init__(self, name, None) - self.countfld = countfld - self.passon = passon - def i2m(self, pkt, x): - if x is None: - return "" - return str(x) - def decodeRR(self, name, s, p): - ret = s[p:p+10] - type,cls,ttl,rdlen = struct.unpack("!HHIH", ret) - p += 10 - rr = DNSRR("\x00"+ret+s[p:p+rdlen]) - if rr.type in [2, 3, 4, 5]: - rr.rdata = DNSgetstr(s,p)[0] - del(rr.rdlen) - - p += rdlen - - rr.rrname = name - return rr,p - def getfield(self, pkt, s): - if type(s) is tuple : - s,p = s - else: - p = 0 - ret = None - c = getattr(pkt, self.countfld) - if c > len(s): - warning("wrong value: DNS.%s=%i" % (self.countfld,c)) - return s,"" - while c: - c -= 1 - name,p = DNSgetstr(s,p) - rr,p = self.decodeRR(name, s, p) - if ret is None: - ret = rr - else: - ret.add_payload(rr) - if self.passon: - return (s,p),ret - else: - return s[p:],ret - - -class DNSQRField(DNSRRField): - holds_packets=1 - def decodeRR(self, name, s, p): - ret = s[p:p+4] - p += 4 - rr = DNSQR("\x00"+ret) - rr.qname = name - return rr,p - - - -class RDataField(StrLenField): - def m2i(self, pkt, s): - family = None - if pkt.type == 1: - family = socket.AF_INET - elif pkt.type == 28: - family = socket.AF_INET6 - elif pkt.type == 12: - s = DNSgetstr(s, 0)[0] - if family is not None: - s = inet_ntop(family, s) - return s - def i2m(self, pkt, s): - if pkt.type == 1: - if s: - s = inet_aton(s) - elif pkt.type == 28: - if s: - s = inet_pton(socket.AF_INET6, s) - elif pkt.type in [2,3,4,5]: - s = "".join(map(lambda x: chr(len(x))+x, s.split("."))) - if ord(s[-1]): - s += "\x00" - return s - -class RDLenField(Field): - def __init__(self, name): - Field.__init__(self, name, None, "H") - def i2m(self, pkt, x): - if x is None: - rdataf = pkt.get_field("rdata") - x = len(rdataf.i2m(pkt, pkt.rdata)) - return x - def i2h(self, pkt, x): - if x is None: - rdataf = pkt.get_field("rdata") - x = len(rdataf.i2m(pkt, pkt.rdata)) - return x - -# seconds between 01-01-1900 and 01-01-1970 -ntp_basetime = 2208988800 - -class TimeStampField(BitField): - def __init__(self, name, default, size): - BitField.__init__(self, name, default, size) - self.size = size - def getfield(self, pkt, s): - s,timestamp = BitField.getfield(self, pkt, s) - - if timestamp: - # timestamp is a 64 bits field : - # + first 32 bits : number of seconds since 1900 - # + last 32 bits : fraction part - timestamp >>= 32 - timestamp -= ntp_basetime - - from time import gmtime, strftime - b = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(timestamp)) - else: - b = 'None' - - return s, b - def addfield(self, pkt, s, val): - t = -1 - if type(val) is str: - from time import strptime, mktime - t = int(mktime(strptime(val))) + ntp_basetime + 3600 - else: - if val == -1: - from time import time - t = int(time()) + ntp_basetime - else: - t = val - t <<= 32 - return BitField.addfield(self,pkt,s, t) - -class ICMPTimeStampField(IntField): - re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") - def i2repr(self, pkt, val): - if val is None: - return "--" - else: - sec, milli = divmod(val, 1000) - min, sec = divmod(sec, 60) - hour, min = divmod(min, 60) - return "%d:%d:%d.%d" %(hour, min, sec, int(milli)) - def any2i(self, pkt, val): - if type(val) is str: - hmsms = self.re_hmsm.match(val) - if hmsms: - h,_,m,_,s,_,ms = hmsms = hmsms.groups() - ms = int(((ms or "")+"000")[:3]) - val = ((int(h)*60+int(m or 0))*60+int(s or 0))*1000+ms - else: - val = 0 - elif val is None: - val = int((time.time()%(24*60*60))*1000) - return val - -class FloatField(BitField): - def getfield(self, pkt, s): - s,b = BitField.getfield(self, pkt, s) - - # fraction point between bits 15 and 16. - sec = b >> 16 - frac = b & (1L << (32+1)) - 1 - frac /= 65536.0 - b = sec+frac - return s,b - -class Dot11SCField(LEShortField): - def is_applicable(self, pkt): - return pkt.type != 1 # control frame - def addfield(self, pkt, s, val): - if self.is_applicable(pkt): - return LEShortField.addfield(self, pkt, s, val) - else: - return s - def getfield(self, pkt, s): - if self.is_applicable(pkt): - return LEShortField.getfield(self, pkt, s) - else: - return s,None - -##################### -#### ASN1 Fields #### -##################### - -class ASN1F_badsequence(Exception): - pass - -class ASN1F_element: - pass - -class ASN1F_optionnal(ASN1F_element): - def __init__(self, field): - self._field=field - def __getattr__(self, attr): - return getattr(self._field,attr) - def dissect(self,pkt,s): - try: - return self._field.dissect(pkt,s) - except ASN1F_badsequence: - self._field.set_val(pkt,None) - return s - except BER_Decoding_Error: - self._field.set_val(pkt,None) - return s - def build(self, pkt): - if self._field.is_empty(pkt): - return "" - return self._field.build(pkt) - -class ASN1F_field(ASN1F_element): - holds_packets=0 - islist=0 - - ASN1_tag = ASN1_Class_UNIVERSAL.ANY - context=ASN1_Class_UNIVERSAL - - def __init__(self, name, default, context=None): - if context is not None: - self.context = context - self.name = name - self.default = default - - def i2repr(self, pkt, x): - if x is None: - x = 0 - return repr(x) - def i2h(self, pkt, x): - return x - def any2i(self, pkt, x): - return x - def m2i(self, pkt, x): - return self.ASN1_tag.get_codec(pkt.ASN1_codec).safedec(x, context=self.context) - def i2m(self, pkt, x): - if x is None: - x = 0 - if isinstance(x, ASN1_Object): - if ( self.ASN1_tag == ASN1_Class_UNIVERSAL.ANY - or x.tag == ASN1_Class_UNIVERSAL.RAW - or x.tag == ASN1_Class_UNIVERSAL.ERROR - or self.ASN1_tag == x.tag ): - return x.enc(pkt.ASN1_codec) - else: - raise ASN1_Error("Encoding Error: got %r instead of an %r for field [%s]" % (x, self.ASN1_tag, self.name)) - return self.ASN1_tag.get_codec(pkt.ASN1_codec).enc(x) - - 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], Packet): - x[i] = x[i].copy() - return x - - def build(self, pkt): - return self.i2m(pkt, getattr(pkt, self.name)) - - def set_val(self, pkt, val): - setattr(pkt, self.name, val) - def is_empty(self, pkt): - return getattr(pkt,self.name) is None - - def dissect(self, pkt, s): - v,s = self.m2i(pkt, s) - self.set_val(pkt, v) - return s - - def get_fields_list(self): - return [self] - - def __hash__(self): - return hash(self.name) - def __str__(self): - return self.name - def __eq__(self, other): - return self.name == other - def __repr__(self): - return self.name - def randval(self): - return RandInt() - - -class ASN1F_INTEGER(ASN1F_field): - ASN1_tag= ASN1_Class_UNIVERSAL.INTEGER - def randval(self): - return RandNum(-2**64, 2**64-1) - -class ASN1F_NULL(ASN1F_INTEGER): - ASN1_tag= ASN1_Class_UNIVERSAL.NULL - -class ASN1F_enum_INTEGER(ASN1F_INTEGER): - def __init__(self, name, default, enum): - ASN1F_INTEGER.__init__(self, name, default) - 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 - def any2i_one(self, pkt, x): - if type(x) is str: - x = self.s2i[x] - return x - def i2repr_one(self, pkt, x): - return self.i2s.get(x, 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 ASN1F_STRING(ASN1F_field): - ASN1_tag = ASN1_Class_UNIVERSAL.STRING - def randval(self): - return RandString(RandNum(0, 1000)) - -class ASN1F_PRINTABLE_STRING(ASN1F_STRING): - ASN1_tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING - -class ASN1F_BIT_STRING(ASN1F_STRING): - ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING - -class ASN1F_UTC_TIME(ASN1F_STRING): - ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME - -class ASN1F_OID(ASN1F_field): - ASN1_tag = ASN1_Class_UNIVERSAL.OID - def randval(self): - return RandOID() - -class ASN1F_SEQUENCE(ASN1F_field): - ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE - def __init__(self, *seq, **kargs): - if "ASN1_tag" in kargs: - self.ASN1_tag = kargs["ASN1_tag"] - self.seq = seq - def __repr__(self): - return "<%s%r>" % (self.__class__.__name__,self.seq,) - def set_val(self, pkt, val): - for f in self.seq: - f.set_val(pkt,val) - def is_empty(self, pkt): - for f in self.seq: - if not f.is_empty(pkt): - return False - return True - def get_fields_list(self): - return reduce(lambda x,y: x+y.get_fields_list(), self.seq, []) - def build(self, pkt): - s = reduce(lambda x,y: x+y.build(pkt), self.seq, "") - return self.i2m(pkt, s) - def dissect(self, pkt, s): - codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) - try: - i,s,remain = codec.check_type_check_len(s) - for obj in self.seq: - s = obj.dissect(pkt,s) - if s: - warning("Too many bytes to decode sequence: [%r]" % s) # XXX not reversible! - return remain - except ASN1_Error,e: - raise ASN1F_badsequence(e) - -class ASN1F_SET(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_UNIVERSAL.SET - -class ASN1F_SEQUENCE_OF(ASN1F_SEQUENCE): - holds_packets = 1 - islist = 1 - def __init__(self, name, default, asn1pkt, ASN1_tag=0x30): - self.asn1pkt = asn1pkt - self.tag = chr(ASN1_tag) - self.name = name - self.default = default - def i2repr(self, pkt, i): - if i is None: - return [] - return i - def get_fields_list(self): - return [self] - def set_val(self, pkt, val): - ASN1F_field.set_val(self, pkt, val) - def is_empty(self, pkt): - return ASN1F_field.is_empty(self, pkt) - def build(self, pkt): - val = getattr(pkt, self.name) - if isinstance(val, ASN1_Object) and val.tag == ASN1_Class_UNIVERSAL.RAW: - s = val - elif val is None: - s = "" - else: - s = "".join(map(str, val )) - return self.i2m(pkt, s) - def set_val(self, pkt, val): - ASN1F_field.set_val(self, pkt, val) - def dissect(self, pkt, s): - codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) - i,s1,remain = codec.check_type_check_len(s) - lst = [] - while s1: - try: - p = self.asn1pkt(s1) - except ASN1F_badsequence,e: - lst.append(Raw(s1)) - break - lst.append(p) - if Raw in p: - s1 = p[Raw].load - del(p[Raw].underlayer.payload) - else: - break - self.set_val(pkt, lst) - return remain - def randval(self): - return fuzz(self.asn1pkt()) - def __repr__(self): - return "<%s %s>" % (self.__class__.__name__,self.name) - -class ASN1F_PACKET(ASN1F_field): - holds_packets = 1 - def __init__(self, name, default, cls): - ASN1_field.__init__(self, name, default) - self.cls = cls - def i2m(self, pkt, x): - if x is None: - x = "" - return str(x) - def extract_packet(self, cls, x): - try: - c = cls(x) - except ASN1F_badsequence: - c = Raw(x) - cpad = c[Padding] - x = "" - if cpad is not None: - x = cpad.load - del(cpad.underlayer.payload) - return c,x - def m2i(self, pkt, x): - return self.extract_packet(self.cls, x) - - -class ASN1F_CHOICE(ASN1F_PACKET): - ASN1_tag = ASN1_Class_UNIVERSAL.NONE - def __init__(self, name, default, *args): - self.name=name - self.choice = {} - for p in args: - self.choice[p.ASN1_root.ASN1_tag] = p -# self.context=context - self.default=default - def m2i(self, pkt, x): - if len(x) == 0: - return Raw(),"" - raise ASN1_Error("ASN1F_CHOICE: got empty string") - if ord(x[0]) not in self.choice: - return Raw(x),"" # XXX return RawASN1 packet ? Raise error - raise ASN1_Error("Decoding Error: choice [%i] not found in %r" % (ord(x[0]), self.choice.keys())) - - z = ASN1F_PACKET.extract_packet(self, self.choice[ord(x[0])], x) - return z - def randval(self): - return RandChoice(*map(lambda x:fuzz(x()), self.choice.values())) - - - -########################### -## Packet abstract class ## -########################### - -class Packet_metaclass(type): - def __new__(cls, name, bases, dct): - newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct) - for f in newcls.fields_desc: - f.register_owner(newcls) - return newcls - def __getattr__(self, attr): - for k in self.fields_desc: - if k.name == attr: - return k - raise AttributeError(attr) - -class NewDefaultValues(Packet_metaclass): - """NewDefaultValues metaclass. Example usage: - class MyPacket(Packet): - fields_desc = [ StrField("my_field", "my default value"), ] - - class MyPacket_variant(MyPacket): - __metaclass__ = NewDefaultValues - my_field = "my new default value" - """ - def __new__(cls, name, bases, dct): - fields = None - for b in bases: - if hasattr(b,"fields_desc"): - fields = b.fields_desc - break - if fields is None: - raise Scapy_Exception("No fields_desc in superclasses") - - new_fields = [] - for f in fields: - if f.name in dct: - f = f.copy() - f.default = dct[f.name] - del(dct[f.name]) - new_fields.append(f) - dct["fields_desc"] = new_fields - return super(NewDefaultValues, cls).__new__(cls, name, bases, dct) - -class Packet(Gen): - __metaclass__ = Packet_metaclass - name=None - - fields_desc = [] - - aliastypes = [] - overload_fields = {} - - underlayer = None - - payload_guess = [] - initialized = 0 - show_indent=1 - explicit = 0 - - @classmethod - def from_hexcap(cls): - return cls(import_hexcap()) - - @classmethod - def upper_bonds(self): - for fval,upper in self.payload_guess: - print "%-20s %s" % (upper.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.iteritems())) - - @classmethod - def lower_bonds(self): - for lower,fval in self.overload_fields.iteritems(): - print "%-20s %s" % (lower.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.iteritems())) - - def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields): - self.time = time.time() - self.sent_time = 0 - if self.name is None: - self.name = self.__class__.__name__ - self.aliastypes = [ self.__class__ ] + self.aliastypes - self.default_fields = {} - self.overloaded_fields = {} - self.fields={} - self.fieldtype={} - self.packetfields=[] - self.__dict__["payload"] = NoPayload() - self.init_fields() - self.underlayer = _underlayer - self.initialized = 1 - if _pkt: - self.dissect(_pkt) - if not _internal: - self.dissection_done(self) - for f in fields.keys(): - self.fields[f] = self.get_field(f).any2i(self,fields[f]) - if type(post_transform) is list: - self.post_transforms = post_transform - elif post_transform is None: - self.post_transforms = [] - else: - self.post_transforms = [post_transform] - - def init_fields(self): - self.do_init_fields(self.fields_desc) - - def do_init_fields(self, flist): - for f in flist: - self.default_fields[f.name] = f.default - self.fieldtype[f.name] = f - if f.holds_packets: - self.packetfields.append(f) - - def dissection_done(self,pkt): - """DEV: will be called after a dissection is completed""" - self.post_dissection(pkt) - self.payload.dissection_done(pkt) - - def post_dissection(self, pkt): - """DEV: is called after the dissection of the whole packet""" - pass - - def get_field(self, fld): - """DEV: returns the field instance from the name of the field""" - return self.fieldtype[fld] - - def add_payload(self, payload): - if payload is None: - return - elif not isinstance(self.payload, NoPayload): - self.payload.add_payload(payload) - else: - if isinstance(payload, Packet): - self.__dict__["payload"] = payload - payload.add_underlayer(self) - for t in self.aliastypes: - if payload.overload_fields.has_key(t): - self.overloaded_fields = payload.overload_fields[t] - break - elif type(payload) is str: - self.__dict__["payload"] = Raw(load=payload) - else: - raise TypeError("payload must be either 'Packet' or 'str', not [%s]" % repr(payload)) - def remove_payload(self): - self.payload.remove_underlayer(self) - self.__dict__["payload"] = NoPayload() - self.overloaded_fields = {} - def add_underlayer(self, underlayer): - self.underlayer = underlayer - def remove_underlayer(self,other): - self.underlayer = None - def copy(self): - """Returns a deep copy of the instance.""" - clone = self.__class__() - clone.fields = self.fields.copy() - for k in clone.fields: - clone.fields[k]=self.get_field(k).do_copy(clone.fields[k]) - clone.default_fields = self.default_fields.copy() - clone.overloaded_fields = self.overloaded_fields.copy() - clone.overload_fields = self.overload_fields.copy() - clone.underlayer=self.underlayer - clone.explicit=self.explicit - clone.post_transforms=self.post_transforms[:] - clone.__dict__["payload"] = self.payload.copy() - clone.payload.add_underlayer(clone) - return clone - - def getfieldval(self, attr): - if attr in self.fields: - return self.fields[attr] - if attr in self.overloaded_fields: - return self.overloaded_fields[attr] - if attr in self.default_fields: - return self.default_fields[attr] - return self.payload.getfieldval(attr) - - def getfield_and_val(self, attr): - if attr in self.fields: - return self.get_field(attr),self.fields[attr] - if attr in self.overloaded_fields: - return self.get_field(attr),self.overloaded_fields[attr] - if attr in self.default_fields: - return self.get_field(attr),self.default_fields[attr] - return self.payload.getfield_and_val(attr) - - def __getattr__(self, attr): - if self.initialized: - fld,v = self.getfield_and_val(attr) - if fld is not None: - return fld.i2h(self, v) - return v - raise AttributeError(attr) - - def setfieldval(self, attr, val): - if self.default_fields.has_key(attr): - fld = self.get_field(attr) - if fld is None: - any2i = lambda x,y: y - else: - any2i = fld.any2i - self.fields[attr] = any2i(self, val) - self.explicit=0 - elif attr == "payload": - self.remove_payload() - self.add_payload(val) - else: - self.payload.setfieldval(attr,val) - - def __setattr__(self, attr, val): - if self.initialized: - try: - self.setfieldval(attr,val) - except AttributeError: - pass - else: - return - self.__dict__[attr] = val - - def delfieldval(self, attr): - if self.fields.has_key(attr): - del(self.fields[attr]) - self.explicit=0 # in case a default value must be explicited - elif self.default_fields.has_key(attr): - pass - elif attr == "payload": - self.remove_payload() - else: - self.payload.delfieldval(attr) - - def __delattr__(self, attr): - if self.initialized: - try: - self.delfieldval(attr) - except AttributeError: - pass - else: - return - if self.__dict__.has_key(attr): - del(self.__dict__[attr]) - else: - raise AttributeError(attr) - - def __repr__(self): - s = "" - ct = conf.color_theme - for f in self.fields_desc: - if isinstance(f, ConditionalField) and not f._evalcond(self): - continue - if f.name in self.fields: - val = f.i2repr(self, self.fields[f.name]) - elif f.name in self.overloaded_fields: - val = f.i2repr(self, self.overloaded_fields[f.name]) - else: - continue - if isinstance(f, Emph): - ncol = ct.emph_field_name - vcol = ct.emph_field_value - else: - ncol = ct.field_name - vcol = ct.field_value - - - s += " %s%s%s" % (ncol(f.name), - ct.punct("="), - vcol(val)) - return "%s%s %s %s%s%s"% (ct.punct("<"), - ct.layer_name(self.__class__.__name__), - s, - ct.punct("|"), - repr(self.payload), - ct.punct(">")) - def __str__(self): - return self.build() - def __div__(self, other): - if isinstance(other, Packet): - cloneA = self.copy() - cloneB = other.copy() - cloneA.add_payload(cloneB) - return cloneA - elif type(other) is str: - return self/Raw(load=other) - else: - return other.__rdiv__(self) - def __rdiv__(self, other): - if type(other) is str: - return Raw(load=other)/self - else: - raise TypeError - def __mul__(self, other): - if type(other) is int: - return [self]*other - else: - raise TypeError - def __rmul__(self,other): - return self.__mul__(other) - - def __nonzero__(self): - return True - def __len__(self): - return len(self.__str__()) - def do_build(self): - p="" - for f in self.fields_desc: - p = f.addfield(self, p, self.getfieldval(f.name)) - return p - - def post_build(self, pkt, pay): - """DEV: called right after the current layer is build.""" - return pkt+pay - - def build_payload(self): - return self.payload.build(internal=1) - - def build(self,internal=0): - if not self.explicit: - self = self.__iter__().next() - pkt = self.do_build() - for t in self.post_transforms: - pkt = t(pkt) - pay = self.build_payload() - try: - p = self.post_build(pkt,pay) - except TypeError: - log_runtime.error("API changed! post_build() now takes 2 arguments. Compatibility is only assured for a short transition time") - p = self.post_build(pkt+pay) - if not internal: - pad = self.payload.getlayer(Padding) - if pad: - p += pad.build() - p = self.build_done(p) - return p - - def build_done(self, p): - return self.payload.build_done(p) - - def do_build_ps(self): - p="" - pl = [] - q="" - for f in self.fields_desc: - p = f.addfield(self, p, self.getfieldval(f.name) ) - if type(p) is str: - r = p[len(q):] - q = p - else: - r = "" - pl.append( (f, f.i2repr(self,self.getfieldval(f.name)), r) ) - - pkt,lst = self.payload.build_ps(internal=1) - p += pkt - lst.append( (self, pl) ) - - return p,lst - - def build_ps(self,internal=0): - p,lst = self.do_build_ps() -# if not internal: -# pkt = self -# while pkt.haslayer(Padding): -# pkt = pkt.getlayer(Padding) -# lst.append( (pkt, [ ("loakjkjd", pkt.load, pkt.load) ] ) ) -# p += pkt.load -# pkt = pkt.payload - return p,lst - - - def psdump(self, filename=None, **kargs): - """psdump(filename=None, layer_shift=0, rebuild=1) -Creates an EPS file describing a packet. If filename is not provided a temporary file is created and gs is called.""" - canvas = self.canvas_dump(**kargs) - if filename is None: - fname = "/tmp/scapy.%i"%os.getpid() - canvas.writeEPSfile(fname) - os.system("%s '%s.eps' &" % (conf.prog.psreader,fname)) - else: - canvas.writeEPSfile(filename) - - def pdfdump(self, filename=None, **kargs): - """pdfdump(filename=None, layer_shift=0, rebuild=1) - Creates a PDF file describing a packet. If filename is not provided a temporary file is created and xpdf is called.""" - canvas = self.canvas_dump(**kargs) - if filename is None: - fname = "/tmp/scapy.%i"%os.getpid() - canvas.writePDFfile(fname) - os.system("%s '%s.pdf' &" % (conf.prog.pdfreader,fname)) - else: - canvas.writePDFfile(filename) - - - def canvas_dump(self, layer_shift=0, rebuild=1): - canvas = pyx.canvas.canvas() - if rebuild: - p,t = self.__class__(str(self)).build_ps() - else: - p,t = self.build_ps() - YTXT=len(t) - for n,l in t: - YTXT += len(l) - YTXT = float(YTXT) - YDUMP=YTXT - - XSTART = 1 - XDSTART = 10 - y = 0.0 - yd = 0.0 - xd = 0 - XMUL= 0.55 - YMUL = 0.4 - - backcolor=colgen(0.6, 0.8, 1.0, trans=pyx.color.rgb) - forecolor=colgen(0.2, 0.5, 0.8, trans=pyx.color.rgb) -# backcolor=makecol(0.376, 0.729, 0.525, 1.0) - - - def hexstr(x): - s = [] - for c in x: - s.append("%02x" % ord(c)) - return " ".join(s) - - - def make_dump_txt(x,y,txt): - return pyx.text.text(XDSTART+x*XMUL, (YDUMP-y)*YMUL, r"\tt{%s}"%hexstr(txt), [pyx.text.size.Large]) - - def make_box(o): - return pyx.box.rect(o.left(), o.bottom(), o.width(), o.height(), relcenter=(0.5,0.5)) - - def make_frame(lst): - if len(lst) == 1: - b = lst[0].bbox() - b.enlarge(pyx.unit.u_pt) - return b.path() - else: - fb = lst[0].bbox() - fb.enlarge(pyx.unit.u_pt) - lb = lst[-1].bbox() - lb.enlarge(pyx.unit.u_pt) - if len(lst) == 2 and fb.left() > lb.right(): - return pyx.path.path(pyx.path.moveto(fb.right(), fb.top()), - pyx.path.lineto(fb.left(), fb.top()), - pyx.path.lineto(fb.left(), fb.bottom()), - pyx.path.lineto(fb.right(), fb.bottom()), - pyx.path.moveto(lb.left(), lb.top()), - pyx.path.lineto(lb.right(), lb.top()), - pyx.path.lineto(lb.right(), lb.bottom()), - pyx.path.lineto(lb.left(), lb.bottom())) - else: - # XXX - gb = lst[1].bbox() - if gb != lb: - gb.enlarge(pyx.unit.u_pt) - kb = lst[-2].bbox() - if kb != gb and kb != lb: - kb.enlarge(pyx.unit.u_pt) - return pyx.path.path(pyx.path.moveto(fb.left(), fb.top()), - pyx.path.lineto(fb.right(), fb.top()), - pyx.path.lineto(fb.right(), kb.bottom()), - pyx.path.lineto(lb.right(), kb.bottom()), - pyx.path.lineto(lb.right(), lb.bottom()), - pyx.path.lineto(lb.left(), lb.bottom()), - pyx.path.lineto(lb.left(), gb.top()), - pyx.path.lineto(fb.left(), gb.top()), - pyx.path.closepath(),) - - - def make_dump(s, shift=0, y=0, col=None, bkcol=None, larg=16): - c = pyx.canvas.canvas() - tlist = [] - while s: - dmp,s = s[:larg-shift],s[larg-shift:] - txt = make_dump_txt(shift, y, dmp) - tlist.append(txt) - shift += len(dmp) - if shift >= 16: - shift = 0 - y += 1 - if col is None: - col = pyx.color.rgb.red - if bkcol is None: - col = pyx.color.rgb.white - c.stroke(make_frame(tlist),[col,pyx.deco.filled([bkcol]),pyx.style.linewidth.Thick]) - for txt in tlist: - c.insert(txt) - return c, tlist[-1].bbox(), shift, y - - - last_shift,last_y=0,0.0 - while t: - bkcol = backcolor.next() - proto,fields = t.pop() - y += 0.5 - pt = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % proto.name, [ pyx.text.size.Large]) - y += 1 - ptbb=pt.bbox() - ptbb.enlarge(pyx.unit.u_pt*2) - canvas.stroke(ptbb.path(),[pyx.color.rgb.black, pyx.deco.filled([bkcol])]) - canvas.insert(pt) - for fname, fval, fdump in fields: - col = forecolor.next() - ft = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fname.name)) - if fval is not None: - if len(fval) > 18: - fval = fval[:18]+"[...]" - else: - fval="" - vt = pyx.text.text(XSTART+3, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fval)) - y += 1.0 - if fdump: - dt,target,last_shift,last_y = make_dump(fdump, last_shift, last_y, col, bkcol) - - dtb = dt.bbox() - dtb=target - vtb = vt.bbox() - bxvt = make_box(vtb) - bxdt = make_box(dtb) - dtb.enlarge(pyx.unit.u_pt) - try: - if yd < 0: - cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=-90) - else: - cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=90) - except: - pass - else: - canvas.stroke(cnx,[pyx.style.linewidth.thin,pyx.deco.earrow.small,col]) - - canvas.insert(dt) - - canvas.insert(ft) - canvas.insert(vt) - last_y += layer_shift - - return canvas - - - - def extract_padding(self, s): - """DEV: to be overloaded to extract current layer's padding. Return a couple of strings (actual layer, padding)""" - return s,None - - def post_dissect(self, s): - """DEV: is called right after the current layer has been dissected""" - return s - - def pre_dissect(self, s): - """DEV: is called right before the current layer is dissected""" - return s - - def do_dissect(self, s): - flist = self.fields_desc[:] - flist.reverse() - while s and flist: - f = flist.pop() - s,fval = f.getfield(self, s) - self.fields[f.name] = fval - - return s - - def do_dissect_payload(self, s): - if s: - cls = self.guess_payload_class(s) - try: - p = cls(s, _internal=1, _underlayer=self) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - if isinstance(cls,type) and issubclass(cls,Packet): - log_runtime.error("%s dissector failed" % cls.name) - else: - log_runtime.error("%s.guess_payload_class() returned [%s]" % (self.__class__.__name__,repr(cls))) - if cls is not None: - raise - p = Raw(s, _internal=1, _underlayer=self) - self.add_payload(p) - - def dissect(self, s): - s = self.pre_dissect(s) - - s = self.do_dissect(s) - - s = self.post_dissect(s) - - payl,pad = self.extract_padding(s) - self.do_dissect_payload(payl) - if pad and conf.padding: - self.add_payload(Padding(pad)) - - - def guess_payload_class(self, payload): - """DEV: Guesses the next payload class from layer bonds. Can be overloaded to use a different mechanism.""" - for t in self.aliastypes: - for fval, cls in t.payload_guess: - ok = 1 - for k in fval.keys(): - if not hasattr(self, k) or fval[k] != self.getfieldval(k): - ok = 0 - break - if ok: - return cls - return self.default_payload_class(payload) - - def default_payload_class(self, payload): - """DEV: Returns the default payload class if nothing has been found by the guess_payload_class() method.""" - return Raw - - def hide_defaults(self): - """Removes fields' values that are the same as default values.""" - for k in self.fields.keys(): - if self.default_fields.has_key(k): - if self.default_fields[k] == self.fields[k]: - del(self.fields[k]) - self.payload.hide_defaults() - - def clone_with(self, payload=None, **kargs): - pkt = self.__class__() - pkt.explicit = 1 - pkt.fields = kargs - pkt.time = self.time - pkt.underlayer = self.underlayer - pkt.overload_fields = self.overload_fields.copy() - pkt.post_transforms = self.post_transforms - if payload is not None: - pkt.add_payload(payload) - return pkt - - - def __iter__(self): - def loop(todo, done, self=self): - if todo: - eltname = todo.pop() - elt = self.getfieldval(eltname) - if not isinstance(elt, Gen): - if self.get_field(eltname).islist: - elt = SetGen([elt]) - else: - elt = SetGen(elt) - for e in elt: - done[eltname]=e - for x in loop(todo[:], done): - yield x - else: - if isinstance(self.payload,NoPayload): - payloads = [None] - else: - payloads = self.payload - for payl in payloads: - done2=done.copy() - for k in done2: - if isinstance(done2[k], VolatileValue): - done2[k] = done2[k]._fix() - pkt = self.clone_with(payload=payl, **done2) - yield pkt - - if self.explicit: - todo = [] - done = self.fields - else: - todo = [ k for (k,v) in itertools.chain(self.default_fields.iteritems(), - self.overloaded_fields.iteritems()) - if isinstance(v, VolatileValue) ] + self.fields.keys() - done = {} - return loop(todo, done) - - def __gt__(self, other): - """True if other is an answer from self (self ==> other).""" - if isinstance(other, Packet): - return other < self - elif type(other) is str: - return 1 - else: - raise TypeError((self, other)) - def __lt__(self, other): - """True if self is an answer from other (other ==> self).""" - if isinstance(other, Packet): - return self.answers(other) - elif type(other) is str: - return 1 - else: - raise TypeError((self, other)) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return False - for f in self.fields_desc: - if f not in other.fields_desc: - return False - if self.getfieldval(f.name) != other.getfieldval(f.name): - return False - return self.payload == other.payload - - def __ne__(self, other): - return not self.__eq__(other) - - def hashret(self): - """DEV: returns a string that has the same value for a request and its answer.""" - return self.payload.hashret() - def answers(self, other): - """DEV: true if self is an answer from other""" - if other.__class__ == self.__class__: - return self.payload.answers(other.payload) - return 0 - - def haslayer(self, cls): - """true if self has a layer that is an instance of cls. Superseded by "cls in self" syntax.""" - if self.__class__ == cls or self.__class__.__name__ == cls: - return 1 - for f in self.packetfields: - fvalue_gen = self.getfieldval(f.name) - if fvalue_gen is None: - continue - if not f.islist: - fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) - for fvalue in fvalue_gen: - if isinstance(fvalue, Packet): - ret = fvalue.haslayer(cls) - if ret: - return ret - return self.payload.haslayer(cls) - def getlayer(self, cls, nb=1, _track=None): - """Return the nb^th layer that is an instance of cls.""" - if type(cls) is str and "." in cls: - ccls,fld = cls.split(".",1) - else: - ccls,fld = cls,None - if self.__class__ == cls or self.__class__.name == ccls: - if nb == 1: - if fld is None: - return self - else: - return self.getfieldval(fld) - else: - nb -=1 - for f in self.packetfields: - fvalue_gen = self.getfieldval(f.name) - if fvalue_gen is None: - continue - if not f.islist: - fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) - for fvalue in fvalue_gen: - if isinstance(fvalue, Packet): - track=[] - ret = fvalue.getlayer(cls, nb, _track=track) - if ret is not None: - return ret - nb = track[0] - return self.payload.getlayer(cls,nb,_track=_track) - - def __getitem__(self, cls): - if type(cls) is slice: - if cls.stop: - ret = self.getlayer(cls.start, cls.stop) - else: - ret = self.getlayer(cls.start) - if ret is None and cls.step is not None: - ret = cls.step - return ret - else: - return self.getlayer(cls) - - def __contains__(self, cls): - """"cls in self" returns true if self has a layer which is an instance of cls.""" - return self.haslayer(cls) - - - - def display(self,*args,**kargs): # Deprecated. Use show() - """Deprecated. Use show() method.""" - self.show(*args,**kargs) - def show(self, indent=3, lvl="", label_lvl=""): - """Prints a hierarchical view of the packet. "indent" gives the size of indentation for each layer.""" - ct = conf.color_theme - print "%s%s %s %s" % (label_lvl, - ct.punct("###["), - ct.layer_name(self.name), - ct.punct("]###")) - for f in self.fields_desc: - if isinstance(f, ConditionalField) and not f._evalcond(self): - continue - if isinstance(f, Emph): - ncol = ct.emph_field_name - vcol = ct.emph_field_value - else: - ncol = ct.field_name - vcol = ct.field_value - fvalue = self.getfieldval(f.name) - if isinstance(fvalue, Packet) or (f.islist and f.holds_packets and type(fvalue) is list): - print "%s \\%-10s\\" % (label_lvl+lvl, ncol(f.name)) - fvalue_gen = SetGen(fvalue,_iterpacket=0) - for fvalue in fvalue_gen: - fvalue.show(indent=indent, label_lvl=label_lvl+lvl+" |") - else: - print "%s %-10s%s %s" % (label_lvl+lvl, - ncol(f.name), - ct.punct("="), - vcol(f.i2repr(self,fvalue))) - self.payload.show(indent=indent, lvl=lvl+(" "*indent*self.show_indent), label_lvl=label_lvl) - def show2(self): - """Prints a hierarchical view of an assembled version of the packet, so that automatic fields are calculated (checksums, etc.)""" - self.__class__(str(self)).show() - - def sprintf(self, fmt, relax=1): - """sprintf(format, [relax=1]) -> str -where format is a string that can include directives. A directive begins and -ends by % and has the following format %[fmt[r],][cls[:nb].]field%. - -fmt is a classic printf directive, "r" can be appended for raw substitution -(ex: IP.flags=0x18 instead of SA), nb is the number of the layer we want -(ex: for IP/IP packets, IP:2.src is the src of the upper IP layer). -Special case : "%.time%" is the creation time. -Ex : p.sprintf("%.time% %-15s,IP.src% -> %-15s,IP.dst% %IP.chksum% " - "%03xr,IP.proto% %r,TCP.flags%") - -Moreover, the format string can include conditionnal statements. A conditionnal -statement looks like : {layer:string} where layer is a layer name, and string -is the string to insert in place of the condition if it is true, i.e. if layer -is present. If layer is preceded by a "!", the result si inverted. Conditions -can be imbricated. A valid statement can be : - p.sprintf("This is a{TCP: TCP}{UDP: UDP}{ICMP:n ICMP} packet") - p.sprintf("{IP:%IP.dst% {ICMP:%ICMP.type%}{TCP:%TCP.dport%}}") - -A side effect is that, to obtain "{" and "}" characters, you must use -"%(" and "%)". -""" - - escape = { "%": "%", - "(": "{", - ")": "}" } - - - # Evaluate conditions - while "{" in fmt: - i = fmt.rindex("{") - j = fmt[i+1:].index("}") - cond = fmt[i+1:i+j+1] - k = cond.find(":") - if k < 0: - raise Scapy_Exception("Bad condition in format string: [%s] (read sprintf doc!)"%cond) - cond,format = cond[:k],cond[k+1:] - res = False - if cond[0] == "!": - res = True - cond = cond[1:] - if self.haslayer(cond): - res = not res - if not res: - format = "" - fmt = fmt[:i]+format+fmt[i+j+2:] - - # Evaluate directives - s = "" - while "%" in fmt: - i = fmt.index("%") - s += fmt[:i] - fmt = fmt[i+1:] - if fmt and fmt[0] in escape: - s += escape[fmt[0]] - fmt = fmt[1:] - continue - try: - i = fmt.index("%") - sfclsfld = fmt[:i] - fclsfld = sfclsfld.split(",") - if len(fclsfld) == 1: - f = "s" - clsfld = fclsfld[0] - elif len(fclsfld) == 2: - f,clsfld = fclsfld - else: - raise Scapy_Exception - if "." in clsfld: - cls,fld = clsfld.split(".") - else: - cls = self.__class__.__name__ - fld = clsfld - num = 1 - if ":" in cls: - cls,num = cls.split(":") - num = int(num) - fmt = fmt[i+1:] - except: - raise Scapy_Exception("Bad format string [%%%s%s]" % (fmt[:25], fmt[25:] and "...")) - else: - if fld == "time": - val = time.strftime("%H:%M:%S.%%06i", time.localtime(self.time)) % int((self.time-int(self.time))*1000000) - elif cls == self.__class__.__name__ and hasattr(self, fld): - if num > 1: - val = self.payload.sprintf("%%%s,%s:%s.%s%%" % (f,cls,num-1,fld), relax) - f = "s" - elif f[-1] == "r": # Raw field value - val = getattr(self,fld) - f = f[:-1] - if not f: - f = "s" - else: - val = getattr(self,fld) - if fld in self.fieldtype: - val = self.fieldtype[fld].i2repr(self,val) - else: - val = self.payload.sprintf("%%%s%%" % sfclsfld, relax) - f = "s" - s += ("%"+f) % val - - s += fmt - return s - - def mysummary(self): - """DEV: can be overloaded to return a string that summarizes the layer. - Only one mysummary() is used in a whole packet summary: the one of the upper layer, - except if a mysummary() also returns (as a couple) a list of layers whose - mysummary() must be called if they are present.""" - return "" - - def summary(self, intern=0): - """Prints a one line summary of a packet.""" - found,s,needed = self.payload.summary(intern=1) - if s: - s = " / "+s - ret = "" - if not found or self.__class__ in needed: - ret = self.mysummary() - if type(ret) is tuple: - ret,n = ret - needed += n - if ret or needed: - found = 1 - if not ret: - ret = self.__class__.__name__ - ret = "%s%s" % (ret,s) - if intern: - return found,ret,needed - else: - return ret - - def lastlayer(self,layer=None): - """Returns the uppest layer of the packet""" - return self.payload.lastlayer(self) - - def decode_payload_as(self,cls): - """Reassembles the payload and decode it using another packet class""" - s = str(self.payload) - self.payload = cls(s) - - def libnet(self): - """Not ready yet. Should give the necessary C code that interfaces with libnet to recreate the packet""" - print "libnet_build_%s(" % self.__class__.name.lower() - det = self.__class__(str(self)) - for f in self.fields_desc: - val = det.getfieldval(f.name) - if val is None: - val = 0 - elif type(val) is int: - val = str(val) - else: - val = '"%s"' % str(val) - print "\t%s, \t\t/* %s */" % (val,f.name) - print ");" - def command(self): - """Returns a string representing the command you have to type to obtain the same packet""" - f = [] - for fn,fv in self.fields.items(): - fld = self.get_field(fn) - if isinstance(fv, Packet): - fv = fv.command() - elif fld.islist and fld.holds_packets and type(fv) is list: - fv = "[%s]" % ",".join( map(Packet.command, fv)) - else: - fv = repr(fv) - f.append("%s=%s" % (fn, fv)) - c = "%s(%s)" % (self.__class__.__name__, ", ".join(f)) - pc = self.payload.command() - if pc: - c += "/"+pc - return c - - -class ASN1_Packet(Packet): - ASN1_root = None - ASN1_codec = None - def init_fields(self): - flist = self.ASN1_root.get_fields_list() - self.do_init_fields(flist) - self.fields_desc = flist - def do_build(self): - return self.ASN1_root.build(self) - def do_dissect(self, x): - return self.ASN1_root.dissect(self, x) - - -class NoPayload(Packet,object): - def __new__(cls, *args, **kargs): - singl = cls.__dict__.get("__singl__") - if singl is None: - cls.__singl__ = singl = object.__new__(cls) - Packet.__init__(singl, *args, **kargs) - return singl - def __init__(self, *args, **kargs): - pass - def dissection_done(self,pkt): - return - def add_payload(self, payload): - raise Scapy_Exception("Can't add payload to NoPayload instance") - def remove_payload(self): - pass - def add_underlayer(self,underlayer): - pass - def remove_underlayer(self,other): - pass - def copy(self): - return self - def __repr__(self): - return "" - def __str__(self): - return "" - def __nonzero__(self): - return False - def build(self, internal=0): - return "" - def build_done(self, p): - return p - def build_ps(self, internal=0): - return "",[] - def getfieldval(self, attr): - raise AttributeError(attr) - def getfield_and_val(self, attr): - raise AttributeError(attr) - def setfieldval(self, attr, val): - raise AttributeError(attr) - def delfieldval(self, attr): - raise AttributeError(attr) - def __getattr__(self, attr): - if attr in self.__dict__: - return self.__dict__[attr] - elif attr in self.__class__.__dict__: - return self.__class__.__dict__[attr] - else: - raise AttributeError, attr - def hide_defaults(self): - pass - def __iter__(self): - return iter([]) - def __eq__(self, other): - if isinstance(other, NoPayload): - return True - return False - def hashret(self): - return "" - def answers(self, other): - return isinstance(other, NoPayload) or isinstance(other, Padding) - def haslayer(self, cls): - return 0 - def getlayer(self, cls, nb=1, _track=None): - if _track is not None: - _track.append(nb) - return None - def show(self, indent=3, lvl="", label_lvl=""): - pass - def sprintf(self, fmt, relax): - if relax: - return "??" - else: - raise Scapy_Exception("Format not found [%s]"%fmt) - def summary(self, intern=0): - return 0,"",[] - def lastlayer(self,layer): - return layer - def command(self): - return "" - - -#################### -## packet classes ## -#################### - - -class Raw(Packet): - name = "Raw" - fields_desc = [ StrField("load", "") ] - def answers(self, other): - return 1 -# s = str(other) -# t = self.load -# l = min(len(s), len(t)) -# return s[:l] == t[:l] - -class Padding(Raw): - name = "Padding" - def build(self, internal=0): - if internal: - return "" - else: - return Raw.build(self) - -class Ether(Packet): - name = "Ethernet" - fields_desc = [ DestMACField("dst"), - SourceMACField("src"), - XShortEnumField("type", 0x0000, ETHER_TYPES) ] - def hashret(self): - return struct.pack("H",self.type)+self.payload.hashret() - def answers(self, other): - if isinstance(other,Ether): - if self.type == other.type: - return self.payload.answers(other.payload) - return 0 - def mysummary(self): - return self.sprintf("%src% > %dst% (%type%)") - -class PPPoE(Packet): - name = "PPP over Ethernet" - fields_desc = [ BitField("version", 1, 4), - BitField("type", 1, 4), - ByteEnumField("code", 0, {0:"Session"}), - XShortField("sessionid", 0x0), - ShortField("len", None) ] - - def post_build(self, p, pay): - p += pay - if self.len is None: - l = len(p)-6 - p = p[:4]+struct.pack("!H", l)+p[6:] - return p - -class PPPoED(PPPoE): - name = "PPP over Ethernet Discovery" - fields_desc = [ BitField("version", 1, 4), - BitField("type", 1, 4), - ByteEnumField("code", 0x09, {0x09:"PADI",0x07:"PADO",0x19:"PADR",0x65:"PADS",0xa7:"PADT"}), - XShortField("sessionid", 0x0), - ShortField("len", None) ] - -class Dot3(Packet): - name = "802.3" - fields_desc = [ MACField("dst", ETHER_BROADCAST), - MACField("src", ETHER_ANY), - LenField("len", None, "H") ] - def extract_padding(self,s): - l = self.len - return s[:l],s[l:] - def answers(self, other): - if isinstance(other,Dot3): - return self.payload.answers(other.payload) - return 0 - def mysummary(self): - return "802.3 %s > %s" % (self.src, self.dst) - - -class LLC(Packet): - name = "LLC" - fields_desc = [ XByteField("dsap", 0x00), - XByteField("ssap", 0x00), - ByteField("ctrl", 0) ] - - -class CookedLinux(Packet): - name = "cooked linux" - fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast", - 4:"sent-by-us"}), #XXX incomplete - XShortField("lladdrtype",512), - ShortField("lladdrlen",0), - StrFixedLenField("src","",8), - XShortEnumField("proto",0x800,ETHER_TYPES) ] - - - -class SNAP(Packet): - name = "SNAP" - fields_desc = [ X3BytesField("OUI",0x000000), - XShortEnumField("code", 0x000, ETHER_TYPES) ] - - -class Dot1Q(Packet): - name = "802.1Q" - aliastypes = [ Ether ] - fields_desc = [ BitField("prio", 0, 3), - BitField("id", 0, 1), - BitField("vlan", 1, 12), - XShortEnumField("type", 0x0000, ETHER_TYPES) ] - def answers(self, other): - if isinstance(other,Dot1Q): - if ( (self.type == other.type) and - (self.vlan == other.vlan) ): - return self.payload.answers(other.payload) - else: - return self.payload.answers(other) - return 0 - def default_payload_class(self, pay): - if self.type <= 1500: - return LLC - return Raw - def extract_padding(self,s): - if self.type <= 1500: - return s[:self.type],s[self.type:] - return s,None - def mysummary(self): - if isinstance(self.underlayer, Ether): - return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%") - else: - return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%") - - - - -class RadioTap(Packet): - name = "RadioTap dummy" - fields_desc = [ ByteField('version', 0), - ByteField('pad', 0), - FieldLenField('len', None, 'notdecoded', '@H', adjust=lambda pkt,x:x+8), - FlagsField('present', None, -32, ['TSFT','Flags','Rate','Channel','FHSS','dBm_AntSignal', - 'dBm_AntNoise','Lock_Quality','TX_Attenuation','dB_TX_Attenuation', - 'dBm_TX_Power', 'Antenna', 'dB_AntSignal', 'dB_AntNoise', - 'b14', 'b15','b16','b17','b18','b19','b20','b21','b22','b23', - 'b24','b25','b26','b27','b28','b29','b30','Ext']), - StrLenField('notdecoded', "", length_from= lambda pkt:pkt.len-8) ] - -class STP(Packet): - name = "Spanning Tree Protocol" - fields_desc = [ ShortField("proto", 0), - ByteField("version", 0), - ByteField("bpdutype", 0), - ByteField("bpduflags", 0), - ShortField("rootid", 0), - MACField("rootmac", ETHER_ANY), - IntField("pathcost", 0), - ShortField("bridgeid", 0), - MACField("bridgemac", ETHER_ANY), - ShortField("portid", 0), - BCDFloatField("age", 1), - BCDFloatField("maxage", 20), - BCDFloatField("hellotime", 2), - BCDFloatField("fwddelay", 15) ] - - -class EAPOL(Packet): - name = "EAPOL" - fields_desc = [ ByteField("version", 1), - ByteEnumField("type", 0, ["EAP_PACKET", "START", "LOGOFF", "KEY", "ASF"]), - LenField("len", None, "H") ] - - EAP_PACKET= 0 - START = 1 - LOGOFF = 2 - KEY = 3 - ASF = 4 - def extract_padding(self, s): - l = self.len - return s[:l],s[l:] - def hashret(self): - return chr(self.type)+self.payload.hashret() - def answers(self, other): - if isinstance(other,EAPOL): - if ( (self.type == self.EAP_PACKET) and - (other.type == self.EAP_PACKET) ): - return self.payload.answers(other.payload) - return 0 - def mysummary(self): - return self.sprintf("EAPOL %EAPOL.type%") - - -class EAP(Packet): - name = "EAP" - fields_desc = [ ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}), - ByteField("id", 0), - ShortField("len",None), - ConditionalField(ByteEnumField("type",0, {1:"ID",4:"MD5"}), lambda pkt:pkt.code not in [EAP.SUCCESS, EAP.FAILURE]) - - ] - - REQUEST = 1 - RESPONSE = 2 - SUCCESS = 3 - FAILURE = 4 - TYPE_ID = 1 - TYPE_MD5 = 4 - def answers(self, other): - if isinstance(other,EAP): - if self.code == self.REQUEST: - return 0 - elif self.code == self.RESPONSE: - if ( (other.code == self.REQUEST) and - (other.type == self.type) ): - return 1 - elif other.code == self.RESPONSE: - return 1 - return 0 - - def post_build(self, p, pay): - if self.len is None: - l = len(p)+len(pay) - p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:] - return p+pay - - -class ARP(Packet): - name = "ARP" - fields_desc = [ XShortField("hwtype", 0x0001), - XShortEnumField("ptype", 0x0800, ETHER_TYPES), - ByteField("hwlen", 6), - ByteField("plen", 4), - ShortEnumField("op", 1, {"who-has":1, "is-at":2, "RARP-req":3, "RARP-rep":4, "Dyn-RARP-req":5, "Dyn-RAR-rep":6, "Dyn-RARP-err":7, "InARP-req":8, "InARP-rep":9}), - ARPSourceMACField("hwsrc"), - SourceIPField("psrc","pdst"), - MACField("hwdst", ETHER_ANY), - IPField("pdst", "0.0.0.0") ] - who_has = 1 - is_at = 2 - def answers(self, other): - if isinstance(other,ARP): - if ( (self.op == self.is_at) and - (other.op == self.who_has) and - (self.psrc == other.pdst) ): - return 1 - return 0 - def extract_padding(self, s): - return "",s - def mysummary(self): - if self.op == self.is_at: - return "ARP is at %s says %s" % (self.hwsrc, self.psrc) - elif self.op == self.who_has: - return "ARP who has %s says %s" % (self.pdst, self.psrc) - else: - return "ARP %ARP.op% %ARP.psrc% > %ARP.pdst%" - - -class IP(Packet, IPTools): - name = "IP" - fields_desc = [ BitField("version" , 4 , 4), - BitField("ihl", None, 4), - XByteField("tos", 0), - ShortField("len", None), - ShortField("id", 1), - FlagsField("flags", 0, 3, ["MF","DF","evil"]), - BitField("frag", 0, 13), - ByteField("ttl", 64), - ByteEnumField("proto", 0, IP_PROTOS), - XShortField("chksum", None), - #IPField("src", "127.0.0.1"), - Emph(SourceIPField("src","dst")), - Emph(IPField("dst", "127.0.0.1")), - IPoptionsField("options", "") ] - def post_build(self, p, pay): - ihl = self.ihl - if ihl is None: - ihl = len(p)/4 - p = chr(((self.version&0xf)<<4) | ihl&0x0f)+p[1:] - if self.len is None: - l = len(p)+len(pay) - p = p[:2]+struct.pack("!H", l)+p[4:] - if self.chksum is None: - ck = checksum(p) - p = p[:10]+chr(ck>>8)+chr(ck&0xff)+p[12:] - return p+pay - - def extract_padding(self, s): - l = self.len - (self.ihl << 2) - return s[:l],s[l:] - - def send(self, s, slp=0): - for p in self: - try: - s.sendto(str(p), (p.dst,0)) - except socket.error, msg: - log_runtime.error(msg) - if slp: - time.sleep(slp) - def hashret(self): - if ( (self.proto == socket.IPPROTO_ICMP) - and (isinstance(self.payload, ICMP)) - and (self.payload.type in [3,4,5,11,12]) ): - return self.payload.payload.hashret() - else: - if conf.checkIPsrc and conf.checkIPaddr: - return strxor(inet_aton(self.src),inet_aton(self.dst))+struct.pack("B",self.proto)+self.payload.hashret() - else: - return struct.pack("B", self.proto)+self.payload.hashret() - def answers(self, other): - if not isinstance(other,IP): - return 0 - if conf.checkIPaddr and (self.dst != other.src): - return 0 - if ( (self.proto == socket.IPPROTO_ICMP) and - (isinstance(self.payload, ICMP)) and - (self.payload.type in [3,4,5,11,12]) ): - # ICMP error message - return self.payload.payload.answers(other) - - else: - if ( (conf.checkIPaddr and (self.src != other.dst)) or - (self.proto != other.proto) ): - return 0 - return self.payload.answers(other.payload) - def mysummary(self): - s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") - if self.frag: - s += " frag:%i" % self.frag - return s - - - -class TCP(Packet): - name = "TCP" - fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES), - ShortEnumField("dport", 80, TCP_SERVICES), - IntField("seq", 0), - IntField("ack", 0), - BitField("dataofs", None, 4), - BitField("reserved", 0, 4), - FlagsField("flags", 0x2, 8, "FSRPAUEC"), - ShortField("window", 8192), - XShortField("chksum", None), - ShortField("urgptr", 0), - TCPOptionsField("options", {}) ] - def post_build(self, p, pay): - p += pay - dataofs = self.dataofs - if dataofs is None: - dataofs = 5+((len(self.get_field("options").i2m(self,self.options))+3)/4) - p = p[:12]+chr((dataofs << 4) | ord(p[12])&0x0f)+p[13:] - if self.chksum is None: - if isinstance(self.underlayer, IP): - if self.underlayer.len is not None: - ln = self.underlayer.len-20 - else: - ln = len(p) - psdhdr = struct.pack("!4s4sHH", - inet_aton(self.underlayer.src), - inet_aton(self.underlayer.dst), - self.underlayer.proto, - ln) - ck=checksum(psdhdr+p) - p = p[:16]+struct.pack("!H", ck)+p[18:] - elif isinstance(self.underlayer, IPv6) or isinstance(self.underlayer, _IPv6OptionHeader): - ck = in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) - p = p[:16]+struct.pack("!H", ck)+p[18:] - else: - warning("No IP underlayer to compute checksum. Leaving null.") - return p - def hashret(self): - if conf.checkIPsrc: - return struct.pack("H",self.sport ^ self.dport)+self.payload.hashret() - else: - return self.payload.hashret() - def answers(self, other): - if not isinstance(other, TCP): - return 0 - if conf.checkIPsrc: - if not ((self.sport == other.dport) and - (self.dport == other.sport)): - return 0 - if (abs(other.seq-self.ack) > 2+len(other.payload)): - return 0 - return 1 - def mysummary(self): - if isinstance(self.underlayer, IP): - return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") - elif isinstance(self.underlayer, IPv6): - return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") - else: - return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") - -class UDP(Packet): - name = "UDP" - fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES), - ShortEnumField("dport", 53, UDP_SERVICES), - ShortField("len", None), - XShortField("chksum", None), ] - def post_build(self, p, pay): - p += pay - l = self.len - if l is None: - l = len(p) - p = p[:4]+struct.pack("!H",l)+p[6:] - if self.chksum is None: - if isinstance(self.underlayer, IP): - if self.underlayer.len is not None: - ln = self.underlayer.len-20 - else: - ln = len(p) - psdhdr = struct.pack("!4s4sHH", - inet_aton(self.underlayer.src), - inet_aton(self.underlayer.dst), - self.underlayer.proto, - ln) - ck=checksum(psdhdr+p) - p = p[:6]+struct.pack("!H", ck)+p[8:] - elif isinstance(self.underlayer, IPv6) or isinstance(self.underlayer, _IPv6OptionHeader): - ck = in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) - p = p[:6]+struct.pack("!H", ck)+p[8:] - else: - warning("No IP underlayer to compute checksum. Leaving null.") - return p - def extract_padding(self, s): - l = self.len - 8 - return s[:l],s[l:] - def hashret(self): - return self.payload.hashret() - def answers(self, other): - if not isinstance(other, UDP): - return 0 - if conf.checkIPsrc: - if self.dport != other.sport: - return 0 - return self.payload.answers(other.payload) - def mysummary(self): - if isinstance(self.underlayer, IP): - return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") - elif isinstance(self.underlayer, IPv6): - return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") - else: - return self.sprintf("UDP %UDP.sport% > %UDP.dport%") - -icmptypes = { 0 : "echo-reply", - 3 : "dest-unreach", - 4 : "source-quench", - 5 : "redirect", - 8 : "echo-request", - 9 : "router-advertisement", - 10 : "router-solicitation", - 11 : "time-exceeded", - 12 : "parameter-problem", - 13 : "timestamp-request", - 14 : "timestamp-reply", - 15 : "information-request", - 16 : "information-response", - 17 : "address-mask-request", - 18 : "address-mask-reply" } - -class ICMP(Packet): - name = "ICMP" - fields_desc = [ ByteEnumField("type",8, icmptypes), - ByteField("code",0), - XShortField("chksum", None), - ConditionalField(XShortField("id",0), lambda pkt:pkt.type in [0,8,13,14,15,16]), - ConditionalField(XShortField("seq",0), lambda pkt:pkt.type in [0,8,13,14,15,16]), - ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13,14]), - ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13,14]), - ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13,14]), - ConditionalField(IPField("gw","0.0.0.0"), lambda pkt:pkt.type==5), - ConditionalField(ByteField("ptr",0), lambda pkt:pkt.type==12), - ConditionalField(X3BytesField("reserved",0), lambda pkt:pkt.type==12), - ConditionalField(IntField("unused",0), lambda pkt:pkt.type not in [0,5,8,12,13,14,15,16]), - ] - def post_build(self, p, pay): - p += pay - if self.chksum is None: - ck = checksum(p) - p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] - return p - - def hashret(self): - return struct.pack("HH",self.id,self.seq)+self.payload.hashret() - def answers(self, other): - if not isinstance(other,ICMP): - return 0 - if ( (other.type,self.type) in [(8,0),(13,14),(15,16),(17,18)] and - self.id == other.id and - self.seq == other.seq ): - return 1 - return 0 - - def guess_payload_class(self, payload): - if self.type in [3,4,5,11,12]: - return IPerror - else: - return None - def mysummary(self): - if isinstance(self.underlayer, IP): - return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%") - else: - return self.sprintf("ICMP %ICMP.type% %ICMP.code%") - - - - - -class IPerror(IP): - name = "IP in ICMP" - def answers(self, other): - if not isinstance(other, IP): - return 0 - if not ( ((conf.checkIPsrc == 0) or (self.dst == other.dst)) and - (self.src == other.src) and - ( ((conf.checkIPID == 0) - or (self.id == other.id) - or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and - (self.proto == other.proto) ): - return 0 - return self.payload.answers(other.payload) - def mysummary(self): - return Packet.mysummary(self) - - -class TCPerror(TCP): - name = "TCP in ICMP" - def answers(self, other): - if not isinstance(other, TCP): - return 0 - if conf.checkIPsrc: - if not ((self.sport == other.sport) and - (self.dport == other.dport)): - return 0 - if conf.check_TCPerror_seqack: - if self.seq is not None: - if self.seq != other.seq: - return 0 - if self.ack is not None: - if self.ack != other.ack: - return 0 - return 1 - def mysummary(self): - return Packet.mysummary(self) - - -class UDPerror(UDP): - name = "UDP in ICMP" - def answers(self, other): - if not isinstance(other, UDP): - return 0 - if conf.checkIPsrc: - if not ((self.sport == other.sport) and - (self.dport == other.dport)): - return 0 - return 1 - def mysummary(self): - return Packet.mysummary(self) - - - -class ICMPerror(ICMP): - name = "ICMP in ICMP" - def answers(self, other): - if not isinstance(other,ICMP): - return 0 - if not ((self.type == other.type) and - (self.code == other.code)): - return 0 - if self.code in [0,8,13,14,17,18]: - if (self.id == other.id and - self.seq == other.seq): - return 1 - else: - return 0 - else: - return 1 - def mysummary(self): - return Packet.mysummary(self) - -class IPv6(Packet): - """See http://namabiiru.hongo.wide.ad.jp/scapy6""" - name = "IPv6 not implemented here." - def __init__(self, *args, **kargs): - log_interactive.error(self.name) - def __repr__(self): - return "<IPv6: ERROR not implemented>" - -class _IPv6OptionHeader(Packet): - """See http://namabiiru.hongo.wide.ad.jp/scapy6""" - name = "IPv6 not implemented here." - def __init__(self, *args, **kargs): - log_interactive.error(self.name) - def __repr__(self): - return "<IPv6: ERROR not implemented>" - - -class L2TP(Packet): - fields_desc = [ ShortEnumField("pkt_type",2,{2:"data"}), - ShortField("len", None), - ShortField("tunnel_id", 0), - ShortField("session_id", 0), - ShortField("ns", 0), - ShortField("nr", 0), - ShortField("offset", 0) ] - - def post_build(self, pkt, pay): - if self.len is None: - l = len(pkt)+len(pay) - pkt = pkt[:2]+struct.pack("!H", l)+pkt[4:] - return pkt+pay - - -_PPP_proto = { 0x0001: "Padding Protocol", - 0x0003: "ROHC small-CID [RFC3095]", - 0x0005: "ROHC large-CID [RFC3095]", - 0x0021: "Internet Protocol version 4", - 0x0023: "OSI Network Layer", - 0x0025: "Xerox NS IDP", - 0x0027: "DECnet Phase IV", - 0x0029: "Appletalk", - 0x002b: "Novell IPX", - 0x002d: "Van Jacobson Compressed TCP/IP", - 0x002f: "Van Jacobson Uncompressed TCP/IP", - 0x0031: "Bridging PDU", - 0x0033: "Stream Protocol (ST-II)", - 0x0035: "Banyan Vines", - 0x0037: "reserved (until 1993) [Typo in RFC1172]", - 0x0039: "AppleTalk EDDP", - 0x003b: "AppleTalk SmartBuffered", - 0x003d: "Multi-Link [RFC1717]", - 0x003f: "NETBIOS Framing", - 0x0041: "Cisco Systems", - 0x0043: "Ascom Timeplex", - 0x0045: "Fujitsu Link Backup and Load Balancing (LBLB)", - 0x0047: "DCA Remote Lan", - 0x0049: "Serial Data Transport Protocol (PPP-SDTP)", - 0x004b: "SNA over 802.2", - 0x004d: "SNA", - 0x004f: "IPv6 Header Compression", - 0x0051: "KNX Bridging Data [ianp]", - 0x0053: "Encryption [Meyer]", - 0x0055: "Individual Link Encryption [Meyer]", - 0x0057: "Internet Protocol version 6 [Hinden]", - 0x0059: "PPP Muxing [RFC3153]", - 0x005b: "Vendor-Specific Network Protocol (VSNP) [RFC3772]", - 0x0061: "RTP IPHC Full Header [RFC3544]", - 0x0063: "RTP IPHC Compressed TCP [RFC3544]", - 0x0065: "RTP IPHC Compressed Non TCP [RFC3544]", - 0x0067: "RTP IPHC Compressed UDP 8 [RFC3544]", - 0x0069: "RTP IPHC Compressed RTP 8 [RFC3544]", - 0x006f: "Stampede Bridging", - 0x0071: "Reserved [Fox]", - 0x0073: "MP+ Protocol [Smith]", - 0x007d: "reserved (Control Escape) [RFC1661]", - 0x007f: "reserved (compression inefficient [RFC1662]", - 0x0081: "Reserved Until 20-Oct-2000 [IANA]", - 0x0083: "Reserved Until 20-Oct-2000 [IANA]", - 0x00c1: "NTCITS IPI [Ungar]", - 0x00cf: "reserved (PPP NLID)", - 0x00fb: "single link compression in multilink [RFC1962]", - 0x00fd: "compressed datagram [RFC1962]", - 0x00ff: "reserved (compression inefficient)", - 0x0201: "802.1d Hello Packets", - 0x0203: "IBM Source Routing BPDU", - 0x0205: "DEC LANBridge100 Spanning Tree", - 0x0207: "Cisco Discovery Protocol [Sastry]", - 0x0209: "Netcs Twin Routing [Korfmacher]", - 0x020b: "STP - Scheduled Transfer Protocol [Segal]", - 0x020d: "EDP - Extreme Discovery Protocol [Grosser]", - 0x0211: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", - 0x0213: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", - 0x0231: "Luxcom", - 0x0233: "Sigma Network Systems", - 0x0235: "Apple Client Server Protocol [Ridenour]", - 0x0281: "MPLS Unicast [RFC3032] ", - 0x0283: "MPLS Multicast [RFC3032]", - 0x0285: "IEEE p1284.4 standard - data packets [Batchelder]", - 0x0287: "ETSI TETRA Network Protocol Type 1 [Nieminen]", - 0x0289: "Multichannel Flow Treatment Protocol [McCann]", - 0x2063: "RTP IPHC Compressed TCP No Delta [RFC3544]", - 0x2065: "RTP IPHC Context State [RFC3544]", - 0x2067: "RTP IPHC Compressed UDP 16 [RFC3544]", - 0x2069: "RTP IPHC Compressed RTP 16 [RFC3544]", - 0x4001: "Cray Communications Control Protocol [Stage]", - 0x4003: "CDPD Mobile Network Registration Protocol [Quick]", - 0x4005: "Expand accelerator protocol [Rachmani]", - 0x4007: "ODSICP NCP [Arvind]", - 0x4009: "DOCSIS DLL [Gaedtke]", - 0x400B: "Cetacean Network Detection Protocol [Siller]", - 0x4021: "Stacker LZS [Simpson]", - 0x4023: "RefTek Protocol [Banfill]", - 0x4025: "Fibre Channel [Rajagopal]", - 0x4027: "EMIT Protocols [Eastham]", - 0x405b: "Vendor-Specific Protocol (VSP) [RFC3772]", - 0x8021: "Internet Protocol Control Protocol", - 0x8023: "OSI Network Layer Control Protocol", - 0x8025: "Xerox NS IDP Control Protocol", - 0x8027: "DECnet Phase IV Control Protocol", - 0x8029: "Appletalk Control Protocol", - 0x802b: "Novell IPX Control Protocol", - 0x802d: "reserved", - 0x802f: "reserved", - 0x8031: "Bridging NCP", - 0x8033: "Stream Protocol Control Protocol", - 0x8035: "Banyan Vines Control Protocol", - 0x8037: "reserved (until 1993)", - 0x8039: "reserved", - 0x803b: "reserved", - 0x803d: "Multi-Link Control Protocol", - 0x803f: "NETBIOS Framing Control Protocol", - 0x8041: "Cisco Systems Control Protocol", - 0x8043: "Ascom Timeplex", - 0x8045: "Fujitsu LBLB Control Protocol", - 0x8047: "DCA Remote Lan Network Control Protocol (RLNCP)", - 0x8049: "Serial Data Control Protocol (PPP-SDCP)", - 0x804b: "SNA over 802.2 Control Protocol", - 0x804d: "SNA Control Protocol", - 0x804f: "IP6 Header Compression Control Protocol", - 0x8051: "KNX Bridging Control Protocol [ianp]", - 0x8053: "Encryption Control Protocol [Meyer]", - 0x8055: "Individual Link Encryption Control Protocol [Meyer]", - 0x8057: "IPv6 Control Protovol [Hinden]", - 0x8059: "PPP Muxing Control Protocol [RFC3153]", - 0x805b: "Vendor-Specific Network Control Protocol (VSNCP) [RFC3772]", - 0x806f: "Stampede Bridging Control Protocol", - 0x8073: "MP+ Control Protocol [Smith]", - 0x8071: "Reserved [Fox]", - 0x807d: "Not Used - reserved [RFC1661]", - 0x8081: "Reserved Until 20-Oct-2000 [IANA]", - 0x8083: "Reserved Until 20-Oct-2000 [IANA]", - 0x80c1: "NTCITS IPI Control Protocol [Ungar]", - 0x80cf: "Not Used - reserved [RFC1661]", - 0x80fb: "single link compression in multilink control [RFC1962]", - 0x80fd: "Compression Control Protocol [RFC1962]", - 0x80ff: "Not Used - reserved [RFC1661]", - 0x8207: "Cisco Discovery Protocol Control [Sastry]", - 0x8209: "Netcs Twin Routing [Korfmacher]", - 0x820b: "STP - Control Protocol [Segal]", - 0x820d: "EDPCP - Extreme Discovery Protocol Ctrl Prtcl [Grosser]", - 0x8235: "Apple Client Server Protocol Control [Ridenour]", - 0x8281: "MPLSCP [RFC3032]", - 0x8285: "IEEE p1284.4 standard - Protocol Control [Batchelder]", - 0x8287: "ETSI TETRA TNP1 Control Protocol [Nieminen]", - 0x8289: "Multichannel Flow Treatment Protocol [McCann]", - 0xc021: "Link Control Protocol", - 0xc023: "Password Authentication Protocol", - 0xc025: "Link Quality Report", - 0xc027: "Shiva Password Authentication Protocol", - 0xc029: "CallBack Control Protocol (CBCP)", - 0xc02b: "BACP Bandwidth Allocation Control Protocol [RFC2125]", - 0xc02d: "BAP [RFC2125]", - 0xc05b: "Vendor-Specific Authentication Protocol (VSAP) [RFC3772]", - 0xc081: "Container Control Protocol [KEN]", - 0xc223: "Challenge Handshake Authentication Protocol", - 0xc225: "RSA Authentication Protocol [Narayana]", - 0xc227: "Extensible Authentication Protocol [RFC2284]", - 0xc229: "Mitsubishi Security Info Exch Ptcl (SIEP) [Seno]", - 0xc26f: "Stampede Bridging Authorization Protocol", - 0xc281: "Proprietary Authentication Protocol [KEN]", - 0xc283: "Proprietary Authentication Protocol [Tackabury]", - 0xc481: "Proprietary Node ID Authentication Protocol [KEN]"} - - -class HDLC(Packet): - fields_desc = [ XByteField("address",0xff), - XByteField("control",0x03) ] - -class PPP_metaclass(Packet_metaclass): - def __call__(self, _pkt=None, *args, **kargs): - cls = self - if _pkt and _pkt[0] == '\xff': - cls = HDLC - i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) - i.__init__(_pkt=_pkt, *args, **kargs) - return i - - -class PPP(Packet): - __metaclass__ = PPP_metaclass - name = "PPP Link Layer" - fields_desc = [ ShortEnumField("proto", 0x0021, _PPP_proto) ] - -_PPP_conftypes = { 1:"Configure-Request", - 2:"Configure-Ack", - 3:"Configure-Nak", - 4:"Configure-Reject", - 5:"Terminate-Request", - 6:"Terminate-Ack", - 7:"Code-Reject", - 8:"Protocol-Reject", - 9:"Echo-Request", - 10:"Echo-Reply", - 11:"Discard-Request", - 14:"Reset-Request", - 15:"Reset-Ack", - } - -class PPP_Option_metaclass(Packet_metaclass): - _known_options={} - def __call__(self, _pkt=None, *args, **kargs): - cls = self - if _pkt: - t = ord(_pkt[0]) - cls = self._known_options.get(t,self) - i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) - i.__init__(_pkt=_pkt, *args, **kargs) - return i - def _register(self, cls): - self._known_options[cls.fields_desc[0].default] = cls - - -### PPP IPCP stuff (RFC 1332) - -# All IPCP options are defined below (names and associated classes) -_PPP_ipcpopttypes = { 1:"IP-Addresses (Deprecated)", - 2:"IP-Compression-Protocol", - 3:"IP-Address", - 4:"Mobile-IPv4", # not implemented, present for completeness - 129:"Primary-DNS-Address", - 130:"Primary-NBNS-Address", - 131:"Secondary-DNS-Address", - 132:"Secondary-NBNS-Address"} - - - -class PPP_IPCP_Option_metaclass(PPP_Option_metaclass): - _known_options={} - - -class PPP_IPCP_Option(Packet): - __metaclass__=PPP_IPCP_Option_metaclass - name = "PPP IPCP Option" - fields_desc = [ ByteEnumField("type" , None , _PPP_ipcpopttypes), - FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), - StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] - def extract_padding(self, pay): - return "",pay - -class PPP_IPCP_Specific_Option_metaclass(PPP_IPCP_Option_metaclass): - def __new__(cls, name, bases, dct): - newcls = super(PPP_IPCP_Specific_Option_metaclass, cls).__new__(cls, name, bases, dct) - PPP_IPCP_Option._register(newcls) - - -class PPP_IPCP_Option_IPAddress(PPP_IPCP_Option): - __metaclass__=PPP_IPCP_Specific_Option_metaclass - name = "PPP IPCP Option: IP Address" - fields_desc = [ ByteEnumField("type" , 3 , _PPP_ipcpopttypes), - FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), - IPField("data","0.0.0.0"), - ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] - -class PPP_IPCP_Option_DNS1(PPP_IPCP_Option): - __metaclass__=PPP_IPCP_Specific_Option_metaclass - name = "PPP IPCP Option: DNS1 Address" - fields_desc = [ ByteEnumField("type" , 129 , _PPP_ipcpopttypes), - FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), - IPField("data","0.0.0.0"), - ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] - -class PPP_IPCP_Option_DNS2(PPP_IPCP_Option): - __metaclass__=PPP_IPCP_Specific_Option_metaclass - name = "PPP IPCP Option: DNS2 Address" - fields_desc = [ ByteEnumField("type" , 131 , _PPP_ipcpopttypes), - FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), - IPField("data","0.0.0.0"), - ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] - -class PPP_IPCP_Option_NBNS1(PPP_IPCP_Option): - __metaclass__=PPP_IPCP_Specific_Option_metaclass - name = "PPP IPCP Option: NBNS1 Address" - fields_desc = [ ByteEnumField("type" , 130 , _PPP_ipcpopttypes), - FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), - IPField("data","0.0.0.0"), - ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] - -class PPP_IPCP_Option_NBNS2(PPP_IPCP_Option): - __metaclass__=PPP_IPCP_Specific_Option_metaclass - name = "PPP IPCP Option: NBNS2 Address" - fields_desc = [ ByteEnumField("type" , 132 , _PPP_ipcpopttypes), - FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), - IPField("data","0.0.0.0"), - ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] - - - - -class PPP_IPCP(Packet): - fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), - XByteField("id", 0 ), - FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), - PacketListField("options", [], PPP_IPCP_Option, length_from=lambda p:p.len-4,) ] - - -### ECP - -_PPP_ecpopttypes = { 0:"OUI", - 1:"DESE", } - -class PPP_ECP_Option_metaclass(PPP_Option_metaclass): - _known_options={} - - - -class PPP_ECP_Option(Packet): - __metaclass__=PPP_ECP_Option_metaclass - name = "PPP ECP Option" - fields_desc = [ ByteEnumField("type" , None , _PPP_ecpopttypes), - FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), - StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] - def extract_padding(self, pay): - return "",pay - -class PPP_ECP_Specific_Option_metaclass(PPP_ECP_Option_metaclass): - def __new__(cls, name, bases, dct): - newcls = super(PPP_ECP_Specific_Option_metaclass, cls).__new__(cls, name, bases, dct) - PPP_ECP_Option._register(newcls) - - -class PPP_ECP_Option_OUI(PPP_ECP_Option): - __metaclass__=PPP_ECP_Specific_Option_metaclass - fields_desc = [ ByteEnumField("type" , 0 , _PPP_ecpopttypes), - FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), - StrFixedLenField("oui","",3), - ByteField("subtype",0), - StrLenField("data", "", length_from=lambda p:p.len-6) ] - - - -class PPP_ECP(Packet): - fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), - XByteField("id", 0 ), - FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), - PacketListField("options", [], PPP_ECP_Option, length_from=lambda p:p.len-4,) ] - - - - -class DNS(Packet): - name = "DNS" - fields_desc = [ ShortField("id",0), - BitField("qr",0, 1), - BitEnumField("opcode", 0, 4, {0:"QUERY",1:"IQUERY",2:"STATUS"}), - BitField("aa", 0, 1), - BitField("tc", 0, 1), - BitField("rd", 0, 1), - BitField("ra", 0 ,1), - BitField("z", 0, 3), - BitEnumField("rcode", 0, 4, {0:"ok", 1:"format-error", 2:"server-failure", 3:"name-error", 4:"not-implemented", 5:"refused"}), - DNSRRCountField("qdcount", None, "qd"), - DNSRRCountField("ancount", None, "an"), - DNSRRCountField("nscount", None, "ns"), - DNSRRCountField("arcount", None, "ar"), - DNSQRField("qd", "qdcount"), - DNSRRField("an", "ancount"), - DNSRRField("ns", "nscount"), - DNSRRField("ar", "arcount",0) ] - def answers(self, other): - return (isinstance(other, DNS) - and self.id == other.id - and self.qr == 1 - and other.qr == 0) - - def mysummary(self): - type = ["Qry","Ans"][self.qr] - name = "" - if self.qr: - type = "Ans" - if self.ancount > 0 and isinstance(self.an, DNSRR): - name = ' "%s"' % self.an.rdata - else: - type = "Qry" - if self.qdcount > 0 and isinstance(self.qd, DNSQR): - name = ' "%s"' % self.qd.qname - return 'DNS %s%s ' % (type, name) - -dnstypes = { 0:"ANY", 255:"ALL", - 1:"A", 2:"NS", 3:"MD", 4:"MD", 5:"CNAME", 6:"SOA", 7: "MB", 8:"MG", - 9:"MR",10:"NULL",11:"WKS",12:"PTR",13:"HINFO",14:"MINFO",15:"MX",16:"TXT", - 17:"RP",18:"AFSDB",28:"AAAA", 33:"SRV",38:"A6",39:"DNAME"} - -dnsqtypes = {251:"IXFR",252:"AXFR",253:"MAILB",254:"MAILA",255:"ALL"} -dnsqtypes.update(dnstypes) -dnsclasses = {1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY'} - - -class DNSQR(Packet): - name = "DNS Question Record" - show_indent=0 - fields_desc = [ DNSStrField("qname",""), - ShortEnumField("qtype", 1, dnsqtypes), - ShortEnumField("qclass", 1, dnsclasses) ] - - - -class DNSRR(Packet): - name = "DNS Resource Record" - show_indent=0 - fields_desc = [ DNSStrField("rrname",""), - ShortEnumField("type", 1, dnstypes), - ShortEnumField("rclass", 1, dnsclasses), - IntField("ttl", 0), - RDLenField("rdlen"), - RDataField("rdata", "", length_from=lambda pkt:pkt.rdlen) ] - -dhcpmagic="c\x82Sc" - - -class BOOTP(Packet): - name = "BOOTP" - fields_desc = [ ByteEnumField("op",1, {1:"BOOTREQUEST", 2:"BOOTREPLY"}), - ByteField("htype",1), - ByteField("hlen",6), - ByteField("hops",0), - IntField("xid",0), - ShortField("secs",0), - FlagsField("flags", 0, 16, "???????????????B"), - IPField("ciaddr","0.0.0.0"), - IPField("yiaddr","0.0.0.0"), - IPField("siaddr","0.0.0.0"), - IPField("giaddr","0.0.0.0"), - Field("chaddr","", "16s"), - Field("sname","","64s"), - Field("file","","128s"), - StrField("options","") ] - def guess_payload_class(self, payload): - if self.options[:len(dhcpmagic)] == dhcpmagic: - return DHCP - else: - return Packet.guess_payload_class(self, payload) - def extract_padding(self,s): - if self.options[:len(dhcpmagic)] == dhcpmagic: - # set BOOTP options to DHCP magic cookie and make rest a payload of DHCP options - payload = self.options[len(dhcpmagic):] - self.options = self.options[:len(dhcpmagic)] - return payload, None - else: - return "", None - def hashret(self): - return struct.pack("L", self.xid) - def answers(self, other): - if not isinstance(other, BOOTP): - return 0 - return self.xid == other.xid - - - -#DHCP_UNKNOWN, DHCP_IP, DHCP_IPLIST, DHCP_TYPE \ -#= range(4) -# - -DHCPTypes = { - 1: "discover", - 2: "offer", - 3: "request", - 4: "decline", - 5: "ack", - 6: "nak", - 7: "release", - 8: "inform", - 9: "force_renew", - 10:"lease_query", - 11:"lease_unassigned", - 12:"lease_unknown", - 13:"lease_active", - } - -DHCPOptions = { - 0: "pad", - 1: IPField("subnet_mask", "0.0.0.0"), - 2: "time_zone", - 3: IPField("router","0.0.0.0"), - 4: IPField("time_server","0.0.0.0"), - 5: IPField("IEN_name_server","0.0.0.0"), - 6: IPField("name_server","0.0.0.0"), - 7: IPField("log_server","0.0.0.0"), - 8: IPField("cookie_server","0.0.0.0"), - 9: IPField("lpr_server","0.0.0.0"), - 12: "hostname", - 14: "dump_path", - 15: "domain", - 17: "root_disk_path", - 22: "max_dgram_reass_size", - 23: "default_ttl", - 24: "pmtu_timeout", - 28: IPField("broadcast_address","0.0.0.0"), - 35: "arp_cache_timeout", - 36: "ether_or_dot3", - 37: "tcp_ttl", - 38: "tcp_keepalive_interval", - 39: "tcp_keepalive_garbage", - 40: "NIS_domain", - 41: IPField("NIS_server","0.0.0.0"), - 42: IPField("NTP_server","0.0.0.0"), - 43: "vendor_specific", - 44: IPField("NetBIOS_server","0.0.0.0"), - 45: IPField("NetBIOS_dist_server","0.0.0.0"), - 50: IPField("requested_addr","0.0.0.0"), - 51: IntField("lease_time", 43200), - 54: IPField("server_id","0.0.0.0"), - 55: "param_req_list", - 57: ShortField("max_dhcp_size", 1500), - 58: IntField("renewal_time", 21600), - 59: IntField("rebinding_time", 37800), - 60: "vendor_class_id", - 61: "client_id", - - 64: "NISplus_domain", - 65: IPField("NISplus_server","0.0.0.0"), - 69: IPField("SMTP_server","0.0.0.0"), - 70: IPField("POP3_server","0.0.0.0"), - 71: IPField("NNTP_server","0.0.0.0"), - 72: IPField("WWW_server","0.0.0.0"), - 73: IPField("Finger_server","0.0.0.0"), - 74: IPField("IRC_server","0.0.0.0"), - 75: IPField("StreetTalk_server","0.0.0.0"), - 76: "StreetTalk_Dir_Assistance", - 82: "relay_agent_Information", - 53: ByteEnumField("message-type", 1, DHCPTypes), - # 55: DHCPRequestListField("request-list"), - 255: "end" - } - -DHCPRevOptions = {} - -for k,v in DHCPOptions.iteritems(): - if type(v) is str: - n = v - v = None - else: - n = v.name - DHCPRevOptions[n] = (k,v) -del(n) -del(v) -del(k) - - - - - -class DHCPOptionsField(StrField): - islist=1 - def i2repr(self,pkt,x): - s = [] - for v in x: - if type(v) is tuple and len(v) in [2,3]: - if DHCPRevOptions.has_key(v[0]) and isinstance(DHCPRevOptions[v[0]][1],Field): - f = DHCPRevOptions[v[0]][1] - vv = f.i2repr(pkt,v[1]) - else: - vv = repr(v[1]) - r = "%s=%s" % (v[0],vv) - if len(v) > 2: - r += " (garbage=%r)" % v[2] - s.append(r) - else: - s.append(sane(v)) - return "[%s]" % (" ".join(s)) - - def getfield(self, pkt, s): - return "", self.m2i(pkt, s) - def m2i(self, pkt, x): - opt = [] - while x: - o = ord(x[0]) - if o == 255: - opt.append("end") - x = x[1:] - continue - if o == 0: - opt.append("pad") - x = x[1:] - continue - if len(x) < 2 or len(x) < ord(x[1])+2: - opt.append(x) - break - elif DHCPOptions.has_key(o): - f = DHCPOptions[o] - - if isinstance(f, str): - olen = ord(x[1]) - opt.append( (f,x[2:olen+2]) ) - x = x[olen+2:] - else: - olen = ord(x[1]) - try: - left, val = f.getfield(pkt,x[2:olen+2]) - except: - opt.append(x) - break - if left: - otuple = (f.name, val, left) - else: - otuple = (f.name, val) - opt.append(otuple) - x = x[olen+2:] - else: - olen = ord(x[1]) - opt.append((o, x[2:olen+2])) - x = x[olen+2:] - return opt - def i2m(self, pkt, x): - if type(x) is str: - return x - s = "" - for o in x: - if type(o) is tuple and len(o) in [2,3]: - name, val = o[:2] - - if isinstance(name, int): - onum, oval = name, val - elif DHCPRevOptions.has_key(name): - onum, f = DHCPRevOptions[name] - if f is None: - oval = val - else: - oval = f.addfield(pkt,"",f.any2i(pkt,val)) - else: - warning("Unknown field option %s" % name) - continue - if len(o) > 2: - oval += o[2] - - s += chr(onum) - s += chr(len(oval)) - s += oval - - elif (type(o) is str and DHCPRevOptions.has_key(o) and - DHCPRevOptions[o][1] == None): - s += chr(DHCPRevOptions[o][0]) - elif type(o) is int: - s += chr(o)+"\0" - elif type(o) is str: - s += o - else: - warning("Malformed option %s" % o) - return s - - -class DHCP(Packet): - name = "DHCP options" - fields_desc = [ DHCPOptionsField("options","") ] - - -class Dot11(Packet): - name = "802.11" - fields_desc = [ - BitField("subtype", 0, 4), - BitEnumField("type", 0, 2, ["Management", "Control", "Data", "Reserved"]), - BitField("proto", 0, 2), - FlagsField("FCfield", 0, 8, ["to-DS", "from-DS", "MF", "retry", "pw-mgt", "MD", "wep", "order"]), - ShortField("ID",0), - MACField("addr1", ETHER_ANY), - Dot11Addr2MACField("addr2", ETHER_ANY), - Dot11Addr3MACField("addr3", ETHER_ANY), - Dot11SCField("SC", 0), - Dot11Addr4MACField("addr4", ETHER_ANY) - ] - def mysummary(self): - return self.sprintf("802.11 %Dot11.type% %Dot11.subtype% %Dot11.addr2% > %Dot11.addr1%") - def guess_payload_class(self, payload): - if self.type == 0x02 and (self.subtype >= 0x08 and self.subtype <=0xF and self.subtype != 0xD): - return Dot11QoS - elif self.FCfield & 0x40: - return Dot11WEP - else: - return Packet.guess_payload_class(self, payload) - def answers(self, other): - if isinstance(other,Dot11): - if self.type == 0: # management - if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA - return 0 - if (other.subtype,self.subtype) in [(0,1),(2,3),(4,5)]: - return 1 - if self.subtype == other.subtype == 11: # auth - return self.payload.answers(other.payload) - elif self.type == 1: # control - return 0 - elif self.type == 2: # data - return self.payload.answers(other.payload) - elif self.type == 3: # reserved - return 0 - return 0 - def unwep(self, key=None, warn=1): - if self.FCfield & 0x40 == 0: - if warn: - warning("No WEP to remove") - return - if isinstance(self.payload.payload, NoPayload): - if key or conf.wepkey: - self.payload.decrypt(key) - if isinstance(self.payload.payload, NoPayload): - if warn: - warning("Dot11 can't be decrypted. Check conf.wepkey.") - return - self.FCfield &= ~0x40 - self.payload=self.payload.payload - - -class Dot11QoS(Packet): - name = "802.11 QoS" - fields_desc = [ BitField("TID",None,4), - BitField("EOSP",None,1), - BitField("Ack Policy",None,2), - BitField("Reserved",None,1), - ByteField("TXOP",None) ] - def guess_payload_class(self, payload): - if isinstance(self.underlayer, Dot11): - if self.underlayer.FCfield & 0x40: - return Dot11WEP - return Packet.guess_payload_class(self, payload) - - -capability_list = [ "res8", "res9", "short-slot", "res11", - "res12", "DSSS-OFDM", "res14", "res15", - "ESS", "IBSS", "CFP", "CFP-req", - "privacy", "short-preamble", "PBCC", "agility"] - -reason_code = {0:"reserved",1:"unspec", 2:"auth-expired", - 3:"deauth-ST-leaving", - 4:"inactivity", 5:"AP-full", 6:"class2-from-nonauth", - 7:"class3-from-nonass", 8:"disas-ST-leaving", - 9:"ST-not-auth"} - -status_code = {0:"success", 1:"failure", 10:"cannot-support-all-cap", - 11:"inexist-asso", 12:"asso-denied", 13:"algo-unsupported", - 14:"bad-seq-num", 15:"challenge-failure", - 16:"timeout", 17:"AP-full",18:"rate-unsupported" } - -class Dot11Beacon(Packet): - name = "802.11 Beacon" - fields_desc = [ LELongField("timestamp", 0), - LEShortField("beacon_interval", 0x0064), - FlagsField("cap", 0, 16, capability_list) ] - - -class Dot11Elt(Packet): - name = "802.11 Information Element" - fields_desc = [ ByteEnumField("ID", 0, {0:"SSID", 1:"Rates", 2: "FHset", 3:"DSset", 4:"CFset", 5:"TIM", 6:"IBSSset", 16:"challenge", - 42:"ERPinfo", 46:"QoS Capability", 47:"ERPinfo", 48:"RSNinfo", 50:"ESRates",221:"vendor",68:"reserved"}), - FieldLenField("len", None, "info", "B"), - StrLenField("info", "", length_from=lambda x:x.len) ] - def mysummary(self): - if self.ID == 0: - return "SSID=%s"%repr(self.info),[Dot11] - else: - return "" - -class Dot11ATIM(Packet): - name = "802.11 ATIM" - -class Dot11Disas(Packet): - name = "802.11 Disassociation" - fields_desc = [ LEShortEnumField("reason", 1, reason_code) ] - -class Dot11AssoReq(Packet): - name = "802.11 Association Request" - fields_desc = [ FlagsField("cap", 0, 16, capability_list), - LEShortField("listen_interval", 0x00c8) ] - - -class Dot11AssoResp(Packet): - name = "802.11 Association Response" - fields_desc = [ FlagsField("cap", 0, 16, capability_list), - LEShortField("status", 0), - LEShortField("AID", 0) ] - -class Dot11ReassoReq(Packet): - name = "802.11 Reassociation Request" - fields_desc = [ FlagsField("cap", 0, 16, capability_list), - MACField("current_AP", ETHER_ANY), - LEShortField("listen_interval", 0x00c8) ] - - -class Dot11ReassoResp(Dot11AssoResp): - name = "802.11 Reassociation Response" - -class Dot11ProbeReq(Packet): - name = "802.11 Probe Request" - -class Dot11ProbeResp(Packet): - name = "802.11 Probe Response" - fields_desc = [ LELongField("timestamp", 0), - LEShortField("beacon_interval", 0x0064), - FlagsField("cap", 0, 16, capability_list) ] - -class Dot11Auth(Packet): - name = "802.11 Authentication" - fields_desc = [ LEShortEnumField("algo", 0, ["open", "sharedkey"]), - LEShortField("seqnum", 0), - LEShortEnumField("status", 0, status_code) ] - def answers(self, other): - if self.seqnum == other.seqnum+1: - return 1 - return 0 - -class Dot11Deauth(Packet): - name = "802.11 Deauthentication" - fields_desc = [ LEShortEnumField("reason", 1, reason_code) ] - - - -class Dot11WEP(Packet): - name = "802.11 WEP packet" - fields_desc = [ StrFixedLenField("iv", "\0\0\0", 3), - ByteField("keyid", 0), - StrField("wepdata",None,remain=4), - IntField("icv",None) ] - - def post_dissect(self, s): -# self.icv, = struct.unpack("!I",self.wepdata[-4:]) -# self.wepdata = self.wepdata[:-4] - self.decrypt() - - def build_payload(self): - if self.wepdata is None: - return Packet.build_payload(self) - return "" - - def post_build(self, p, pay): - if self.wepdata is None: - key = conf.wepkey - if key: - if self.icv is None: - pay += struct.pack("<I",crc32(pay)) - icv = "" - else: - icv = p[4:8] - c = ARC4.new(self.iv+key) - p = p[:4]+c.encrypt(pay)+icv - else: - warning("No WEP key set (conf.wepkey).. strange results expected..") - return p - - - def decrypt(self,key=None): - if key is None: - key = conf.wepkey - if key: - c = ARC4.new(self.iv+key) - self.add_payload(LLC(c.decrypt(self.wepdata))) - - - -class PrismHeader(Packet): - """ iwpriv wlan0 monitor 3 """ - name = "Prism header" - fields_desc = [ LEIntField("msgcode",68), - LEIntField("len",144), - StrFixedLenField("dev","",16), - LEIntField("hosttime_did",0), - LEShortField("hosttime_status",0), - LEShortField("hosttime_len",0), - LEIntField("hosttime",0), - LEIntField("mactime_did",0), - LEShortField("mactime_status",0), - LEShortField("mactime_len",0), - LEIntField("mactime",0), - LEIntField("channel_did",0), - LEShortField("channel_status",0), - LEShortField("channel_len",0), - LEIntField("channel",0), - LEIntField("rssi_did",0), - LEShortField("rssi_status",0), - LEShortField("rssi_len",0), - LEIntField("rssi",0), - LEIntField("sq_did",0), - LEShortField("sq_status",0), - LEShortField("sq_len",0), - LEIntField("sq",0), - LEIntField("signal_did",0), - LEShortField("signal_status",0), - LEShortField("signal_len",0), - LESignedIntField("signal",0), - LEIntField("noise_did",0), - LEShortField("noise_status",0), - LEShortField("noise_len",0), - LEIntField("noise",0), - LEIntField("rate_did",0), - LEShortField("rate_status",0), - LEShortField("rate_len",0), - LEIntField("rate",0), - LEIntField("istx_did",0), - LEShortField("istx_status",0), - LEShortField("istx_len",0), - LEIntField("istx",0), - LEIntField("frmlen_did",0), - LEShortField("frmlen_status",0), - LEShortField("frmlen_len",0), - LEIntField("frmlen",0), - ] - def answers(self, other): - if isinstance(other, PrismHeader): - return self.payload.answers(other.payload) - else: - return self.payload.answers(other) - - - -class HSRP(Packet): - name = "HSRP" - fields_desc = [ - ByteField("version", 0), - ByteEnumField("opcode", 0, { 0:"Hello"}), - ByteEnumField("state", 16, { 16:"Active"}), - ByteField("hellotime", 3), - ByteField("holdtime", 10), - ByteField("priority", 120), - ByteField("group", 1), - ByteField("reserved", 0), - StrFixedLenField("auth","cisco",8), - IPField("virtualIP","192.168.1.1") ] - - - - - - - -class NTP(Packet): - # RFC 1769 - name = "NTP" - fields_desc = [ - BitEnumField('leap', 0, 2, - { 0: 'nowarning', - 1: 'longminute', - 2: 'shortminute', - 3: 'notsync'}), - BitField('version', 3, 3), - BitEnumField('mode', 3, 3, - { 0: 'reserved', - 1: 'sym_active', - 2: 'sym_passive', - 3: 'client', - 4: 'server', - 5: 'broadcast', - 6: 'control', - 7: 'private'}), - BitField('stratum', 2, 8), - BitField('poll', 0xa, 8), ### XXX : it's a signed int - BitField('precision', 0, 8), ### XXX : it's a signed int - FloatField('delay', 0, 32), - FloatField('dispersion', 0, 32), - IPField('id', "127.0.0.1"), - TimeStampField('ref', 0, 64), - TimeStampField('orig', -1, 64), # -1 means current time - TimeStampField('recv', 0, 64), - TimeStampField('sent', -1, 64) - ] - def mysummary(self): - return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%") - - -class GRE(Packet): - name = "GRE" - fields_desc = [ BitField("chksumpresent",0,1), - BitField("reserved0",0,12), - BitField("version",0,3), - XShortEnumField("proto", 0x0000, ETHER_TYPES), - ConditionalField(XShortField("chksum",None),lambda pkt:pkt.chksumpresent==1), - ConditionalField(XShortField("reserved1",None),lambda pkt:pkt.chksumpresent==1), - ] - def post_build(self, p, pay): - p += pay - if self.chksumpresent and self.chksum is None: - c = checksum(p) - p = p[:4]+chr((c>>8)&0xff)+chr(c&0xff)+p[6:] - return p - - -class Radius(Packet): - name = "Radius" - fields_desc = [ ByteEnumField("code", 1, {1: "Access-Request", - 2: "Access-Accept", - 3: "Access-Reject", - 4: "Accounting-Request", - 5: "Accounting-Accept", - 6: "Accounting-Status", - 7: "Password-Request", - 8: "Password-Ack", - 9: "Password-Reject", - 10: "Accounting-Message", - 11: "Access-Challenge", - 12: "Status-Server", - 13: "Status-Client", - 21: "Resource-Free-Request", - 22: "Resource-Free-Response", - 23: "Resource-Query-Request", - 24: "Resource-Query-Response", - 25: "Alternate-Resource-Reclaim-Request", - 26: "NAS-Reboot-Request", - 27: "NAS-Reboot-Response", - 29: "Next-Passcode", - 30: "New-Pin", - 31: "Terminate-Session", - 32: "Password-Expired", - 33: "Event-Request", - 34: "Event-Response", - 40: "Disconnect-Request", - 41: "Disconnect-ACK", - 42: "Disconnect-NAK", - 43: "CoA-Request", - 44: "CoA-ACK", - 45: "CoA-NAK", - 50: "IP-Address-Allocate", - 51: "IP-Address-Release", - 253: "Experimental-use", - 254: "Reserved", - 255: "Reserved"} ), - ByteField("id", 0), - ShortField("len", None), - StrFixedLenField("authenticator","",16) ] - def post_build(self, p, pay): - p += pay - l = self.len - if l is None: - l = len(p) - p = p[:2]+struct.pack("!H",l)+p[4:] - return p - - - - -class RIP(Packet): - name = "RIP header" - fields_desc = [ - ByteEnumField("command",1,{1:"req",2:"resp",3:"traceOn",4:"traceOff",5:"sun", - 6:"trigReq",7:"trigResp",8:"trigAck",9:"updateReq", - 10:"updateResp",11:"updateAck"}), - ByteField("version",1), - ShortField("null",0), - ] - -class RIPEntry(Packet): - name = "RIP entry" - fields_desc = [ - ShortEnumField("AF",2,{2:"IP"}), - ShortField("RouteTag",0), - IPField("addr","0.0.0.0"), - IPField("mask","0.0.0.0"), - IPField("nextHop","0.0.0.0"), - IntEnumField("metric",1,{16:"Unreach"}), - ] - - - - -ISAKMP_payload_type = ["None","SA","Proposal","Transform","KE","ID","CERT","CR","Hash", - "SIG","Nonce","Notification","Delete","VendorID"] - -ISAKMP_exchange_type = ["None","base","identity prot.", - "auth only", "aggressive", "info"] - - -class ISAKMP_class(Packet): - def guess_payload_class(self, payload): - np = self.next_payload - if np == 0: - return Raw - elif np < len(ISAKMP_payload_type): - pt = ISAKMP_payload_type[np] - return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload) - else: - return ISAKMP_payload - - -class ISAKMP(ISAKMP_class): # rfc2408 - name = "ISAKMP" - fields_desc = [ - StrFixedLenField("init_cookie","",8), - StrFixedLenField("resp_cookie","",8), - ByteEnumField("next_payload",0,ISAKMP_payload_type), - XByteField("version",0x10), - ByteEnumField("exch_type",0,ISAKMP_exchange_type), - FlagsField("flags",0, 8, ["encryption","commit","auth_only","res3","res4","res5","res6","res7"]), # XXX use a Flag field - IntField("id",0), - IntField("length",None) - ] - - def guess_payload_class(self, payload): - if self.flags & 1: - return Raw - return ISAKMP_class.guess_payload_class(self, payload) - - def answers(self, other): - if isinstance(other, ISAKMP): - if other.init_cookie == self.init_cookie: - return 1 - return 0 - def post_build(self, p, pay): - p += pay - if self.length is None: - p = p[:24]+struct.pack("!I",len(p))+p[28:] - return p - - - - -class ISAKMP_payload_Transform(ISAKMP_class): - name = "IKE Transform" - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), -# ShortField("len",None), - ShortField("length",None), - ByteField("num",None), - ByteEnumField("id",1,{1:"KEY_IKE"}), - ShortField("res2",0), - ISAKMPTransformSetField("transforms",None,length_from=lambda x:x.length-8) -# XIntField("enc",0x80010005L), -# XIntField("hash",0x80020002L), -# XIntField("auth",0x80030001L), -# XIntField("group",0x80040002L), -# XIntField("life_type",0x800b0001L), -# XIntField("durationh",0x000c0004L), -# XIntField("durationl",0x00007080L), - ] - def post_build(self, p, pay): - if self.length is None: - l = len(p) - p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:] - p += pay - return p - - - - -class ISAKMP_payload_Proposal(ISAKMP_class): - name = "IKE proposal" -# ISAKMP_payload_type = 0 - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), - FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), - ByteField("proposal",1), - ByteEnumField("proto",1,{1:"ISAKMP"}), - FieldLenField("SPIsize",None,"SPI","B"), - ByteField("trans_nb",None), - StrLenField("SPI","",length_from=lambda x:x.SPIsize), - PacketLenField("trans",Raw(),ISAKMP_payload_Transform,length_from=lambda x:x.length-8), - ] - - -class ISAKMP_payload(ISAKMP_class): - name = "ISAKMP payload" - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), - FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), - StrLenField("load","",length_from=lambda x:x.length-4), - ] - - -class ISAKMP_payload_VendorID(ISAKMP_class): - name = "ISAKMP Vendor ID" - overload_fields = { ISAKMP: { "next_payload":13 }} - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), - FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), - StrLenField("vendorID","",length_from=lambda x:x.length-4), - ] - -class ISAKMP_payload_SA(ISAKMP_class): - name = "ISAKMP SA" - overload_fields = { ISAKMP: { "next_payload":1 }} - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), - FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+12), - IntEnumField("DOI",1,{1:"IPSEC"}), - IntEnumField("situation",1,{1:"identity"}), - PacketLenField("prop",Raw(),ISAKMP_payload_Proposal,length_from=lambda x:x.length-12), - ] - -class ISAKMP_payload_Nonce(ISAKMP_class): - name = "ISAKMP Nonce" - overload_fields = { ISAKMP: { "next_payload":10 }} - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), - FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), - StrLenField("load","",length_from=lambda x:x.length-4), - ] - -class ISAKMP_payload_KE(ISAKMP_class): - name = "ISAKMP Key Exchange" - overload_fields = { ISAKMP: { "next_payload":4 }} - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), - FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), - StrLenField("load","",length_from=lambda x:x.length-4), - ] - -class ISAKMP_payload_ID(ISAKMP_class): - name = "ISAKMP Identification" - overload_fields = { ISAKMP: { "next_payload":5 }} - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), - FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), - ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}), - ByteEnumField("ProtoID",0,{0:"Unused"}), - ShortEnumField("Port",0,{0:"Unused"}), -# IPField("IdentData","127.0.0.1"), - StrLenField("load","",length_from=lambda x:x.length-8), - ] - - - -class ISAKMP_payload_Hash(ISAKMP_class): - name = "ISAKMP Hash" - overload_fields = { ISAKMP: { "next_payload":8 }} - fields_desc = [ - ByteEnumField("next_payload",None,ISAKMP_payload_type), - ByteField("res",0), - FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), - StrLenField("load","",length_from=lambda x:x.length-4), - ] - - - -ISAKMP_payload_type_overload = {} -for i in range(len(ISAKMP_payload_type)): - name = "ISAKMP_payload_%s" % ISAKMP_payload_type[i] - if name in globals(): - ISAKMP_payload_type_overload[globals()[name]] = {"next_payload":i} - -del(i) -del(name) -ISAKMP_class.overload_fields = ISAKMP_payload_type_overload.copy() - - - - -# Cisco Skinny protocol - -# shamelessly ripped from Ethereal dissector -skinny_messages = { -# Station -> Callmanager - 0x0000: "KeepAliveMessage", - 0x0001: "RegisterMessage", - 0x0002: "IpPortMessage", - 0x0003: "KeypadButtonMessage", - 0x0004: "EnblocCallMessage", - 0x0005: "StimulusMessage", - 0x0006: "OffHookMessage", - 0x0007: "OnHookMessage", - 0x0008: "HookFlashMessage", - 0x0009: "ForwardStatReqMessage", - 0x000A: "SpeedDialStatReqMessage", - 0x000B: "LineStatReqMessage", - 0x000C: "ConfigStatReqMessage", - 0x000D: "TimeDateReqMessage", - 0x000E: "ButtonTemplateReqMessage", - 0x000F: "VersionReqMessage", - 0x0010: "CapabilitiesResMessage", - 0x0011: "MediaPortListMessage", - 0x0012: "ServerReqMessage", - 0x0020: "AlarmMessage", - 0x0021: "MulticastMediaReceptionAck", - 0x0022: "OpenReceiveChannelAck", - 0x0023: "ConnectionStatisticsRes", - 0x0024: "OffHookWithCgpnMessage", - 0x0025: "SoftKeySetReqMessage", - 0x0026: "SoftKeyEventMessage", - 0x0027: "UnregisterMessage", - 0x0028: "SoftKeyTemplateReqMessage", - 0x0029: "RegisterTokenReq", - 0x002A: "MediaTransmissionFailure", - 0x002B: "HeadsetStatusMessage", - 0x002C: "MediaResourceNotification", - 0x002D: "RegisterAvailableLinesMessage", - 0x002E: "DeviceToUserDataMessage", - 0x002F: "DeviceToUserDataResponseMessage", - 0x0030: "UpdateCapabilitiesMessage", - 0x0031: "OpenMultiMediaReceiveChannelAckMessage", - 0x0032: "ClearConferenceMessage", - 0x0033: "ServiceURLStatReqMessage", - 0x0034: "FeatureStatReqMessage", - 0x0035: "CreateConferenceResMessage", - 0x0036: "DeleteConferenceResMessage", - 0x0037: "ModifyConferenceResMessage", - 0x0038: "AddParticipantResMessage", - 0x0039: "AuditConferenceResMessage", - 0x0040: "AuditParticipantResMessage", - 0x0041: "DeviceToUserDataVersion1Message", -# Callmanager -> Station */ - 0x0081: "RegisterAckMessage", - 0x0082: "StartToneMessage", - 0x0083: "StopToneMessage", - 0x0085: "SetRingerMessage", - 0x0086: "SetLampMessage", - 0x0087: "SetHkFDetectMessage", - 0x0088: "SetSpeakerModeMessage", - 0x0089: "SetMicroModeMessage", - 0x008A: "StartMediaTransmission", - 0x008B: "StopMediaTransmission", - 0x008C: "StartMediaReception", - 0x008D: "StopMediaReception", - 0x008F: "CallInfoMessage", - 0x0090: "ForwardStatMessage", - 0x0091: "SpeedDialStatMessage", - 0x0092: "LineStatMessage", - 0x0093: "ConfigStatMessage", - 0x0094: "DefineTimeDate", - 0x0095: "StartSessionTransmission", - 0x0096: "StopSessionTransmission", - 0x0097: "ButtonTemplateMessage", - 0x0098: "VersionMessage", - 0x0099: "DisplayTextMessage", - 0x009A: "ClearDisplay", - 0x009B: "CapabilitiesReqMessage", - 0x009C: "EnunciatorCommandMessage", - 0x009D: "RegisterRejectMessage", - 0x009E: "ServerResMessage", - 0x009F: "Reset", - 0x0100: "KeepAliveAckMessage", - 0x0101: "StartMulticastMediaReception", - 0x0102: "StartMulticastMediaTransmission", - 0x0103: "StopMulticastMediaReception", - 0x0104: "StopMulticastMediaTransmission", - 0x0105: "OpenReceiveChannel", - 0x0106: "CloseReceiveChannel", - 0x0107: "ConnectionStatisticsReq", - 0x0108: "SoftKeyTemplateResMessage", - 0x0109: "SoftKeySetResMessage", - 0x0110: "SelectSoftKeysMessage", - 0x0111: "CallStateMessage", - 0x0112: "DisplayPromptStatusMessage", - 0x0113: "ClearPromptStatusMessage", - 0x0114: "DisplayNotifyMessage", - 0x0115: "ClearNotifyMessage", - 0x0116: "ActivateCallPlaneMessage", - 0x0117: "DeactivateCallPlaneMessage", - 0x0118: "UnregisterAckMessage", - 0x0119: "BackSpaceReqMessage", - 0x011A: "RegisterTokenAck", - 0x011B: "RegisterTokenReject", - 0x0042: "DeviceToUserDataResponseVersion1Message", - 0x011C: "StartMediaFailureDetection", - 0x011D: "DialedNumberMessage", - 0x011E: "UserToDeviceDataMessage", - 0x011F: "FeatureStatMessage", - 0x0120: "DisplayPriNotifyMessage", - 0x0121: "ClearPriNotifyMessage", - 0x0122: "StartAnnouncementMessage", - 0x0123: "StopAnnouncementMessage", - 0x0124: "AnnouncementFinishMessage", - 0x0127: "NotifyDtmfToneMessage", - 0x0128: "SendDtmfToneMessage", - 0x0129: "SubscribeDtmfPayloadReqMessage", - 0x012A: "SubscribeDtmfPayloadResMessage", - 0x012B: "SubscribeDtmfPayloadErrMessage", - 0x012C: "UnSubscribeDtmfPayloadReqMessage", - 0x012D: "UnSubscribeDtmfPayloadResMessage", - 0x012E: "UnSubscribeDtmfPayloadErrMessage", - 0x012F: "ServiceURLStatMessage", - 0x0130: "CallSelectStatMessage", - 0x0131: "OpenMultiMediaChannelMessage", - 0x0132: "StartMultiMediaTransmission", - 0x0133: "StopMultiMediaTransmission", - 0x0134: "MiscellaneousCommandMessage", - 0x0135: "FlowControlCommandMessage", - 0x0136: "CloseMultiMediaReceiveChannel", - 0x0137: "CreateConferenceReqMessage", - 0x0138: "DeleteConferenceReqMessage", - 0x0139: "ModifyConferenceReqMessage", - 0x013A: "AddParticipantReqMessage", - 0x013B: "DropParticipantReqMessage", - 0x013C: "AuditConferenceReqMessage", - 0x013D: "AuditParticipantReqMessage", - 0x013F: "UserToDeviceDataVersion1Message", - } - - - -class Skinny(Packet): - name="Skinny" - fields_desc = [ LEIntField("len",0), - LEIntField("res",0), - LEIntEnumField("msg",0,skinny_messages) ] - -_rtp_payload_types = { - # http://www.iana.org/assignments/rtp-parameters - 0: 'G.711 PCMU', 3: 'GSM', - 4: 'G723', 5: 'DVI4', - 6: 'DVI4', 7: 'LPC', - 8: 'PCMA', 9: 'G722', - 10: 'L16', 11: 'L16', - 12: 'QCELP', 13: 'CN', - 14: 'MPA', 15: 'G728', - 16: 'DVI4', 17: 'DVI4', - 18: 'G729', 25: 'CelB', - 26: 'JPEG', 28: 'nv', - 31: 'H261', 32: 'MPV', - 33: 'MP2T', 34: 'H263' } - -class RTP(Packet): - name="RTP" - fields_desc = [ BitField('version', 2, 2), - BitField('padding', 0, 1), - BitField('extension', 0, 1), - BitFieldLenField('numsync', None, 4, count_of='sync'), - BitField('marker', 0, 1), - BitEnumField('payload', 0, 7, _rtp_payload_types), - ShortField('sequence', 0), - IntField('timestamp', 0), - IntField('sourcesync', 0), - FieldListField('sync', [], IntField("id",0), count_from=lambda pkt:pkt.numsync) ] - -### SEBEK - - -class SebekHead(Packet): - name = "Sebek header" - fields_desc = [ XIntField("magic", 0xd0d0d0), - ShortField("version", 1), - ShortEnumField("type", 0, {"read":0, "write":1, - "socket":2, "open":3}), - IntField("counter", 0), - IntField("time_sec", 0), - IntField("time_usec", 0) ] - def mysummary(self): - return self.sprintf("Sebek Header v%SebekHead.version% %SebekHead.type%") - -# we need this because Sebek headers differ between v1 and v3, and -# between v3 type socket and v3 others - -class SebekV1(Packet): - name = "Sebek v1" - fields_desc = [ IntField("pid", 0), - IntField("uid", 0), - IntField("fd", 0), - StrFixedLenField("command", "", 12), - FieldLenField("data_length", None, "data",fmt="I"), - StrLenField("data", "", length_from=lambda x:x.data_length) ] - def mysummary(self): - if isinstance(self.underlayer, SebekHead): - return self.underlayer.sprintf("Sebek v1 %SebekHead.type% (%SebekV1.command%)") - else: - return self.sprintf("Sebek v1 (%SebekV1.command%)") - -class SebekV3(Packet): - name = "Sebek v3" - fields_desc = [ IntField("parent_pid", 0), - IntField("pid", 0), - IntField("uid", 0), - IntField("fd", 0), - IntField("inode", 0), - StrFixedLenField("command", "", 12), - FieldLenField("data_length", None, "data",fmt="I"), - StrLenField("data", "", length_from=lambda x:x.data_length) ] - def mysummary(self): - if isinstance(self.underlayer, SebekHead): - return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3.command%)") - else: - return self.sprintf("Sebek v3 (%SebekV3.command%)") - -class SebekV2(SebekV3): - def mysummary(self): - if isinstance(self.underlayer, SebekHead): - return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.command%)") - else: - return self.sprintf("Sebek v2 (%SebekV2.command%)") - -class SebekV3Sock(Packet): - name = "Sebek v2 socket" - fields_desc = [ IntField("parent_pid", 0), - IntField("pid", 0), - IntField("uid", 0), - IntField("fd", 0), - IntField("inode", 0), - StrFixedLenField("command", "", 12), - IntField("data_length", 15), - IPField("dip", "127.0.0.1"), - ShortField("dport", 0), - IPField("sip", "127.0.0.1"), - ShortField("sport", 0), - ShortEnumField("call", 0, { "bind":2, - "connect":3, "listen":4, - "accept":5, "sendmsg":16, - "recvmsg":17, "sendto":11, - "recvfrom":12}), - ByteEnumField("proto", 0, IP_PROTOS) ] - def mysummary(self): - if isinstance(self.underlayer, SebekHead): - return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3Sock.command%)") - else: - return self.sprintf("Sebek v3 socket (%SebekV3Sock.command%)") - -class SebekV2Sock(SebekV3Sock): - def mysummary(self): - if isinstance(self.underlayer, SebekHead): - return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.command%)") - else: - return self.sprintf("Sebek v2 socket (%SebekV2Sock.command%)") - -class MGCP(Packet): - name = "MGCP" - longname = "Media Gateway Control Protocol" - fields_desc = [ StrStopField("verb","AUEP"," ", -1), - StrFixedLenField("sep1"," ",1), - StrStopField("transaction_id","1234567"," ", -1), - StrFixedLenField("sep2"," ",1), - StrStopField("endpoint","dummy@dummy.net"," ", -1), - StrFixedLenField("sep3"," ",1), - StrStopField("version","MGCP 1.0 NCS 1.0","\x0a", -1), - StrFixedLenField("sep4","\x0a",1), - ] - - -#class MGCP(Packet): -# name = "MGCP" -# longname = "Media Gateway Control Protocol" -# fields_desc = [ ByteEnumField("type",0, ["request","response","others"]), -# ByteField("code0",0), -# ByteField("code1",0), -# ByteField("code2",0), -# ByteField("code3",0), -# ByteField("code4",0), -# IntField("trasid",0), -# IntField("req_time",0), -# ByteField("is_duplicate",0), -# ByteField("req_available",0) ] -# -class GPRS(Packet): - name = "GPRSdummy" - fields_desc = [ - StrStopField("dummy","","\x65\x00\x00",1) - ] - - -class HCI_Hdr(Packet): - name = "HCI header" - fields_desc = [ ByteEnumField("type",2,{1:"command",2:"ACLdata",3:"SCOdata",4:"event",5:"vendor"}),] - - def mysummary(self): - return self.sprintf("HCI %type%") - -class HCI_ACL_Hdr(Packet): - name = "HCI ACL header" - fields_desc = [ ByteField("handle",0), # Actually, handle is 12 bits and flags is 4. - ByteField("flags",0), # I wait to write a LEBitField - LEShortField("len",None), ] - def post_build(self, p, pay): - p += pay - if self.len is None: - l = len(p)-4 - p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] - return p - - -class L2CAP_Hdr(Packet): - name = "L2CAP header" - fields_desc = [ LEShortField("len",None), - LEShortEnumField("cid",0,{1:"control"}),] - - def post_build(self, p, pay): - p += pay - if self.len is None: - l = len(p)-4 - p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] - return p - - - -class L2CAP_CmdHdr(Packet): - name = "L2CAP command header" - fields_desc = [ - ByteEnumField("code",8,{1:"rej",2:"conn_req",3:"conn_resp", - 4:"conf_req",5:"conf_resp",6:"disconn_req", - 7:"disconn_resp",8:"echo_req",9:"echo_resp", - 10:"info_req",11:"info_resp"}), - ByteField("id",0), - LEShortField("len",None) ] - def post_build(self, p, pay): - p += pay - if self.len is None: - l = len(p)-4 - p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] - return p - def answers(self, other): - if other.id == self.id: - if self.code == 1: - return 1 - if other.code in [2,4,6,8,10] and self.code == other.code+1: - if other.code == 8: - return 1 - return self.payload.answers(other.payload) - return 0 - -class L2CAP_ConnReq(Packet): - name = "L2CAP Conn Req" - fields_desc = [ LEShortEnumField("psm",0,{1:"SDP",3:"RFCOMM",5:"telephony control"}), - LEShortField("scid",0), - ] - -class L2CAP_ConnResp(Packet): - name = "L2CAP Conn Resp" - fields_desc = [ LEShortField("dcid",0), - LEShortField("scid",0), - LEShortEnumField("result",0,["no_info","authen_pend","author_pend"]), - LEShortEnumField("status",0,["success","pend","bad_psm", - "cr_sec_block","cr_no_mem"]), - ] - def answers(self, other): - return self.scid == other.scid - -class L2CAP_CmdRej(Packet): - name = "L2CAP Command Rej" - fields_desc = [ LEShortField("reason",0), - ] - - -class L2CAP_ConfReq(Packet): - name = "L2CAP Conf Req" - fields_desc = [ LEShortField("dcid",0), - LEShortField("flags",0), - ] - -class L2CAP_ConfResp(Packet): - name = "L2CAP Conf Resp" - fields_desc = [ LEShortField("scid",0), - LEShortField("flags",0), - LEShortEnumField("result",0,["success","unaccept","reject","unknown"]), - ] - def answers(self, other): - return self.scid == other.scid - - -class L2CAP_DisconnReq(Packet): - name = "L2CAP Disconn Req" - fields_desc = [ LEShortField("dcid",0), - LEShortField("scid",0), ] - -class L2CAP_DisconnResp(Packet): - name = "L2CAP Disconn Resp" - fields_desc = [ LEShortField("dcid",0), - LEShortField("scid",0), ] - def answers(self, other): - return self.scid == other.scid - - - -class L2CAP_InfoReq(Packet): - name = "L2CAP Info Req" - fields_desc = [ LEShortEnumField("type",0,{1:"CL_MTU",2:"FEAT_MASK"}), - StrField("data","") - ] - - -class L2CAP_InfoResp(Packet): - name = "L2CAP Info Resp" - fields_desc = [ LEShortField("type",0), - LEShortEnumField("result",0,["success","not_supp"]), - StrField("data",""), ] - def answers(self, other): - return self.type == other.type - - - - -class NetBIOS_DS(Packet): - name = "NetBIOS datagram service" - fields_desc = [ - ByteEnumField("type",17, {17:"direct_group"}), - ByteField("flags",0), - XShortField("id",0), - IPField("src","127.0.0.1"), - ShortField("sport",138), - ShortField("len",None), - ShortField("ofs",0), - NetBIOSNameField("srcname",""), - NetBIOSNameField("dstname",""), - ] - def post_build(self, p, pay): - p += pay - if self.len is None: - l = len(p)-14 - p = p[:10]+struct.pack("!H", l)+p[12:] - return p - -# ShortField("length",0), -# ShortField("Delimitor",0), -# ByteField("command",0), -# ByteField("data1",0), -# ShortField("data2",0), -# ShortField("XMIt",0), -# ShortField("RSPCor",0), -# StrFixedLenField("dest","",16), -# StrFixedLenField("source","",16), -# -# ] -# - -# IR - -class IrLAPHead(Packet): - name = "IrDA Link Access Protocol Header" - fields_desc = [ XBitField("Address", 0x7f, 7), - BitEnumField("Type", 1, 1, {"Response":0, - "Command":1})] - -class IrLAPCommand(Packet): - name = "IrDA Link Access Protocol Command" - fields_desc = [ XByteField("Control", 0), - XByteField("Format identifier", 0), - XIntField("Source address", 0), - XIntField("Destination address", 0xffffffffL), - XByteField("Discovery flags", 0x1), - ByteEnumField("Slot number", 255, {"final":255}), - XByteField("Version", 0)] - - -class IrLMP(Packet): - name = "IrDA Link Management Protocol" - fields_desc = [ XShortField("Service hints", 0), - XByteField("Character set", 0), - StrField("Device name", "") ] - - -#NetBIOS - - -# Name Query Request -# Node Status Request -class NBNSQueryRequest(Packet): - name="NBNS query request" - fields_desc = [ShortField("NAME_TRN_ID",0), - ShortField("FLAGS", 0x0110), - ShortField("QDCOUNT",1), - ShortField("ANCOUNT",0), - ShortField("NSCOUNT",0), - ShortField("ARCOUNT",0), - NetBIOSNameField("QUESTION_NAME","windows"), - ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), - ByteField("NULL",0), - ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), - ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"})] - -# Name Registration Request -# Name Refresh Request -# Name Release Request or Demand -class NBNSRequest(Packet): - name="NBNS request" - fields_desc = [ShortField("NAME_TRN_ID",0), - ShortField("FLAGS", 0x2910), - ShortField("QDCOUNT",1), - ShortField("ANCOUNT",0), - ShortField("NSCOUNT",0), - ShortField("ARCOUNT",1), - NetBIOSNameField("QUESTION_NAME","windows"), - ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), - ByteField("NULL",0), - ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), - ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), - ShortEnumField("RR_NAME",0xC00C,{0xC00C:"Label String Pointer to QUESTION_NAME"}), - ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), - ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), - IntField("TTL", 0), - ShortField("RDLENGTH", 6), - BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), - BitEnumField("OWNER NODE TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}), - BitEnumField("UNUSED",0,13,{0:"Unused"}), - IPField("NB_ADDRESS", "127.0.0.1")] - -# Name Query Response -# Name Registration Response -class NBNSQueryResponse(Packet): - name="NBNS query response" - fields_desc = [ShortField("NAME_TRN_ID",0), - ShortField("FLAGS", 0x8500), - ShortField("QDCOUNT",0), - ShortField("ANCOUNT",1), - ShortField("NSCOUNT",0), - ShortField("ARCOUNT",0), - NetBIOSNameField("RR_NAME","windows"), - ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), - ByteField("NULL",0), - ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), - ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), - IntField("TTL", 0x493e0), - ShortField("RDLENGTH", 6), - ShortField("NB_FLAGS", 0), - IPField("NB_ADDRESS", "127.0.0.1")] - -# Name Query Response (negative) -# Name Release Response -class NBNSQueryResponseNegative(Packet): - name="NBNS query response (negative)" - fields_desc = [ShortField("NAME_TRN_ID",0), - ShortField("FLAGS", 0x8506), - ShortField("QDCOUNT",0), - ShortField("ANCOUNT",1), - ShortField("NSCOUNT",0), - ShortField("ARCOUNT",0), - NetBIOSNameField("RR_NAME","windows"), - ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), - ByteField("NULL",0), - ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), - ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), - IntField("TTL",0), - ShortField("RDLENGTH",6), - BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), - BitEnumField("OWNER NODE TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}), - BitEnumField("UNUSED",0,13,{0:"Unused"}), - IPField("NB_ADDRESS", "127.0.0.1")] - -# Node Status Response -class NBNSNodeStatusResponse(Packet): - name="NBNS Node Status Response" - fields_desc = [ShortField("NAME_TRN_ID",0), - ShortField("FLAGS", 0x8500), - ShortField("QDCOUNT",0), - ShortField("ANCOUNT",1), - ShortField("NSCOUNT",0), - ShortField("ARCOUNT",0), - NetBIOSNameField("RR_NAME","windows"), - ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), - ByteField("NULL",0), - ShortEnumField("RR_TYPE",0x21, {0x20:"NB",0x21:"NBSTAT"}), - ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), - IntField("TTL",0), - ShortField("RDLENGTH",83), - ByteField("NUM_NAMES",1)] - -# Service for Node Status Response -class NBNSNodeStatusResponseService(Packet): - name="NBNS Node Status Response Service" - fields_desc = [StrFixedLenField("NETBIOS_NAME","WINDOWS ",15), - ByteEnumField("SUFFIX",0,{0:"workstation",0x03:"messenger service",0x20:"file server service",0x1b:"domain master browser",0x1c:"domain controller", 0x1e:"browser election service"}), - ByteField("NAME_FLAGS",0x4), - ByteEnumField("UNUSED",0,{0:"unused"})] - -# End of Node Status Response packet -class NBNSNodeStatusResponseEnd(Packet): - name="NBNS Node Status Response" - fields_desc = [SourceMACField("MAC_ADDRESS"), - BitField("STATISTICS",0,57*8)] - -# Wait for Acknowledgement Response -class NBNSWackResponse(Packet): - name="NBNS Wait for Acknowledgement Response" - fields_desc = [ShortField("NAME_TRN_ID",0), - ShortField("FLAGS", 0xBC07), - ShortField("QDCOUNT",0), - ShortField("ANCOUNT",1), - ShortField("NSCOUNT",0), - ShortField("ARCOUNT",0), - NetBIOSNameField("RR_NAME","windows"), - ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), - ByteField("NULL",0), - ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), - ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), - IntField("TTL", 2), - ShortField("RDLENGTH",2), - BitField("RDATA",10512,16)] #10512=0010100100010000 - -class NBTDatagram(Packet): - name="NBT Datagram Packet" - fields_desc= [ByteField("Type", 0x10), - ByteField("Flags", 0x02), - ShortField("ID", 0), - IPField("SourceIP", "127.0.0.1"), - ShortField("SourcePort", 138), - ShortField("Length", 272), - ShortField("Offset", 0), - NetBIOSNameField("SourceName","windows"), - ShortEnumField("SUFFIX1",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), - ByteField("NULL",0), - NetBIOSNameField("DestinationName","windows"), - ShortEnumField("SUFFIX2",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), - ByteField("NULL",0)] - - -class NBTSession(Packet): - name="NBT Session Packet" - fields_desc= [ByteEnumField("TYPE",0,{0x00:"Session Message",0x81:"Session Request",0x82:"Positive Session Response",0x83:"Negative Session Response",0x84:"Retarget Session Response",0x85:"Session Keepalive"}), - BitField("RESERVED",0x00,7), - BitField("LENGTH",0,17)] - - -# SMB NetLogon Response Header -class SMBNetlogon_Protocol_Response_Header(Packet): - name="SMBNetlogon Protocol Response Header" - fields_desc = [StrFixedLenField("Start","\xffSMB",4), - ByteEnumField("Command",0x25,{0x25:"Trans"}), - ByteField("Error_Class",0x02), - ByteField("Reserved",0), - LEShortField("Error_code",4), - ByteField("Flags",0), - LEShortField("Flags2",0x0000), - LEShortField("PIDHigh",0x0000), - LELongField("Signature",0x0), - LEShortField("Unused",0x0), - LEShortField("TID",0), - LEShortField("PID",0), - LEShortField("UID",0), - LEShortField("MID",0), - ByteField("WordCount",17), - LEShortField("TotalParamCount",0), - LEShortField("TotalDataCount",112), - LEShortField("MaxParamCount",0), - LEShortField("MaxDataCount",0), - ByteField("MaxSetupCount",0), - ByteField("unused2",0), - LEShortField("Flags3",0), - ByteField("TimeOut1",0xe8), - ByteField("TimeOut2",0x03), - LEShortField("unused3",0), - LEShortField("unused4",0), - LEShortField("ParamCount2",0), - LEShortField("ParamOffset",0), - LEShortField("DataCount",112), - LEShortField("DataOffset",92), - ByteField("SetupCount", 3), - ByteField("unused5", 0)] - -# SMB MailSlot Protocol -class SMBMailSlot(Packet): - name = "SMB Mail Slot Protocol" - fields_desc = [LEShortField("opcode", 1), - LEShortField("priority", 1), - LEShortField("class", 2), - LEShortField("size", 135), - StrNullField("name","\MAILSLOT\NET\GETDC660")] - -# SMB NetLogon Protocol Response Tail SAM -class SMBNetlogon_Protocol_Response_Tail_SAM(Packet): - name = "SMB Netlogon Protocol Response Tail SAM" - fields_desc = [ByteEnumField("Command", 0x17, {0x12:"SAM logon request", 0x17:"SAM Active directory Response"}), - ByteField("unused", 0), - ShortField("Data1", 0), - ShortField("Data2", 0xfd01), - ShortField("Data3", 0), - ShortField("Data4", 0xacde), - ShortField("Data5", 0x0fe5), - ShortField("Data6", 0xd10a), - ShortField("Data7", 0x374c), - ShortField("Data8", 0x83e2), - ShortField("Data9", 0x7dd9), - ShortField("Data10", 0x3a16), - ShortField("Data11", 0x73ff), - ByteField("Data12", 0x04), - StrFixedLenField("Data13", "rmff", 4), - ByteField("Data14", 0x0), - ShortField("Data16", 0xc018), - ByteField("Data18", 0x0a), - StrFixedLenField("Data20", "rmff-win2k", 10), - ByteField("Data21", 0xc0), - ShortField("Data22", 0x18c0), - ShortField("Data23", 0x180a), - StrFixedLenField("Data24", "RMFF-WIN2K", 10), - ShortField("Data25", 0), - ByteField("Data26", 0x17), - StrFixedLenField("Data27", "Default-First-Site-Name", 23), - ShortField("Data28", 0x00c0), - ShortField("Data29", 0x3c10), - ShortField("Data30", 0x00c0), - ShortField("Data31", 0x0200), - ShortField("Data32", 0x0), - ShortField("Data33", 0xac14), - ShortField("Data34", 0x0064), - ShortField("Data35", 0x0), - ShortField("Data36", 0x0), - ShortField("Data37", 0x0), - ShortField("Data38", 0x0), - ShortField("Data39", 0x0d00), - ShortField("Data40", 0x0), - ShortField("Data41", 0xffff)] - -# SMB NetLogon Protocol Response Tail LM2.0 -class SMBNetlogon_Protocol_Response_Tail_LM20(Packet): - name = "SMB Netlogon Protocol Response Tail LM20" - fields_desc = [ByteEnumField("Command",0x06,{0x06:"LM 2.0 Response to logon request"}), - ByteField("unused", 0), - StrFixedLenField("DblSlash", "\\\\", 2), - StrNullField("ServerName","WIN"), - LEShortField("LM20Token", 0xffff)] - -# SMBNegociate Protocol Request Header -class SMBNegociate_Protocol_Request_Header(Packet): - name="SMBNegociate Protocol Request Header" - fields_desc = [StrFixedLenField("Start","\xffSMB",4), - ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), - ByteField("Error_Class",0), - ByteField("Reserved",0), - LEShortField("Error_code",0), - ByteField("Flags",0x18), - LEShortField("Flags2",0x0000), - LEShortField("PIDHigh",0x0000), - LELongField("Signature",0x0), - LEShortField("Unused",0x0), - LEShortField("TID",0), - LEShortField("PID",1), - LEShortField("UID",0), - LEShortField("MID",2), - ByteField("WordCount",0), - LEShortField("ByteCount",12)] - -# SMB Negociate Protocol Request Tail -class SMBNegociate_Protocol_Request_Tail(Packet): - name="SMB Negociate Protocol Request Tail" - fields_desc=[ByteField("BufferFormat",0x02), - StrNullField("BufferData","NT LM 0.12")] - -# SMBNegociate Protocol Response Advanced Security -class SMBNegociate_Protocol_Response_Advanced_Security(Packet): - name="SMBNegociate Protocol Response Advanced Security" - fields_desc = [StrFixedLenField("Start","\xffSMB",4), - ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), - ByteField("Error_Class",0), - ByteField("Reserved",0), - LEShortField("Error_Code",0), - ByteField("Flags",0x98), - LEShortField("Flags2",0x0000), - LEShortField("PIDHigh",0x0000), - LELongField("Signature",0x0), - LEShortField("Unused",0x0), - LEShortField("TID",0), - LEShortField("PID",1), - LEShortField("UID",0), - LEShortField("MID",2), - ByteField("WordCount",17), - LEShortField("DialectIndex",7), - ByteField("SecurityMode",0x03), - LEShortField("MaxMpxCount",50), - LEShortField("MaxNumberVC",1), - LEIntField("MaxBufferSize",16144), - LEIntField("MaxRawSize",65536), - LEIntField("SessionKey",0x0000), - LEShortField("ServerCapabilities",0xf3f9), - BitField("UnixExtensions",0,1), - BitField("Reserved2",0,7), - BitField("ExtendedSecurity",1,1), - BitField("CompBulk",0,2), - BitField("Reserved3",0,5), -# There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. - LEIntField("ServerTimeHigh",0xD6228000L), - LEIntField("ServerTimeLow",0x1C4EF94), - LEShortField("ServerTimeZone",0x3c), - ByteField("EncryptionKeyLength",0), - LEFieldLenField("ByteCount", None, "SecurityBlob", adjust=lambda pkt,x:x-16), - BitField("GUID",0,128), - StrLenField("SecurityBlob", "", length_from=lambda x:x.ByteCount+16)] - -# SMBNegociate Protocol Response No Security -# When using no security, with EncryptionKeyLength=8, you must have an EncryptionKey before the DomainName -class SMBNegociate_Protocol_Response_No_Security(Packet): - name="SMBNegociate Protocol Response No Security" - fields_desc = [StrFixedLenField("Start","\xffSMB",4), - ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), - ByteField("Error_Class",0), - ByteField("Reserved",0), - LEShortField("Error_Code",0), - ByteField("Flags",0x98), - LEShortField("Flags2",0x0000), - LEShortField("PIDHigh",0x0000), - LELongField("Signature",0x0), - LEShortField("Unused",0x0), - LEShortField("TID",0), - LEShortField("PID",1), - LEShortField("UID",0), - LEShortField("MID",2), - ByteField("WordCount",17), - LEShortField("DialectIndex",7), - ByteField("SecurityMode",0x03), - LEShortField("MaxMpxCount",50), - LEShortField("MaxNumberVC",1), - LEIntField("MaxBufferSize",16144), - LEIntField("MaxRawSize",65536), - LEIntField("SessionKey",0x0000), - LEShortField("ServerCapabilities",0xf3f9), - BitField("UnixExtensions",0,1), - BitField("Reserved2",0,7), - BitField("ExtendedSecurity",0,1), - FlagsField("CompBulk",0,2,"CB"), - BitField("Reserved3",0,5), - # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. - LEIntField("ServerTimeHigh",0xD6228000L), - LEIntField("ServerTimeLow",0x1C4EF94), - LEShortField("ServerTimeZone",0x3c), - ByteField("EncryptionKeyLength",8), - LEShortField("ByteCount",24), - BitField("EncryptionKey",0,64), - StrNullField("DomainName","WORKGROUP"), - StrNullField("ServerName","RMFF1")] - -# SMBNegociate Protocol Response No Security No Key -class SMBNegociate_Protocol_Response_No_Security_No_Key(Packet): - namez="SMBNegociate Protocol Response No Security No Key" - fields_desc = [StrFixedLenField("Start","\xffSMB",4), - ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), - ByteField("Error_Class",0), - ByteField("Reserved",0), - LEShortField("Error_Code",0), - ByteField("Flags",0x98), - LEShortField("Flags2",0x0000), - LEShortField("PIDHigh",0x0000), - LELongField("Signature",0x0), - LEShortField("Unused",0x0), - LEShortField("TID",0), - LEShortField("PID",1), - LEShortField("UID",0), - LEShortField("MID",2), - ByteField("WordCount",17), - LEShortField("DialectIndex",7), - ByteField("SecurityMode",0x03), - LEShortField("MaxMpxCount",50), - LEShortField("MaxNumberVC",1), - LEIntField("MaxBufferSize",16144), - LEIntField("MaxRawSize",65536), - LEIntField("SessionKey",0x0000), - LEShortField("ServerCapabilities",0xf3f9), - BitField("UnixExtensions",0,1), - BitField("Reserved2",0,7), - BitField("ExtendedSecurity",0,1), - FlagsField("CompBulk",0,2,"CB"), - BitField("Reserved3",0,5), - # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. - LEIntField("ServerTimeHigh",0xD6228000L), - LEIntField("ServerTimeLow",0x1C4EF94), - LEShortField("ServerTimeZone",0x3c), - ByteField("EncryptionKeyLength",0), - LEShortField("ByteCount",16), - StrNullField("DomainName","WORKGROUP"), - StrNullField("ServerName","RMFF1")] - -# Session Setup AndX Request -class SMBSession_Setup_AndX_Request(Packet): - name="Session Setup AndX Request" - fields_desc=[StrFixedLenField("Start","\xffSMB",4), - ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), - ByteField("Error_Class",0), - ByteField("Reserved",0), - LEShortField("Error_Code",0), - ByteField("Flags",0x18), - LEShortField("Flags2",0x0001), - LEShortField("PIDHigh",0x0000), - LELongField("Signature",0x0), - LEShortField("Unused",0x0), - LEShortField("TID",0), - LEShortField("PID",1), - LEShortField("UID",0), - LEShortField("MID",2), - ByteField("WordCount",13), - ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), - ByteField("Reserved2",0), - LEShortField("AndXOffset",96), - LEShortField("MaxBufferS",2920), - LEShortField("MaxMPXCount",50), - LEShortField("VCNumber",0), - LEIntField("SessionKey",0), - LEFieldLenField("ANSIPasswordLength",None,"ANSIPassword"), - LEShortField("UnicodePasswordLength",0), - LEIntField("Reserved3",0), - LEShortField("ServerCapabilities",0x05), - BitField("UnixExtensions",0,1), - BitField("Reserved4",0,7), - BitField("ExtendedSecurity",0,1), - BitField("CompBulk",0,2), - BitField("Reserved5",0,5), - LEShortField("ByteCount",35), - StrLenField("ANSIPassword", "Pass",length_from=lambda x:x.ANSIPasswordLength), - StrNullField("Account","GUEST"), - StrNullField("PrimaryDomain", ""), - StrNullField("NativeOS","Windows 4.0"), - StrNullField("NativeLanManager","Windows 4.0"), - ByteField("WordCount2",4), - ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), - ByteField("Reserved6",0), - LEShortField("AndXOffset2",0), - LEShortField("Flags3",0x2), - LEShortField("PasswordLength",0x1), - LEShortField("ByteCount2",18), - ByteField("Password",0), - StrNullField("Path","\\\\WIN2K\\IPC$"), - StrNullField("Service","IPC")] - -# Session Setup AndX Response -class SMBSession_Setup_AndX_Response(Packet): - name="Session Setup AndX Response" - fields_desc=[StrFixedLenField("Start","\xffSMB",4), - ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), - ByteField("Error_Class",0), - ByteField("Reserved",0), - LEShortField("Error_Code",0), - ByteField("Flags",0x90), - LEShortField("Flags2",0x1001), - LEShortField("PIDHigh",0x0000), - LELongField("Signature",0x0), - LEShortField("Unused",0x0), - LEShortField("TID",0), - LEShortField("PID",1), - LEShortField("UID",0), - LEShortField("MID",2), - ByteField("WordCount",3), - ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), - ByteField("Reserved2",0), - LEShortField("AndXOffset",66), - LEShortField("Action",0), - LEShortField("ByteCount",25), - StrNullField("NativeOS","Windows 4.0"), - StrNullField("NativeLanManager","Windows 4.0"), - StrNullField("PrimaryDomain",""), - ByteField("WordCount2",3), - ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), - ByteField("Reserved3",0), - LEShortField("AndXOffset2",80), - LEShortField("OptionalSupport",0x01), - LEShortField("ByteCount2",5), - StrNullField("Service","IPC"), - StrNullField("NativeFileSystem","")] - -class MobileIP(Packet): - name = "Mobile IP (RFC3344)" - fields_desc = [ ByteEnumField("type", 1, {1:"RRQ", 3:"RRP"}) ] - -class MobileIPRRQ(Packet): - name = "Mobile IP Registration Request (RFC3344)" - fields_desc = [ XByteField("flags", 0), - ShortField("lifetime", 180), - IPField("homeaddr", "0.0.0.0"), - IPField("haaddr", "0.0.0.0"), - IPField("coaddr", "0.0.0.0"), - Field("id", "", "64s") ] - -class MobileIPRRP(Packet): - name = "Mobile IP Registration Reply (RFC3344)" - fields_desc = [ ByteField("code", 0), - ShortField("lifetime", 180), - IPField("homeaddr", "0.0.0.0"), - IPField("haaddr", "0.0.0.0"), - Field("id", "", "64s") ] - -class MobileIPTunnelData(Packet): - name = "Mobile IP Tunnel Data Message (RFC3519)" - fields_desc = [ ByteField("nexthdr", 4), - ShortField("res", 0) ] - - -# Cisco Netflow Protocol version 1 -class NetflowHeader(Packet): - name = "Netflow Header" - fields_desc = [ ShortField("version", 1) ] - -class NetflowHeaderV1(Packet): - name = "Netflow Header V1" - fields_desc = [ ShortField("count", 0), - IntField("sysUptime", 0), - IntField("unixSecs", 0), - IntField("unixNanoSeconds", 0) ] - - -class NetflowRecordV1(Packet): - name = "Netflow Record" - fields_desc = [ IPField("ipsrc", "0.0.0.0"), - IPField("ipdst", "0.0.0.0"), - IPField("nexthop", "0.0.0.0"), - ShortField("inputIfIndex", 0), - ShortField("outpuIfIndex", 0), - IntField("dpkts", 0), - IntField("dbytes", 0), - IntField("starttime", 0), - IntField("endtime", 0), - ShortField("srcport", 0), - ShortField("dstport", 0), - ShortField("padding", 0), - ByteField("proto", 0), - ByteField("tos", 0), - IntField("padding1", 0), - IntField("padding2", 0) ] - - -TFTP_operations = { 1:"RRQ",2:"WRQ",3:"DATA",4:"ACK",5:"ERROR",6:"OACK" } - - -class TFTP(Packet): - name = "TFTP opcode" - fields_desc = [ ShortEnumField("op", 1, TFTP_operations), ] - - - -class TFTP_RRQ(Packet): - name = "TFTP Read Request" - fields_desc = [ StrNullField("filename", ""), - StrNullField("mode", "octet") ] - def answers(self, other): - return 0 - def mysummary(self): - return self.sprintf("RRQ %filename%"),[UDP] - - -class TFTP_WRQ(Packet): - name = "TFTP Write Request" - fields_desc = [ StrNullField("filename", ""), - StrNullField("mode", "octet") ] - def answers(self, other): - return 0 - def mysummary(self): - return self.sprintf("WRQ %filename%"),[UDP] - -class TFTP_DATA(Packet): - name = "TFTP Data" - fields_desc = [ ShortField("block", 0) ] - def answers(self, other): - return self.block == 1 and isinstance(other, TFTP_RRQ) - def mysummary(self): - return self.sprintf("DATA %block%"),[UDP] - -class TFTP_Option(Packet): - fields_desc = [ StrNullField("oname",""), - StrNullField("value","") ] - def extract_padding(self, pkt): - return "",pkt - -class TFTP_Options(Packet): - fields_desc = [ PacketListField("options", [], TFTP_Option, length_from=lambda x:None) ] - - -class TFTP_ACK(Packet): - name = "TFTP Ack" - fields_desc = [ ShortField("block", 0) ] - def answers(self, other): - if isinstance(other, TFTP_DATA): - return self.block == other.block - elif isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_OACK): - return self.block == 0 - return 0 - def mysummary(self): - return self.sprintf("ACK %block%"),[UDP] - -TFTP_Error_Codes = { 0: "Not defined", - 1: "File not found", - 2: "Access violation", - 3: "Disk full or allocation exceeded", - 4: "Illegal TFTP operation", - 5: "Unknown transfer ID", - 6: "File already exists", - 7: "No such user", - 8: "Terminate transfer due to option negotiation", - } - -class TFTP_ERROR(Packet): - name = "TFTP Error" - fields_desc = [ ShortEnumField("errorcode", 0, TFTP_Error_Codes), - StrNullField("errormsg", "")] - def answers(self, other): - return (isinstance(other, TFTP_DATA) or - isinstance(other, TFTP_RRQ) or - isinstance(other, TFTP_WRQ) or - isinstance(other, TFTP_ACK)) - def mysummary(self): - return self.sprintf("ERROR %errorcode%: %errormsg%"),[UDP] - - -class TFTP_OACK(Packet): - name = "TFTP Option Ack" - fields_desc = [ ] - def answers(self, other): - return isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_RRQ) - - -########## -## SNMP ## -########## - -######[ ASN1 class ]###### - -class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): - name="SNMP" - PDU_GET = 0xa0 - PDU_NEXT = 0xa1 - PDU_RESPONSE = 0xa2 - PDU_SET = 0xa3 - PDU_TRAPv1 = 0xa4 - PDU_BULK = 0xa5 - PDU_INFORM = 0xa6 - PDU_TRAPv2 = 0xa7 - - -class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_GET - -class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_NEXT - -class ASN1_SNMP_PDU_RESPONSE(ASN1_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_RESPONSE - -class ASN1_SNMP_PDU_SET(ASN1_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_SET - -class ASN1_SNMP_PDU_TRAPv1(ASN1_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_TRAPv1 - -class ASN1_SNMP_PDU_BULK(ASN1_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_BULK - -class ASN1_SNMP_PDU_INFORM(ASN1_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_INFORM - -class ASN1_SNMP_PDU_TRAPv2(ASN1_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_TRAPv2 - - -######[ BER codecs ]####### - -class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_GET - -class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_NEXT - -class BERcodec_SNMP_PDU_RESPONSE(BERcodec_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_RESPONSE - -class BERcodec_SNMP_PDU_SET(BERcodec_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_SET - -class BERcodec_SNMP_PDU_TRAPv1(BERcodec_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_TRAPv1 - -class BERcodec_SNMP_PDU_BULK(BERcodec_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_BULK - -class BERcodec_SNMP_PDU_INFORM(BERcodec_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_INFORM - -class BERcodec_SNMP_PDU_TRAPv2(BERcodec_SEQUENCE): - tag = ASN1_Class_SNMP.PDU_TRAPv2 - - - -######[ ASN1 fields ]###### - -class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_SNMP.PDU_GET - -class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_SNMP.PDU_NEXT - -class ASN1F_SNMP_PDU_RESPONSE(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_SNMP.PDU_RESPONSE - -class ASN1F_SNMP_PDU_SET(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_SNMP.PDU_SET - -class ASN1F_SNMP_PDU_TRAPv1(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv1 - -class ASN1F_SNMP_PDU_BULK(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_SNMP.PDU_BULK - -class ASN1F_SNMP_PDU_INFORM(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_SNMP.PDU_INFORM - -class ASN1F_SNMP_PDU_TRAPv2(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv2 - - - -######[ SNMP Packet ]###### - -SNMP_error = { 0: "no_error", - 1: "too_big", - 2: "no_such_name", - 3: "bad_value", - 4: "read_only", - 5: "generic_error", - 6: "no_access", - 7: "wrong_type", - 8: "wrong_length", - 9: "wrong_encoding", - 10: "wrong_value", - 11: "no_creation", - 12: "inconsistent_value", - 13: "ressource_unavailable", - 14: "commit_failed", - 15: "undo_failed", - 16: "authorization_error", - 17: "not_writable", - 18: "inconsistent_name", - } - -SNMP_trap_types = { 0: "cold_start", - 1: "warm_start", - 2: "link_down", - 3: "link_up", - 4: "auth_failure", - 5: "egp_neigh_loss", - 6: "enterprise_specific", - } - -class SNMPvarbind(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"), - ASN1F_field("value",ASN1_NULL(0)) - ) - - -class SNMPget(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0), - ASN1F_enum_INTEGER("error",0, SNMP_error), - ASN1F_INTEGER("error_index",0), - ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) - ) - -class SNMPnext(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0), - ASN1F_enum_INTEGER("error",0, SNMP_error), - ASN1F_INTEGER("error_index",0), - ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) - ) - -class SNMPresponse(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SNMP_PDU_RESPONSE( ASN1F_INTEGER("id",0), - ASN1F_enum_INTEGER("error",0, SNMP_error), - ASN1F_INTEGER("error_index",0), - ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) - ) - -class SNMPset(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SNMP_PDU_SET( ASN1F_INTEGER("id",0), - ASN1F_enum_INTEGER("error",0, SNMP_error), - ASN1F_INTEGER("error_index",0), - ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) - ) - -class SNMPtrapv1(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SNMP_PDU_TRAPv1( ASN1F_INTEGER("id",0), - ASN1F_OID("enterprise", "1.3"), - ASN1F_STRING("agent_addr",""), - ASN1F_enum_INTEGER("generic_trap", 0, SNMP_trap_types), - ASN1F_INTEGER("specific_trap", 0), - ASN1F_INTEGER("time_stamp", IntAutoTime()), - ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) - ) - -class SNMPbulk(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SNMP_PDU_BULK( ASN1F_INTEGER("id",0), - ASN1F_INTEGER("non_repeaters",0), - ASN1F_INTEGER("max_repetitions",0), - ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) - ) - -class SNMPinform(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SNMP_PDU_INFORM( ASN1F_INTEGER("id",0), - ASN1F_enum_INTEGER("error",0, SNMP_error), - ASN1F_INTEGER("error_index",0), - ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) - ) - -class SNMPtrapv2(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SNMP_PDU_TRAPv2( ASN1F_INTEGER("id",0), - ASN1F_enum_INTEGER("error",0, SNMP_error), - ASN1F_INTEGER("error_index",0), - ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) - ) - - -class SNMP(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SEQUENCE( - ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}), - ASN1F_STRING("community","public"), - ASN1F_CHOICE("PDU", SNMPget(), - SNMPget, SNMPnext, SNMPresponse, SNMPset, - SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) - ) - def answers(self, other): - return ( isinstance(self.PDU, SNMPresponse) and - ( isinstance(other.PDU, SNMPget) or - isinstance(other.PDU, SNMPnext) or - isinstance(other.PDU, SNMPset) ) and - self.PDU.id == other.PDU.id ) - - -########## -## X509 ## -########## - -######[ ASN1 class ]###### - -class ASN1_Class_X509(ASN1_Class_UNIVERSAL): - name="X509" - CONT0 = 0xa0 - CONT1 = 0xa1 - CONT2 = 0xa2 - CONT3 = 0xa3 - -class ASN1_X509_CONT0(ASN1_SEQUENCE): - tag = ASN1_Class_X509.CONT0 - -class ASN1_X509_CONT1(ASN1_SEQUENCE): - tag = ASN1_Class_X509.CONT1 - -class ASN1_X509_CONT2(ASN1_SEQUENCE): - tag = ASN1_Class_X509.CONT2 - -class ASN1_X509_CONT3(ASN1_SEQUENCE): - tag = ASN1_Class_X509.CONT3 - -######[ BER codecs ]####### - -class BERcodec_X509_CONT0(BERcodec_SEQUENCE): - tag = ASN1_Class_X509.CONT0 - -class BERcodec_X509_CONT1(BERcodec_SEQUENCE): - tag = ASN1_Class_X509.CONT1 - -class BERcodec_X509_CONT2(BERcodec_SEQUENCE): - tag = ASN1_Class_X509.CONT2 - -class BERcodec_X509_CONT3(BERcodec_SEQUENCE): - tag = ASN1_Class_X509.CONT3 - -######[ ASN1 fields ]###### - -class ASN1F_X509_CONT0(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_X509.CONT0 - -class ASN1F_X509_CONT1(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_X509.CONT1 - -class ASN1F_X509_CONT2(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_X509.CONT2 - -class ASN1F_X509_CONT3(ASN1F_SEQUENCE): - ASN1_tag = ASN1_Class_X509.CONT3 - -######[ X509 packets ]###### - -class X509RDN(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SET( - ASN1F_SEQUENCE( ASN1F_OID("oid","2.5.4.6"), - ASN1F_PRINTABLE_STRING("value","") - ) - ) - -class X509v3Ext(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_field("val",ASN1_NULL(0)) - - -class X509Cert(ASN1_Packet): - ASN1_codec = ASN1_Codecs.BER - ASN1_root = ASN1F_SEQUENCE( - ASN1F_SEQUENCE( - ASN1F_optionnal(ASN1F_X509_CONT0(ASN1F_INTEGER("version",3))), - ASN1F_INTEGER("sn",1), - ASN1F_SEQUENCE(ASN1F_OID("sign_algo","1.2.840.113549.1.1.5"), - ASN1F_field("sa_value",ASN1_NULL(0))), - ASN1F_SEQUENCE_OF("issuer",[],X509RDN), - ASN1F_SEQUENCE(ASN1F_UTC_TIME("not_before",ZuluTime(-600)), # ten minutes ago - ASN1F_UTC_TIME("not_after",ZuluTime(+86400))), # for 24h - ASN1F_SEQUENCE_OF("subject",[],X509RDN), - ASN1F_SEQUENCE( - ASN1F_SEQUENCE(ASN1F_OID("pubkey_algo","1.2.840.113549.1.1.1"), - ASN1F_field("pk_value",ASN1_NULL(0))), - ASN1F_BIT_STRING("pubkey","") - ), - ASN1F_optionnal(ASN1F_X509_CONT3(ASN1F_SEQUENCE_OF("x509v3ext",[],X509v3Ext))), - - ), - ASN1F_SEQUENCE(ASN1F_OID("sign_algo2","1.2.840.113549.1.1.5"), - ASN1F_field("sa2_value",ASN1_NULL(0))), - ASN1F_BIT_STRING("signature","") - ) - - - - -################# -## Bind layers ## -################# - - -def bind_bottom_up(lower, upper, __fval=None, **fval): - if __fval is not None: - fval.update(__fval) - lower.payload_guess = lower.payload_guess[:] - lower.payload_guess.append((fval, upper)) - - -def bind_top_down(lower, upper, __fval=None, **fval): - if __fval is not None: - fval.update(__fval) - upper.overload_fields = upper.overload_fields.copy() - upper.overload_fields[lower] = fval - -def bind_layers(lower, upper, __fval=None, **fval): - if __fval is not None: - fval.update(__fval) - bind_top_down(lower, upper, **fval) - bind_bottom_up(lower, upper, **fval) - -def split_bottom_up(lower, upper, __fval=None, **fval): - if __fval is not None: - fval.update(__fval) - def do_filter((f,u),upper=upper,fval=fval): - if u != upper: - return True - for k in fval: - if k not in f or f[k] != fval[k]: - return True - return False - lower.payload_guess = filter(do_filter, lower.payload_guess) - -def split_top_down(lower, upper, __fval=None, **fval): - if __fval is not None: - fval.update(__fval) - if lower in upper.overload_fields: - ofval = upper.overload_fields[lower] - for k in fval: - if k not in ofval or ofval[k] != fval[k]: - return - upper.overload_fields = upper.overload_fields.copy() - del(upper.overload_fields[lower]) - -def split_layers(lower, upper, __fval=None, **fval): - if __fval is not None: - fval.update(__fval) - split_bottom_up(lower, upper, **fval) - split_top_down(lower, upper, **fval) - - -bind_layers( Dot3, LLC, ) -bind_layers( GPRS, IP, ) -bind_layers( PrismHeader, Dot11, ) -bind_layers( RadioTap, Dot11, ) -bind_layers( Dot11, LLC, type=2) -bind_layers( Dot11QoS, LLC, ) -bind_layers( L2TP, PPP, ) -bind_layers( HDLC, PPP, ) -bind_layers( PPP, IP, proto=33) -bind_layers( PPP, PPP_IPCP, proto=0x8021) -bind_layers( PPP, PPP_ECP, proto=0x8053) -bind_layers( Ether, LLC, type=122) -bind_layers( Ether, Dot1Q, type=33024) -bind_layers( Ether, Ether, type=1) -bind_layers( Ether, ARP, type=2054) -bind_layers( Ether, IP, type=2048) -bind_layers( Ether, EAPOL, type=34958) -bind_layers( Ether, EAPOL, dst='01:80:c2:00:00:03', type=34958) -bind_layers( Ether, PPPoED, type=34915) -bind_layers( Ether, PPPoE, type=34916) -bind_layers( CookedLinux, LLC, proto=122) -bind_layers( CookedLinux, Dot1Q, proto=33024) -bind_layers( CookedLinux, Ether, proto=1) -bind_layers( CookedLinux, ARP, proto=2054) -bind_layers( CookedLinux, IP, proto=2048) -bind_layers( CookedLinux, EAPOL, proto=34958) -bind_layers( CookedLinux, PPPoED, proto=34915) -bind_layers( CookedLinux, PPPoE, proto=34916) -bind_layers( GRE, LLC, proto=122) -bind_layers( GRE, Dot1Q, proto=33024) -bind_layers( GRE, Ether, proto=1) -bind_layers( GRE, ARP, proto=2054) -bind_layers( GRE, IP, proto=2048) -bind_layers( GRE, EAPOL, proto=34958) -bind_layers( PPPoE, PPP, code=0) -bind_layers( EAPOL, EAP, type=0) -bind_layers( LLC, STP, dsap=66, ssap=66, ctrl=3) -bind_layers( LLC, SNAP, dsap=170, ssap=170, ctrl=3) -bind_layers( SNAP, Dot1Q, code=33024) -bind_layers( SNAP, Ether, code=1) -bind_layers( SNAP, ARP, code=2054) -bind_layers( SNAP, IP, code=2048) -bind_layers( SNAP, EAPOL, code=34958) -bind_layers( SNAP, STP, code=267) -bind_layers( IPerror, IPerror, frag=0, proto=4) -bind_layers( IPerror, ICMPerror, frag=0, proto=1) -bind_layers( IPerror, TCPerror, frag=0, proto=6) -bind_layers( IPerror, UDPerror, frag=0, proto=17) -bind_layers( IP, IP, frag=0, proto=4) -bind_layers( IP, ICMP, frag=0, proto=1) -bind_layers( IP, TCP, frag=0, proto=6) -bind_layers( IP, UDP, frag=0, proto=17) -bind_layers( IP, GRE, frag=0, proto=47) -bind_layers( UDP, L2TP, sport=1701, dport=1701) -bind_layers( UDP, SNMP, sport=161) -bind_layers( UDP, SNMP, dport=161) -bind_layers( UDP, MGCP, dport=2727) -bind_layers( UDP, MGCP, sport=2727) -bind_layers( UDP, DNS, dport=53) -bind_layers( UDP, DNS, sport=53) -bind_layers( UDP, ISAKMP, dport=500, sport=500) -bind_layers( UDP, HSRP, dport=1985, sport=1985) -bind_layers( UDP, NTP, dport=123, sport=123) -bind_layers( UDP, BOOTP, dport=67, sport=68) -bind_layers( UDP, BOOTP, dport=68, sport=67) -bind_layers( BOOTP, DHCP, options='c\x82Sc') -bind_layers( UDP, RIP, sport=520) -bind_layers( UDP, RIP, dport=520) -bind_layers( RIP, RIPEntry, ) -bind_layers( RIPEntry, RIPEntry, ) -bind_layers( Dot11, Dot11AssoReq, subtype=0, type=0) -bind_layers( Dot11, Dot11AssoResp, subtype=1, type=0) -bind_layers( Dot11, Dot11ReassoReq, subtype=2, type=0) -bind_layers( Dot11, Dot11ReassoResp, subtype=3, type=0) -bind_layers( Dot11, Dot11ProbeReq, subtype=4, type=0) -bind_layers( Dot11, Dot11ProbeResp, subtype=5, type=0) -bind_layers( Dot11, Dot11Beacon, subtype=8, type=0) -bind_layers( Dot11, Dot11ATIM, subtype=9, type=0) -bind_layers( Dot11, Dot11Disas, subtype=10, type=0) -bind_layers( Dot11, Dot11Auth, subtype=11, type=0) -bind_layers( Dot11, Dot11Deauth, subtype=12, type=0) -bind_layers( Dot11Beacon, Dot11Elt, ) -bind_layers( Dot11AssoReq, Dot11Elt, ) -bind_layers( Dot11AssoResp, Dot11Elt, ) -bind_layers( Dot11ReassoReq, Dot11Elt, ) -bind_layers( Dot11ReassoResp, Dot11Elt, ) -bind_layers( Dot11ProbeReq, Dot11Elt, ) -bind_layers( Dot11ProbeResp, Dot11Elt, ) -bind_layers( Dot11Auth, Dot11Elt, ) -bind_layers( Dot11Elt, Dot11Elt, ) -bind_layers( TCP, Skinny, dport=2000) -bind_layers( TCP, Skinny, sport=2000) -bind_layers( UDP, SebekHead, sport=1101) -bind_layers( UDP, SebekHead, dport=1101) -bind_layers( UDP, SebekHead, dport=1101, sport=1101) -bind_layers( SebekHead, SebekV1, version=1) -bind_layers( SebekHead, SebekV2Sock, version=2, type=2) -bind_layers( SebekHead, SebekV2, version=2) -bind_layers( SebekHead, SebekV3Sock, version=3, type=2) -bind_layers( SebekHead, SebekV3, version=3) -bind_layers( CookedLinux, IrLAPHead, proto=23) -bind_layers( IrLAPHead, IrLAPCommand, Type=1) -bind_layers( IrLAPCommand, IrLMP, ) -bind_layers( UDP, NBNSQueryRequest, dport=137) -bind_layers( UDP, NBNSRequest, dport=137) -bind_layers( UDP, NBNSQueryResponse, sport=137) -bind_layers( UDP, NBNSQueryResponseNegative, sport=137) -bind_layers( UDP, NBNSNodeStatusResponse, sport=137) -bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) -bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) -bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseService, ) -bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseEnd, ) -bind_layers( UDP, NBNSWackResponse, sport=137) -bind_layers( UDP, NBTDatagram, dport=138) -bind_layers( TCP, NBTSession, dport=139) -bind_layers( NBTSession, SMBNegociate_Protocol_Request_Header, ) -bind_layers( SMBNegociate_Protocol_Request_Header, SMBNegociate_Protocol_Request_Tail, ) -bind_layers( SMBNegociate_Protocol_Request_Tail, SMBNegociate_Protocol_Request_Tail, ) -bind_layers( NBTSession, SMBNegociate_Protocol_Response_Advanced_Security, ExtendedSecurity=1) -bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security, ExtendedSecurity=0, EncryptionKeyLength=8) -bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security_No_Key, ExtendedSecurity=0, EncryptionKeyLength=0) -bind_layers( NBTSession, SMBSession_Setup_AndX_Request, ) -bind_layers( NBTSession, SMBSession_Setup_AndX_Response, ) -bind_layers( HCI_Hdr, HCI_ACL_Hdr, type=2) -bind_layers( HCI_Hdr, Raw, ) -bind_layers( HCI_ACL_Hdr, L2CAP_Hdr, ) -bind_layers( L2CAP_Hdr, L2CAP_CmdHdr, cid=1) -bind_layers( L2CAP_CmdHdr, L2CAP_CmdRej, code=1) -bind_layers( L2CAP_CmdHdr, L2CAP_ConnReq, code=2) -bind_layers( L2CAP_CmdHdr, L2CAP_ConnResp, code=3) -bind_layers( L2CAP_CmdHdr, L2CAP_ConfReq, code=4) -bind_layers( L2CAP_CmdHdr, L2CAP_ConfResp, code=5) -bind_layers( L2CAP_CmdHdr, L2CAP_DisconnReq, code=6) -bind_layers( L2CAP_CmdHdr, L2CAP_DisconnResp, code=7) -bind_layers( L2CAP_CmdHdr, L2CAP_InfoReq, code=10) -bind_layers( L2CAP_CmdHdr, L2CAP_InfoResp, code=11) -bind_layers( UDP, MobileIP, sport=434) -bind_layers( UDP, MobileIP, dport=434) -bind_layers( MobileIP, MobileIPRRQ, type=1) -bind_layers( MobileIP, MobileIPRRP, type=3) -bind_layers( MobileIP, MobileIPTunnelData, type=4) -bind_layers( MobileIPTunnelData, IP, nexthdr=4) -bind_layers( NetflowHeader, NetflowHeaderV1, version=1) -bind_layers( NetflowHeaderV1, NetflowRecordV1, ) - -bind_layers(UDP, TFTP, dport=69) -bind_layers(TFTP, TFTP_RRQ, op=1) -bind_layers(TFTP, TFTP_WRQ, op=2) -bind_layers(TFTP, TFTP_DATA, op=3) -bind_layers(TFTP, TFTP_ACK, op=4) -bind_layers(TFTP, TFTP_ERROR, op=5) -bind_layers(TFTP, TFTP_OACK, op=6) -bind_layers(TFTP_RRQ, TFTP_Options) -bind_layers(TFTP_WRQ, TFTP_Options) -bind_layers(TFTP_OACK, TFTP_Options) - - -################### -## Fragmentation ## -################### - -def fragment(pkt, fragsize=1480): - fragsize = (fragsize+7)/8*8 - lst = [] - for p in pkt: - s = str(p[IP].payload) - nb = (len(s)+fragsize-1)/fragsize - for i in range(nb): - q = p.copy() - del(q[IP].payload) - del(q[IP].chksum) - del(q[IP].len) - if i == nb-1: - q[IP].flags &= ~1 - else: - q[IP].flags |= 1 - q[IP].frag = i*fragsize/8 - r = Raw(load=s[i*fragsize:(i+1)*fragsize]) - r.overload_fields = p[IP].payload.overload_fields.copy() - q.add_payload(r) - lst.append(q) - return lst - -def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): - if overlap_fragsize is None: - overlap_fragsize = fragsize - q = p.copy() - del(q[IP].payload) - q[IP].add_payload(overlap) - - qfrag = fragment(q, overlap_fragsize) - qfrag[-1][IP].flags |= 1 - return qfrag+fragment(p, fragsize) - -def defrag(plist): - """defrag(plist) -> ([not fragmented], [defragmented], - [ [bad fragments], [bad fragments], ... ])""" - frags = {} - nofrag = PacketList() - for p in plist: - ip = p[IP] - if IP not in p: - nofrag.append(p) - continue - if ip.frag == 0 and ip.flags & 1 == 0: - nofrag.append(p) - continue - uniq = (ip.id,ip.src,ip.dst,ip.proto) - if uniq in frags: - frags[uniq].append(p) - else: - frags[uniq] = PacketList([p]) - defrag = [] - missfrag = [] - for lst in frags.itervalues(): - lst.sort(lambda x,y:cmp(x.frag, y.frag)) - p = lst[0] - if p.frag > 0: - missfrag.append(lst) - continue - p = p.copy() - if Padding in p: - del(p[Padding].underlayer.payload) - ip = p[IP] - if ip.len is None or ip.ihl is None: - clen = len(ip.payload) - else: - clen = ip.len - (ip.ihl<<2) - txt = Raw() - for q in lst[1:]: - if clen != q.frag<<3: - if clen > q.frag<<3: - warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) - missfrag.append(lst) - txt = None - break - if q[IP].len is None or q[IP].ihl is None: - clen += len(q[IP].payload) - else: - clen += q[IP].len - (q[IP].ihl<<2) - if Padding in q: - del(q[Padding].underlayer.payload) - txt.add_payload(q[IP].payload.copy()) - - if txt is None: - continue - - ip.flags &= ~1 # !MF - del(ip.chksum) - del(ip.len) - p = p/txt - defrag.append(p) - defrag2=PacketList() - for p in defrag: - defrag2.append(p.__class__(str(p))) - return nofrag,defrag2,missfrag - -def defragment(plist): - """defrag(plist) -> plist defragmented as much as possible """ - frags = {} - final = [] - - pos = 0 - for p in plist: - p._defrag_pos = pos - pos += 1 - if IP in p: - ip = p[IP] - if ip.frag != 0 or ip.flags & 1: - ip = p[IP] - uniq = (ip.id,ip.src,ip.dst,ip.proto) - if uniq in frags: - frags[uniq].append(p) - else: - frags[uniq] = [p] - continue - final.append(p) - - defrag = [] - missfrag = [] - for lst in frags.itervalues(): - lst.sort(lambda x,y:cmp(x.frag, y.frag)) - p = lst[0] - if p.frag > 0: - missfrag += lst - continue - p = p.copy() - if Padding in p: - del(p[Padding].underlayer.payload) - ip = p[IP] - if ip.len is None or ip.ihl is None: - clen = len(ip.payload) - else: - clen = ip.len - (ip.ihl<<2) - txt = Raw() - for q in lst[1:]: - if clen != q.frag<<3: - if clen > q.frag<<3: - warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) - missfrag += lst - txt = None - break - if q[IP].len is None or q[IP].ihl is None: - clen += len(q[IP].payload) - else: - clen += q[IP].len - (q[IP].ihl<<2) - if Padding in q: - del(q[Padding].underlayer.payload) - txt.add_payload(q[IP].payload.copy()) - - if txt is None: - continue - - ip.flags &= ~1 # !MF - del(ip.chksum) - del(ip.len) - p = p/txt - p._defrag_pos = lst[-1]._defrag_pos - defrag.append(p) - defrag2=[] - for p in defrag: - q = p.__class__(str(p)) - q._defrag_pos = p._defrag_pos - defrag2.append(q) - final += defrag2 - final += missfrag - final.sort(lambda x,y: cmp(x._defrag_pos, y._defrag_pos)) - for p in final: - del(p._defrag_pos) - - if hasattr(plist, "listname"): - name = "Defragmented %s" % plist.listname - else: - name = "Defragmented" - - - return PacketList(final, name=name) - - - - - - -################### -## Super sockets ## -################### - -def Ether_Dot3_Dispatcher(pkt=None, **kargs): - if type(pkt) is str and len(pkt) >= 14 and struct.unpack("!H", pkt[12:14])[0] <= 1500: - return Dot3(pkt, **kargs) - return Ether(pkt, **kargs) - -# According to libdnet -LLTypes = { ARPHDR_ETHER : Ether_Dot3_Dispatcher, - ARPHDR_METRICOM : Ether_Dot3_Dispatcher, - ARPHDR_LOOPBACK : Ether_Dot3_Dispatcher, - 9: PPP, - 12 : IP, - 101 : IP, - 801 : Dot11, - 802 : PrismHeader, - 803 : RadioTap, - 105 : Dot11, - 113 : CookedLinux, - 119 : PrismHeader, # for atheros - 127 : RadioTap, - 144 : CookedLinux, # called LINUX_IRDA, similar to CookedLinux - 783 : IrLAPHead, - 0xB1E70073L : HCI_Hdr, # I invented this one - } - -LLNumTypes = { Ether : ARPHDR_ETHER, - PPP: 9, - IP : 12, - IP : 101, - Dot11 : 801, - PrismHeader : 802, - RadioTap : 803, - RadioTap : 127, - Dot11 : 105, - CookedLinux : 113, - CookedLinux : 144, - IrLAPHead : 783 - } - -L3Types = { ETH_P_IP : IP, - ETH_P_ARP : ARP, - ETH_P_ALL : IP - } - - -def flush_fd(fd): - if type(fd) is not int: - fd = fd.fileno() - while 1: - r,w,e = select([fd],[],[],0) - if r: - os.read(fd,MTU) - else: - break - -class SuperSocket: - closed=0 - def __init__(self, family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0): - self.ins = socket.socket(family, type, proto) - self.outs = self.ins - self.promisc=None - def send(self, x): - sx = str(x) - x.sent_time = time.time() - return self.outs.send(sx) - def recv(self, x): - return Raw(self.ins.recv(x)) - def fileno(self): - return self.ins.fileno() - def close(self): - if self.closed: - return - self.closed=1 - if self.ins != self.outs: - if self.outs and self.outs.fileno() != -1: - self.outs.close() - if self.ins and self.ins.fileno() != -1: - self.ins.close() - def bind_in(self, addr): - self.ins.bind(addr) - def bind_out(self, addr): - self.outs.bind(addr) - - -class L3RawSocket(SuperSocket): - def __init__(self, type = ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0): - self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) - self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) - self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) - def recv(self, x): - return Ether(self.ins.recv(x)).payload - def send(self, x): - try: - sx = str(x) - x.sent_time = time.time() - self.outs.sendto(sx,(x.dst,0)) - except socket.error,msg: - log_runtime.error(msg) - - - -class L3PacketSocket(SuperSocket): - def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): - self.type = type - self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) - self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) - flush_fd(self.ins) - if iface: - self.ins.bind((iface, type)) - if not nofilter: - if conf.except_filter: - if filter: - filter = "(%s) and not (%s)" % (filter, conf.except_filter) - else: - filter = "not (%s)" % conf.except_filter - if filter is not None: - attach_filter(self.ins, filter) - self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) - self.outs = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) - self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) - if promisc is None: - promisc = conf.promisc - self.promisc = promisc - if self.promisc: - if iface is None: - self.iff = get_if_list() - else: - if iface.__class__ is list: - self.iff = iface - else: - self.iff = [iface] - for i in self.iff: - set_promisc(self.ins, i) - def close(self): - if self.closed: - return - self.closed=1 - if self.promisc: - for i in self.iff: - set_promisc(self.ins, i, 0) - SuperSocket.close(self) - def recv(self, x): - pkt, sa_ll = self.ins.recvfrom(x) - if sa_ll[2] == socket.PACKET_OUTGOING: - return None - if LLTypes.has_key(sa_ll[3]): - cls = LLTypes[sa_ll[3]] - lvl = 2 - elif L3Types.has_key(sa_ll[1]): - cls = L3Types[sa_ll[1]] - lvl = 3 - else: - warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3])) - cls = Ether - lvl = 2 - - try: - pkt = cls(pkt) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - pkt = Raw(pkt) - if lvl == 2: - pkt = pkt.payload - - if pkt is not None: - pkt.time = get_last_packet_timestamp(self.ins) - return pkt - - def send(self, x): - if isinstance(x, IPv6): - iff,a,gw = conf.route6.route(x.dst) - elif hasattr(x,"dst"): - iff,a,gw = conf.route.route(x.dst) - else: - iff = conf.iface - sdto = (iff, self.type) - self.outs.bind(sdto) - sn = self.outs.getsockname() - ll = lambda x:x - if sn[3] in (ARPHDR_PPP,ARPHDR_TUN): - sdto = (iff, ETH_P_IP) - if LLTypes.has_key(sn[3]): - ll = lambda x:LLTypes[sn[3]]()/x - try: - sx = str(ll(x)) - x.sent_time = time.time() - self.outs.sendto(sx, sdto) - except socket.error,msg: - x.sent_time = time.time() # bad approximation - if conf.auto_fragment and msg[0] == 90: - for p in fragment(x): - self.outs.sendto(str(ll(p)), sdto) - else: - raise - - - - -class L2Socket(SuperSocket): - def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): - if iface is None: - iface = conf.iface - self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) - self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) - flush_fd(self.ins) - if not nofilter: - if conf.except_filter: - if filter: - filter = "(%s) and not (%s)" % (filter, conf.except_filter) - else: - filter = "not (%s)" % conf.except_filter - if filter is not None: - attach_filter(self.ins, filter) - self.ins.bind((iface, type)) - self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) - self.outs = self.ins - self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) - sa_ll = self.outs.getsockname() - if LLTypes.has_key(sa_ll[3]): - self.LL = LLTypes[sa_ll[3]] - elif L3Types.has_key(sa_ll[1]): - self.LL = L3Types[sa_ll[1]] - else: - warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3])) - self.LL = Ether - - def recv(self, x): - pkt, sa_ll = self.ins.recvfrom(x) - if sa_ll[2] == socket.PACKET_OUTGOING: - return None - try: - q = self.LL(pkt) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - q = Raw(pkt) - q.time = get_last_packet_timestamp(self.ins) - return q - - -class L2ListenSocket(SuperSocket): - def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None, nofilter=0): - self.type = type - self.outs = None - self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) - self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) - flush_fd(self.ins) - if iface is not None: - self.ins.bind((iface, type)) - if not nofilter: - if conf.except_filter: - if filter: - filter = "(%s) and not (%s)" % (filter, conf.except_filter) - else: - filter = "not (%s)" % conf.except_filter - if filter is not None: - attach_filter(self.ins, filter) - if promisc is None: - promisc = conf.sniff_promisc - self.promisc = promisc - if iface is None: - self.iff = get_if_list() - else: - if iface.__class__ is list: - self.iff = iface - else: - self.iff = [iface] - if self.promisc: - for i in self.iff: - set_promisc(self.ins, i) - self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) - def close(self): - if self.promisc: - for i in self.iff: - set_promisc(self.ins, i, 0) - SuperSocket.close(self) - - def recv(self, x): - pkt, sa_ll = self.ins.recvfrom(x) - if LLTypes.has_key(sa_ll[3]): - cls = LLTypes[sa_ll[3]] - elif L3Types.has_key(sa_ll[1]): - cls = L3Types[sa_ll[1]] - else: - warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3])) - cls = Ether - - try: - pkt = cls(pkt) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - pkt = Raw(pkt) - pkt.time = get_last_packet_timestamp(self.ins) - return pkt - - def send(self, x): - raise Scapy_Exception("Can't send anything with L2ListenSocket") - - - -class L3dnetSocket(SuperSocket): - def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): - self.iflist = {} - self.ins = pcap.pcapObject() - if iface is None: - iface = conf.iface - self.iface = iface - self.ins.open_live(iface, 1600, 0, 100) - try: - ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) - except: - pass - if nofilter: - if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap - filter = "ether proto %i" % type - else: - filter = None - else: - if conf.except_filter: - if filter: - filter = "(%s) and not (%s)" % (filter, conf.except_filter) - else: - filter = "not (%s)" % conf.except_filter - if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap - if filter: - filter = "(ether proto %i) and (%s)" % (type,filter) - else: - filter = "ether proto %i" % type - if filter: - self.ins.setfilter(filter, 0, 0) - def send(self, x): - if isinstance(x, IPv6): - iff,a,gw = conf.route6.route(x.dst) - elif hasattr(x,"dst"): - iff,a,gw = conf.route.route(x.dst) - else: - iff = conf.iface - ifs = self.iflist.get(iff) - if ifs is None: - self.iflist[iff] = ifs = dnet.eth(iff) - sx = str(Ether()/x) - x.sent_time = time.time() - ifs.send(sx) - def recv(self,x=MTU): - ll = self.ins.datalink() - if LLTypes.has_key(ll): - cls = LLTypes[ll] - else: - warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll)) - cls = Ether - - pkt = self.ins.next() - if pkt is not None: - l,pkt,ts = pkt - if pkt is None: - return - - try: - pkt = cls(pkt) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - pkt = Raw(pkt) - pkt.time = ts - return pkt.payload - - def nonblock_recv(self): - self.ins.setnonblock(1) - p = self.recv() - self.ins.setnonblock(0) - return p - - def close(self): - if hasattr(self, "ins"): - del(self.ins) - if hasattr(self, "outs"): - del(self.outs) - -class L2dnetSocket(SuperSocket): - def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): - if iface is None: - iface = conf.iface - self.iface = iface - self.ins = pcap.pcapObject() - self.ins.open_live(iface, 1600, 0, 100) - try: - ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) - except: - pass - if nofilter: - if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap - filter = "ether proto %i" % type - else: - filter = None - else: - if conf.except_filter: - if filter: - filter = "(%s) and not (%s)" % (filter, conf.except_filter) - else: - filter = "not (%s)" % conf.except_filter - if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap - if filter: - filter = "(ether proto %i) and (%s)" % (type,filter) - else: - filter = "ether proto %i" % type - if filter: - self.ins.setfilter(filter, 0, 0) - self.outs = dnet.eth(iface) - def recv(self,x): - ll = self.ins.datalink() - if LLTypes.has_key(ll): - cls = LLTypes[ll] - else: - warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll)) - cls = Ether - - pkt = self.ins.next() - if pkt is not None: - l,pkt,ts = pkt - if pkt is None: - return - - try: - pkt = cls(pkt) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - pkt = Raw(pkt) - pkt.time = ts - return pkt - - def nonblock_recv(self): - self.ins.setnonblock(1) - p = self.recv(MTU) - self.ins.setnonblock(0) - return p - - def close(self): - if hasattr(self, "ins"): - del(self.ins) - if hasattr(self, "outs"): - del(self.outs) - - - - - -class L2pcapListenSocket(SuperSocket): - def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None): - self.type = type - self.outs = None - self.ins = pcap.pcapObject() - self.iface = iface - if iface is None: - iface = conf.iface - if promisc is None: - promisc = conf.sniff_promisc - self.promisc = promisc - self.ins.open_live(iface, 1600, self.promisc, 100) - try: - ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) - except: - pass - if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given - if conf.except_filter: - if filter: - filter = "(%s) and not (%s)" % (filter, conf.except_filter) - else: - filter = "not (%s)" % conf.except_filter - if filter: - self.ins.setfilter(filter, 0, 0) - - def close(self): - del(self.ins) - - def recv(self, x): - ll = self.ins.datalink() - if LLTypes.has_key(ll): - cls = LLTypes[ll] - else: - warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll)) - cls = Ether - - pkt = None - while pkt is None: - pkt = self.ins.next() - if pkt is not None: - l,pkt,ts = pkt - - try: - pkt = cls(pkt) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - pkt = Raw(pkt) - pkt.time = ts - return pkt - - def send(self, x): - raise Scapy_Exception("Can't send anything with L2pcapListenSocket") - - -class SimpleSocket(SuperSocket): - def __init__(self, sock): - self.ins = sock - self.outs = sock - - -class StreamSocket(SimpleSocket): - def __init__(self, sock, basecls=Raw): - SimpleSocket.__init__(self, sock) - self.basecls = basecls - - def recv(self, x=MTU): - pkt = self.ins.recv(x, socket.MSG_PEEK) - x = len(pkt) - pkt = self.basecls(pkt) - pad = pkt[Padding] - if pad is not None and pad.underlayer is not None: - del(pad.underlayer.payload) - while pad is not None and not isinstance(pad, NoPayload): - x -= len(pad.load) - pad = pad.payload - self.ins.recv(x) - return pkt - - -class BluetoothL2CAPSocket(SuperSocket): - def __init__(self, peer): - s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, - socket.BTPROTO_L2CAP) - s.connect((peer,0)) - - self.ins = self.outs = s - - def recv(self, x): - return L2CAP_CmdHdr(self.ins.recv(x)) - - -class BluetoothHCISocket(SuperSocket): - def __init__(self, iface=0x10000, type=None): - s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) - s.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR,1) - s.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP,1) - s.setsockopt(socket.SOL_HCI, socket.HCI_FILTER, struct.pack("IIIh2x", 0xffffffffL,0xffffffffL,0xffffffffL,0)) #type mask, event mask, event mask, opcode - s.bind((iface,)) - self.ins = self.outs = s -# s.connect((peer,0)) - - - def recv(self, x): - return HCI_Hdr(self.ins.recv(x)) - - - -#################### -## Send / Receive ## -#################### - - - - -def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0): - if not isinstance(pkt, Gen): - pkt = SetGen(pkt) - - if verbose is None: - verbose = conf.verb - debug.recv = PacketList([],"Unanswered") - debug.sent = PacketList([],"Sent") - debug.match = SndRcvList([]) - nbrecv=0 - ans = [] - # do it here to fix random fields, so that parent and child have the same - all_stimuli = tobesent = [p for p in pkt] - notans = len(tobesent) - - hsent={} - for i in tobesent: - h = i.hashret() - if h in hsent: - hsent[h].append(i) - else: - hsent[h] = [i] - if retry < 0: - retry = -retry - autostop=retry - else: - autostop=0 - - - while retry >= 0: - found=0 - - if timeout < 0: - timeout = None - - rdpipe,wrpipe = os.pipe() - rdpipe=os.fdopen(rdpipe) - wrpipe=os.fdopen(wrpipe,"w") - - pid=1 - try: - pid = os.fork() - if pid == 0: - try: - sys.stdin.close() - rdpipe.close() - try: - i = 0 - if verbose: - print "Begin emission:" - for p in tobesent: - pks.send(p) - i += 1 - time.sleep(inter) - if verbose: - print "Finished to send %i packets." % i - except SystemExit: - pass - except KeyboardInterrupt: - pass - except: - log_runtime.exception("--- Error in child %i" % os.getpid()) - log_runtime.info("--- Error in child %i" % os.getpid()) - finally: - try: - os.setpgrp() # Chance process group to avoid ctrl-C - sent_times = [p.sent_time for p in all_stimuli if p.sent_time] - cPickle.dump( (arp_cache,sent_times), wrpipe ) - wrpipe.close() - except: - pass - elif pid < 0: - log_runtime.error("fork error") - else: - wrpipe.close() - stoptime = 0 - remaintime = None - inmask = [rdpipe,pks] - try: - try: - while 1: - if stoptime: - remaintime = stoptime-time.time() - if remaintime <= 0: - break - r = None - if FREEBSD or DARWIN: - inp, out, err = select(inmask,[],[], 0.05) - if len(inp) == 0 or pks in inp: - r = pks.nonblock_recv() - else: - inp, out, err = select(inmask,[],[], remaintime) - if len(inp) == 0: - break - if pks in inp: - r = pks.recv(MTU) - if rdpipe in inp: - if timeout: - stoptime = time.time()+timeout - del(inmask[inmask.index(rdpipe)]) - if r is None: - continue - ok = 0 - h = r.hashret() - if h in hsent: - hlst = hsent[h] - for i in range(len(hlst)): - if r.answers(hlst[i]): - ans.append((hlst[i],r)) - if verbose > 1: - os.write(1, "*") - ok = 1 - if not multi: - del(hlst[i]) - notans -= 1; - else: - if not hasattr(hlst[i], '_answered'): - notans -= 1; - hlst[i]._answered = 1; - break - if notans == 0 and not multi: - break - if not ok: - if verbose > 1: - os.write(1, ".") - nbrecv += 1 - if conf.debug_match: - debug.recv.append(r) - except KeyboardInterrupt: - if chainCC: - raise - finally: - try: - ac,sent_times = cPickle.load(rdpipe) - except EOFError: - warning("Child died unexpectedly. Packets may have not been sent %i"%os.getpid()) - else: - arp_cache.update(ac) - for p,t in zip(all_stimuli, sent_times): - p.sent_time = t - os.waitpid(pid,0) - finally: - if pid == 0: - os._exit(0) - - remain = reduce(list.__add__, hsent.values(), []) - if multi: - remain = filter(lambda p: not hasattr(p, '_answered'), remain); - - if autostop and len(remain) > 0 and len(remain) != len(tobesent): - retry = autostop - - tobesent = remain - if len(tobesent) == 0: - break - retry -= 1 - - if conf.debug_match: - debug.sent=PacketList(remain[:],"Sent") - debug.match=SndRcvList(ans[:]) - - #clean the ans list to delete the field _answered - if (multi): - for s,r in ans: - if hasattr(s, '_answered'): - del(s._answered) - - if verbose: - print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans) - return SndRcvList(ans),PacketList(remain,"Unanswered"),debug.recv - - -def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs): - if not isinstance(x, Gen): - x = SetGen(x) - if verbose is None: - verbose = conf.verb - n = 0 - if count is not None: - loop = -count - elif not loop: - loop=-1 - try: - while loop: - for p in x: - s.send(p) - n += 1 - if verbose: - os.write(1,".") - time.sleep(inter) - if loop < 0: - loop += 1 - except KeyboardInterrupt: - pass - s.close() - if verbose: - print "\nSent %i packets." % n - -def send(x, inter=0, loop=0, count=None, verbose=None, *args, **kargs): - """Send packets at layer 3 -send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" - __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose) - -def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, *args, **kargs): - """Send packets at layer 2 -sendp(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" - if iface is None and iface_hint is not None: - iface = conf.route.route(iface_hint)[0] - __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose) - -def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, iface=None): - """Send packets at layer 2 using tcpreplay for performance - pps: packets per second - mpbs: MBits per second - realtime: use packet's timestamp, bending time with realtime value - loop: number of times to process the packet list - iface: output interface """ - if iface is None: - iface = conf.iface - options = ["--intf1=%s" % iface ] - if pps is not None: - options.append("--pps=%i" % pps) - elif mbps is not None: - options.append("--mbps=%i" % mbps) - elif realtime is not None: - options.append("--multiplier=%i" % realtime) - else: - options.append("--topspeed") - - if loop: - options.append("--loop=%i" % loop) - - f = os.tempnam("scapy") - options.append(f) - wrpcap(f, x) - try: - try: - os.spawnlp(os.P_WAIT, conf.prog.tcpreplay, conf.prog.tcpreplay, *options) - except KeyboardInterrupt: - log_interactive.info("Interrupted by user") - finally: - os.unlink(f) - - - - - -def sr(x,filter=None, iface=None, nofilter=0, *args,**kargs): - """Send and receive packets at layer 3 -nofilter: put 1 to avoid use of bpf filters -retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered -timeout: how much time to wait after the last packet has been sent -verbose: set verbosity level -multi: whether to accept multiple answers for the same stimulus -filter: provide a BPF filter -iface: listen answers only on the given interface""" - if not kargs.has_key("timeout"): - kargs["timeout"] = -1 - s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) - a,b,c=sndrcv(s,x,*args,**kargs) - s.close() - return a,b - -def sr1(x,filter=None,iface=None, nofilter=0, *args,**kargs): - """Send packets at layer 3 and return only the first answer -nofilter: put 1 to avoid use of bpf filters -retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered -timeout: how much time to wait after the last packet has been sent -verbose: set verbosity level -multi: whether to accept multiple answers for the same stimulus -filter: provide a BPF filter -iface: listen answers only on the given interface""" - if not kargs.has_key("timeout"): - kargs["timeout"] = -1 - s=conf.L3socket(filter=filter, nofilter=nofilter, iface=iface) - a,b,c=sndrcv(s,x,*args,**kargs) - s.close() - if len(a) > 0: - return a[0][1] - else: - return None - -def srp(x,iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args,**kargs): - """Send and receive packets at layer 2 -nofilter: put 1 to avoid use of bpf filters -retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered -timeout: how much time to wait after the last packet has been sent -verbose: set verbosity level -multi: whether to accept multiple answers for the same stimulus -filter: provide a BPF filter -iface: work only on the given interface""" - if not kargs.has_key("timeout"): - kargs["timeout"] = -1 - if iface is None and iface_hint is not None: - iface = conf.route.route(iface_hint)[0] - s = conf.L2socket(iface=iface, filter=filter, nofilter=nofilter, type=type) - a,b,c=sndrcv(s ,x,*args,**kargs) - s.close() - return a,b - -def srp1(*args,**kargs): - """Send and receive packets at layer 2 and return only the first answer -nofilter: put 1 to avoid use of bpf filters -retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered -timeout: how much time to wait after the last packet has been sent -verbose: set verbosity level -multi: whether to accept multiple answers for the same stimulus -filter: provide a BPF filter -iface: work only on the given interface""" - if not kargs.has_key("timeout"): - kargs["timeout"] = -1 - a,b=srp(*args,**kargs) - if len(a) > 0: - return a[0][1] - else: - return None - -def __sr_loop(srfunc, pkts, prn=lambda x:x[1].summary(), prnfail=lambda x:x.summary(), inter=1, timeout=None, count=None, verbose=None, store=1, *args, **kargs): - n = 0 - r = 0 - ct = conf.color_theme - if verbose is None: - verbose = conf.verb - parity = 0 - ans=[] - unans=[] - if timeout is None: - timeout = min(2*inter, 5) - try: - while 1: - parity ^= 1 - col = [ct.even,ct.odd][parity] - if count is not None: - if count == 0: - break - count -= 1 - start = time.time() - print "\rsend...\r", - res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=1, *args, **kargs) - n += len(res[0])+len(res[1]) - r += len(res[0]) - if verbose > 1 and prn and len(res[0]) > 0: - msg = "RECV %i:" % len(res[0]) - print "\r"+ct.success(msg), - for p in res[0]: - print col(prn(p)) - print " "*len(msg), - if verbose > 1 and prnfail and len(res[1]) > 0: - msg = "fail %i:" % len(res[1]) - print "\r"+ct.fail(msg), - for p in res[1]: - print col(prnfail(p)) - print " "*len(msg), - if verbose > 1 and not (prn or prnfail): - print "recv:%i fail:%i" % tuple(map(len, res[:2])) - if store: - ans += res[0] - unans += res[1] - end=time.time() - if end-start < inter: - time.sleep(inter+start-end) - except KeyboardInterrupt: - pass - - if verbose and n>0: - print ct.normal("\nSent %i packets, received %i packets. %3.1f%% hits." % (n,r,100.0*r/n)) - return SndRcvList(ans),PacketList(unans) - -def srloop(pkts, *args, **kargs): - """Send a packet at layer 3 in loop and print the answer each time -srloop(pkts, [prn], [inter], [count], ...) --> None""" - return __sr_loop(sr, pkts, *args, **kargs) - -def srploop(pkts, *args, **kargs): - """Send a packet at layer 2 in loop and print the answer each time -srloop(pkts, [prn], [inter], [count], ...) --> None""" - return __sr_loop(srp, pkts, *args, **kargs) - - -def sndrcvflood(pks, pkt, prn=lambda (s,r):r.summary(), chainCC=0, store=1, unique=0): - if not isinstance(pkt, Gen): - pkt = SetGen(pkt) - tobesent = [p for p in pkt] - received = SndRcvList() - seen = {} - - hsent={} - for i in tobesent: - h = i.hashret() - if h in hsent: - hsent[h].append(i) - else: - hsent[h] = [i] - - def send_in_loop(tobesent): - while 1: - for p in tobesent: - yield p - - packets_to_send = send_in_loop(tobesent) - - ssock = rsock = pks.fileno() - - try: - while 1: - readyr,readys,_ = select([rsock],[ssock],[]) - if ssock in readys: - pks.send(packets_to_send.next()) - - if rsock in readyr: - p = pks.recv(MTU) - if p is None: - continue - h = p.hashret() - if h in hsent: - hlst = hsent[h] - for i in hlst: - if p.answers(i): - res = prn((i,p)) - if unique: - if res in seen: - continue - seen[res] = None - if res is not None: - print res - if store: - received.append((i,p)) - except KeyboardInterrupt: - if chainCC: - raise - return received - -def srflood(x,filter=None, iface=None, nofilter=None, *args,**kargs): - """Flood and receive packets at layer 3 -prn: function applied to packets received. Ret val is printed if not None -store: if 1 (default), store answers and return them -unique: only consider packets whose print -nofilter: put 1 to avoid use of bpf filters -filter: provide a BPF filter -iface: listen answers only on the given interface""" - s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) - r=sndrcvflood(s,x,*args,**kargs) - s.close() - return r - -def srpflood(x,filter=None, iface=None, iface_hint=None, nofilter=None, *args,**kargs): - """Flood and receive packets at layer 2 -prn: function applied to packets received. Ret val is printed if not None -store: if 1 (default), store answers and return them -unique: only consider packets whose print -nofilter: put 1 to avoid use of bpf filters -filter: provide a BPF filter -iface: listen answers only on the given interface""" - if iface is None and iface_hint is not None: - iface = conf.route.route(iface_hint)[0] - s = conf.L2socket(filter=filter, iface=iface, nofilter=nofilter) - r=sndrcvflood(s,x,*args,**kargs) - s.close() - return r - - -## Bluetooth - - -def srbt(peer, pkts, inter=0.1, *args, **kargs): - s = conf.BTsocket(peer=peer) - a,b,c=sndrcv(s,pkts,inter=inter,*args,**kargs) - s.close() - return a,b - -def srbt1(peer, pkts, *args, **kargs): - a,b = srbt(peer, pkts, *args, **kargs) - if len(a) > 0: - return a[0][1] - - - - - -############################# -## pcap capture file stuff ## -############################# - -def wrpcap(filename, pkt, *args, **kargs): - """Write a list of packets to a pcap file -gz: set to 1 to save a gzipped capture -linktype: force linktype value -endianness: "<" or ">", force endianness""" - PcapWriter(filename, *args, **kargs).write(pkt) - -def rdpcap(filename, count=-1): - """Read a pcap file and return a packet list -count: read only <count> packets""" - return PcapReader(filename).read_all(count=count) - -class PcapReader: - """A stateful pcap reader - - Based entirely on scapy.rdpcap(), this class allows for packets - to be dispatched without having to be loaded into memory all at - once - """ - - def __init__(self, filename): - self.filename = filename - try: - self.f = gzip.open(filename,"rb") - magic = self.f.read(4) - except IOError: - self.f = open(filename,"rb") - magic = self.f.read(4) - if magic == "\xa1\xb2\xc3\xd4": #big endian - self.endian = ">" - elif magic == "\xd4\xc3\xb2\xa1": #little endian - self.endian = "<" - else: - raise RuntimeWarning, "Not a pcap capture file (bad magic)" - hdr = self.f.read(20) - if len(hdr)<20: - raise RuntimeWarning, "Invalid pcap file (too short)" - vermaj,vermin,tz,sig,snaplen,linktype = struct.unpack(self.endian+"HHIIII",hdr) - self.LLcls = LLTypes.get(linktype, Raw) - if self.LLcls == Raw: - warning("PcapReader: unkonwon LL type [%i]/[%#x]. Using Raw packets" % (linktype,linktype)) - - def __iter__(self): - return self - - def next(self): - """impliment the iterator protocol on a set of packets in a - pcap file - """ - pkt = self.read_packet() - if pkt == None: - raise StopIteration - return pkt - - - def read_packet(self): - """return a single packet read from the file - - returns None when no more packets are available - """ - hdr = self.f.read(16) - if len(hdr) < 16: - return None - sec,usec,caplen,olen = struct.unpack(self.endian+"IIII", hdr) - s = self.f.read(caplen) - try: - p = self.LLcls(s) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - p = Raw(s) - p.time = sec+0.000001*usec - return p - - def dispatch(self, callback): - """call the specified callback routine for each packet read - - This is just a convienience function for the main loop - that allows for easy launching of packet processing in a - thread. - """ - p = self.read_packet() - while p != None: - callback(p) - p = self.read_packet() - - def read_all(self,count=-1): - """return a list of all packets in the pcap file - """ - res=[] - while count != 0: - count -= 1 - p = self.read_packet() - if p is None: - break - res.append(p) - return PacketList(res,name = os.path.basename(self.filename)) - - def recv(self, size): - """ Emulate a socket - """ - return self.read_packet() - - def fileno(self): - return self.f.fileno() - - - -class PcapWriter: - """A stream PCAP writer with more control than wrpcap()""" - def __init__(self, filename, linktype=None, gz=False, endianness="", append=False, sync=False): - """ - linktype: force linktype to a given value. If None, linktype is taken - from the first writter packet - gz: compress the capture on the fly - endianness: force an endianness (little:"<", big:">"). Default is native - append: append packets to the capture file instead of truncating it - sync: do not bufferize writes to the capture file - """ - - self.linktype = linktype - self.header_present = 0 - self.append=append - self.gz = gz - self.endian = endianness - self.filename=filename - self.sync=sync - bufsz=4096 - if sync: - bufsz=0 - - self.f = [open,gzip.open][gz](filename,append and "ab" or "wb", gz and 9 or bufsz) - - - - def fileno(self): - return self.f.fileno() - - def _write_header(self, pkt): - self.header_present=1 - - if self.linktype == None: - if type(pkt) is list or type(pkt) is tuple: - pkt = pkt[0] - self.linktype = LLNumTypes.get(pkt.__class__,1) - - if self.append: - # Even if prone to race conditions, this seems to be - # safest way to tell whether the header is already present - # because we have to handle compressed streams that - # are not as flexible as basic files - g = [open,gzip.open][self.gz](self.filename,"rb") - if g.read(16): - return - - self.f.write(struct.pack(self.endian+"IHHIIII", 0xa1b2c3d4L, - 2, 4, 0, 0, MTU, self.linktype)) - self.f.flush() - - - def write(self, pkt): - """accepts a either a single packet or a list of packets - to be written to the dumpfile - """ - if not self.header_present: - self._write_header(pkt) - for p in pkt: - self._write_packet(p) - - def _write_packet(self, packet): - """writes a single packet to the pcap file - """ - s = str(packet) - l = len(s) - sec = int(packet.time) - usec = int((packet.time-sec)*1000000) - self.f.write(struct.pack(self.endian+"IIII", sec, usec, l, l)) - self.f.write(s) - if self.gz and self.sync: - self.f.flush() - - def flush(self): - return self.f.flush() - def close(self): - return self.f.close() - - -re_extract_hexcap = re.compile("^(0x[0-9a-fA-F]{2,}[ :\t]|(0x)?[0-9a-fA-F]{2,}:|(0x)?[0-9a-fA-F]{3,}[: \t]|) *(([0-9a-fA-F]{2} {,2}){,16})") - -def import_hexcap(): - p = "" - try: - while 1: - l = raw_input().strip() - try: - p += re_extract_hexcap.match(l).groups()[3] - except: - warning("Parsing error during hexcap") - continue - except EOFError: - pass - - p = p.replace(" ","") - p2="" - for i in range(len(p)/2): - p2 += chr(int(p[2*i:2*i+2],16)) - return p2 - - - -def wireshark(pktlist): - f = os.tempnam("scapy") - wrpcap(f, pktlist) - os.spawnlp(os.P_NOWAIT, conf.prog.wireshark, conf.prog.wireshark, "-r", f) - -def hexedit(x): - x = str(x) - f = os.tempnam("scapy") - open(f,"w").write(x) - os.spawnlp(os.P_WAIT, conf.prog.hexedit, conf.prog.hexedit, f) - x = open(f).read() - os.unlink(f) - return x - - -##################### -## knowledge bases ## -##################### - -class KnowledgeBase: - def __init__(self, filename): - self.filename = filename - self.base = None - - def lazy_init(self): - self.base = "" - - def reload(self, filename = None): - if filename is not None: - self.filename = filename - oldbase = self.base - self.base = None - self.lazy_init() - if self.base is None: - self.base = oldbase - - def get_base(self): - if self.base is None: - self.lazy_init() - return self.base - - - -########################## -## IP location database ## -########################## - -class IPCountryKnowledgeBase(KnowledgeBase): - """ -How to generate the base : -db = [] -for l in open("GeoIPCountryWhois.csv").readlines(): - s,e,c = l.split(",")[2:5] - db.append((int(s[1:-1]),int(e[1:-1]),c[1:-1])) -cPickle.dump(gzip.open("xxx","w"),db) -""" - def lazy_init(self): - self.base = load_object(self.filename) - - -class CountryLocKnowledgeBase(KnowledgeBase): - def lazy_init(self): - f=open(self.filename) - self.base = {} - while 1: - l = f.readline() - if not l: - break - l = l.strip().split(",") - if len(l) != 3: - continue - c,lat,long = l - - self.base[c] = (float(long),float(lat)) - f.close() - - - - -def locate_ip(ip): - ip=map(int,ip.split(".")) - ip = ip[3]+(ip[2]<<8L)+(ip[1]<<16L)+(ip[0]<<24L) - - cloc = country_loc_kdb.get_base() - db = IP_country_kdb.get_base() - - d=0 - f=len(db)-1 - while (f-d) > 1: - guess = (d+f)/2 - if ip > db[guess][0]: - d = guess - else: - f = guess - s,e,c = db[guess] - if s <= ip and ip <= e: - return cloc.get(c,None) - - - - -############### -## p0f stuff ## -############### - -# File format (according to p0f.fp) : -# -# wwww:ttt:D:ss:OOO...:QQ:OS:Details -# -# wwww - window size -# ttt - initial TTL -# D - don't fragment bit (0=unset, 1=set) -# ss - overall SYN packet size -# OOO - option value and order specification -# QQ - quirks list -# OS - OS genre -# details - OS description - - - -class p0fKnowledgeBase(KnowledgeBase): - def __init__(self, filename): - KnowledgeBase.__init__(self, filename) - #self.ttl_range=[255] - def lazy_init(self): - try: - f=open(self.filename) - except IOError: - warning("Can't open base %s" % self.filename) - return - try: - self.base = [] - for l in f: - if l[0] in ["#","\n"]: - continue - l = tuple(l.split(":")) - if len(l) < 8: - continue - li = map(int,l[1:4]) - #if li[0] not in self.ttl_range: - # self.ttl_range.append(li[0]) - # self.ttl_range.sort() - self.base.append((l[0], li[0], li[1], li[2], l[4], l[5], l[6], l[7][:-1])) - except: - warning("Can't parse p0f database (new p0f version ?)") - self.base = None - f.close() - - -def packet2p0f(pkt): - while pkt.haslayer(IP) and pkt.haslayer(TCP): - pkt = pkt.getlayer(IP) - if isinstance(pkt.payload, TCP): - break - pkt = pkt.payload - - if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): - raise TypeError("Not a TCP/IP packet") - if pkt.payload.flags & 0x13 != 0x02: #S,!A,!F - raise TypeError("Not a syn packet") - - #t = p0f_kdb.ttl_range[:] - #t += [pkt.ttl] - #t.sort() - #ttl=t[t.index(pkt.ttl)+1] - ttl = pkt.ttl - - df = (pkt.flags & 2) / 2 - ss = len(pkt) - # from p0f/config.h : PACKET_BIG = 100 - if ss > 100: - ss = 0 - - ooo = "" - mss = -1 - qqT = False - qqP = False - #qqBroken = False - ilen = (pkt[TCP].dataofs << 2) - 20 # from p0f.c - for option in pkt.payload.options: - ilen -= 1 - if option[0] == "MSS": - ooo += "M" + str(option[1]) + "," - mss = option[1] - # FIXME: qqBroken - ilen -= 3 - elif option[0] == "WScale": - ooo += "W" + str(option[1]) + "," - # FIXME: qqBroken - ilen -= 2 - elif option[0] == "Timestamp": - if option[1][0] == 0: - ooo += "T0," - else: - ooo += "T," - if option[1][1] != 0: - qqT = True - ilen -= 9 - elif option[0] == "SAckOK": - ooo += "S," - ilen -= 1 - elif option[0] == "NOP": - ooo += "N," - elif option[0] == "EOL": - ooo += "E," - if ilen > 0: - qqP = True - else: - ooo += "?," - # FIXME: ilen - ooo = ooo[:-1] - if ooo == "": ooo = "." - - win = pkt.payload.window - if mss != -1: - if win % mss == 0: - win = "S" + str(win/mss) - elif win % (mss + 40) == 0: - win = "T" + str(win/(mss+40)) - win = str(win) - - qq = "" - - if qqP: - qq += "P" - if pkt[IP].id == 0: - qq += "Z" - if pkt[IP].options != '': - qq += "I" - if pkt[TCP].urgptr != 0: - qq += "U" - if pkt[TCP].reserved != 0: - qq += "X" - if pkt[TCP].ack != 0: - qq += "A" - if qqT: - qq += "T" - if pkt[TCP].flags & 40 != 0: - # U or P - qq += "F" - if not isinstance(pkt[TCP].payload, NoPayload): - qq += "D" - # FIXME : "!" - broken options segment - - if qq == "": - qq = "." - - return (win, - ttl, - df, - ss, - ooo, - qq) - -def p0f_correl(x,y): - d = 0 - # wwww can be "*" or "%nn" - d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0)) - # ttl - d += (y[1] >= x[1] and y[1] - x[1] < 32) - for i in [2, 3, 5]: - d += (x[i] == y[i]) - xopt = x[4].split(",") - yopt = y[4].split(",") - if len(xopt) == len(yopt): - same = True - for i in range(len(xopt)): - if not (xopt[i] == yopt[i] or - (len(yopt[i]) == 2 and len(xopt[i]) > 1 and - yopt[i][1] == "*" and xopt[i][0] == yopt[i][0]) or - (len(yopt[i]) > 2 and len(xopt[i]) > 1 and - yopt[i][1] == "%" and xopt[i][0] == yopt[i][0] and - int(xopt[i][1:]) % int(yopt[i][2:]) == 0)): - same = False - break - if same: - d += len(xopt) - return d - - -def p0f(pkt): - """Passive OS fingerprinting: which OS emitted this TCP SYN ? -p0f(packet) -> accuracy, [list of guesses] -""" - pb = p0f_kdb.get_base() - if not pb: - warning("p0f base empty.") - return [] - s = len(pb[0][0]) - r = [] - sig = packet2p0f(pkt) - max = len(sig[4].split(",")) + 5 - for b in pb: - d = p0f_correl(sig,b) - if d == max: - r.append((b[6], b[7], b[1] - pkt[IP].ttl)) - return r - - -def prnp0f(pkt): - try: - r = p0f(pkt) - except: - return - if r == []: - r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt))) + ":?:?]", None) - else: - r = r[0] - uptime = None - try: - uptime = pkt2uptime(pkt) - except: - pass - if uptime == 0: - uptime = None - res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) - if uptime is not None: - res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n -> %IP.dst%:%TCP.dport%") - else: - res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport%") - if r[2] is not None: - res += " (distance " + str(r[2]) + ")" - print res - - -def pkt2uptime(pkt, HZ=100): - """Calculate the date the machine which emitted the packet booted using TCP timestamp -pkt2uptime(pkt, [HZ=100])""" - if not isinstance(pkt, Packet): - raise TypeError("Not a TCP packet") - if isinstance(pkt,NoPayload): - raise TypeError("Not a TCP packet") - if not isinstance(pkt, TCP): - return pkt2uptime(pkt.payload) - for opt in pkt.options: - if opt[0] == "Timestamp": - #t = pkt.time - opt[1][0] * 1.0/HZ - #return time.ctime(t) - t = opt[1][0] / HZ - return t - raise TypeError("No timestamp option") - - - -################# -## Queso stuff ## -################# - - -def quesoTCPflags(flags): - if flags == "-": - return "-" - flv = "FSRPAUXY" - v = 0 - for i in flags: - v |= 2**flv.index(i) - return "%x" % v - -class QuesoKnowledgeBase(KnowledgeBase): - def lazy_init(self): - try: - f = open(self.filename) - except IOError: - return - self.base = {} - p = None - try: - for l in f: - l = l.strip() - if not l or l[0] == ';': - continue - if l[0] == '*': - if p is not None: - p[""] = name - name = l[1:].strip() - p = self.base - continue - if l[0] not in list("0123456"): - continue - res = l[2:].split() - res[-1] = quesoTCPflags(res[-1]) - res = " ".join(res) - if not p.has_key(res): - p[res] = {} - p = p[res] - if p is not None: - p[""] = name - except: - self.base = None - warning("Can't load queso base [%s]", self.filename) - f.close() - - - - -def queso_sig(target, dport=80, timeout=3): - p = queso_kdb.get_base() - ret = [] - for flags in ["S", "SA", "F", "FA", "SF", "P", "SEC"]: - ans, unans = sr(IP(dst=target)/TCP(dport=dport,flags=flags,seq=RandInt()), - timeout=timeout, verbose=0) - if len(ans) == 0: - rs = "- - - -" - else: - s,r = ans[0] - rs = "%i" % (r.seq != 0) - if not r.ack: - r += " 0" - elif r.ack-s.seq > 666: - rs += " R" % 0 - else: - rs += " +%i" % (r.ack-s.seq) - rs += " %X" % r.window - rs += " %x" % r.payload.flags - ret.append(rs) - return ret - -def queso_search(sig): - p = queso_kdb.get_base() - sig.reverse() - ret = [] - try: - while sig: - s = sig.pop() - p = p[s] - if p.has_key(""): - ret.append(p[""]) - except KeyError: - pass - return ret - - -def queso(*args,**kargs): - """Queso OS fingerprinting -queso(target, dport=80, timeout=3)""" - return queso_search(queso_sig(*args, **kargs)) - - - -###################### -## nmap OS fp stuff ## -###################### - - -class NmapKnowledgeBase(KnowledgeBase): - def lazy_init(self): - try: - f=open(self.filename) - except IOError: - return - - self.base = [] - name = None - try: - for l in f: - l = l.strip() - if not l or l[0] == "#": - continue - if l[:12] == "Fingerprint ": - if name is not None: - self.base.append((name,sig)) - name = l[12:].strip() - sig={} - p = self.base - continue - elif l[:6] == "Class ": - continue - op = l.find("(") - cl = l.find(")") - if op < 0 or cl < 0: - warning("error reading nmap os fp base file") - continue - test = l[:op] - s = map(lambda x: x.split("="), l[op+1:cl].split("%")) - si = {} - for n,v in s: - si[n] = v - sig[test]=si - if name is not None: - self.base.append((name,sig)) - except: - self.base = None - warning("Can't read nmap database [%s](new nmap version ?)" % self.filename) - f.close() - -def TCPflags2str(f): - fl="FSRPAUEC" - s="" - for i in range(len(fl)): - if f & 1: - s = fl[i]+s - f >>= 1 - return s - -def nmap_tcppacket_sig(pkt): - r = {} - if pkt is not None: -# r["Resp"] = "Y" - r["DF"] = (pkt.flags & 2) and "Y" or "N" - r["W"] = "%X" % pkt.window - r["ACK"] = pkt.ack==2 and "S++" or pkt.ack==1 and "S" or "O" - r["Flags"] = TCPflags2str(pkt.payload.flags) - r["Ops"] = "".join(map(lambda x: x[0][0],pkt.payload.options)) - else: - r["Resp"] = "N" - return r - - -def nmap_udppacket_sig(S,T): - r={} - if T is None: - r["Resp"] = "N" - else: - r["DF"] = (T.flags & 2) and "Y" or "N" - r["TOS"] = "%X" % T.tos - r["IPLEN"] = "%X" % T.len - r["RIPTL"] = "%X" % T.payload.payload.len - r["RID"] = S.id == T.payload.payload.id and "E" or "F" - r["RIPCK"] = S.chksum == T.getlayer(IPerror).chksum and "E" or T.getlayer(IPerror).chksum == 0 and "0" or "F" - r["UCK"] = S.payload.chksum == T.getlayer(UDPerror).chksum and "E" or T.getlayer(UDPerror).chksum ==0 and "0" or "F" - r["ULEN"] = "%X" % T.getlayer(UDPerror).len - r["DAT"] = T.getlayer(Raw) is None and "E" or S.getlayer(Raw).load == T.getlayer(Raw).load and "E" or "F" - return r - - - -def nmap_match_one_sig(seen, ref): - c = 0 - for k in seen.keys(): - if ref.has_key(k): - if seen[k] in ref[k].split("|"): - c += 1 - if c == 0 and seen.get("Resp") == "N": - return 0.7 - else: - return 1.0*c/len(seen.keys()) - - - -def nmap_sig(target, oport=80, cport=81, ucport=1): - res = {} - - tcpopt = [ ("WScale", 10), - ("NOP",None), - ("MSS", 256), - ("Timestamp",(123,0)) ] - tests = [ IP(dst=target, id=1)/TCP(seq=1, sport=5001, dport=oport, options=tcpopt, flags="CS"), - IP(dst=target, id=1)/TCP(seq=1, sport=5002, dport=oport, options=tcpopt, flags=0), - IP(dst=target, id=1)/TCP(seq=1, sport=5003, dport=oport, options=tcpopt, flags="SFUP"), - IP(dst=target, id=1)/TCP(seq=1, sport=5004, dport=oport, options=tcpopt, flags="A"), - IP(dst=target, id=1)/TCP(seq=1, sport=5005, dport=cport, options=tcpopt, flags="S"), - IP(dst=target, id=1)/TCP(seq=1, sport=5006, dport=cport, options=tcpopt, flags="A"), - IP(dst=target, id=1)/TCP(seq=1, sport=5007, dport=cport, options=tcpopt, flags="FPU"), - IP(str(IP(dst=target)/UDP(sport=5008,dport=ucport)/(300*"i"))) ] - - ans, unans = sr(tests, timeout=2) - ans += map(lambda x: (x,None), unans) - - for S,T in ans: - if S.sport == 5008: - res["PU"] = nmap_udppacket_sig(S,T) - else: - t = "T%i" % (S.sport-5000) - if T is not None and T.haslayer(ICMP): - warning("Test %s answered by an ICMP" % t) - T=None - res[t] = nmap_tcppacket_sig(T) - - return res - -def nmap_probes2sig(tests): - tests=tests.copy() - res = {} - if "PU" in tests: - res["PU"] = nmap_udppacket_sig(*tests["PU"]) - del(tests["PU"]) - for k in tests: - res[k] = nmap_tcppacket_sig(tests[k]) - return res - - -def nmap_search(sigs): - guess = 0,[] - for os,fp in nmap_kdb.get_base(): - c = 0.0 - for t in sigs.keys(): - if t in fp: - c += nmap_match_one_sig(sigs[t], fp[t]) - c /= len(sigs.keys()) - if c > guess[0]: - guess = c,[ os ] - elif c == guess[0]: - guess[1].append(os) - return guess - - -def nmap_fp(target, oport=80, cport=81): - """nmap fingerprinting -nmap_fp(target, [oport=80,] [cport=81,]) -> list of best guesses with accuracy -""" - sigs = nmap_sig(target, oport, cport) - return nmap_search(sigs) - - -def nmap_sig2txt(sig): - torder = ["TSeq","T1","T2","T3","T4","T5","T6","T7","PU"] - korder = ["Class", "gcd", "SI", "IPID", "TS", - "Resp", "DF", "W", "ACK", "Flags", "Ops", - "TOS", "IPLEN", "RIPTL", "RID", "RIPCK", "UCK", "ULEN", "DAT" ] - txt=[] - for i in sig.keys(): - if i not in torder: - torder.append(i) - for t in torder: - sl = sig.get(t) - if sl is None: - continue - s = [] - for k in korder: - v = sl.get(k) - if v is None: - continue - s.append("%s=%s"%(k,v)) - txt.append("%s(%s)" % (t, "%".join(s))) - return "\n".join(txt) - - - - - -################### -## User commands ## -################### - - -def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg): - """Sniff packets -sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets - - count: number of packets to capture. 0 means infinity - store: wether to store sniffed packets or discard them - prn: function to apply to each packet. If something is returned, - it is displayed. Ex: - ex: prn = lambda x: x.summary() -lfilter: python function applied to each packet to determine - if further action may be done - ex: lfilter = lambda x: x.haslayer(Padding) -offline: pcap file to read packets from, instead of sniffing them -timeout: stop sniffing after a given time (default: None) -L2socket: use the provided L2socket - """ - c = 0 - - if offline is None: - if L2socket is None: - L2socket = conf.L2listen - s = L2socket(type=ETH_P_ALL, *arg, **karg) - else: - s = PcapReader(offline) - - lst = [] - if timeout is not None: - stoptime = time.time()+timeout - remain = None - while 1: - try: - if timeout is not None: - remain = stoptime-time.time() - if remain <= 0: - break - sel = select([s],[],[],remain) - if s in sel[0]: - p = s.recv(MTU) - if p is None: - break - if lfilter and not lfilter(p): - continue - if store: - lst.append(p) - c += 1 - if prn: - r = prn(p) - if r is not None: - print r - if count > 0 and c >= count: - break - except KeyboardInterrupt: - break - s.close() - return PacketList(lst,"Sniffed") - - - -def arpcachepoison(target, victim, interval=60): - """Poison target's cache with (your MAC,victim's IP) couple -arpcachepoison(target, victim, [interval=60]) -> None -""" - tmac = getmacbyip(target) - p = Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target) - try: - while 1: - sendp(p, iface_hint=target) - if conf.verb > 1: - os.write(1,".") - time.sleep(interval) - except KeyboardInterrupt: - pass - -def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, filter=None, timeout=2, verbose=None, **kargs): - """Instant TCP traceroute -traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) -> None -""" - if verbose is None: - verbose = conf.verb - if filter is None: - # we only consider ICMP error packets and TCP packets with at - # least the ACK flag set *and* either the SYN or the RST flag - # set - filter="(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" - if l4 is None: - a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport), - timeout=timeout, filter=filter, verbose=verbose, **kargs) - else: - # this should always work - filter="ip" - a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/l4, - timeout=timeout, filter=filter, verbose=verbose, **kargs) - - a = TracerouteResult(a.res) - if verbose: - a.show() - return a,b - - - - -def arping(net, timeout=2, cache=0, verbose=None, **kargs): - """Send ARP who-has requests to determine which hosts are up -arping(net, [cache=0,] [iface=conf.iface,] [verbose=conf.verb]) -> None -Set cache=True if you want arping to modify internal ARP-Cache""" - if verbose is None: - verbose = conf.verb - ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net), verbose=verbose, - filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) - ans = ARPingResult(ans.res) - - if cache and ans is not None: - for pair in ans: - arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time()) - if verbose: - ans.show() - return ans,unans - -def dyndns_add(nameserver, name, rdata, type="A", ttl=10): - """Send a DNS add message to a nameserver for "name" to have a new "rdata" -dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok) - -example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1") -RFC2136 -""" - zone = name[name.find(".")+1:] - r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, - qd=[DNSQR(qname=zone, qtype="SOA")], - ns=[DNSRR(rrname=name, type="A", - ttl=ttl, rdata=rdata)]), - verbose=0, timeout=5) - if r and r.haslayer(DNS): - return r.getlayer(DNS).rcode - else: - return -1 - - - - -def dyndns_del(nameserver, name, type="ALL", ttl=10): - """Send a DNS delete message to a nameserver for "name" -dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok) - -example: dyndns_del("ns1.toto.com", "dyn.toto.com") -RFC2136 -""" - zone = name[name.find(".")+1:] - r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, - qd=[DNSQR(qname=zone, qtype="SOA")], - ns=[DNSRR(rrname=name, type=type, - rclass="ANY", ttl=0, rdata="")]), - verbose=0, timeout=5) - if r and r.haslayer(DNS): - return r.getlayer(DNS).rcode - else: - return -1 - - -def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00",**kargs): - """Try to guess if target is in Promisc mode. The target is provided by its ip.""" - - responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip),type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0,**kargs) - - return responses is not None - -def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs): - """Send ARP who-has requests to determine which hosts are in promiscuous mode - promiscping(net, iface=conf.iface)""" - ans,unans = srp(Ether(dst=fake_bcast)/ARP(pdst=net), - filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) - ans = ARPingResult(ans.res, name="PROMISCPing") - - ans.display() - return ans,unans - -def ikescan(ip): - return sr(IP(dst=ip)/UDP()/ISAKMP(init_cookie=RandString(8), - exch_type=2)/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())) - - -def dhcp_request(iface=None,**kargs): - if conf.checkIPaddr != 0: - warning("conf.checkIPaddr is not 0, I may not be able to match the answer") - if iface is None: - iface = conf.iface - fam,hw = get_if_raw_hwaddr(iface) - return srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67) - /BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]),iface=iface,**kargs) - -def snmpwalk(dst, oid="1", community="public"): - try: - while 1: - r = sr1(IP(dst=dst)/UDP(sport=RandShort())/SNMP(community=community, PDU=SNMPnext(varbindlist=[SNMPvarbind(oid=oid)])),timeout=2, chainCC=1, verbose=0, retry=2) - if ICMP in r: - print repr(r) - break - if r is None: - print "No answers" - break - print "%-40s: %r" % (r[SNMPvarbind].oid.val,r[SNMPvarbind].value) - oid = r[SNMPvarbind].oid - - except KeyboardInterrupt: - pass - - -##################### -## Reporting stuff ## -##################### - -def report_ports(target, ports): - """portscan a target and output a LaTeX table -report_ports(target, ports) -> string""" - ans,unans = sr(IP(dst=target)/TCP(dport=ports),timeout=5) - rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" - for s,r in ans: - if not r.haslayer(ICMP): - if r.payload.flags == 0x12: - rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") - rep += "\\hline\n" - for s,r in ans: - if r.haslayer(ICMP): - rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") - elif r.payload.flags != 0x12: - rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") - rep += "\\hline\n" - for i in unans: - rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") - rep += "\\hline\n\\end{tabular}\n" - return rep - - -def __make_table(yfmtfunc, fmtfunc, endline, list, fxyz, sortx=None, sorty=None, seplinefunc=None): - vx = {} - vy = {} - vz = {} - vxf = {} - vyf = {} - l = 0 - for e in list: - xx,yy,zz = map(str, fxyz(e)) - l = max(len(yy),l) - vx[xx] = max(vx.get(xx,0), len(xx), len(zz)) - vy[yy] = None - vz[(xx,yy)] = zz - - vxk = vx.keys() - vyk = vy.keys() - if sortx: - vxk.sort(sortx) - else: - try: - vxk.sort(lambda x,y:int(x)-int(y)) - except: - try: - vxk.sort(lambda x,y: cmp(atol(x),atol(y))) - except: - vxk.sort() - if sorty: - vyk.sort(sorty) - else: - try: - vyk.sort(lambda x,y:int(x)-int(y)) - except: - try: - vyk.sort(lambda x,y: cmp(atol(x),atol(y))) - except: - vyk.sort() - - - if seplinefunc: - sepline = seplinefunc(l, map(lambda x:vx[x],vxk)) - print sepline - - fmt = yfmtfunc(l) - print fmt % "", - for x in vxk: - vxf[x] = fmtfunc(vx[x]) - print vxf[x] % x, - print endline - if seplinefunc: - print sepline - for y in vyk: - print fmt % y, - for x in vxk: - print vxf[x] % vz.get((x,y), "-"), - print endline - if seplinefunc: - print sepline - -def make_table(*args, **kargs): - __make_table(lambda l:"%%-%is" % l, lambda l:"%%-%is" % l, "", *args, **kargs) - -def make_lined_table(*args, **kargs): - __make_table(lambda l:"%%-%is |" % l, lambda l:"%%-%is |" % l, "", - seplinefunc=lambda a,x:"+".join(map(lambda y:"-"*(y+2), [a-1]+x+[-2])), - *args, **kargs) - -def make_tex_table(*args, **kargs): - __make_table(lambda l: "%s", lambda l: "& %s", "\\\\", seplinefunc=lambda a,x:"\\hline", *args, **kargs) - - -###################### -## Online doc stuff ## -###################### - - -def lsc(cmd=None): - """List user commands""" - if cmd is None: - for c in user_commands: - doc = "No doc. available" - if c.__doc__: - doc = c.__doc__.split("\n")[0] - - print "%-16s : %s" % (c.__name__, doc) - else: - print cmd.__doc__ - -def ls(obj=None): - """List available layers, or infos on a given layer""" - if obj is None: - import __builtin__ - all = __builtin__.__dict__.copy() - all.update(globals()) - objlst = filter(lambda (n,o): isinstance(o,type) and issubclass(o,Packet), all.items()) - objlst.sort(lambda x,y:cmp(x[0],y[0])) - for n,o in objlst: - print "%-10s : %s" %(n,o.name) - else: - if isinstance(obj, type) and issubclass(obj, Packet): - for f in obj.fields_desc: - print "%-10s : %-20s = (%s)" % (f.name, f.__class__.__name__, repr(f.default)) - elif isinstance(obj, Packet): - for f in obj.fields_desc: - print "%-10s : %-20s = %-15s (%s)" % (f.name, f.__class__.__name__, repr(getattr(obj,f.name)), repr(f.default)) - if not isinstance(obj.payload, NoPayload): - print "--" - ls(obj.payload) - - - else: - print "Not a packet class. Type 'ls()' to list packet classes." - - - - - -user_commands = [ sr, sr1, srp, srp1, srloop, srploop, sniff, p0f, arpcachepoison, send, sendp, traceroute, arping, ls, lsc, queso, nmap_fp, report_ports, dyndns_add, dyndns_del, is_promisc, promiscping ] - - -############## -## Automata ## -############## - -class ATMT: - STATE = "State" - ACTION = "Action" - CONDITION = "Condition" - RECV = "Receive condition" - TIMEOUT = "Timeout condition" - - class NewStateRequested(Exception): - def __init__(self, state_func, automaton, *args, **kargs): - self.func = state_func - self.state = state_func.atmt_state - self.initial = state_func.atmt_initial - self.error = state_func.atmt_error - self.final = state_func.atmt_final - Exception.__init__(self, "Request state [%s]" % self.state) - self.automaton = automaton - self.args = args - self.kargs = kargs - self.action_parameters() # init action parameters - def action_parameters(self, *args, **kargs): - self.action_args = args - self.action_kargs = kargs - return self - def run(self): - return self.func(self.automaton, *self.args, **self.kargs) - - @staticmethod - def state(initial=0,final=0,error=0): - def deco(f,initial=initial, final=final): - f.atmt_type = ATMT.STATE - f.atmt_state = f.func_name - f.atmt_initial = initial - f.atmt_final = final - f.atmt_error = error - def state_wrapper(self, *args, **kargs): - return ATMT.NewStateRequested(f, self, *args, **kargs) - - state_wrapper.func_name = "%s_wrapper" % f.func_name - state_wrapper.atmt_type = ATMT.STATE - state_wrapper.atmt_state = f.func_name - state_wrapper.atmt_initial = initial - state_wrapper.atmt_final = final - state_wrapper.atmt_error = error - state_wrapper.atmt_origfunc = f - return state_wrapper - return deco - @staticmethod - def action(cond, prio=0): - def deco(f,cond=cond): - if not hasattr(f,"atmt_type"): - f.atmt_cond = {} - f.atmt_type = ATMT.ACTION - f.atmt_cond[cond.atmt_condname] = prio - return f - return deco - @staticmethod - def condition(state, prio=0): - def deco(f, state=state): - f.atmt_type = ATMT.CONDITION - f.atmt_state = state.atmt_state - f.atmt_condname = f.func_name - f.atmt_prio = prio - return f - return deco - @staticmethod - def receive_condition(state, prio=0): - def deco(f, state=state): - f.atmt_type = ATMT.RECV - f.atmt_state = state.atmt_state - f.atmt_condname = f.func_name - f.atmt_prio = prio - return f - return deco - @staticmethod - def timeout(state, timeout): - def deco(f, state=state, timeout=timeout): - f.atmt_type = ATMT.TIMEOUT - f.atmt_state = state.atmt_state - f.atmt_timeout = timeout - f.atmt_condname = f.func_name - return f - return deco - - -class Automaton_metaclass(type): - def __new__(cls, name, bases, dct): - cls = super(Automaton_metaclass, cls).__new__(cls, name, bases, dct) - cls.states={} - cls.state = None - cls.recv_conditions={} - cls.conditions={} - cls.timeout={} - cls.actions={} - cls.initial_states=[] - - members = {} - classes = [cls] - while classes: - c = classes.pop(0) # order is important to avoid breaking method overloading - classes += list(c.__bases__) - for k,v in c.__dict__.iteritems(): - if k not in members: - members[k] = v - - decorated = [v for v in members.itervalues() - if type(v) is types.FunctionType and hasattr(v, "atmt_type")] - - for m in decorated: - if m.atmt_type == ATMT.STATE: - s = m.atmt_state - cls.states[s] = m - cls.recv_conditions[s]=[] - cls.conditions[s]=[] - cls.timeout[s]=[] - if m.atmt_initial: - cls.initial_states.append(m) - elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT]: - cls.actions[m.atmt_condname] = [] - - for m in decorated: - if m.atmt_type == ATMT.CONDITION: - cls.conditions[m.atmt_state].append(m) - elif m.atmt_type == ATMT.RECV: - cls.recv_conditions[m.atmt_state].append(m) - elif m.atmt_type == ATMT.TIMEOUT: - cls.timeout[m.atmt_state].append((m.atmt_timeout, m)) - elif m.atmt_type == ATMT.ACTION: - for c in m.atmt_cond: - cls.actions[c].append(m) - - - for v in cls.timeout.itervalues(): - v.sort(lambda (t1,f1),(t2,f2): cmp(t1,t2)) - v.append((None, None)) - for v in itertools.chain(cls.conditions.itervalues(), - cls.recv_conditions.itervalues()): - v.sort(lambda c1,c2: cmp(c1.atmt_prio,c2.atmt_prio)) - for condname,actlst in cls.actions.iteritems(): - actlst.sort(lambda c1,c2: cmp(c1.atmt_cond[condname], c2.atmt_cond[condname])) - - return cls - - - def graph(self, **kargs): - s = 'digraph "%s" {\n' % self.__class__.__name__ - - se = "" # Keep initial nodes at the begining for better rendering - for st in self.states.itervalues(): - if st.atmt_initial: - se = ('\t"%s" [ style=filled, fillcolor=blue, shape=box, root=true];\n' % st.atmt_state)+se - elif st.atmt_final: - se += '\t"%s" [ style=filled, fillcolor=green, shape=octagon ];\n' % st.atmt_state - elif st.atmt_error: - se += '\t"%s" [ style=filled, fillcolor=red, shape=octagon ];\n' % st.atmt_state - s += se - - for st in self.states.values(): - for n in st.atmt_origfunc.func_code.co_names+st.atmt_origfunc.func_code.co_consts: - if n in self.states: - s += '\t"%s" -> "%s" [ color=green ];\n' % (st.atmt_state,n) - - - for c,k,v in [("purple",k,v) for k,v in self.conditions.items()]+[("red",k,v) for k,v in self.recv_conditions.items()]: - for f in v: - for n in f.func_code.co_names+f.func_code.co_consts: - if n in self.states: - l = f.atmt_condname - for x in self.actions[f.atmt_condname]: - l += "\\l>[%s]" % x.func_name - s += '\t"%s" -> "%s" [label="%s", color=%s];\n' % (k,n,l,c) - for k,v in self.timeout.iteritems(): - for t,f in v: - if f is None: - continue - for n in f.func_code.co_names+f.func_code.co_consts: - if n in self.states: - l = "%s/%.1fs" % (f.atmt_condname,t) - for x in self.actions[f.atmt_condname]: - l += "\\l>[%s]" % x.func_name - s += '\t"%s" -> "%s" [label="%s",color=blue];\n' % (k,n,l) - s += "}\n" - return do_graph(s, **kargs) - - - -class Automaton: - __metaclass__ = Automaton_metaclass - - def __init__(self, *args, **kargs): - self.debug_level=0 - self.init_args=args - self.init_kargs=kargs - self.parse_args(*args, **kargs) - - def debug(self, lvl, msg): - if self.debug_level >= lvl: - log_interactive.debug(msg) - - - - - class ErrorState(Exception): - def __init__(self, msg, result=None): - Exception.__init__(self, msg) - self.result = result - class Stuck(ErrorState): - pass - - def parse_args(self, debug=0, store=1, **kargs): - self.debug_level=debug - self.socket_kargs = kargs - self.store_packets = store - - - def master_filter(self, pkt): - return True - - def run_condition(self, cond, *args, **kargs): - try: - cond(self,*args, **kargs) - except ATMT.NewStateRequested, state_req: - self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state)) - if cond.atmt_type == ATMT.RECV: - self.packets.append(args[0]) - for action in self.actions[cond.atmt_condname]: - self.debug(2, " + Running action [%s]" % action.func_name) - action(self, *state_req.action_args, **state_req.action_kargs) - raise - else: - self.debug(2, "%s [%s] not taken" % (cond.atmt_type, cond.atmt_condname)) - - - def run(self, *args, **kargs): - # Update default parameters - a = args+self.init_args[len(args):] - k = self.init_kargs - k.update(kargs) - self.parse_args(*a,**k) - - # Start the automaton - self.state=self.initial_states[0](self) - self.send_sock = conf.L3socket() - l = conf.L2listen(**self.socket_kargs) - self.packets = PacketList(name="session[%s]"%self.__class__.__name__) - while 1: - try: - self.debug(1, "## state=[%s]" % self.state.state) - - # Entering a new state. First, call new state function - state_output = self.state.run() - if self.state.error: - raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), result=state_output) - if self.state.final: - return state_output - - if state_output is None: - state_output = () - elif type(state_output) is not list: - state_output = state_output, - - # Then check immediate conditions - for cond in self.conditions[self.state.state]: - self.run_condition(cond, *state_output) - - # If still there and no conditions left, we are stuck! - if ( len(self.recv_conditions[self.state.state]) == 0 - and len(self.timeout[self.state.state]) == 1 ): - raise self.Stuck("stuck in [%s]" % self.state.state,result=state_output) - - # Finally listen and pay attention to timeouts - expirations = iter(self.timeout[self.state.state]) - next_timeout,timeout_func = expirations.next() - t0 = time.time() - - while 1: - t = time.time()-t0 - if next_timeout is not None: - if next_timeout <= t: - self.run_condition(timeout_func, *state_output) - next_timeout,timeout_func = expirations.next() - if next_timeout is None: - remain = None - else: - remain = next_timeout-t - - r,_,_ = select([l],[],[],remain) - if l in r: - pkt = l.recv(MTU) - if pkt is not None: - if self.master_filter(pkt): - self.debug(3, "RECVD: %s" % pkt.summary()) - for rcvcond in self.recv_conditions[self.state.state]: - self.run_condition(rcvcond, pkt, *state_output) - else: - self.debug(4, "FILTR: %s" % pkt.summary()) - - except ATMT.NewStateRequested,state_req: - self.debug(2, "switching from [%s] to [%s]" % (self.state.state,state_req.state)) - self.state = state_req - except KeyboardInterrupt: - self.debug(1,"Interrupted by user") - break - - def my_send(self, pkt): - self.send_sock.send(pkt) - - def send(self, pkt): - self.my_send(pkt) - self.debug(3,"SENT : %s" % pkt.summary()) - self.packets.append(pkt.copy()) - - - - - - -class TFTP_read(Automaton): - def parse_args(self, filename, server, sport = None, port=69, **kargs): - Automaton.parse_args(self, **kargs) - self.filename = filename - self.server = server - self.port = port - self.sport = sport - - - def master_filter(self, pkt): - return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt - and pkt[UDP].dport == self.my_tid - and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) - - # BEGIN - @ATMT.state(initial=1) - def BEGIN(self): - self.blocksize=512 - self.my_tid = self.sport or RandShort()._fix() - bind_bottom_up(UDP, TFTP, dport=self.my_tid) - self.server_tid = None - self.res = "" - - self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() - self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet") - self.send(self.last_packet) - self.awaiting=1 - - raise self.WAITING() - - # WAITING - @ATMT.state() - def WAITING(self): - pass - - - @ATMT.receive_condition(WAITING) - def receive_data(self, pkt): - if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: - if self.server_tid is None: - self.server_tid = pkt[UDP].sport - self.l3[UDP].dport = self.server_tid - raise self.RECEIVING(pkt) - - @ATMT.receive_condition(WAITING, prio=1) - def receive_error(self, pkt): - if TFTP_ERROR in pkt: - raise self.ERROR(pkt) - - - @ATMT.timeout(WAITING, 3) - def timeout_waiting(self): - raise self.WAITING() - @ATMT.action(timeout_waiting) - def retransmit_last_packet(self): - self.send(self.last_packet) - - @ATMT.action(receive_data) -# @ATMT.action(receive_error) - def send_ack(self): - self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting) - self.send(self.last_packet) - - - # RECEIVED - @ATMT.state() - def RECEIVING(self, pkt): - if Raw in pkt: - recvd = pkt[Raw].load - else: - recvd = "" - self.res += recvd - self.awaiting += 1 - if len(recvd) == self.blocksize: - raise self.WAITING() - raise self.END() - - # ERROR - @ATMT.state(error=1) - def ERROR(self,pkt): - split_bottom_up(UDP, TFTP, dport=self.my_tid) - return pkt[TFTP_ERROR].summary() - - #END - @ATMT.state(final=1) - def END(self): - split_bottom_up(UDP, TFTP, dport=self.my_tid) - return self.res - - - - -class TFTP_write(Automaton): - def parse_args(self, filename, data, server, sport=None, port=69,**kargs): - Automaton.parse_args(self, **kargs) - self.filename = filename - self.server = server - self.port = port - self.sport = sport - self.blocksize = 512 - self.origdata = data - - def master_filter(self, pkt): - return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt - and pkt[UDP].dport == self.my_tid - and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) - - - # BEGIN - @ATMT.state(initial=1) - def BEGIN(self): - self.data = [ self.origdata[i*self.blocksize:(i+1)*self.blocksize] - for i in range( len(self.origdata)/self.blocksize+1) ] - self.my_tid = self.sport or RandShort()._fix() - bind_bottom_up(UDP, TFTP, dport=self.my_tid) - self.server_tid = None - - self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() - self.last_packet = self.l3/TFTP_WRQ(filename=self.filename, mode="octet") - self.send(self.last_packet) - self.res = "" - self.awaiting=0 - - raise self.WAITING_ACK() - - # WAITING_ACK - @ATMT.state() - def WAITING_ACK(self): - pass - - @ATMT.receive_condition(WAITING_ACK) - def received_ack(self,pkt): - if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.awaiting: - if self.server_tid is None: - self.server_tid = pkt[UDP].sport - self.l3[UDP].dport = self.server_tid - raise self.SEND_DATA() - - @ATMT.receive_condition(WAITING_ACK) - def received_error(self, pkt): - if TFTP_ERROR in pkt: - raise self.ERROR(pkt) - - @ATMT.timeout(WAITING_ACK, 3) - def timeout_waiting(self): - raise self.WAITING_ACK() - @ATMT.action(timeout_waiting) - def retransmit_last_packet(self): - self.send(self.last_packet) - - # SEND_DATA - @ATMT.state() - def SEND_DATA(self): - self.awaiting += 1 - self.last_packet = self.l3/TFTP_DATA(block=self.awaiting)/self.data.pop(0) - self.send(self.last_packet) - if self.data: - raise self.WAITING_ACK() - raise self.END() - - - # ERROR - @ATMT.state(error=1) - def ERROR(self,pkt): - split_bottom_up(UDP, TFTP, dport=self.my_tid) - return pkt[TFTP_ERROR].summary() - - # END - @ATMT.state(final=1) - def END(self): - split_bottom_up(UDP, TFTP, dport=self.my_tid) - - -class TFTP_WRQ_server(Automaton): - - def parse_args(self, ip=None, sport=None, *args, **kargs): - Automaton.parse_args(self, *args, **kargs) - self.ip = ip - self.sport = sport - - def master_filter(self, pkt): - return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) - - @ATMT.state(initial=1) - def BEGIN(self): - self.blksize=512 - self.blk=1 - self.filedata="" - self.my_tid = self.sport or random.randint(10000,65500) - bind_bottom_up(UDP, TFTP, dport=self.my_tid) - - @ATMT.receive_condition(BEGIN) - def receive_WRQ(self,pkt): - if TFTP_WRQ in pkt: - raise self.WAIT_DATA().action_parameters(pkt) - - @ATMT.action(receive_WRQ) - def ack_WRQ(self, pkt): - ip = pkt[IP] - self.ip = ip.dst - self.dst = ip.src - self.filename = pkt[TFTP_WRQ].filename - options = pkt[TFTP_Options] - self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=pkt.sport)/TFTP() - if options is None: - self.last_packet = self.l3/TFTP_ACK(block=0) - self.send(self.last_packet) - else: - opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] - if opt: - self.blksize = int(opt[0].value) - self.debug(2,"Negotiated new blksize at %i" % self.blksize) - self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) - self.send(self.last_packet) - - @ATMT.state() - def WAIT_DATA(self): - pass - - @ATMT.timeout(WAIT_DATA, 1) - def resend_ack(self): - self.send(self.last_packet) - raise self.WAIT_DATA() - - @ATMT.receive_condition(WAIT_DATA) - def receive_data(self, pkt): - if TFTP_DATA in pkt: - data = pkt[TFTP_DATA] - if data.block == self.blk: - raise self.DATA(data) - - @ATMT.action(receive_data) - def ack_data(self): - self.last_packet = self.l3/TFTP_ACK(block = self.blk) - self.send(self.last_packet) - - @ATMT.state() - def DATA(self, data): - self.filedata += data.load - if len(data.load) < self.blksize: - raise self.END() - self.blk += 1 - raise self.WAIT_DATA() - - @ATMT.state(final=1) - def END(self): - return self.filename,self.filedata - split_bottom_up(UDP, TFTP, dport=self.my_tid) - - -class TFTP_RRQ_server(Automaton): - def parse_args(self, store=None, joker=None, dir=None, ip=None, sport=None, serve_one=False, **kargs): - Automaton.parse_args(self,**kargs) - if store is None: - store = {} - if dir is not None: - self.dir = os.path.join(os.path.abspath(dir),"") - else: - self.dir = None - self.store = store - self.joker = joker - self.ip = ip - self.sport = sport - self.serve_one = serve_one - self.my_tid = self.sport or random.randint(10000,65500) - bind_bottom_up(UDP, TFTP, dport=self.my_tid) - - def master_filter(self, pkt): - return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) - - @ATMT.state(initial=1) - def WAIT_RRQ(self): - self.blksize=512 - self.blk=0 - - @ATMT.receive_condition(WAIT_RRQ) - def receive_rrq(self, pkt): - if TFTP_RRQ in pkt: - raise self.RECEIVED_RRQ(pkt) - - - @ATMT.state() - def RECEIVED_RRQ(self, pkt): - ip = pkt[IP] - options = pkt[TFTP_Options] - self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=ip.sport)/TFTP() - self.filename = pkt[TFTP_RRQ].filename - self.blk=1 - self.data = None - if self.filename in self.store: - self.data = self.store[self.filename] - elif self.dir is not None: - fn = os.path.abspath(os.path.join(self.dir, self.filename)) - if fn.startswith(self.dir): # Check we're still in the server's directory - try: - self.data=open(fn).read() - except IOError: - pass - if self.data is None: - self.data = self.joker - - if options: - opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] - if opt: - self.blksize = int(opt[0].value) - self.debug(2,"Negotiated new blksize at %i" % self.blksize) - self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) - self.send(self.last_packet) - - - - - @ATMT.condition(RECEIVED_RRQ) - def file_in_store(self): - if self.data is not None: - self.blknb = len(self.data)/self.blksize+1 - raise self.SEND_FILE() - - @ATMT.condition(RECEIVED_RRQ) - def file_not_found(self): - if self.data is None: - raise self.WAIT_RRQ() - @ATMT.action(file_not_found) - def send_error(self): - self.send(self.l3/TFTP_ERROR(errorcode=1, errormsg=TFTP_Error_Codes[1])) - - @ATMT.state() - def SEND_FILE(self): - self.send(self.l3/TFTP_DATA(block=self.blk)/self.data[(self.blk-1)*self.blksize:self.blk*self.blksize]) - - @ATMT.timeout(SEND_FILE, 3) - def timeout_waiting_ack(self): - raise self.SEND_FILE() - - @ATMT.receive_condition(SEND_FILE) - def received_ack(self, pkt): - if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.blk: - raise self.RECEIVED_ACK() - @ATMT.state() - def RECEIVED_ACK(self): - self.blk += 1 - - @ATMT.condition(RECEIVED_ACK) - def no_more_data(self): - if self.blk > self.blknb: - if self.serve_one: - raise self.END() - raise self.WAIT_RRQ() - @ATMT.condition(RECEIVED_ACK, prio=2) - def data_remaining(self): - raise self.SEND_FILE() - - @ATMT.state(final=1) - def END(self): - split_bottom_up(UDP, TFTP, dport=self.my_tid) - - - - -######################## -## Answering machines ## -######################## - -class ReferenceAM(type): - def __new__(cls, name, bases, dct): - o = super(ReferenceAM, cls).__new__(cls, name, bases, dct) - if o.function_name: - globals()[o.function_name] = lambda o=o,*args,**kargs: o(*args,**kargs)() - return o - - -class AnsweringMachine(object): - __metaclass__ = ReferenceAM - function_name = "" - filter = None - sniff_options = { "store":0 } - sniff_options_list = [ "store", "iface", "count", "promisc", "filter", "type", "prn" ] - send_options = { "verbose":0 } - send_options_list = ["iface", "inter", "loop", "verbose"] - send_function = staticmethod(send) - - - def __init__(self, **kargs): - self.mode = 0 - if self.filter: - kargs.setdefault("filter",self.filter) - kargs.setdefault("prn", self.reply) - self.optam1 = {} - self.optam2 = {} - self.optam0 = {} - doptsend,doptsniff = self.parse_all_options(1, kargs) - self.defoptsend = self.send_options.copy() - self.defoptsend.update(doptsend) - self.defoptsniff = self.sniff_options.copy() - self.defoptsniff.update(doptsniff) - self.optsend,self.optsniff = [{},{}] - - def __getattr__(self, attr): - for d in [self.optam2, self.optam1]: - if attr in d: - return d[attr] - raise AttributeError,attr - - def __setattr__(self, attr, val): - mode = self.__dict__.get("mode",0) - if mode == 0: - self.__dict__[attr] = val - else: - [self.optam1, self.optam2][mode-1][attr] = val - - def parse_options(self): - pass - - def parse_all_options(self, mode, kargs): - sniffopt = {} - sendopt = {} - for k in kargs.keys(): - if k in self.sniff_options_list: - sniffopt[k] = kargs[k] - if k in self.send_options_list: - sendopt[k] = kargs[k] - if k in self.sniff_options_list+self.send_options_list: - del(kargs[k]) - if mode != 2 or kargs: - if mode == 1: - self.optam0 = kargs - elif mode == 2 and kargs: - k = self.optam0.copy() - k.update(kargs) - self.parse_options(**k) - kargs = k - omode = self.__dict__.get("mode",0) - self.__dict__["mode"] = mode - self.parse_options(**kargs) - self.__dict__["mode"] = omode - return sendopt,sniffopt - - def is_request(self, req): - return 1 - - def make_reply(self, req): - return req - - def send_reply(self, reply): - self.send_function(reply, **self.optsend) - - def print_reply(self, req, reply): - print "%s ==> %s" % (req.summary(),reply.summary()) - - def reply(self, pkt): - if not self.is_request(pkt): - return - reply = self.make_reply(pkt) - self.send_reply(reply) - if conf.verb >= 0: - self.print_reply(pkt, reply) - - def run(self, *args, **kargs): - log_interactive.warning("run() method deprecated. The intance is now callable") - self(*args,**kargs) - - def __call__(self, *args, **kargs): - optsend,optsniff = self.parse_all_options(2,kargs) - self.optsend=self.defoptsend.copy() - self.optsend.update(optsend) - self.optsniff=self.defoptsniff.copy() - self.optsniff.update(optsniff) - - try: - self.sniff() - except KeyboardInterrupt: - print "Interrupted by user" - - def sniff(self): - sniff(**self.optsniff) - - -class BOOTP_am(AnsweringMachine): - function_name = "bootpd" - filter = "udp and port 68 and port 67" - send_function = staticmethod(sendp) - def parse_options(self, pool=Net("192.168.1.128/25"), network="192.168.1.0/24",gw="192.168.1.1", - domain="localnet", renewal_time=60, lease_time=1800): - if type(pool) is str: - poom = Net(pool) - self.domain = domain - netw,msk = (network.split("/")+["32"])[:2] - msk = itom(int(msk)) - self.netmask = ltoa(msk) - self.network = ltoa(atol(netw)&msk) - self.broadcast = ltoa( atol(self.network) | (0xffffffff&~msk) ) - self.gw = gw - if isinstance(pool,Gen): - pool = [k for k in pool if k not in [gw, self.network, self.broadcast]] - pool.reverse() - if len(pool) == 1: - pool, = pool - self.pool = pool - self.lease_time = lease_time - self.renewal_time = renewal_time - self.leases = {} - - def is_request(self, req): - if not req.haslayer(BOOTP): - return 0 - reqb = req.getlayer(BOOTP) - if reqb.op != 1: - return 0 - return 1 - - def print_reply(self, req, reply): - print "Reply %s to %s" % (reply.getlayer(IP).dst,reply.dst) - - def make_reply(self, req): - mac = req.src - if type(self.pool) is list: - if not self.leases.has_key(mac): - self.leases[mac] = self.pool.pop() - ip = self.leases[mac] - else: - ip = self.pool - - repb = req.getlayer(BOOTP).copy() - repb.op="BOOTREPLY" - repb.yiaddr = ip - repb.siaddr = self.gw - repb.ciaddr = self.gw - repb.giaddr = self.gw - del(repb.payload) - rep=Ether(dst=mac)/IP(dst=ip)/UDP(sport=req.dport,dport=req.sport)/repb - return rep - - -class DHCP_am(BOOTP_am): - function_name="dhcpd" - def make_reply(self, req): - resp = BOOTP_am.make_reply(self, req) - if DHCP in req: - dhcp_options = [(op[0],{1:2,3:5}.get(op[1],op[1])) - for op in req[DHCP].options - if type(op) is tuple and op[0] == "message-type"] - dhcp_options += [("server_id",self.gw), - ("domain", self.domain), - ("router", self.gw), - ("name_server", self.gw), - ("broadcast_address", self.broadcast), - ("subnet_mask", self.netmask), - ("renewal_time", self.renewal_time), - ("lease_time", self.lease_time), - "end" - ] - resp /= DHCP(options=dhcp_options) - return resp - - - -class DNS_am(AnsweringMachine): - function_name="dns_spoof" - filter = "udp port 53" - - def parse_options(self, joker="192.168.1.1", match=None): - if match is None: - self.match = {} - else: - self.match = match - self.joker=joker - - def is_request(self, req): - return req.haslayer(DNS) and req.getlayer(DNS).qr == 0 - - def make_reply(self, req): - ip = req.getlayer(IP) - dns = req.getlayer(DNS) - resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport) - rdata = self.match.get(dns.qd.qname, self.joker) - resp /= DNS(id=dns.id, qr=1, qd=dns.qd, - an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata)) - return resp - - -class WiFi_am(AnsweringMachine): - """Before using this, initialize "iffrom" and "ifto" interfaces: -iwconfig iffrom mode monitor -iwpriv orig_ifto hostapd 1 -ifconfig ifto up -note: if ifto=wlan0ap then orig_ifto=wlan0 -note: ifto and iffrom must be set on the same channel -ex: -ifconfig eth1 up -iwconfig eth1 mode monitor -iwconfig eth1 channel 11 -iwpriv wlan0 hostapd 1 -ifconfig wlan0ap up -iwconfig wlan0 channel 11 -iwconfig wlan0 essid dontexist -iwconfig wlan0 mode managed -""" - function_name = "airpwn" - filter = None - - def parse_options(iffrom, ifto, replace, pattern="", ignorepattern=""): - self.iffrom = iffrom - self.ifto = ifto - ptrn = re.compile(pattern) - iptrn = re.compile(ignorepattern) - - def is_request(self, pkt): - if not isinstance(pkt,Dot11): - return 0 - if not pkt.FCfield & 1: - return 0 - if not pkt.haslayer(TCP): - return 0 - ip = pkt.getlayer(IP) - tcp = pkt.getlayer(TCP) - pay = str(tcp.payload) - if not self.ptrn.match(pay): - return 0 - if self.iptrn.match(pay): - return 0 - - def make_reply(self, p): - ip = p.getlayer(IP) - tcp = p.getlayer(TCP) - pay = str(tcp.payload) - del(p.payload.payload.payload) - p.FCfield="from-DS" - p.addr1,p.addr2 = p.addr2,p.addr1 - p /= IP(src=ip.dst,dst=ip.src) - p /= TCP(sport=tcp.dport, dport=tcp.sport, - seq=tcp.ack, ack=tcp.seq+len(pay), - flags="PA") - q = p.copy() - p /= self.replace - q.ID += 1 - q.getlayer(TCP).flags="RA" - q.getlayer(TCP).seq+=len(replace) - return [p,q] - - def print_reply(self): - print p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%") - - def send_reply(self, reply): - sendp(reply, iface=self.ifto, **self.optsend) - - def sniff(self): - sniff(iface=self.iffrom, **self.optsniff) - - - -class ARP_am(AnsweringMachine): - function_name="farpd" - filter = "arp" - send_function = staticmethod(sendp) - - def parse_options(self, IP_addr=None, iface=None, ARP_addr=None): - self.IP_addr=IP_addr - self.iface=iface - self.ARP_addr=ARP_addr - - def is_request(self, req): - return (req.haslayer(ARP) and - req.getlayer(ARP).op == 1 and - (self.IP_addr == None or self.IP_addr == req.getlayer(ARP).pdst)) - - def make_reply(self, req): - ether = req.getlayer(Ether) - arp = req.getlayer(ARP) - iff,a,gw = conf.route.route(arp.psrc) - if self.iface != None: - iff = iface - ARP_addr = self.ARP_addr - IP_addr = arp.pdst - resp = Ether(dst=ether.src, - src=ARP_addr)/ARP(op="is-at", - hwsrc=ARP_addr, - psrc=IP_addr, - hwdst=arp.hwsrc, - pdst=arp.pdst) - return resp - - def sniff(self): - sniff(iface=self.iface, **self.optsniff) - - -############# -## Fuzzing ## -############# - - -def fuzz(p, _inplace=0): - if not _inplace: - p = p.copy() - q = p - while not isinstance(q, NoPayload): - for f in q.fields_desc: - if isinstance(f, PacketListField): - for r in getattr(q, f.name): - print "fuzzing", repr(r) - fuzz(r, _inplace=1) - elif f.default is not None: - rnd = f.randval() - if rnd is not None: - q.default_fields[f.name] = rnd - q = q.payload - return p - - - - -################### -## Testing stuff ## -################### - - - -def merge(x,y): - if len(x) > len(y): - y += "\x00"*(len(x)-len(y)) - elif len(x) < len(y): - x += "\x00"*(len(y)-len(x)) - m = "" - for i in range(len(x)/ss): - m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)] - return m -# return "".join(map(str.__add__, x, y)) - - -def voip_play(s1,list=None,**kargs): - FIFO="/tmp/conv1.%i.%%i" % os.getpid() - FIFO1=FIFO % 1 - FIFO2=FIFO % 2 - - os.mkfifo(FIFO1) - os.mkfifo(FIFO2) - try: - os.system("soxmix -t .ul %s -t .ul %s -t ossdsp /dev/dsp &" % (FIFO1,FIFO2)) - - c1=open(FIFO1,"w", 4096) - c2=open(FIFO2,"w", 4096) - fcntl.fcntl(c1.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) - fcntl.fcntl(c2.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) - - # dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") - def play(pkt,last=[]): - if not pkt: - return - if not pkt.haslayer(UDP): - return - ip=pkt.getlayer(IP) - if s1 in [ip.src, ip.dst]: - if not last: - last.append(pkt) - return - load=last.pop() - # x1 = load.load[12:] - c1.write(load.load[12:]) - if load.getlayer(IP).src == ip.src: - # x2 = "" - c2.write("\x00"*len(load.load[12:])) - last.append(pkt) - else: - # x2 = pkt.load[:12] - c2.write(pkt.load[12:]) - # dsp.write(merge(x1,x2)) - - if list is None: - sniff(store=0, prn=play, **kargs) - else: - for p in list: - play(p) - finally: - os.unlink(FIFO1) - os.unlink(FIFO2) - - - -def voip_play1(s1,list=None,**kargs): - - - dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") - def play(pkt): - if not pkt: - return - if not pkt.haslayer(UDP): - return - ip=pkt.getlayer(IP) - if s1 in [ip.src, ip.dst]: - dsp.write(pkt.getlayer(Raw).load[12:]) - try: - if list is None: - sniff(store=0, prn=play, **kargs) - else: - for p in list: - play(p) - finally: - dsp.close() - rd.close() - -def voip_play2(s1,**kargs): - dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") - def play(pkt,last=[]): - if not pkt: - return - if not pkt.haslayer(UDP): - return - ip=pkt.getlayer(IP) - if s1 in [ip.src, ip.dst]: - if not last: - last.append(pkt) - return - load=last.pop() - x1 = load.load[12:] -# c1.write(load.load[12:]) - if load.getlayer(IP).src == ip.src: - x2 = "" -# c2.write("\x00"*len(load.load[12:])) - last.append(pkt) - else: - x2 = pkt.load[:12] -# c2.write(pkt.load[12:]) - dsp.write(merge(x1,x2)) - - sniff(store=0, prn=play, **kargs) - -def voip_play3(lst=None,**kargs): - dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") - try: - def play(pkt, dsp=dsp): - if pkt and pkt.haslayer(UDP) and pkt.haslayer(Raw): - dsp.write(pkt.getlayer(RTP).load) - if lst is None: - sniff(store=0, prn=play, **kargs) - else: - for p in lst: - play(p) - finally: - try: - dsp.close() - rd.close() - except: - pass - - -def IPID_count(lst, funcID=lambda x:x[1].id, funcpres=lambda x:x[1].summary()): - idlst = map(funcID, lst) - idlst.sort() - classes = [idlst[0]]+map(lambda x:x[1],filter(lambda (x,y): abs(x-y)>50, map(lambda x,y: (x,y),idlst[:-1], idlst[1:]))) - lst = map(lambda x:(funcID(x), funcpres(x)), lst) - lst.sort() - print "Probably %i classes:" % len(classes), classes - for id,pr in lst: - print "%5i" % id, pr - - - - - -last=None - - -def tethereal(*args,**kargs): - sniff(prn=lambda x: x.display(),*args,**kargs) - -def etherleak(target, **kargs): - return srpflood(Ether()/ARP(pdst=target), prn=lambda (s,r): Padding in r and hexstr(r[Padding].load), - filter="arp", **kargs) - - -def fragleak(target,sport=123, dport=123, timeout=0.2, onlyasc=0): - load = "XXXXYYYYYYYYYY" -# getmacbyip(target) -# pkt = IP(dst=target, id=RandShort(), options="\x22"*40)/UDP()/load - pkt = IP(dst=target, id=RandShort(), options="\x00"*40, flags=1)/UDP(sport=sport, dport=sport)/load - s=conf.L3socket() - intr=0 - found={} - try: - while 1: - try: - if not intr: - s.send(pkt) - sin,sout,serr = select([s],[],[],timeout) - if not sin: - continue - ans=s.recv(1600) - if not isinstance(ans, IP): #TODO: IPv6 - continue - if not isinstance(ans.payload, ICMP): - continue - if not isinstance(ans.payload.payload, IPerror): - continue - if ans.payload.payload.dst != target: - continue - if ans.src != target: - print "leak from", ans.src, - - -# print repr(ans) - if not ans.haslayer(Padding): - continue - - -# print repr(ans.payload.payload.payload.payload) - -# if not isinstance(ans.payload.payload.payload.payload, Raw): -# continue -# leak = ans.payload.payload.payload.payload.load[len(load):] - leak = ans.getlayer(Padding).load - if leak not in found: - found[leak]=None - linehexdump(leak, onlyasc=onlyasc) - except KeyboardInterrupt: - if intr: - raise - intr=1 - except KeyboardInterrupt: - pass - -def fragleak2(target, timeout=0.4, onlyasc=0): - found={} - try: - while 1: - p = sr1(IP(dst=target, options="\x00"*40, proto=200)/"XXXXYYYYYYYYYYYY",timeout=timeout,verbose=0) - if not p: - continue - if Padding in p: - leak = p[Padding].load - if leak not in found: - found[leak]=None - linehexdump(leak,onlyasc=onlyasc) - except: - pass - - - -plst=[] -def get_toDS(): - global plst - while 1: - p,=sniff(iface="eth1",count=1) - if not isinstance(p,Dot11): - continue - if p.FCfield & 1: - plst.append(p) - print "." - - -# if not ifto.endswith("ap"): -# print "iwpriv %s hostapd 1" % ifto -# os.system("iwpriv %s hostapd 1" % ifto) -# ifto += "ap" -# -# os.system("iwconfig %s mode monitor" % iffrom) -# - -def airpwn(iffrom, ifto, replace, pattern="", ignorepattern=""): - """Before using this, initialize "iffrom" and "ifto" interfaces: -iwconfig iffrom mode monitor -iwpriv orig_ifto hostapd 1 -ifconfig ifto up -note: if ifto=wlan0ap then orig_ifto=wlan0 -note: ifto and iffrom must be set on the same channel -ex: -ifconfig eth1 up -iwconfig eth1 mode monitor -iwconfig eth1 channel 11 -iwpriv wlan0 hostapd 1 -ifconfig wlan0ap up -iwconfig wlan0 channel 11 -iwconfig wlan0 essid dontexist -iwconfig wlan0 mode managed -""" - - ptrn = re.compile(pattern) - iptrn = re.compile(ignorepattern) - def do_airpwn(p, ifto=ifto, replace=replace, ptrn=ptrn, iptrn=iptrn): - if not isinstance(p,Dot11): - return - if not p.FCfield & 1: - return - if not p.haslayer(TCP): - return - ip = p.getlayer(IP) - tcp = p.getlayer(TCP) - pay = str(tcp.payload) -# print "got tcp" - if not ptrn.match(pay): - return -# print "match 1" - if iptrn.match(pay): - return -# print "match 2" - del(p.payload.payload.payload) - p.FCfield="from-DS" - p.addr1,p.addr2 = p.addr2,p.addr1 - q = p.copy() - p /= IP(src=ip.dst,dst=ip.src) - p /= TCP(sport=tcp.dport, dport=tcp.sport, - seq=tcp.ack, ack=tcp.seq+len(pay), - flags="PA") - q = p.copy() - p /= replace - q.ID += 1 - q.getlayer(TCP).flags="RA" - q.getlayer(TCP).seq+=len(replace) - - sendp([p,q], iface=ifto, verbose=0) -# print "send",repr(p) -# print "send",repr(q) - print p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%") - - sniff(iface=iffrom,prn=do_airpwn) - - - - -################## -## Color themes ## -################## - -class Color: - normal = "\033[0m" - black = "\033[30m" - red = "\033[31m" - green = "\033[32m" - yellow = "\033[33m" - blue = "\033[34m" - purple = "\033[35m" - cyan = "\033[36m" - grey = "\033[37m" - - bold = "\033[1m" - uline = "\033[4m" - blink = "\033[5m" - invert = "\033[7m" - - -class ColorTheme: - def __repr__(self): - return "<%s>" % self.__class__.__name__ - def __getattr__(self, attr): - return lambda x:x - - -class NoTheme(ColorTheme): - pass - - -class AnsiColorTheme(ColorTheme): - def __getattr__(self, attr): - if attr.startswith("__"): - raise AttributeError(attr) - s = "style_%s" % attr - if s in self.__class__.__dict__: - before = getattr(self, s) - after = self.style_normal - else: - before = after = "" - - def do_style(val, fmt=None, before=before, after=after): - if fmt is None: - if type(val) is not str: - val = str(val) - else: - val = fmt % val - return before+val+after - return do_style - - - style_normal = "" - style_prompt = "" - style_punct = "" - style_id = "" - style_not_printable = "" - style_layer_name = "" - style_field_name = "" - style_field_value = "" - style_emph_field_name = "" - style_emph_field_value = "" - style_packetlist_name = "" - style_packetlist_proto = "" - style_packetlist_value = "" - style_fail = "" - style_success = "" - style_odd = "" - style_even = "" - style_opening = "" - style_active = "" - style_closed = "" - style_left = "" - style_right = "" - -class BlackAndWhite(AnsiColorTheme): - pass - -class DefaultTheme(AnsiColorTheme): - style_normal = Color.normal - style_prompt = Color.blue+Color.bold - style_punct = Color.normal - style_id = Color.blue+Color.bold - style_not_printable = Color.grey - style_layer_name = Color.red+Color.bold - style_field_name = Color.blue - style_field_value = Color.purple - style_emph_field_name = Color.blue+Color.uline+Color.bold - style_emph_field_value = Color.purple+Color.uline+Color.bold - style_packetlist_name = Color.red+Color.bold - style_packetlist_proto = Color.blue - style_packetlist_value = Color.purple - style_fail = Color.red+Color.bold - style_success = Color.blue+Color.bold - style_even = Color.black+Color.bold - style_odd = Color.black - style_opening = Color.yellow - style_active = Color.black - style_closed = Color.grey - style_left = Color.blue+Color.invert - style_right = Color.red+Color.invert - -class BrightTheme(AnsiColorTheme): - style_normal = Color.normal - style_punct = Color.normal - style_id = Color.yellow+Color.bold - style_layer_name = Color.red+Color.bold - style_field_name = Color.yellow+Color.bold - style_field_value = Color.purple+Color.bold - style_emph_field_name = Color.yellow+Color.bold - style_emph_field_value = Color.green+Color.bold - style_packetlist_name = Color.red+Color.bold - style_packetlist_proto = Color.yellow+Color.bold - style_packetlist_value = Color.purple+Color.bold - style_fail = Color.red+Color.bold - style_success = Color.blue+Color.bold - style_even = Color.black+Color.bold - style_odd = Color.black - style_left = Color.cyan+Color.invert - style_right = Color.purple+Color.invert - - -class RastaTheme(AnsiColorTheme): - style_normal = Color.normal+Color.green+Color.bold - style_prompt = Color.yellow+Color.bold - style_punct = Color.red - style_id = Color.green+Color.bold - style_not_printable = Color.green - style_layer_name = Color.red+Color.bold - style_field_name = Color.yellow+Color.bold - style_field_value = Color.green+Color.bold - style_emph_field_name = Color.green - style_emph_field_value = Color.green - style_packetlist_name = Color.red+Color.bold - style_packetlist_proto = Color.yellow+Color.bold - style_packetlist_value = Color.green+Color.bold - style_fail = Color.red - style_success = Color.red+Color.bold - style_even = Color.yellow - style_odd = Color.green - style_left = Color.yellow+Color.invert - style_right = Color.red+Color.invert - - -class FormatTheme(ColorTheme): - def __getattr__(self, attr): - if attr.startswith("__"): - raise AttributeError(attr) - col = self.__class__.__dict__.get("style_%s" % attr, "%s") - def do_style(val, fmt=None, col=col): - if fmt is None: - if type(val) is not str: - val = str(val) - else: - val = fmt % val - return col % val - return do_style - - -class LatexTheme(FormatTheme): - style_prompt = r"\textcolor{blue}{%s}" - style_not_printable = r"\textcolor{gray}{%s}" - style_layer_name = r"\textcolor{red}{\bf %s}" - style_field_name = r"\textcolor{blue}{%s}" - style_field_value = r"\textcolor{purple}{%s}" - style_emph_field_name = r"\textcolor{blue}{\underline{%s}}" #ul - style_emph_field_value = r"\textcolor{purple}{\underline{%s}}" #ul - style_packetlist_name = r"\textcolor{red}{\bf %s}" - style_packetlist_proto = r"\textcolor{blue}{%s}" - style_packetlist_value = r"\textcolor{purple}{%s}" - style_fail = r"\textcolor{red}{\bf %s}" - style_success = r"\textcolor{blue}{\bf %s}" - style_left = r"\textcolor{blue}{%s}" - style_right = r"\textcolor{red}{%s}" -# style_even = r"}{\bf " -# style_odd = "" - -class LatexTheme2(FormatTheme): - style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@" - style_not_printable = r"@`@textcolor@[@gray@]@@[@%s@]@" - style_layer_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" - style_field_name = r"@`@textcolor@[@blue@]@@[@%s@]@" - style_field_value = r"@`@textcolor@[@purple@]@@[@%s@]@" - style_emph_field_name = r"@`@textcolor@[@blue@]@@[@@`@underline@[@%s@]@@]@" - style_emph_field_value = r"@`@textcolor@[@purple@]@@[@@`@underline@[@%s@]@@]@" - style_packetlist_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" - style_packetlist_proto = r"@`@textcolor@[@blue@]@@[@%s@]@" - style_packetlist_value = r"@`@textcolor@[@purple@]@@[@%s@]@" - style_fail = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" - style_success = r"@`@textcolor@[@blue@]@@[@@`@bfserices@[@@]@%s@]@" - style_even = r"@`@textcolor@[@gray@]@@[@@`@bfseries@[@@]@%s@]@" -# style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@" - style_left = r"@`@textcolor@[@blue@]@@[@%s@]@" - style_right = r"@`@textcolor@[@red@]@@[@%s@]@" - -class HTMLTheme(FormatTheme): - style_prompt = "<span class=prompt>%s</span>" - style_not_printable = "<span class=not_printable>%s</span>" - style_layer_name = "<span class=layer_name>%s</span>" - style_field_name = "<span class=field_name>%s</span>" - style_field_value = "<span class=field_value>%s</span>" - style_emph_field_name = "<span class=emph_field_name>%s</span>" - style_emph_field_value = "<span class=emph_field_value>%s</span>" - style_packetlist_name = "<span class=packetlist_name>%s</span>" - style_packetlist_proto = "<span class=packetlist_proto>%s</span>" - style_packetlist_value = "<span class=packetlist_value>%s</span>" - style_fail = "<span class=fail>%s</span>" - style_success = "<span class=success>%s</span>" - style_even = "<span class=even>%s</span>" - style_odd = "<span class=odd>%s</span>" - style_left = "<span class=left>%s</span>" - style_right = "<span class=right>%s</span>" - -class HTMLTheme2(HTMLTheme): - style_prompt = "#[#span class=prompt#]#%s#[#/span#]#" - style_not_printable = "#[#span class=not_printable#]#%s#[#/span#]#" - style_layer_name = "#[#span class=layer_name#]#%s#[#/span#]#" - style_field_name = "#[#span class=field_name#]#%s#[#/span#]#" - style_field_value = "#[#span class=field_value#]#%s#[#/span#]#" - style_emph_field_name = "#[#span class=emph_field_name#]#%s#[#/span#]#" - style_emph_field_value = "#[#span class=emph_field_value#]#%s#[#/span#]#" - style_packetlist_name = "#[#span class=packetlist_name#]#%s#[#/span#]#" - style_packetlist_proto = "#[#span class=packetlist_proto#]#%s#[#/span#]#" - style_packetlist_value = "#[#span class=packetlist_value#]#%s#[#/span#]#" - style_fail = "#[#span class=fail#]#%s#[#/span#]#" - style_success = "#[#span class=success#]#%s#[#/span#]#" - style_even = "#[#span class=even#]#%s#[#/span#]#" - style_odd = "#[#span class=odd#]#%s#[#/span#]#" - style_left = "#[#span class=left#]#%s#[#/span#]#" - style_right = "#[#span class=right#]#%s#[#/span#]#" - - -class ColorPrompt: - __prompt = ">>> " - def __str__(self): - try: - ct = conf.color_theme - if isinstance(ct, AnsiColorTheme): - ## ^A and ^B delimit invisible caracters for readline to count right - return "\001%s\002" % ct.prompt("\002"+conf.prompt+"\001") - else: - return ct.prompt(conf.prompt) - except: - return self.__prompt - -############ -## Config ## -############ - -class ConfClass: - def configure(self, cnf): - self.__dict__ = cnf.__dict__.copy() - def __repr__(self): - return str(self) - def __str__(self): - s="Version = %s\n" % VERSION - keys = self.__class__.__dict__.copy() - keys.update(self.__dict__) - keys = keys.keys() - keys.sort() - for i in keys: - if i[0] != "_": - s += "%-10s = %s\n" % (i, repr(getattr(self, i))) - return s[:-1] - -class ProgPath(ConfClass): - pdfreader = "acroread" - psreader = "gv" - dot = "dot" - display = "display" - tcpdump = "tcpdump" - tcpreplay = "tcpreplay" - hexedit = "hexer" - wireshark = "wireshark" - -class Resolve: - def __init__(self): - self.fields = {} - def add(self, *flds): - for fld in flds: - self.fields[fld]=None - def remove(self, *flds): - for fld in flds: - if fld in self.fields: - del(self.fields[fld]) - def __contains__(self, elt): - return elt in self.fields - def __repr__(self): - return "<Resolve [%s]>" % " ".join(str(x) for x in self.fields) - - - - -class Conf(ConfClass): - """This object contains the configuration of scapy. -session : filename where the session will be saved -stealth : if 1, prevents any unwanted packet to go out (ARP, DNS, ...) -checkIPID: if 0, doesn't check that IPID matches between IP sent and ICMP IP citation received - if 1, checks that they either are equal or byte swapped equals (bug in some IP stacks) - if 2, strictly checks that they are equals -checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks) -check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation -iff : selects the default output interface for srp() and sendp(). default:"eth0") -verb : level of verbosity, from 0 (almost mute) to 3 (verbose) -promisc : default mode for listening socket (to get answers if you spoof on a lan) -sniff_promisc : default mode for sniff() -filter : bpf filter added to every sniffing socket to exclude traffic from analysis -histfile : history file -padding : includes padding in desassembled packets -except_filter : BPF filter for packets to ignore -debug_match : when 1, store received packet that are not matched into debug.recv -route : holds the Scapy routing table and provides methods to manipulate it -warning_threshold : how much time between warnings from the same place -ASN1_default_codec: Codec used by default for ASN1 objects -mib : holds MIB direct access dictionnary -resolve : holds list of fields for which resolution should be done -noenum : holds list of enum fields for which conversion to string should NOT be done -AS_resolver: choose the AS resolver class to use -extensions_paths: path or list of paths where extensions are to be looked for -""" - session = "" - stealth = "not implemented" - iface = get_working_if() - checkIPID = 0 - checkIPsrc = 1 - checkIPaddr = 1 - check_TCPerror_seqack = 0 - verb = 2 - prompt = ">>> " - promisc = 1 - sniff_promisc = 1 - L3socket = L3PacketSocket - L2socket = L2Socket - L2listen = L2ListenSocket - BTsocket = BluetoothL2CAPSocket - histfile = os.path.join(os.environ["HOME"], ".scapy_history") - padding = 1 - p0f_base ="/etc/p0f/p0f.fp" - queso_base ="/etc/queso.conf" - nmap_base ="/usr/share/nmap/nmap-os-fingerprints" - IPCountry_base = "GeoIPCountry4Scapy.gz" - countryLoc_base = "countryLoc.csv" - gnuplot_world = "world.dat" - except_filter = "" - debug_match = 0 - route = Route() - wepkey = "" - auto_fragment = 1 - debug_dissector = 0 - color_theme = DefaultTheme() - warning_threshold = 5 - ASN1_default_codec = ASN1_Codecs.BER - mib = MIBDict(_name="MIB") - prog = ProgPath() - resolve = Resolve() - noenum = Resolve() - ethertypes = ETHER_TYPES - protocols = IP_PROTOS - services_tcp = TCP_SERVICES - services_udp = UDP_SERVICES - manufdb = MANUFDB - AS_resolver = AS_resolver_multi() - extensions_paths = "." - - -conf=Conf() - -betteriface = conf.route.route("0.0.0.0", verbose=0)[0] -if betteriface != "lo": #XXX linux specific... - conf.iface = betteriface -del(betteriface) - -if PCAP: - conf.L2listen=L2pcapListenSocket - if DNET: - conf.L3socket=L3dnetSocket - conf.L2socket=L2dnetSocket - - -p0f_kdb = p0fKnowledgeBase(conf.p0f_base) -queso_kdb = QuesoKnowledgeBase(conf.queso_base) -nmap_kdb = NmapKnowledgeBase(conf.nmap_base) -IP_country_kdb = IPCountryKnowledgeBase(conf.IPCountry_base) -country_loc_kdb = CountryLocKnowledgeBase(conf.countryLoc_base) - - -######################### -##### Autorun stuff ##### -######################### - -class StopAutorun(Scapy_Exception): - code_run = "" - -class ScapyAutorunInterpreter(code.InteractiveInterpreter): - def __init__(self, *args, **kargs): - code.InteractiveInterpreter.__init__(self, *args, **kargs) - self.error = 0 - def showsyntaxerror(self, *args, **kargs): - self.error = 1 - return code.InteractiveInterpreter.showsyntaxerror(self, *args, **kargs) - def showtraceback(self, *args, **kargs): - self.error = 1 - exc_type, exc_value, exc_tb = sys.exc_info() - if isinstance(exc_value, StopAutorun): - raise exc_value - return code.InteractiveInterpreter.showtraceback(self, *args, **kargs) - - -def autorun_commands(cmds,my_globals=None,verb=0): - sv = conf.verb - import __builtin__ - try: - try: - if my_globals is None: - my_globals = globals() - conf.verb = verb - interp = ScapyAutorunInterpreter(my_globals) - cmd = "" - cmds = cmds.splitlines() - cmds.append("") # ensure we finish multiline commands - cmds.reverse() - __builtin__.__dict__["_"] = None - while 1: - if cmd: - sys.stderr.write(sys.__dict__.get("ps2","... ")) - else: - sys.stderr.write(str(sys.__dict__.get("ps1",ColorPrompt()))) - - l = cmds.pop() - print l - cmd += "\n"+l - if interp.runsource(cmd): - continue - if interp.error: - return 0 - cmd = "" - if len(cmds) <= 1: - break - except SystemExit: - pass - finally: - conf.verb = sv - return _ - -def autorun_get_interactive_session(cmds, **kargs): - class StringWriter: - def __init__(self): - self.s = "" - def write(self, x): - self.s += x - - sw = StringWriter() - sstdout,sstderr = sys.stdout,sys.stderr - try: - try: - sys.stdout = sys.stderr = sw - res = autorun_commands(cmds, **kargs) - except StopAutorun,e: - e.code_run = sw.s - raise - finally: - sys.stdout,sys.stderr = sstdout,sstderr - return sw.s,res - -def autorun_get_text_interactive_session(cmds, **kargs): - ct = conf.color_theme - try: - conf.color_theme = NoTheme() - s,res = autorun_get_interactive_session(cmds, **kargs) - finally: - conf.color_theme = ct - return s,res - -def autorun_get_ansi_interactive_session(cmds, **kargs): - ct = conf.color_theme - try: - conf.color_theme = DefaultTheme() - s,res = autorun_get_interactive_session(cmds, **kargs) - finally: - conf.color_theme = ct - return s,res - -def autorun_get_html_interactive_session(cmds, **kargs): - ct = conf.color_theme - to_html = lambda s: s.replace("<","<").replace(">",">").replace("#[#","<").replace("#]#",">") - try: - try: - conf.color_theme = HTMLTheme2() - s,res = autorun_get_interactive_session(cmds, **kargs) - except StopAutorun,e: - e.code_run = to_html(e.code_run) - raise - finally: - conf.color_theme = ct - - return to_html(s),res - -def autorun_get_latex_interactive_session(cmds, **kargs): - ct = conf.color_theme - to_latex = lambda s: tex_escape(s).replace("@[@","{").replace("@]@","}").replace("@`@","\\") - try: - try: - conf.color_theme = LatexTheme2() - s,res = autorun_get_interactive_session(cmds, **kargs) - except StopAutorun,e: - e.code_run = to_latex(e.code_run) - raise - finally: - conf.color_theme = ct - return to_latex(s),res - - -################ -##### Main ##### -################ - -def scapy_write_history_file(readline): - if conf.histfile: - try: - readline.write_history_file(conf.histfile) - except IOError,e: - try: - warning("Could not write history to [%s]\n\t (%s)" % (conf.histfile,e)) - tmp = os.tempnam("","scapy") - readline.write_history_file(tmp) - warning("Wrote history to [%s]" % tmp) - except: - warning("Cound not write history to [%s]. Discarded" % tmp) - - -def interact(mydict=None,argv=None,mybanner=None,loglevel=1): - global session - import code,sys,cPickle,types,os,imp,getopt,logging - - logging.getLogger("scapy").setLevel(loglevel) - - the_banner = "Welcome to Scapy (%s)" - if mybanner is not None: - the_banner += "\n" - the_banner += mybanner - - if argv is None: - argv = sys.argv - -# scapy_module = argv[0][argv[0].rfind("/")+1:] -# if not scapy_module: -# scapy_module = "scapy" -# else: -# if scapy_module.endswith(".py"): -# scapy_module = scapy_module[:-3] -# -# scapy=imp.load_module("scapy",*imp.find_module(scapy_module)) - - -# __builtin__.__dict__.update(scapy.__dict__) - import __builtin__ - __builtin__.__dict__.update(globals()) - globkeys = globals().keys() - globkeys.append("scapy_session") - if mydict is not None: - __builtin__.__dict__.update(mydict) - globkeys += mydict.keys() - - import re, atexit - try: - import rlcompleter,readline - except ImportError: - log_loading.info("Can't load Python libreadline or completer") - READLINE=0 - else: - READLINE=1 - class ScapyCompleter(rlcompleter.Completer): - def global_matches(self, text): - matches = [] - n = len(text) - for lst in [dir(__builtin__), session.keys()]: - for word in lst: - if word[:n] == text and word != "__builtins__": - matches.append(word) - return matches - - - def attr_matches(self, text): - m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) - if not m: - return - expr, attr = m.group(1, 3) - try: - object = eval(expr) - except: - object = eval(expr, session) - if isinstance(object, Packet) or isinstance(object, Packet_metaclass): - words = filter(lambda x: x[0]!="_",dir(object)) - words += [x.name for x in object.fields_desc] - else: - words = dir(object) - if hasattr( object,"__class__" ): - words = words + rlcompleter.get_class_members(object.__class__) - matches = [] - n = len(attr) - for word in words: - if word[:n] == attr and word != "__builtins__": - matches.append("%s.%s" % (expr, word)) - return matches - - readline.set_completer(ScapyCompleter().complete) - readline.parse_and_bind("C-o: operate-and-get-next") - readline.parse_and_bind("tab: complete") - - - session=None - session_name="" - CONFIG_FILE = DEFAULT_CONFIG_FILE - - iface = None - try: - opts=getopt.getopt(argv[1:], "hs:Cc:") - for opt, parm in opts[0]: - if opt == "-h": - usage() - elif opt == "-s": - session_name = parm - elif opt == "-c": - CONFIG_FILE = parm - elif opt == "-C": - CONFIG_FILE = None - - if len(opts[1]) > 0: - raise getopt.GetoptError("Too many parameters : [%s]" % string.join(opts[1]),None) - - - except getopt.GetoptError, msg: - log_loading.error(msg) - sys.exit(1) - - - if CONFIG_FILE: - read_config_file(CONFIG_FILE) - - if session_name: - try: - os.stat(session_name) - except OSError: - log_loading.info("New session [%s]" % session_name) - else: - try: - try: - session = cPickle.load(gzip.open(session_name,"rb")) - except IOError: - session = cPickle.load(open(session_name,"rb")) - log_loading.info("Using session [%s]" % session_name) - except EOFError: - log_loading.error("Error opening session [%s]" % session_name) - except AttributeError: - log_loading.error("Error opening session [%s]. Attribute missing" % session_name) - - if session: - if "conf" in session: - conf.configure(session["conf"]) - session["conf"] = conf - else: - conf.session = session_name - session={"conf":conf} - - else: - session={"conf": conf} - - __builtin__.__dict__["scapy_session"] = session - - - if READLINE: - if conf.histfile: - try: - readline.read_history_file(conf.histfile) - except IOError: - pass - atexit.register(scapy_write_history_file,readline) - - sys.ps1 = ColorPrompt() - code.interact(banner = the_banner % (VERSION), local=session) - - if conf.session: - save_session(conf.session, session) - - - for k in globkeys: - try: - del(__builtin__.__dict__[k]) - except: - pass - -def read_config_file(configfile): - try: - execfile(configfile) - except IOError,e: - log_loading.warning("Cannot read config file [%s] [%s]" % (configfile,e)) - except Exception,e: - log_loading.exception("Error during evaluation of config file [%s]" % configfile) - - -if __name__ == "__main__": - interact() -else: - if DEFAULT_CONFIG_FILE: - read_config_file(DEFAULT_CONFIG_FILE) +interact() diff --git a/scapy/__init__.py b/scapy/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/scapy/ansmachine.py b/scapy/ansmachine.py new file mode 100644 index 0000000000000000000000000000000000000000..adf7f1f32226bb196d1d5b8dea57691f106866da --- /dev/null +++ b/scapy/ansmachine.py @@ -0,0 +1,119 @@ +######################## +## Answering machines ## +######################## + +from sendrecv import send,sendp + +class ReferenceAM(type): + def __new__(cls, name, bases, dct): + o = super(ReferenceAM, cls).__new__(cls, name, bases, dct) + if o.function_name: + globals()[o.function_name] = lambda o=o,*args,**kargs: o(*args,**kargs)() + return o + + +class AnsweringMachine(object): + __metaclass__ = ReferenceAM + function_name = "" + filter = None + sniff_options = { "store":0 } + sniff_options_list = [ "store", "iface", "count", "promisc", "filter", "type", "prn" ] + send_options = { "verbose":0 } + send_options_list = ["iface", "inter", "loop", "verbose"] + send_function = staticmethod(send) + + + def __init__(self, **kargs): + self.mode = 0 + if self.filter: + kargs.setdefault("filter",self.filter) + kargs.setdefault("prn", self.reply) + self.optam1 = {} + self.optam2 = {} + self.optam0 = {} + doptsend,doptsniff = self.parse_all_options(1, kargs) + self.defoptsend = self.send_options.copy() + self.defoptsend.update(doptsend) + self.defoptsniff = self.sniff_options.copy() + self.defoptsniff.update(doptsniff) + self.optsend,self.optsniff = [{},{}] + + def __getattr__(self, attr): + for d in [self.optam2, self.optam1]: + if attr in d: + return d[attr] + raise AttributeError,attr + + def __setattr__(self, attr, val): + mode = self.__dict__.get("mode",0) + if mode == 0: + self.__dict__[attr] = val + else: + [self.optam1, self.optam2][mode-1][attr] = val + + def parse_options(self): + pass + + def parse_all_options(self, mode, kargs): + sniffopt = {} + sendopt = {} + for k in kargs.keys(): + if k in self.sniff_options_list: + sniffopt[k] = kargs[k] + if k in self.send_options_list: + sendopt[k] = kargs[k] + if k in self.sniff_options_list+self.send_options_list: + del(kargs[k]) + if mode != 2 or kargs: + if mode == 1: + self.optam0 = kargs + elif mode == 2 and kargs: + k = self.optam0.copy() + k.update(kargs) + self.parse_options(**k) + kargs = k + omode = self.__dict__.get("mode",0) + self.__dict__["mode"] = mode + self.parse_options(**kargs) + self.__dict__["mode"] = omode + return sendopt,sniffopt + + def is_request(self, req): + return 1 + + def make_reply(self, req): + return req + + def send_reply(self, reply): + self.send_function(reply, **self.optsend) + + def print_reply(self, req, reply): + print "%s ==> %s" % (req.summary(),reply.summary()) + + def reply(self, pkt): + if not self.is_request(pkt): + return + reply = self.make_reply(pkt) + self.send_reply(reply) + if conf.verb >= 0: + self.print_reply(pkt, reply) + + def run(self, *args, **kargs): + log_interactive.warning("run() method deprecated. The intance is now callable") + self(*args,**kargs) + + def __call__(self, *args, **kargs): + optsend,optsniff = self.parse_all_options(2,kargs) + self.optsend=self.defoptsend.copy() + self.optsend.update(optsend) + self.optsniff=self.defoptsniff.copy() + self.optsniff.update(optsniff) + + try: + self.sniff() + except KeyboardInterrupt: + print "Interrupted by user" + + def sniff(self): + sniff(**self.optsniff) + diff --git a/scapy/arch.py b/scapy/arch.py new file mode 100644 index 0000000000000000000000000000000000000000..debf2c1bbed57e8081cee7fcd948dba6f14b2336 --- /dev/null +++ b/scapy/arch.py @@ -0,0 +1,421 @@ + +import sys,os,struct,socket,time +from fcntl import ioctl +from data import * + +try: + import Gnuplot + GNUPLOT=1 +except ImportError: + log_loading.info("did not find python gnuplot wrapper . Won't be able to plot") + GNUPLOT=0 + +try: + import pyx + PYX=1 +except ImportError: + log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump()") + PYX=0 + + +LINUX=sys.platform.startswith("linux") +OPENBSD=sys.platform.startswith("openbsd") +FREEBSD=sys.platform.startswith("freebsd") +DARWIN=sys.platform.startswith("darwin") +BIG_ENDIAN= struct.pack("H",1) == "\x00\x01" +X86_64 = (os.uname()[4] == 'x86_64') +SOLARIS=sys.platform.startswith("sunos") + +if LINUX: + DNET=PCAP=0 +else: + DNET=PCAP=1 + + +if PCAP: + try: + import pcap + PCAP = 1 + except ImportError: + if LINUX: + log_loading.warning("did not find pcap module. Fallback to linux primitives") + PCAP = 0 + else: + if __name__ == "__main__": + log_loading.error("did not find pcap module") + raise SystemExit + else: + raise + +if DNET: + try: + import dnet + DNET = 1 + except ImportError: + if LINUX: + log_loading.warning("did not find dnet module. Fallback to linux primitives") + DNET = 0 + else: + if __name__ == "__main__": + log_loading.error("did not find dnet module") + raise SystemExit + else: + raise + +if not PCAP: + f = os.popen("tcpdump -V 2> /dev/null") + if f.close() >> 8 == 0x7f: + log_loading.warning("Failed to execute tcpdump. Check it is installed and in the PATH") + TCPDUMP=0 + else: + TCPDUMP=1 + del(f) + + + +try: + from Crypto.Cipher import ARC4 +except ImportError: + log_loading.info("Can't find Crypto python lib. Won't be able to decrypt WEP") + + +# Workarround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470 +try: + socket.inet_aton("255.255.255.255") +except socket.error: + def inet_aton(x): + if x == "255.255.255.255": + return "\xff"*4 + else: + return socket.inet_aton(x) +else: + inet_aton = socket.inet_aton + +inet_ntoa = socket.inet_ntoa +try: + inet_ntop = socket.inet_ntop + inet_pton = socket.inet_pton +except AttributeError: + log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present") + + +if SOLARIS: + # GRE is missing on Solaris + socket.IPPROTO_GRE = 47 + + + +###################### +## Interfaces stuff ## +###################### + + +if DNET: + def get_if_raw_hwaddr(iff): + if iff[:2] == "lo": + return (772, '\x00'*6) + try: + l = dnet.intf().get(iff) + l = l["link_addr"] + except: + raise Scapy_Exception("Error in attempting to get hw address for interface [%s]" % iff) + return l.type,l.data + def get_if_raw_addr(ifname): + i = dnet.intf() + return i.get(ifname)["addr"].data +else: + def get_if_raw_hwaddr(iff): + return struct.unpack("16xh6s8x",get_if(iff,SIOCGIFHWADDR)) + + def get_if_raw_addr(iff): + try: + return get_if(iff, SIOCGIFADDR)[20:24] + except IOError: + return "\0\0\0\0" + + +if PCAP: + def get_if_list(): + # remove 'any' interface + return map(lambda x:x[0],filter(lambda x:x[1] is None,pcap.findalldevs())) + def get_working_if(): + try: + return pcap.lookupdev() + except Exception: + return 'lo' + + def attach_filter(s, filter): + warning("attach_filter() should not be called in PCAP mode") + def set_promisc(s,iff,val=1): + warning("set_promisc() should not be called in DNET/PCAP mode") + +else: + def get_if_list(): + f=open("/proc/net/dev","r") + lst = [] + f.readline() + f.readline() + for l in f: + lst.append(l.split(":")[0].strip()) + return lst + def get_working_if(): + for i in get_if_list(): + if i == 'lo': + continue + ifflags = struct.unpack("16xH14x",get_if(i,SIOCGIFFLAGS))[0] + if ifflags & IFF_UP: + return i + return "lo" + def attach_filter(s, filter): + # XXX We generate the filter on the interface conf.iface + # because tcpdump open the "any" interface and ppp interfaces + # in cooked mode. As we use them in raw mode, the filter will not + # work... one solution could be to use "any" interface and translate + # the filter from cooked mode to raw mode + # mode + if not TCPDUMP: + return + try: + f = os.popen("%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump,conf.iface,filter)) + except OSError,msg: + log_interactive.warning("Failed to execute tcpdump: (%s)") + return + lines = f.readlines() + if f.close(): + raise Scapy_Exception("Filter parse error") + nb = int(lines[0]) + bpf = "" + for l in lines[1:]: + bpf += struct.pack("HBBI",*map(long,l.split())) + + # XXX. Argl! We need to give the kernel a pointer on the BPF, + # python object header seems to be 20 bytes. 36 bytes for x86 64bits arch. + if X86_64: + bpfh = struct.pack("HL", nb, id(bpf)+36) + else: + bpfh = struct.pack("HI", nb, id(bpf)+20) + s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh) + + def set_promisc(s,iff,val=1): + mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, "") + if val: + cmd = PACKET_ADD_MEMBERSHIP + else: + cmd = PACKET_DROP_MEMBERSHIP + s.setsockopt(SOL_PACKET, cmd, mreq) + + + +################## +## Routes stuff ## +################## + +if not LINUX: + + def new_read_routes(): + + rtlst = [] + def addrt(rt,lst): + dst,gw = rt + lst.append(rt) + + r = dnet.route() + print r.loop(addrt, rtlst) + return rtlst + + def read_routes(): + if SOLARIS: + f=os.popen("netstat -rvn") # -f inet + elif FREEBSD: + f=os.popen("netstat -rnW") # -W to handle long interface names + else: + f=os.popen("netstat -rn") # -f inet + ok = 0 + mtu_present = False + routes = [] + for l in f.readlines(): + if not l: + break + l = l.strip() + if l.find("----") >= 0: # a separation line + continue + if l.find("Destination") >= 0: + ok = 1 + if l.find("Mtu") >= 0: + mtu_present = True + continue + if ok == 0: + continue + if not l: + break + if SOLARIS: + dest,mask,gw,netif,mxfrg,rtt,ref,flg = l.split()[:8] + else: + if mtu_present: + dest,gw,flg,ref,use,mtu,netif = l.split()[:7] + else: + dest,gw,flg,ref,use,netif = l.split()[:6] + if flg.find("Lc") >= 0: + continue + if dest == "default": + dest = 0L + netmask = 0L + else: + if SOLARIS: + netmask = atol(mask) + elif "/" in dest: + dest,netmask = dest.split("/") + netmask = itom(int(netmask)) + else: + netmask = itom((dest.count(".") + 1) * 8) + dest += ".0"*(3-dest.count(".")) + dest = atol(dest) + if not "G" in flg: + gw = '0.0.0.0' + ifaddr = get_if_addr(netif) + routes.append((dest,netmask,gw,netif,ifaddr)) + f.close() + return routes + + def read_interfaces(): + i = dnet.intf() + ifflist = {} + def addif(iff,lst): + if not iff.has_key("addr"): + return + if not iff.has_key("link_addr"): + return + rawip = iff["addr"].data + ip = inet_ntoa(rawip) + rawll = iff["link_addr"].data + ll = str2mac(rawll) + lst[iff["name"]] = (rawll,ll,rawip,ip) + i.loop(addif, ifflist) + return ifflist + + +else: + + def read_routes(): + f=open("/proc/net/route","r") + routes = [] + s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x","lo")) + addrfamily = struct.unpack("h",ifreq[16:18])[0] + if addrfamily == socket.AF_INET: + ifreq2 = ioctl(s, SIOCGIFNETMASK,struct.pack("16s16x","lo")) + msk = socket.ntohl(struct.unpack("I",ifreq2[20:24])[0]) + dst = socket.ntohl(struct.unpack("I",ifreq[20:24])[0]) & msk + ifaddr = inet_ntoa(ifreq[20:24]) + routes.append((dst, msk, "0.0.0.0", "lo", ifaddr)) + else: + warning("Interface lo: unkown address family (%i)"% addrfamily) + + for l in f.readlines()[1:]: + iff,dst,gw,flags,x,x,x,msk,x,x,x = l.split() + flags = int(flags,16) + if flags & RTF_UP == 0: + continue + if flags & RTF_REJECT: + continue + try: + ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",iff)) + except IOError: # interface is present in routing tables but does not have any assigned IP + ifaddr="0.0.0.0" + else: + addrfamily = struct.unpack("h",ifreq[16:18])[0] + if addrfamily == socket.AF_INET: + ifaddr = inet_ntoa(ifreq[20:24]) + else: + warning("Interface %s: unkown address family (%i)"%(iff, addrfamily)) + continue + routes.append((socket.htonl(long(dst,16))&0xffffffffL, + socket.htonl(long(msk,16))&0xffffffffL, + inet_ntoa(struct.pack("I",long(gw,16))), + iff, ifaddr)) + + f.close() + return routes + + def get_if(iff,cmd): + s=socket.socket() + ifreq = ioctl(s, cmd, struct.pack("16s16x",iff)) + s.close() + return ifreq + + + def get_if_index(iff): + return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0]) + + def get_last_packet_timestamp(sock): + ts = ioctl(sock, SIOCGSTAMP, "12345678") + s,us = struct.unpack("II",ts) + return s+us/1000000.0 + + +def get_if_addr(iff): + return inet_ntoa(get_if_raw_addr(iff)) + +def get_if_hwaddr(iff): + addrfamily, mac = get_if_raw_hwaddr(iff) + if addrfamily in [ARPHDR_ETHER,ARPHDR_LOOPBACK]: + return str2mac(mac) + else: + raise Scapy_Exception("Unsupported address family (%i) for interface [%s]" % (addrfamily,iff)) + + + +##################### +## ARP cache stuff ## +##################### + +ARPTIMEOUT=120 + +# XXX Fill arp_cache with /etc/ether and arp cache +arp_cache={} + +if 0 and DNET: ## XXX Can't use this because it does not resolve IPs not in cache + dnet_arp_object = dnet.arp() + def getmacbyip(ip, chainCC=0): + tmp = map(ord, inet_aton(ip)) + if (tmp[0] & 0xf0) == 0xe0: # mcast @ + return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) + iff,a,gw = conf.route.route(ip) + if iff == "lo": + return "ff:ff:ff:ff:ff:ff" + if gw != "0.0.0.0": + ip = gw + res = dnet_arp_object.get(dnet.addr(ip)) + if res is None: + return None + else: + return res.ntoa() +else: + def getmacbyip(ip, chainCC=0): + tmp = map(ord, inet_aton(ip)) + if (tmp[0] & 0xf0) == 0xe0: # mcast @ + return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) + iff,a,gw = conf.route.route(ip) + if ( (iff == "lo") or (ip == conf.route.get_if_bcast(iff)) ): + return "ff:ff:ff:ff:ff:ff" + if gw != "0.0.0.0": + ip = gw + + if arp_cache.has_key(ip): + mac, timeout = arp_cache[ip] + if not timeout or (time.time()-timeout < ARPTIMEOUT): + return mac + + res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), + type=ETH_P_ARP, + iface = iff, + timeout=2, + verbose=0, + chainCC=chainCC, + nofilter=1) + if res is not None: + mac = res.payload.hwsrc + arp_cache[ip] = (mac,time.time()) + return mac + return None + diff --git a/scapy/asn1.py b/scapy/asn1.py new file mode 100644 index 0000000000000000000000000000000000000000..456f1eaa94f2ebe125f1e3f28b56a2096d450303 --- /dev/null +++ b/scapy/asn1.py @@ -0,0 +1,272 @@ +import random +from error import Scapy_Exception +from volatile import RandField +from utils import Enum_metaclass, EnumElement + +class RandASN1Object(RandField): + def __init__(self, objlist=None): + if objlist is None: + objlist = map(lambda x:x._asn1_obj, + filter(lambda x:hasattr(x,"_asn1_obj"), ASN1_Class_UNIVERSAL.__rdict__.values())) + self.objlist = objlist + self.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + def _fix(self, n=0): + o = random.choice(self.objlist) + if issubclass(o, ASN1_INTEGER): + return o(int(random.gauss(0,1000))) + elif issubclass(o, ASN1_STRING): + z = int(random.expovariate(0.05)+1) + return o("".join([random.choice(self.chars) for i in range(z)])) + elif issubclass(o, ASN1_SEQUENCE) and (n < 10): + z = int(random.expovariate(0.08)+1) + return o(map(lambda x:x._fix(n+1), [self.__class__(objlist=self.objlist)]*z)) + return ASN1_INTEGER(int(random.gauss(0,1000))) + + +############## +#### ASN1 #### +############## + +class ASN1_Error(Scapy_Exception): + pass + +class ASN1_Encoding_Error(ASN1_Error): + pass + +class ASN1_Decoding_Error(ASN1_Error): + pass + +class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error): + pass + + + +class ASN1Codec(EnumElement): + def register_stem(cls, stem): + cls._stem = stem + def dec(cls, s, context=None): + return cls._stem.dec(s, context=context) + def safedec(cls, s, context=None): + return cls._stem.safedec(s, context=context) + def get_stem(cls): + return cls.stem + + +class ASN1_Codecs_metaclass(Enum_metaclass): + element_class = ASN1Codec + +class ASN1_Codecs: + __metaclass__ = ASN1_Codecs_metaclass + BER = 1 + DER = 2 + PER = 3 + CER = 4 + LWER = 5 + BACnet = 6 + OER = 7 + SER = 8 + XER = 9 + +class ASN1Tag(EnumElement): + def __init__(self, key, value, context=None, codec=None): + EnumElement.__init__(self, key, value) + self._context = context + if codec == None: + codec = {} + self._codec = codec + def clone(self): # /!\ not a real deep copy. self.codec is shared + return self.__class__(self._key, self._value, self._context, self._codec) + def register_asn1_object(self, asn1obj): + self._asn1_obj = asn1obj + def asn1_object(self, val): + if hasattr(self,"_asn1_obj"): + return self._asn1_obj(val) + raise ASN1_Error("%r does not have any assigned ASN1 object" % self) + def register(self, codecnum, codec): + self._codec[codecnum] = codec + def get_codec(self, codec): + try: + c = self._codec[codec] + except KeyError,msg: + raise ASN1_Error("Codec %r not found for tag %r" % (codec, self)) + return c + +class ASN1_Class_metaclass(Enum_metaclass): + element_class = ASN1Tag + def __new__(cls, name, bases, dct): # XXX factorise a bit with Enum_metaclass.__new__() + for b in bases: + for k,v in b.__dict__.iteritems(): + if k not in dct and isinstance(v,ASN1Tag): + dct[k] = v.clone() + + rdict = {} + for k,v in dct.iteritems(): + if type(v) is int: + v = ASN1Tag(k,v) + dct[k] = v + rdict[v] = v + elif isinstance(v, ASN1Tag): + rdict[v] = v + dct["__rdict__"] = rdict + + cls = type.__new__(cls, name, bases, dct) + for v in cls.__dict__.values(): + if isinstance(v, ASN1Tag): + v.context = cls # overwrite ASN1Tag contexts, even cloned ones + return cls + + +class ASN1_Class: + __metaclass__ = ASN1_Class_metaclass + +class ASN1_Class_UNIVERSAL(ASN1_Class): + name = "UNIVERSAL" + ERROR = -3 + RAW = -2 + NONE = -1 + ANY = 0 + BOOLEAN = 1 + INTEGER = 2 + BIT_STRING = 3 + STRING = 4 + NULL = 5 + OID = 6 + OBJECT_DESCRIPTOR = 7 + EXTERNAL = 8 + REAL = 9 + ENUMERATED = 10 + EMBEDDED_PDF = 11 + UTF8_STRING = 12 + RELATIVE_OID = 13 + SEQUENCE = 0x30#XXX 16 ?? + SET = 0x31 #XXX 17 ?? + NUMERIC_STRING = 18 + PRINTABLE_STRING = 19 + T61_STRING = 20 + VIDEOTEX_STRING = 21 + IA5_STRING = 22 + UTC_TIME = 23 + GENERALIZED_TIME = 24 + GRAPHIC_STRING = 25 + ISO646_STRING = 26 + GENERAL_STRING = 27 + UNIVERSAL_STRING = 28 + CHAR_STRING = 29 + BMP_STRING = 30 + COUNTER32 = 0x41 + TIME_TICKS = 0x43 + +class ASN1_Object_metaclass(type): + def __new__(cls, name, bases, dct): + c = super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct) + try: + c.tag.register_asn1_object(c) + except: + warning("Error registering %r for %r" % (c.tag, c.codec)) + return c + + +class ASN1_Object: + __metaclass__ = ASN1_Object_metaclass + tag = ASN1_Class_UNIVERSAL.ANY + def __init__(self, val): + self.val = val + def enc(self, codec): + return self.tag.get_codec(codec).enc(self.val) + def __repr__(self): + return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) + def __str__(self): + return self.enc(conf.ASN1_default_codec) + def strshow(self, lvl=0): + return (" "*lvl)+repr(self)+"\n" + def show(self, lvl=0): + print self.strshow(lvl) + def __eq__(self, other): + return self.val == other + def __cmp__(self, other): + return cmp(self.val, other) + +class ASN1_DECODING_ERROR(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.ERROR + def __init__(self, val, exc=None): + ASN1_Object.__init__(self, val) + self.exc = exc + def __repr__(self): + return "<%s[%r]{{%s}}>" % (self.__dict__.get("name", self.__class__.__name__), + self.val, self.exc.args[0]) + def enc(self, codec): + if isinstance(self.val, ASN1_Object): + return self.val.enc(codec) + return self.val + +class ASN1_force(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.RAW + def enc(self, codec): + if isinstance(self.val, ASN1_Object): + return self.val.enc(codec) + return self.val + +class ASN1_BADTAG(ASN1_force): + pass + +class ASN1_INTEGER(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.INTEGER + +class ASN1_STRING(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.STRING + +class ASN1_BIT_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.BIT_STRING + +class ASN1_PRINTABLE_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING + +class ASN1_T61_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.T61_STRING + +class ASN1_IA5_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.IA5_STRING + +class ASN1_NUMERIC_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING + +class ASN1_VIDEOTEX_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING + +class ASN1_UTC_TIME(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.UTC_TIME + +class ASN1_TIME_TICKS(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.TIME_TICKS + +class ASN1_BOOLEAN(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.BOOLEAN + +class ASN1_NULL(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.NULL + +class ASN1_COUNTER32(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.COUNTER32 + +class ASN1_SEQUENCE(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.SEQUENCE + def strshow(self, lvl=0): + s = (" "*lvl)+("# %s:" % self.__class__.__name__)+"\n" + for o in self.val: + s += o.strshow(lvl=lvl+1) + return s + +class ASN1_SET(ASN1_SEQUENCE): + tag = ASN1_Class_UNIVERSAL.SET + +class ASN1_OID(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.OID + def __init__(self, val): + val = conf.mib._oid(val) + ASN1_Object.__init__(self, val) + def __repr__(self): + return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), conf.mib._oidname(self.val)) + + + +ASN1_default_codec = ASN1_Codecs.BER diff --git a/scapy/asn1fields.py b/scapy/asn1fields.py new file mode 100644 index 0000000000000000000000000000000000000000..110e285df76ccc7eaa1bdbc8dd473ece93af7290 --- /dev/null +++ b/scapy/asn1fields.py @@ -0,0 +1,301 @@ +from asn1 import * +from ber import * +from volatile import * + +##################### +#### ASN1 Fields #### +##################### + +class ASN1F_badsequence(Exception): + pass + +class ASN1F_element: + pass + +class ASN1F_optionnal(ASN1F_element): + def __init__(self, field): + self._field=field + def __getattr__(self, attr): + return getattr(self._field,attr) + def dissect(self,pkt,s): + try: + return self._field.dissect(pkt,s) + except ASN1F_badsequence: + self._field.set_val(pkt,None) + return s + except BER_Decoding_Error: + self._field.set_val(pkt,None) + return s + def build(self, pkt): + if self._field.is_empty(pkt): + return "" + return self._field.build(pkt) + +class ASN1F_field(ASN1F_element): + holds_packets=0 + islist=0 + + ASN1_tag = ASN1_Class_UNIVERSAL.ANY + context=ASN1_Class_UNIVERSAL + + def __init__(self, name, default, context=None): + if context is not None: + self.context = context + self.name = name + self.default = default + + def i2repr(self, pkt, x): + if x is None: + x = 0 + return repr(x) + def i2h(self, pkt, x): + return x + def any2i(self, pkt, x): + return x + def m2i(self, pkt, x): + return self.ASN1_tag.get_codec(pkt.ASN1_codec).safedec(x, context=self.context) + def i2m(self, pkt, x): + if x is None: + x = 0 + if isinstance(x, ASN1_Object): + if ( self.ASN1_tag == ASN1_Class_UNIVERSAL.ANY + or x.tag == ASN1_Class_UNIVERSAL.RAW + or x.tag == ASN1_Class_UNIVERSAL.ERROR + or self.ASN1_tag == x.tag ): + return x.enc(pkt.ASN1_codec) + else: + raise ASN1_Error("Encoding Error: got %r instead of an %r for field [%s]" % (x, self.ASN1_tag, self.name)) + return self.ASN1_tag.get_codec(pkt.ASN1_codec).enc(x) + + 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], Packet): + x[i] = x[i].copy() + return x + + def build(self, pkt): + return self.i2m(pkt, getattr(pkt, self.name)) + + def set_val(self, pkt, val): + setattr(pkt, self.name, val) + def is_empty(self, pkt): + return getattr(pkt,self.name) is None + + def dissect(self, pkt, s): + v,s = self.m2i(pkt, s) + self.set_val(pkt, v) + return s + + def get_fields_list(self): + return [self] + + def __hash__(self): + return hash(self.name) + def __str__(self): + return self.name + def __eq__(self, other): + return self.name == other + def __repr__(self): + return self.name + def randval(self): + return RandInt() + + +class ASN1F_INTEGER(ASN1F_field): + ASN1_tag= ASN1_Class_UNIVERSAL.INTEGER + def randval(self): + return RandNum(-2**64, 2**64-1) + +class ASN1F_NULL(ASN1F_INTEGER): + ASN1_tag= ASN1_Class_UNIVERSAL.NULL + +class ASN1F_enum_INTEGER(ASN1F_INTEGER): + def __init__(self, name, default, enum): + ASN1F_INTEGER.__init__(self, name, default) + 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 + def any2i_one(self, pkt, x): + if type(x) is str: + x = self.s2i[x] + return x + def i2repr_one(self, pkt, x): + return self.i2s.get(x, 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 ASN1F_STRING(ASN1F_field): + ASN1_tag = ASN1_Class_UNIVERSAL.STRING + def randval(self): + return RandString(RandNum(0, 1000)) + +class ASN1F_PRINTABLE_STRING(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING + +class ASN1F_BIT_STRING(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING + +class ASN1F_UTC_TIME(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME + +class ASN1F_OID(ASN1F_field): + ASN1_tag = ASN1_Class_UNIVERSAL.OID + def randval(self): + return RandOID() + +class ASN1F_SEQUENCE(ASN1F_field): + ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE + def __init__(self, *seq, **kargs): + if "ASN1_tag" in kargs: + self.ASN1_tag = kargs["ASN1_tag"] + self.seq = seq + def __repr__(self): + return "<%s%r>" % (self.__class__.__name__,self.seq,) + def set_val(self, pkt, val): + for f in self.seq: + f.set_val(pkt,val) + def is_empty(self, pkt): + for f in self.seq: + if not f.is_empty(pkt): + return False + return True + def get_fields_list(self): + return reduce(lambda x,y: x+y.get_fields_list(), self.seq, []) + def build(self, pkt): + s = reduce(lambda x,y: x+y.build(pkt), self.seq, "") + return self.i2m(pkt, s) + def dissect(self, pkt, s): + codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) + try: + i,s,remain = codec.check_type_check_len(s) + for obj in self.seq: + s = obj.dissect(pkt,s) + if s: + warning("Too many bytes to decode sequence: [%r]" % s) # XXX not reversible! + return remain + except ASN1_Error,e: + raise ASN1F_badsequence(e) + +class ASN1F_SET(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_UNIVERSAL.SET + +class ASN1F_SEQUENCE_OF(ASN1F_SEQUENCE): + holds_packets = 1 + islist = 1 + def __init__(self, name, default, asn1pkt, ASN1_tag=0x30): + self.asn1pkt = asn1pkt + self.tag = chr(ASN1_tag) + self.name = name + self.default = default + def i2repr(self, pkt, i): + if i is None: + return [] + return i + def get_fields_list(self): + return [self] + def set_val(self, pkt, val): + ASN1F_field.set_val(self, pkt, val) + def is_empty(self, pkt): + return ASN1F_field.is_empty(self, pkt) + def build(self, pkt): + val = getattr(pkt, self.name) + if isinstance(val, ASN1_Object) and val.tag == ASN1_Class_UNIVERSAL.RAW: + s = val + elif val is None: + s = "" + else: + s = "".join(map(str, val )) + return self.i2m(pkt, s) + def set_val(self, pkt, val): + ASN1F_field.set_val(self, pkt, val) + def dissect(self, pkt, s): + codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) + i,s1,remain = codec.check_type_check_len(s) + lst = [] + while s1: + try: + p = self.asn1pkt(s1) + except ASN1F_badsequence,e: + lst.append(Raw(s1)) + break + lst.append(p) + if Raw in p: + s1 = p[Raw].load + del(p[Raw].underlayer.payload) + else: + break + self.set_val(pkt, lst) + return remain + def randval(self): + return fuzz(self.asn1pkt()) + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__,self.name) + +class ASN1F_PACKET(ASN1F_field): + holds_packets = 1 + def __init__(self, name, default, cls): + ASN1_field.__init__(self, name, default) + self.cls = cls + def i2m(self, pkt, x): + if x is None: + x = "" + return str(x) + def extract_packet(self, cls, x): + try: + c = cls(x) + except ASN1F_badsequence: + c = Raw(x) + cpad = c[Padding] + x = "" + if cpad is not None: + x = cpad.load + del(cpad.underlayer.payload) + return c,x + def m2i(self, pkt, x): + return self.extract_packet(self.cls, x) + + +class ASN1F_CHOICE(ASN1F_PACKET): + ASN1_tag = ASN1_Class_UNIVERSAL.NONE + def __init__(self, name, default, *args): + self.name=name + self.choice = {} + for p in args: + self.choice[p.ASN1_root.ASN1_tag] = p +# self.context=context + self.default=default + def m2i(self, pkt, x): + if len(x) == 0: + return Raw(),"" + raise ASN1_Error("ASN1F_CHOICE: got empty string") + if ord(x[0]) not in self.choice: + return Raw(x),"" # XXX return RawASN1 packet ? Raise error + raise ASN1_Error("Decoding Error: choice [%i] not found in %r" % (ord(x[0]), self.choice.keys())) + + z = ASN1F_PACKET.extract_packet(self, self.choice[ord(x[0])], x) + return z + def randval(self): + return RandChoice(*map(lambda x:fuzz(x()), self.choice.values())) + + diff --git a/scapy/asn1packet.py b/scapy/asn1packet.py new file mode 100644 index 0000000000000000000000000000000000000000..e8ce01f3ed676ec728cade123e9a38e442c26dea --- /dev/null +++ b/scapy/asn1packet.py @@ -0,0 +1,15 @@ +from packet import * + +class ASN1_Packet(Packet): + ASN1_root = None + ASN1_codec = None + def init_fields(self): + flist = self.ASN1_root.get_fields_list() + self.do_init_fields(flist) + self.fields_desc = flist + def do_build(self): + return self.ASN1_root.build(self) + def do_dissect(self, x): + return self.ASN1_root.dissect(self, x) + + diff --git a/scapy/automaton.py b/scapy/automaton.py new file mode 100644 index 0000000000000000000000000000000000000000..5893c957c2bc54e842736e1ed8c5e033dfb929cd --- /dev/null +++ b/scapy/automaton.py @@ -0,0 +1,321 @@ +import types,itertools,time +from select import select +from config import conf + +############## +## Automata ## +############## + +class ATMT: + STATE = "State" + ACTION = "Action" + CONDITION = "Condition" + RECV = "Receive condition" + TIMEOUT = "Timeout condition" + + class NewStateRequested(Exception): + def __init__(self, state_func, automaton, *args, **kargs): + self.func = state_func + self.state = state_func.atmt_state + self.initial = state_func.atmt_initial + self.error = state_func.atmt_error + self.final = state_func.atmt_final + Exception.__init__(self, "Request state [%s]" % self.state) + self.automaton = automaton + self.args = args + self.kargs = kargs + self.action_parameters() # init action parameters + def action_parameters(self, *args, **kargs): + self.action_args = args + self.action_kargs = kargs + return self + def run(self): + return self.func(self.automaton, *self.args, **self.kargs) + + @staticmethod + def state(initial=0,final=0,error=0): + def deco(f,initial=initial, final=final): + f.atmt_type = ATMT.STATE + f.atmt_state = f.func_name + f.atmt_initial = initial + f.atmt_final = final + f.atmt_error = error + def state_wrapper(self, *args, **kargs): + return ATMT.NewStateRequested(f, self, *args, **kargs) + + state_wrapper.func_name = "%s_wrapper" % f.func_name + state_wrapper.atmt_type = ATMT.STATE + state_wrapper.atmt_state = f.func_name + state_wrapper.atmt_initial = initial + state_wrapper.atmt_final = final + state_wrapper.atmt_error = error + state_wrapper.atmt_origfunc = f + return state_wrapper + return deco + @staticmethod + def action(cond, prio=0): + def deco(f,cond=cond): + if not hasattr(f,"atmt_type"): + f.atmt_cond = {} + f.atmt_type = ATMT.ACTION + f.atmt_cond[cond.atmt_condname] = prio + return f + return deco + @staticmethod + def condition(state, prio=0): + def deco(f, state=state): + f.atmt_type = ATMT.CONDITION + f.atmt_state = state.atmt_state + f.atmt_condname = f.func_name + f.atmt_prio = prio + return f + return deco + @staticmethod + def receive_condition(state, prio=0): + def deco(f, state=state): + f.atmt_type = ATMT.RECV + f.atmt_state = state.atmt_state + f.atmt_condname = f.func_name + f.atmt_prio = prio + return f + return deco + @staticmethod + def timeout(state, timeout): + def deco(f, state=state, timeout=timeout): + f.atmt_type = ATMT.TIMEOUT + f.atmt_state = state.atmt_state + f.atmt_timeout = timeout + f.atmt_condname = f.func_name + return f + return deco + + +class Automaton_metaclass(type): + def __new__(cls, name, bases, dct): + cls = super(Automaton_metaclass, cls).__new__(cls, name, bases, dct) + cls.states={} + cls.state = None + cls.recv_conditions={} + cls.conditions={} + cls.timeout={} + cls.actions={} + cls.initial_states=[] + + members = {} + classes = [cls] + while classes: + c = classes.pop(0) # order is important to avoid breaking method overloading + classes += list(c.__bases__) + for k,v in c.__dict__.iteritems(): + if k not in members: + members[k] = v + + decorated = [v for v in members.itervalues() + if type(v) is types.FunctionType and hasattr(v, "atmt_type")] + + for m in decorated: + if m.atmt_type == ATMT.STATE: + s = m.atmt_state + cls.states[s] = m + cls.recv_conditions[s]=[] + cls.conditions[s]=[] + cls.timeout[s]=[] + if m.atmt_initial: + cls.initial_states.append(m) + elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT]: + cls.actions[m.atmt_condname] = [] + + for m in decorated: + if m.atmt_type == ATMT.CONDITION: + cls.conditions[m.atmt_state].append(m) + elif m.atmt_type == ATMT.RECV: + cls.recv_conditions[m.atmt_state].append(m) + elif m.atmt_type == ATMT.TIMEOUT: + cls.timeout[m.atmt_state].append((m.atmt_timeout, m)) + elif m.atmt_type == ATMT.ACTION: + for c in m.atmt_cond: + cls.actions[c].append(m) + + + for v in cls.timeout.itervalues(): + v.sort(lambda (t1,f1),(t2,f2): cmp(t1,t2)) + v.append((None, None)) + for v in itertools.chain(cls.conditions.itervalues(), + cls.recv_conditions.itervalues()): + v.sort(lambda c1,c2: cmp(c1.atmt_prio,c2.atmt_prio)) + for condname,actlst in cls.actions.iteritems(): + actlst.sort(lambda c1,c2: cmp(c1.atmt_cond[condname], c2.atmt_cond[condname])) + + return cls + + + def graph(self, **kargs): + s = 'digraph "%s" {\n' % self.__class__.__name__ + + se = "" # Keep initial nodes at the begining for better rendering + for st in self.states.itervalues(): + if st.atmt_initial: + se = ('\t"%s" [ style=filled, fillcolor=blue, shape=box, root=true];\n' % st.atmt_state)+se + elif st.atmt_final: + se += '\t"%s" [ style=filled, fillcolor=green, shape=octagon ];\n' % st.atmt_state + elif st.atmt_error: + se += '\t"%s" [ style=filled, fillcolor=red, shape=octagon ];\n' % st.atmt_state + s += se + + for st in self.states.values(): + for n in st.atmt_origfunc.func_code.co_names+st.atmt_origfunc.func_code.co_consts: + if n in self.states: + s += '\t"%s" -> "%s" [ color=green ];\n' % (st.atmt_state,n) + + + for c,k,v in [("purple",k,v) for k,v in self.conditions.items()]+[("red",k,v) for k,v in self.recv_conditions.items()]: + for f in v: + for n in f.func_code.co_names+f.func_code.co_consts: + if n in self.states: + l = f.atmt_condname + for x in self.actions[f.atmt_condname]: + l += "\\l>[%s]" % x.func_name + s += '\t"%s" -> "%s" [label="%s", color=%s];\n' % (k,n,l,c) + for k,v in self.timeout.iteritems(): + for t,f in v: + if f is None: + continue + for n in f.func_code.co_names+f.func_code.co_consts: + if n in self.states: + l = "%s/%.1fs" % (f.atmt_condname,t) + for x in self.actions[f.atmt_condname]: + l += "\\l>[%s]" % x.func_name + s += '\t"%s" -> "%s" [label="%s",color=blue];\n' % (k,n,l) + s += "}\n" + return do_graph(s, **kargs) + + + +class Automaton: + __metaclass__ = Automaton_metaclass + + def __init__(self, *args, **kargs): + self.debug_level=0 + self.init_args=args + self.init_kargs=kargs + self.parse_args(*args, **kargs) + + def debug(self, lvl, msg): + if self.debug_level >= lvl: + log_interactive.debug(msg) + + + + + class ErrorState(Exception): + def __init__(self, msg, result=None): + Exception.__init__(self, msg) + self.result = result + class Stuck(ErrorState): + pass + + def parse_args(self, debug=0, store=1, **kargs): + self.debug_level=debug + self.socket_kargs = kargs + self.store_packets = store + + + def master_filter(self, pkt): + return True + + def run_condition(self, cond, *args, **kargs): + try: + cond(self,*args, **kargs) + except ATMT.NewStateRequested, state_req: + self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state)) + if cond.atmt_type == ATMT.RECV: + self.packets.append(args[0]) + for action in self.actions[cond.atmt_condname]: + self.debug(2, " + Running action [%s]" % action.func_name) + action(self, *state_req.action_args, **state_req.action_kargs) + raise + else: + self.debug(2, "%s [%s] not taken" % (cond.atmt_type, cond.atmt_condname)) + + + def run(self, *args, **kargs): + # Update default parameters + a = args+self.init_args[len(args):] + k = self.init_kargs + k.update(kargs) + self.parse_args(*a,**k) + + # Start the automaton + self.state=self.initial_states[0](self) + self.send_sock = conf.L3socket() + l = conf.L2listen(**self.socket_kargs) + self.packets = PacketList(name="session[%s]"%self.__class__.__name__) + while 1: + try: + self.debug(1, "## state=[%s]" % self.state.state) + + # Entering a new state. First, call new state function + state_output = self.state.run() + if self.state.error: + raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), result=state_output) + if self.state.final: + return state_output + + if state_output is None: + state_output = () + elif type(state_output) is not list: + state_output = state_output, + + # Then check immediate conditions + for cond in self.conditions[self.state.state]: + self.run_condition(cond, *state_output) + + # If still there and no conditions left, we are stuck! + if ( len(self.recv_conditions[self.state.state]) == 0 + and len(self.timeout[self.state.state]) == 1 ): + raise self.Stuck("stuck in [%s]" % self.state.state,result=state_output) + + # Finally listen and pay attention to timeouts + expirations = iter(self.timeout[self.state.state]) + next_timeout,timeout_func = expirations.next() + t0 = time.time() + + while 1: + t = time.time()-t0 + if next_timeout is not None: + if next_timeout <= t: + self.run_condition(timeout_func, *state_output) + next_timeout,timeout_func = expirations.next() + if next_timeout is None: + remain = None + else: + remain = next_timeout-t + + r,_,_ = select([l],[],[],remain) + if l in r: + pkt = l.recv(MTU) + if pkt is not None: + if self.master_filter(pkt): + self.debug(3, "RECVD: %s" % pkt.summary()) + for rcvcond in self.recv_conditions[self.state.state]: + self.run_condition(rcvcond, pkt, *state_output) + else: + self.debug(4, "FILTR: %s" % pkt.summary()) + + except ATMT.NewStateRequested,state_req: + self.debug(2, "switching from [%s] to [%s]" % (self.state.state,state_req.state)) + self.state = state_req + except KeyboardInterrupt: + self.debug(1,"Interrupted by user") + break + + def my_send(self, pkt): + self.send_sock.send(pkt) + + def send(self, pkt): + self.my_send(pkt) + self.debug(3,"SENT : %s" % pkt.summary()) + self.packets.append(pkt.copy()) + + + diff --git a/scapy/autorun.py b/scapy/autorun.py new file mode 100644 index 0000000000000000000000000000000000000000..4fc93fd774c2a6868d78fc02a3fa71f1f6877a78 --- /dev/null +++ b/scapy/autorun.py @@ -0,0 +1,129 @@ +import code,sys +from error import Scapy_Exception + +######################### +##### Autorun stuff ##### +######################### + +class StopAutorun(Scapy_Exception): + code_run = "" + +class ScapyAutorunInterpreter(code.InteractiveInterpreter): + def __init__(self, *args, **kargs): + code.InteractiveInterpreter.__init__(self, *args, **kargs) + self.error = 0 + def showsyntaxerror(self, *args, **kargs): + self.error = 1 + return code.InteractiveInterpreter.showsyntaxerror(self, *args, **kargs) + def showtraceback(self, *args, **kargs): + self.error = 1 + exc_type, exc_value, exc_tb = sys.exc_info() + if isinstance(exc_value, StopAutorun): + raise exc_value + return code.InteractiveInterpreter.showtraceback(self, *args, **kargs) + + +def autorun_commands(cmds,my_globals=None,verb=0): + sv = conf.verb + import __builtin__ + try: + try: + if my_globals is None: + my_globals = globals() + conf.verb = verb + interp = ScapyAutorunInterpreter(my_globals) + cmd = "" + cmds = cmds.splitlines() + cmds.append("") # ensure we finish multiline commands + cmds.reverse() + __builtin__.__dict__["_"] = None + while 1: + if cmd: + sys.stderr.write(sys.__dict__.get("ps2","... ")) + else: + sys.stderr.write(str(sys.__dict__.get("ps1",ColorPrompt()))) + + l = cmds.pop() + print l + cmd += "\n"+l + if interp.runsource(cmd): + continue + if interp.error: + return 0 + cmd = "" + if len(cmds) <= 1: + break + except SystemExit: + pass + finally: + conf.verb = sv + return _ + +def autorun_get_interactive_session(cmds, **kargs): + class StringWriter: + def __init__(self): + self.s = "" + def write(self, x): + self.s += x + + sw = StringWriter() + sstdout,sstderr = sys.stdout,sys.stderr + try: + try: + sys.stdout = sys.stderr = sw + res = autorun_commands(cmds, **kargs) + except StopAutorun,e: + e.code_run = sw.s + raise + finally: + sys.stdout,sys.stderr = sstdout,sstderr + return sw.s,res + +def autorun_get_text_interactive_session(cmds, **kargs): + ct = conf.color_theme + try: + conf.color_theme = NoTheme() + s,res = autorun_get_interactive_session(cmds, **kargs) + finally: + conf.color_theme = ct + return s,res + +def autorun_get_ansi_interactive_session(cmds, **kargs): + ct = conf.color_theme + try: + conf.color_theme = DefaultTheme() + s,res = autorun_get_interactive_session(cmds, **kargs) + finally: + conf.color_theme = ct + return s,res + +def autorun_get_html_interactive_session(cmds, **kargs): + ct = conf.color_theme + to_html = lambda s: s.replace("<","<").replace(">",">").replace("#[#","<").replace("#]#",">") + try: + try: + conf.color_theme = HTMLTheme2() + s,res = autorun_get_interactive_session(cmds, **kargs) + except StopAutorun,e: + e.code_run = to_html(e.code_run) + raise + finally: + conf.color_theme = ct + + return to_html(s),res + +def autorun_get_latex_interactive_session(cmds, **kargs): + ct = conf.color_theme + to_latex = lambda s: tex_escape(s).replace("@[@","{").replace("@]@","}").replace("@`@","\\") + try: + try: + conf.color_theme = LatexTheme2() + s,res = autorun_get_interactive_session(cmds, **kargs) + except StopAutorun,e: + e.code_run = to_latex(e.code_run) + raise + finally: + conf.color_theme = ct + return to_latex(s),res + + diff --git a/scapy/ber.py b/scapy/ber.py new file mode 100644 index 0000000000000000000000000000000000000000..6d0e748726a5059a86ed3f2886a08a6c91e34517 --- /dev/null +++ b/scapy/ber.py @@ -0,0 +1,300 @@ +from asn1 import ASN1_Decoding_Error,ASN1_BadTag_Decoding_Error,ASN1_Codecs,ASN1_Class_UNIVERSAL + +################## +## BER encoding ## +################## + + + +#####[ BER tools ]##### + + +class BER_Exception(Exception): + pass + +class BER_Decoding_Error(ASN1_Decoding_Error): + def __init__(self, msg, decoded=None, remaining=None): + Exception.__init__(self, msg) + self.remaining = remaining + self.decoded = decoded + def __str__(self): + s = Exception.__str__(self) + if isinstance(self.decoded, BERcodec_Object): + s+="\n### Already decoded ###\n%s" % self.decoded.strshow() + else: + s+="\n### Already decoded ###\n%r" % self.decoded + s+="\n### Remaining ###\n%r" % self.remaining + return s + +class BER_BadTag_Decoding_Error(BER_Decoding_Error, ASN1_BadTag_Decoding_Error): + pass + +def BER_len_enc(l, size=0): + if l <= 127 and size==0: + return chr(l) + s = "" + while l or size>0: + s = chr(l&0xff)+s + l >>= 8L + size -= 1 + if len(s) > 127: + raise BER_Exception("BER_len_enc: Length too long (%i) to be encoded [%r]" % (len(s),s)) + return chr(len(s)|0x80)+s +def BER_len_dec(s): + l = ord(s[0]) + if not l & 0x80: + return l,s[1:] + l &= 0x7f + if len(s) <= l: + raise BER_Decoding_Error("BER_len_dec: Got %i bytes while expecting %i" % (len(s)-1, l),remaining=s) + ll = 0L + for c in s[1:l+1]: + ll <<= 8L + ll |= ord(c) + return ll,s[l+1:] + +def BER_num_enc(l, size=1): + x=[] + while l or size>0: + x.insert(0, l & 0x7f) + if len(x) > 1: + x[0] |= 0x80 + l >>= 7 + size -= 1 + return "".join([chr(k) for k in x]) +def BER_num_dec(s): + x = 0 + for i in range(len(s)): + c = ord(s[i]) + x <<= 7 + x |= c&0x7f + if not c&0x80: + break + if c&0x80: + raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s) + return x, s[i+1:] + +#####[ BER classes ]##### + +class BERcodec_metaclass(type): + def __new__(cls, name, bases, dct): + c = super(BERcodec_metaclass, cls).__new__(cls, name, bases, dct) + try: + c.tag.register(c.codec, c) + except: + warning("Error registering %r for %r" % (c.tag, c.codec)) + return c + + +class BERcodec_Object: + __metaclass__ = BERcodec_metaclass + codec = ASN1_Codecs.BER + tag = ASN1_Class_UNIVERSAL.ANY + + @classmethod + def asn1_object(cls, val): + return cls.tag.asn1_object(val) + + @classmethod + def check_string(cls, s): + if not s: + raise BER_Decoding_Error("%s: Got empty object while expecting tag %r" % + (cls.__name__,cls.tag), remaining=s) + @classmethod + def check_type(cls, s): + cls.check_string(s) + if cls.tag != ord(s[0]): + raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" % + (cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s) + return s[1:] + @classmethod + def check_type_get_len(cls, s): + s2 = cls.check_type(s) + if not s2: + raise BER_Decoding_Error("%s: No bytes while expecting a length" % + cls.__name__, remaining=s) + return BER_len_dec(s2) + @classmethod + def check_type_check_len(cls, s): + l,s3 = cls.check_type_get_len(s) + if len(s3) < l: + raise BER_Decoding_Error("%s: Got %i bytes while expecting %i" % + (cls.__name__, len(s3), l), remaining=s) + return l,s3[:l],s3[l:] + + @classmethod + def do_dec(cls, s, context=None, safe=False): + if context is None: + context = cls.tag.context + cls.check_string(s) + p = ord(s[0]) + if p not in context: + t = s + if len(t) > 18: + t = t[:15]+"..." + raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s) + codec = context[p].get_codec(ASN1_Codecs.BER) + return codec.dec(s,context,safe) + + @classmethod + def dec(cls, s, context=None, safe=False): + if not safe: + return cls.do_dec(s, context, safe) + try: + return cls.do_dec(s, context, safe) + except BER_BadTag_Decoding_Error,e: + o,remain = BERcodec_Object.dec(e.remaining, context, safe) + return ASN1_BADTAG(o),remain + except BER_Decoding_Error, e: + return ASN1_DECODING_ERROR(s, exc=e),"" + except ASN1_Error, e: + return ASN1_DECODING_ERROR(s, exc=e),"" + + @classmethod + def safedec(cls, s, context=None): + return cls.dec(s, context, safe=True) + + + @classmethod + def enc(cls, s): + if type(s) is str: + return BERcodec_STRING.enc(s) + else: + return BERcodec_INTEGER.enc(int(s)) + + + +ASN1_Codecs.BER.register_stem(BERcodec_Object) + + +class BERcodec_INTEGER(BERcodec_Object): + tag = ASN1_Class_UNIVERSAL.INTEGER + @classmethod + def enc(cls, i): + s = [] + while 1: + s.append(i&0xff) + if -127 <= i < 0: + break + if 128 <= i <= 255: + s.append(0) + i >>= 8 + if not i: + break + s = map(chr, s) + s.append(BER_len_enc(len(s))) + s.append(chr(cls.tag)) + s.reverse() + return "".join(s) + @classmethod + def do_dec(cls, s, context=None, safe=False): + l,s,t = cls.check_type_check_len(s) + x = 0L + if s: + if ord(s[0])&0x80: # negative int + x = -1L + for c in s: + x <<= 8 + x |= ord(c) + return cls.asn1_object(x),t + + +class BERcodec_BOOLEAN(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.BOOLEAN + +class BERcodec_NULL(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.NULL + @classmethod + def enc(cls, i): + if i == 0: + return chr(cls.tag)+"\0" + else: + return super(cls,cls).enc(i) + +class BERcodec_STRING(BERcodec_Object): + tag = ASN1_Class_UNIVERSAL.STRING + @classmethod + def enc(cls,s): + return chr(cls.tag)+BER_len_enc(len(s))+s + @classmethod + def do_dec(cls, s, context=None, safe=False): + l,s,t = cls.check_type_check_len(s) + return cls.tag.asn1_object(s),t + +class BERcodec_BIT_STRING(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.BIT_STRING + +class BERcodec_PRINTABLE_STRING(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING + +class BERcodec_T61_STRING (BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.T61_STRING + +class BERcodec_IA5_STRING(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.IA5_STRING + +class BERcodec_UTC_TIME(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.UTC_TIME + +class BERcodec_TIME_TICKS(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.TIME_TICKS + +class BERcodec_COUNTER32(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.COUNTER32 + +class BERcodec_SEQUENCE(BERcodec_Object): + tag = ASN1_Class_UNIVERSAL.SEQUENCE + @classmethod + def enc(cls, l): + if type(l) is not str: + l = "".join(map(lambda x: x.enc(cls.codec), l)) + return chr(cls.tag)+BER_len_enc(len(l))+l + @classmethod + def do_dec(cls, s, context=None, safe=False): + if context is None: + context = cls.tag.context + l,st = cls.check_type_get_len(s) # we may have len(s) < l + s,t = st[:l],st[l:] + obj = [] + while s: + try: + o,s = BERcodec_Object.dec(s, context, safe) + except BER_Decoding_Error, err: + err.remaining += t + if err.decoded is not None: + obj.append(err.decoded) + err.decoded = obj + raise + obj.append(o) + if len(st) < l: + raise BER_Decoding_Error("Not enough bytes to decode sequence", decoded=obj) + return cls.asn1_object(obj),t + +class BERcodec_SET(BERcodec_SEQUENCE): + tag = ASN1_Class_UNIVERSAL.SET + + +class BERcodec_OID(BERcodec_Object): + tag = ASN1_Class_UNIVERSAL.OID + + @classmethod + def enc(cls, oid): + lst = [int(x) for x in oid.strip(".").split(".")] + if len(lst) >= 2: + lst[1] += 40*lst[0] + del(lst[0]) + s = "".join([BER_num_enc(k) for k in lst]) + return chr(cls.tag)+BER_len_enc(len(s))+s + @classmethod + def do_dec(cls, s, context=None, safe=False): + l,s,t = cls.check_type_check_len(s) + lst = [] + while s: + l,s = BER_num_dec(s) + lst.append(l) + if (len(lst) > 0): + lst.insert(0,lst[0]/40) + lst[1] %= 40 + return cls.asn1_object(".".join([str(k) for k in lst])), t + + diff --git a/scapy/config.py b/scapy/config.py new file mode 100644 index 0000000000000000000000000000000000000000..59a37ac6f31dd82cb641bc8fb461a325fa469492 --- /dev/null +++ b/scapy/config.py @@ -0,0 +1,167 @@ +import os +from arch import * +from data import * +from themes import * + +############ +## Config ## +############ + +class ConfClass: + def configure(self, cnf): + self.__dict__ = cnf.__dict__.copy() + def __repr__(self): + return str(self) + def __str__(self): + s="Version = %s\n" % VERSION + keys = self.__class__.__dict__.copy() + keys.update(self.__dict__) + keys = keys.keys() + keys.sort() + for i in keys: + if i[0] != "_": + s += "%-10s = %s\n" % (i, repr(getattr(self, i))) + return s[:-1] + +class ProgPath(ConfClass): + pdfreader = "acroread" + psreader = "gv" + dot = "dot" + display = "display" + tcpdump = "tcpdump" + tcpreplay = "tcpreplay" + hexedit = "hexer" + wireshark = "wireshark" + +class Resolve: + def __init__(self): + self.fields = {} + def add(self, *flds): + for fld in flds: + self.fields[fld]=None + def remove(self, *flds): + for fld in flds: + if fld in self.fields: + del(self.fields[fld]) + def __contains__(self, elt): + return elt in self.fields + def __repr__(self): + return "<Resolve [%s]>" % " ".join(str(x) for x in self.fields) + + +class Num2Layer: + def __init__(self): + self.num2layer = {} + self.layer2num = {} + + def register(self, num, layer): + self.register_num2layer(num, layer) + self.register_layer2num(num, layer) + + def register_num2layer(self, num, layer): + self.num2layer[num] = layer + def register_layer2num(self, num, layer): + self.layer2num[layer] = num + + def __getitem__(self, item): + if isinstance(item, Packet_metaclass): + return self.layer2num[item] + return self.num2layer[item] + def __contains__(self, item): + if isinstance(item, Packet_metaclass): + return item in self.layer2num + return item in self.num2layer + def get(self, item, default=None): + if item in self: + return self[item] + return default + + def __repr__(self): + lst = [] + for num,layer in self.num2layer.iteritems(): + if layer in self.layer2num and self.layer2num[layer] == num: + dir = "<->" + else: + dir = " ->" + lst.append((num,"%#6x %s %-20s (%s)" % (num,dir,layer.__name__,layer.name))) + for layer,num in self.layer2num.iteritems(): + if num not in self.num2layer or self.num2layer[num] != layer: + lst.append((num,"%#6x <- %-20s (%s)" % (num,layer.__name__,layer.name))) + lst.sort() + return "\n".join(y for x,y in lst) + + + + +class Conf(ConfClass): + """This object contains the configuration of scapy. +session : filename where the session will be saved +stealth : if 1, prevents any unwanted packet to go out (ARP, DNS, ...) +checkIPID: if 0, doesn't check that IPID matches between IP sent and ICMP IP citation received + if 1, checks that they either are equal or byte swapped equals (bug in some IP stacks) + if 2, strictly checks that they are equals +checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks) +check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation +iff : selects the default output interface for srp() and sendp(). default:"eth0") +verb : level of verbosity, from 0 (almost mute) to 3 (verbose) +promisc : default mode for listening socket (to get answers if you spoof on a lan) +sniff_promisc : default mode for sniff() +filter : bpf filter added to every sniffing socket to exclude traffic from analysis +histfile : history file +padding : includes padding in desassembled packets +except_filter : BPF filter for packets to ignore +debug_match : when 1, store received packet that are not matched into debug.recv +route : holds the Scapy routing table and provides methods to manipulate it +warning_threshold : how much time between warnings from the same place +ASN1_default_codec: Codec used by default for ASN1 objects +mib : holds MIB direct access dictionnary +resolve : holds list of fields for which resolution should be done +noenum : holds list of enum fields for which conversion to string should NOT be done +AS_resolver: choose the AS resolver class to use +extensions_paths: path or list of paths where extensions are to be looked for +""" + session = "" + stealth = "not implemented" + iface = get_working_if() + checkIPID = 0 + checkIPsrc = 1 + checkIPaddr = 1 + check_TCPerror_seqack = 0 + verb = 2 + prompt = ">>> " + promisc = 1 + sniff_promisc = 1 + l2types = Num2Layer() + l3types = Num2Layer() + L3socket = None + L2socket = None + L2listen = None + histfile = os.path.join(os.environ["HOME"], ".scapy_history") + padding = 1 + p0f_base ="/etc/p0f/p0f.fp" + queso_base ="/etc/queso.conf" + nmap_base ="/usr/share/nmap/nmap-os-fingerprints" + IPCountry_base = "GeoIPCountry4Scapy.gz" + countryLoc_base = "countryLoc.csv" + gnuplot_world = "world.dat" + except_filter = "" + debug_match = 0 + wepkey = "" + route = None # Filed by arch + auto_fragment = 1 + debug_dissector = 0 + color_theme = DefaultTheme() + warning_threshold = 5 + prog = ProgPath() + resolve = Resolve() + noenum = Resolve() + ethertypes = ETHER_TYPES + protocols = IP_PROTOS + services_tcp = TCP_SERVICES + services_udp = UDP_SERVICES + extensions_paths = "." + manufdb = load_manuf("/usr/share/wireshark/wireshark/manuf") + +conf=Conf() + + diff --git a/scapy/dadict.py b/scapy/dadict.py new file mode 100644 index 0000000000000000000000000000000000000000..2f1cee20bd021a6e7ba192ff0b98f423c9f16b46 --- /dev/null +++ b/scapy/dadict.py @@ -0,0 +1,78 @@ +from scapy.error import Scapy_Exception + +############################### +## Direct Access dictionnary ## +############################### + +def fixname(x): + if x and x[0] in "0123456789": + x = "n_"+x + return x.translate("________________________________________________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz_____________________________________________________________________________________________________________________________________") + + +class DADict_Exception(Scapy_Exception): + pass + +class DADict: + def __init__(self, _name="DADict", **kargs): + self._name=_name + self.__dict__.update(kargs) + def fixname(self,val): + return fixname(val) + def __contains__(self, val): + return val in self.__dict__ + def __getitem__(self, attr): + return getattr(self, attr) + def __setitem__(self, attr, val): + return setattr(self, self.fixname(attr), val) + def __iter__(self): + return iter(map(lambda (x,y):y,filter(lambda (x,y):x and x[0]!="_", self.__dict__.items()))) + def _show(self): + for k in self.__dict__.keys(): + if k and k[0] != "_": + print "%10s = %r" % (k,getattr(self,k)) + def __repr__(self): + return "<%s/ %s>" % (self._name," ".join(filter(lambda x:x and x[0]!="_",self.__dict__.keys()))) + + def _branch(self, br, uniq=0): + if uniq and br._name in self: + raise DADict_Exception("DADict: [%s] already branched in [%s]" % (br._name, self._name)) + self[br._name] = br + + def _my_find(self, *args, **kargs): + if args and self._name not in args: + return False + for k in kargs: + if k not in self or self[k] != kargs[k]: + return False + return True + + def _find(self, *args, **kargs): + return self._recurs_find((), *args, **kargs) + def _recurs_find(self, path, *args, **kargs): + if self in path: + return None + if self._my_find(*args, **kargs): + return self + for o in self: + if isinstance(o, DADict): + p = o._recurs_find(path+(self,), *args, **kargs) + if p is not None: + return p + return None + def _find_all(self, *args, **kargs): + return self._recurs_find_all((), *args, **kargs) + def _recurs_find_all(self, path, *args, **kargs): + r = [] + if self in path: + return r + if self._my_find(*args, **kargs): + r.append(self) + for o in self: + if isinstance(o, DADict): + p = o._recurs_find_all(path+(self,), *args, **kargs) + r += p + return r + def keys(self): + return filter(lambda x:x and x[0]!="_", self.__dict__.keys()) + diff --git a/scapy/data.py b/scapy/data.py new file mode 100644 index 0000000000000000000000000000000000000000..889b66c502c2c8b961cd3c1f7d2129acbf8f5f25 --- /dev/null +++ b/scapy/data.py @@ -0,0 +1,230 @@ +import re +from dadict import DADict + +############ +## Consts ## +############ + +ETHER_ANY = "\x00"*6 +ETHER_BROADCAST = "\xff"*6 + +ETH_P_ALL = 3 +ETH_P_IP = 0x800 +ETH_P_ARP = 0x806 + +# From net/if_arp.h +ARPHDR_ETHER = 1 +ARPHDR_METRICOM = 23 +ARPHDR_PPP = 512 +ARPHDR_LOOPBACK = 772 +ARPHDR_TUN = 65534 + +# From bits/ioctls.h +SIOCGIFHWADDR = 0x8927 # Get hardware address +SIOCGIFADDR = 0x8915 # get PA address +SIOCGIFNETMASK = 0x891b # get network PA mask +SIOCGIFNAME = 0x8910 # get iface name +SIOCSIFLINK = 0x8911 # set iface channel +SIOCGIFCONF = 0x8912 # get iface list +SIOCGIFFLAGS = 0x8913 # get flags +SIOCSIFFLAGS = 0x8914 # set flags +SIOCGIFINDEX = 0x8933 # name -> if_index mapping +SIOCGIFCOUNT = 0x8938 # get number of devices +SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval) + + +# From if.h +IFF_UP = 0x1 # Interface is up. +IFF_BROADCAST = 0x2 # Broadcast address valid. +IFF_DEBUG = 0x4 # Turn on debugging. +IFF_LOOPBACK = 0x8 # Is a loopback net. +IFF_POINTOPOINT = 0x10 # Interface is point-to-point link. +IFF_NOTRAILERS = 0x20 # Avoid use of trailers. +IFF_RUNNING = 0x40 # Resources allocated. +IFF_NOARP = 0x80 # No address resolution protocol. +IFF_PROMISC = 0x100 # Receive all packets. + + + +# From netpacket/packet.h +PACKET_ADD_MEMBERSHIP = 1 +PACKET_DROP_MEMBERSHIP = 2 +PACKET_RECV_OUTPUT = 3 +PACKET_RX_RING = 5 +PACKET_STATISTICS = 6 +PACKET_MR_MULTICAST = 0 +PACKET_MR_PROMISC = 1 +PACKET_MR_ALLMULTI = 2 + + +# From bits/socket.h +SOL_PACKET = 263 +# From asm/socket.h +SO_ATTACH_FILTER = 26 +SOL_SOCKET = 1 + +# From net/route.h +RTF_UP = 0x0001 # Route usable +RTF_REJECT = 0x0200 + +# From BSD net/bpf.h +#BIOCIMMEDIATE=0x80044270 +BIOCIMMEDIATE=-2147204496 + +MTU = 1600 + + +# file parsing to get some values : + +def load_protocols(filename): + spaces = re.compile("[ \t]+|\n") + dct = DADict(_name=filename) + try: + for l in open(filename): + try: + shrp = l.find("#") + if shrp >= 0: + l = l[:shrp] + l = l.strip() + if not l: + continue + lt = tuple(re.split(spaces, l)) + if len(lt) < 2 or not lt[0]: + continue + dct[lt[0]] = int(lt[1]) + except Exception,e: + log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) + except IOError: + log_loading.info("Can't open /etc/protocols file") + return dct + +IP_PROTOS=load_protocols("/etc/protocols") + +def load_ethertypes(filename): + spaces = re.compile("[ \t]+|\n") + dct = DADict(_name=filename) + try: + f=open(filename) + for l in f: + try: + shrp = l.find("#") + if shrp >= 0: + l = l[:shrp] + l = l.strip() + if not l: + continue + lt = tuple(re.split(spaces, l)) + if len(lt) < 2 or not lt[0]: + continue + dct[lt[0]] = int(lt[1], 16) + except Exception,e: + log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) + f.close() + except IOError,msg: + pass + return dct + +ETHER_TYPES=load_ethertypes("/etc/ethertypes") + +def load_services(filename): + spaces = re.compile("[ \t]+|\n") + tdct=DADict(_name="%s-tcp"%filename) + udct=DADict(_name="%s-udp"%filename) + try: + f=open(filename) + for l in f: + try: + shrp = l.find("#") + if shrp >= 0: + l = l[:shrp] + l = l.strip() + if not l: + continue + lt = tuple(re.split(spaces, l)) + if len(lt) < 2 or not lt[0]: + continue + if lt[1].endswith("/tcp"): + tdct[lt[0]] = int(lt[1].split('/')[0]) + elif lt[1].endswith("/udp"): + udct[lt[0]] = int(lt[1].split('/')[0]) + except Exception,e: + log_loading.warning("Couldn't file [%s]: line [%r] (%s)" % (filename,l,e)) + f.close() + except IOError: + log_loading.info("Can't open /etc/services file") + return tdct,udct + +TCP_SERVICES,UDP_SERVICES=load_services("/etc/services") + +class ManufDA(DADict): + def fixname(self, val): + return val + def _get_manuf_couple(self, mac): + oui = ":".join(mac.split(":")[:3]).upper() + return self.__dict__.get(oui,(mac,mac)) + def _get_manuf(self, mac): + return self._get_manuf_couple(mac)[1] + def _get_short_manuf(self, mac): + return self._get_manuf_couple(mac)[0] + def _resolve_MAC(self, mac): + oui = ":".join(mac.split(":")[:3]).upper() + if oui in self: + return ":".join([self[oui][0]]+ mac.split(":")[3:]) + return mac + + + + +def load_manuf(filename): + try: + manufdb=ManufDA(_name=filename) + for l in open(filename): + try: + l = l.strip() + if not l or l.startswith("#"): + continue + oui,shrt=l.split()[:2] + i = l.find("#") + if i < 0: + lng=shrt + else: + lng = l[i+2:] + manufdb[oui] = shrt,lng + except Exception,e: + log_loading.warning("Couldn't parse one line from [%s] [%r] (%s)" % (filename, l, e)) + except IOError: + #log_loading.warning("Couldn't open [%s] file" % filename) + pass + return manufdb + + + + + +##################### +## knowledge bases ## +##################### + +class KnowledgeBase: + def __init__(self, filename): + self.filename = filename + self.base = None + + def lazy_init(self): + self.base = "" + + def reload(self, filename = None): + if filename is not None: + self.filename = filename + oldbase = self.base + self.base = None + self.lazy_init() + if self.base is None: + self.base = oldbase + + def get_base(self): + if self.base is None: + self.lazy_init() + return self.base + + diff --git a/scapy/error.py b/scapy/error.py new file mode 100644 index 0000000000000000000000000000000000000000..c67ba14257764235e5a5a23f6c0529fa0f32b0fd --- /dev/null +++ b/scapy/error.py @@ -0,0 +1,51 @@ + +############################# +##### Logging subsystem ##### +############################# + +class Scapy_Exception(Exception): + pass + +import logging,traceback,time + +class ScapyFreqFilter(logging.Filter): + def __init__(self): + logging.Filter.__init__(self) + self.warning_table = {} + def filter(self, record): + wt = conf.warning_threshold + if wt > 0: + stk = traceback.extract_stack() + caller=None + for f,l,n,c in stk: + if n == 'warning': + break + caller = l + tm,nb = self.warning_table.get(caller, (0,0)) + ltm = time.time() + if ltm-tm > wt: + tm = ltm + nb = 0 + else: + if nb < 2: + nb += 1 + if nb == 2: + record.msg = "more "+record.msg + else: + return 0 + self.warning_table[caller] = (tm,nb) + return 1 + +log_scapy = logging.getLogger("scapy") +console_handler = logging.StreamHandler() +console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) +log_scapy.addHandler(console_handler) +log_runtime = logging.getLogger("scapy.runtime") # logs at runtime +log_runtime.addFilter(ScapyFreqFilter()) +log_interactive = logging.getLogger("scapy.interactive") # logs in interactive functions +log_loading = logging.getLogger("scapy.loading") # logs when loading scapy + +if __name__ == "__main__": + log_scapy.setLevel(1) + + diff --git a/scapy/fields.py b/scapy/fields.py new file mode 100644 index 0000000000000000000000000000000000000000..ba7d6074f88931780abf29047afe7c73ace18489 --- /dev/null +++ b/scapy/fields.py @@ -0,0 +1,939 @@ +import struct,copy,socket +from data import * +from utils import * + +############ +## Fields ## +############ + +class Field: + """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""" + if x is None: + x = 0 + 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], Packet): + x[i] = x[i].copy() + return x + def __repr__(self): + return "<Field (%s).%s>" % (",".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: + 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: + _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: + 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: + """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 addfield(self, pkt, s, val): + sval = self._fld.addfield(pkt, "", val) + return s+sval+struct.pack("%is" % (-len(sval)%self._align), self._padwith) + + def __getattr__(self, attr): + return getattr(self._fld,attr) + + +class MACField(Field): + 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 DestMACField(MACField): + def __init__(self, name): + MACField.__init__(self, name, None) + def i2h(self, pkt, x): + if x is None: + dstip = None + if isinstance(pkt.payload, IPv6): + dstip = pkt.payload.dst + elif isinstance(pkt.payload, IP): + dstip = pkt.payload.dst + elif isinstance(pkt.payload, ARP): + dstip = pkt.payload.pdst + if isinstance(dstip, Gen): + dstip = dstip.__iter__().next() + if dstip is not None: + if isinstance(pkt.payload, IPv6): + x = getmacbyip6(dstip, chainCC=1) + else: + x = getmacbyip(dstip, chainCC=1) + if x is None: + x = "ff:ff:ff:ff:ff:ff" + warning("Mac address to reach %s not found\n"%dstip) + return MACField.i2h(self, pkt, x) + def i2m(self, pkt, x): + return MACField.i2m(self, pkt, self.i2h(pkt, x)) + +class SourceMACField(MACField): + def __init__(self, name): + MACField.__init__(self, name, None) + def i2h(self, pkt, x): + if x is None: + dstip = None + if isinstance(pkt.payload, IPv6): + dstip = pkt.payload.dst + elif isinstance(pkt.payload, IP): + dstip = pkt.payload.dst + elif isinstance(pkt.payload, ARP): + dstip = pkt.payload.pdst + if isinstance(dstip, Gen): + dstip = dstip.__iter__().next() + if dstip is not None: + if isinstance(pkt.payload, IPv6): + iff,a,nh = conf.route6.route(dstip) + else: + iff,a,gw = conf.route.route(dstip) + try: + x = get_if_hwaddr(iff) + except: + pass + if x is None: + x = "00:00:00:00:00:00" + return MACField.i2h(self, pkt, x) + def i2m(self, pkt, x): + return MACField.i2m(self, pkt, self.i2h(pkt, x)) + +class ARPSourceMACField(MACField): + def __init__(self, name): + MACField.__init__(self, name, None) + def i2h(self, pkt, x): + if x is None: + dstip = pkt.pdst + if isinstance(dstip, Gen): + dstip = dstip.__iter__().next() + if dstip is not None: + iff,a,gw = conf.route.route(dstip) + try: + x = get_if_hwaddr(iff) + except: + pass + if x is None: + x = "00:00:00:00:00:00" + return MACField.i2h(self, pkt, x) + def i2m(self, pkt, x): + return MACField.i2m(self, pkt, self.i2h(pkt, x)) + +class Dot11AddrMACField(MACField): + def is_applicable(self, pkt): + return 1 + def addfield(self, pkt, s, val): + if self.is_applicable(pkt): + return MACField.addfield(self, pkt, s, val) + else: + return s + def getfield(self, pkt, s): + if self.is_applicable(pkt): + return MACField.getfield(self, pkt, s) + else: + return s,None + +class Dot11Addr2MACField(Dot11AddrMACField): + def is_applicable(self, pkt): + if pkt.type == 1: + return pkt.subtype in [ 0xb, 0xa, 0xe, 0xf] # RTS, PS-Poll, CF-End, CF-End+CF-Ack + return 1 + +class Dot11Addr3MACField(Dot11AddrMACField): + def is_applicable(self, pkt): + if pkt.type in [0,2]: + return 1 + return 0 + +class Dot11Addr4MACField(Dot11AddrMACField): + def is_applicable(self, pkt): + if pkt.type == 2: + if pkt.FCfield & 0x3 == 0x3: # To-DS and From-DS are set + return 1 + return 0 + +class IPField(Field): + 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): + 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 = conf.route.route(getattr(pkt,self.dstname)) + 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]: + x=r[0][1] + else: + warning("More than one possible route for %s"%repr(dst)) + return None + else: + iff,x,gw = conf.route.route(dst) + return IPField.i2h(self, pkt, x) + + + + +class ByteField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "B") + +class XByteField(ByteField): + def i2repr(self, pkt, x): + if x is None: + x = 0 + return lhex(self.i2h(pkt, x)) + +class X3BytesField(XByteField): + 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): + def __init__(self, name, default): + Field.__init__(self, name, default, "H") + +class LEShortField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "<H") + +class XShortField(ShortField): + def i2repr(self, pkt, x): + if x is None: + x = 0 + return lhex(self.i2h(pkt, x)) + + +class IntField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "I") + +class SignedIntField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "i") + def randval(self): + return RandSInt() + +class LEIntField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "<I") + +class LESignedIntField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "<i") + def randval(self): + return RandSInt() + +class XIntField(IntField): + def i2repr(self, pkt, x): + if x is None: + x = 0 + return lhex(self.i2h(pkt, x)) + + +class LongField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "Q") + +class XLongField(LongField): + def i2repr(self, pkt, x): + if x is None: + x = 0 + return lhex(self.i2h(pkt, x)) + +class IEEEFloatField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "f") + +class IEEEDoubleField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "d") + +import traceback +def FIELD_LENGTH_MANAGEMENT_DEPRECATION(x): + try: + for tb in traceback.extract_stack()+[("??",-1,None,"")]: + f,l,_,line = tb + if line.startswith("fields_desc"): + break + except: + f,l="??",-1 + log_loading.warning("Deprecated use of %s (%s l. %i). See http://trac.secdev.org/scapy/wiki/LengthFields" % (x,f,l)) + +class StrField(Field): + def __init__(self, name, default, fmt="H", remain=0, shift=0): + Field.__init__(self,name,default,fmt) + self.remain = remain + self.shift = shift + if shift != 0: + FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) + def i2len(self, pkt, i): + return len(i)+self.shift + def i2m(self, pkt, x): + if x is None: + x = "" + return x + def addfield(self, pkt, s, val): + return s+self.i2m(pkt, val) + def getfield(self, pkt, s): + if self.remain == 0: + return "",self.m2i(pkt, s) + else: + return s[-self.remain:],self.m2i(pkt, s[:-self.remain]) + def randval(self): + return RandBin(RandNum(0,1200)) + +class PacketField(StrField): + holds_packets=1 + def __init__(self, name, default, cls, remain=0, shift=0): + StrField.__init__(self, name, default, remain=remain, shift=shift) + self.cls = cls + def i2m(self, pkt, i): + return str(i) + def m2i(self, pkt, m): + return self.cls(m) + def getfield(self, pkt, s): + i = self.m2i(pkt, s) + remain = "" + if i.haslayer(Padding): + r = i.getlayer(Padding) + del(r.underlayer.payload) + remain = r.load + return remain,i + +class PacketLenField(PacketField): + holds_packets=1 + def __init__(self, name, default, cls, fld=None, length_from=None, shift=0): + PacketField.__init__(self, name, default, cls, shift=shift) + self.length_from = length_from + if fld is not None or shift != 0: + FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) + self.count_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift + def getfield(self, pkt, s): + l = self.length_from(pkt) + i = self.m2i(pkt, s[:l]) + return s[l:],i + + +class PacketListField(PacketField): + islist = 1 + holds_packets=1 + def __init__(self, name, default, cls, fld=None, count_from=None, length_from=None, shift=0): + if default is None: + default = [] # Create a new list for each instance + PacketField.__init__(self, name, default, cls, shift=shift) + self.count_from = count_from + self.length_from = length_from + + if fld is not None or shift != 0: + FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) + if fld is not None: + self.count_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift + + def any2i(self, pkt, x): + if type(x) is not list: + return [x] + else: + return x + def i2count(self, pkt, val): + if type(val) is list: + return len(val) + return 1 + def i2len(self, pkt, val): + return sum( len(p) for p in val ) + def do_copy(self, x): + return map(lambda p:p.copy(), x) + 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) + + lst = [] + ret = "" + remain = s + if l is not None: + remain,ret = s[:l],s[l:] + while remain: + if c is not None: + if c <= 0: + break + c -= 1 + p = self.m2i(pkt,remain) + if Padding in p: + pad = p[Padding] + remain = pad.load + del(pad.underlayer.payload) + else: + remain = "" + lst.append(p) + return remain+ret,lst + def addfield(self, pkt, s, val): + return s+"".join(map(str, val)) + + +class StrFixedLenField(StrField): + def __init__(self, name, default, length=None, length_from=None, shift=0): + StrField.__init__(self, name, default, shift=shift) + self.length_from = length_from + if length is not None: + self.length_from = lambda pkt,length=length: length + def getfield(self, pkt, s): + l = self.length_from(pkt) + return s[l:], self.m2i(pkt,s[:l]) + def addfield(self, pkt, s, val): + l = self.length_from(pkt) + return s+struct.pack("%is"%l,self.i2m(pkt, val)) + def randval(self): + try: + l = self.length_from(None) + except: + l = RandNum(0,200) + return RandBin(l) + +class NetBIOSNameField(StrFixedLenField): + def __init__(self, name, default, length=31, shift=0): + StrFixedLenField.__init__(self, name, default, length, shift=shift) + def i2m(self, pkt, x): + l = self.length_from(pkt)/2 + if x is None: + x = "" + x += " "*(l) + x = x[:l] + x = "".join(map(lambda x: chr(0x41+(ord(x)>>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): + def __init__(self, name, default, fld=None, length_from=None, shift=0): + StrField.__init__(self, name, default, shift=shift) + self.length_from = length_from + if fld is not None or shift != 0: + FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) + self.length_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift + def getfield(self, pkt, s): + l = self.length_from(pkt) + return s[l:], self.m2i(pkt,s[:l]) + +class FieldListField(Field): + islist=1 + def __init__(self, name, default, field, fld=None, shift=0, 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 + if fld is not None or shift != 0: + FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) + self.count_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift + + + 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): + 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): + 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): + 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): + def i2m(self, pkt, x): + if x is None: + x = len(pkt.payload) + return x + +class BCDFloatField(Field): + def i2m(self, pkt, x): + return int(256*x) + def m2i(self, pkt, x): + return x/256.0 + +class BitField(Field): + 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<<self.size) - 1) + bitsdone += self.size + while bitsdone >= 8: + bitsdone -= 8 + s = s+struct.pack("!B", v >> bitsdone) + v &= (1L<<bitsdone)-1 + if bitsdone: + return s,bitsdone,v + else: + return s + def getfield(self, pkt, s): + if type(s) is tuple: + s,bn = s + else: + bn = 0 + # we don't want to process all the string + nb_bytes = (self.size+bn-1)/8 + 1 + w = s[:nb_bytes] + + # split the substring byte by byte + bytes = struct.unpack('!%dB' % nb_bytes , w) + + b = 0L + for c in range(nb_bytes): + b |= long(bytes[c]) << (nb_bytes-c-1)*8 + + # get rid of high order bits + b &= (1L << (nb_bytes*8-bn)) - 1 + + # remove low order bits + b = b >> (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): + 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): + def i2repr(self, pkt, x): + return lhex(self.i2h(pkt,x)) + + +class EnumField(Field): + 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): + 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): + 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): + def __init__(self, name, default, enum): + EnumField.__init__(self, name, default, enum, "H") + +class LEShortEnumField(EnumField): + def __init__(self, name, default, enum): + EnumField.__init__(self, name, default, enum, "<H") + +class ByteEnumField(EnumField): + def __init__(self, name, default, enum): + EnumField.__init__(self, name, default, enum, "B") + +class IntEnumField(EnumField): + def __init__(self, name, default, enum): + EnumField.__init__(self, name, default, enum, "I") + +class SignedIntEnumField(EnumField): + def __init__(self, name, default, enum): + EnumField.__init__(self, name, default, enum, "i") + def randval(self): + return RandSInt() + +class LEIntEnumField(EnumField): + def __init__(self, name, default, enum): + EnumField.__init__(self, name, default, enum, "<I") + +class XShortEnumField(ShortEnumField): + 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 lhex(x) + +# Little endian long field +class LELongField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "<Q") + +# Little endian fixed length field +class LEFieldLenField(FieldLenField): + def __init__(self, name, default, length_of=None, fmt = "<H", count_of=None, adjust=lambda pkt,x:x, fld=None): + FieldLenField.__init__(self, name, default, length_of=length_of, fmt=fmt, fld=fld, adjust=adjust) + + +class FlagsField(BitField): + def __init__(self, name, default, size, names): + BitField.__init__(self, name, default, size) + self.multi = type(names) is list + if self.multi: + self.names = map(lambda x:[x], names) + else: + self.names = names + def any2i(self, pkt, x): + if type(x) is str: + if self.multi: + x = map(lambda y:[y], x.split("+")) + y = 0 + for i in x: + y |= 1 << self.names.index(i) + x = y + return x + def i2repr(self, pkt, x): + if type(x) is list or type(x) is tuple: + return repr(x) + if self.multi: + r = [] + else: + r = "" + i=0 + while x: + if x & 1: + r += self.names[i] + i += 1 + x >>= 1 + if self.multi: + r = "+".join(r) + return r + + + + +class FloatField(BitField): + def getfield(self, pkt, s): + s,b = BitField.getfield(self, pkt, s) + + # fraction point between bits 15 and 16. + sec = b >> 16 + frac = b & (1L << (32+1)) - 1 + frac /= 65536.0 + b = sec+frac + return s,b + diff --git a/scapy/geoip.py b/scapy/geoip.py new file mode 100644 index 0000000000000000000000000000000000000000..fec7814e0472665c2e8df50a7a2ca893e611d47a --- /dev/null +++ b/scapy/geoip.py @@ -0,0 +1,64 @@ +from scapy.data import KnowledgeBase +from scapy.config import conf + +########################## +## IP location database ## +########################## + +class IPCountryKnowledgeBase(KnowledgeBase): + """ +How to generate the base : +db = [] +for l in open("GeoIPCountryWhois.csv").readlines(): + s,e,c = l.split(",")[2:5] + db.append((int(s[1:-1]),int(e[1:-1]),c[1:-1])) +cPickle.dump(gzip.open("xxx","w"),db) +""" + def lazy_init(self): + self.base = load_object(self.filename) + + +class CountryLocKnowledgeBase(KnowledgeBase): + def lazy_init(self): + f=open(self.filename) + self.base = {} + while 1: + l = f.readline() + if not l: + break + l = l.strip().split(",") + if len(l) != 3: + continue + c,lat,long = l + + self.base[c] = (float(long),float(lat)) + f.close() + + + + +def locate_ip(ip): + ip=map(int,ip.split(".")) + ip = ip[3]+(ip[2]<<8L)+(ip[1]<<16L)+(ip[0]<<24L) + + cloc = country_loc_kdb.get_base() + db = IP_country_kdb.get_base() + + d=0 + f=len(db)-1 + while (f-d) > 1: + guess = (d+f)/2 + if ip > db[guess][0]: + d = guess + else: + f = guess + s,e,c = db[guess] + if s <= ip and ip <= e: + return cloc.get(c,None) + + + + + +conf.IP_country_kdb = IPCountryKnowledgeBase(conf.IPCountry_base) +conf.country_loc_kdb = CountryLocKnowledgeBase(conf.countryLoc_base) diff --git a/scapy/layers/__init__.py b/scapy/layers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/scapy/layers/bluetooth.py b/scapy/layers/bluetooth.py new file mode 100644 index 0000000000000000000000000000000000000000..d2f9447f379d5a77954bbe540c4db75017710919 --- /dev/null +++ b/scapy/layers/bluetooth.py @@ -0,0 +1,195 @@ +import socket,struct + +from scapy.config import conf +from scapy.packet import * +from scapy.fields import * +#from scapy.supersocket import SuperSocket +#from scapy.layers.l2 import CookedLinux + + +class HCI_Hdr(Packet): + name = "HCI header" + fields_desc = [ ByteEnumField("type",2,{1:"command",2:"ACLdata",3:"SCOdata",4:"event",5:"vendor"}),] + + def mysummary(self): + return self.sprintf("HCI %type%") + +class HCI_ACL_Hdr(Packet): + name = "HCI ACL header" + fields_desc = [ ByteField("handle",0), # Actually, handle is 12 bits and flags is 4. + ByteField("flags",0), # I wait to write a LEBitField + LEShortField("len",None), ] + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-4 + p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] + return p + + +class L2CAP_Hdr(Packet): + name = "L2CAP header" + fields_desc = [ LEShortField("len",None), + LEShortEnumField("cid",0,{1:"control"}),] + + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-4 + p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] + return p + + + +class L2CAP_CmdHdr(Packet): + name = "L2CAP command header" + fields_desc = [ + ByteEnumField("code",8,{1:"rej",2:"conn_req",3:"conn_resp", + 4:"conf_req",5:"conf_resp",6:"disconn_req", + 7:"disconn_resp",8:"echo_req",9:"echo_resp", + 10:"info_req",11:"info_resp"}), + ByteField("id",0), + LEShortField("len",None) ] + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-4 + p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] + return p + def answers(self, other): + if other.id == self.id: + if self.code == 1: + return 1 + if other.code in [2,4,6,8,10] and self.code == other.code+1: + if other.code == 8: + return 1 + return self.payload.answers(other.payload) + return 0 + +class L2CAP_ConnReq(Packet): + name = "L2CAP Conn Req" + fields_desc = [ LEShortEnumField("psm",0,{1:"SDP",3:"RFCOMM",5:"telephony control"}), + LEShortField("scid",0), + ] + +class L2CAP_ConnResp(Packet): + name = "L2CAP Conn Resp" + fields_desc = [ LEShortField("dcid",0), + LEShortField("scid",0), + LEShortEnumField("result",0,["no_info","authen_pend","author_pend"]), + LEShortEnumField("status",0,["success","pend","bad_psm", + "cr_sec_block","cr_no_mem"]), + ] + def answers(self, other): + return self.scid == other.scid + +class L2CAP_CmdRej(Packet): + name = "L2CAP Command Rej" + fields_desc = [ LEShortField("reason",0), + ] + + +class L2CAP_ConfReq(Packet): + name = "L2CAP Conf Req" + fields_desc = [ LEShortField("dcid",0), + LEShortField("flags",0), + ] + +class L2CAP_ConfResp(Packet): + name = "L2CAP Conf Resp" + fields_desc = [ LEShortField("scid",0), + LEShortField("flags",0), + LEShortEnumField("result",0,["success","unaccept","reject","unknown"]), + ] + def answers(self, other): + return self.scid == other.scid + + +class L2CAP_DisconnReq(Packet): + name = "L2CAP Disconn Req" + fields_desc = [ LEShortField("dcid",0), + LEShortField("scid",0), ] + +class L2CAP_DisconnResp(Packet): + name = "L2CAP Disconn Resp" + fields_desc = [ LEShortField("dcid",0), + LEShortField("scid",0), ] + def answers(self, other): + return self.scid == other.scid + + + +class L2CAP_InfoReq(Packet): + name = "L2CAP Info Req" + fields_desc = [ LEShortEnumField("type",0,{1:"CL_MTU",2:"FEAT_MASK"}), + StrField("data","") + ] + + +class L2CAP_InfoResp(Packet): + name = "L2CAP Info Resp" + fields_desc = [ LEShortField("type",0), + LEShortEnumField("result",0,["success","not_supp"]), + StrField("data",""), ] + def answers(self, other): + return self.type == other.type + + + +bind_layers( HCI_Hdr, HCI_ACL_Hdr, type=2) +bind_layers( HCI_Hdr, Raw, ) +bind_layers( HCI_ACL_Hdr, L2CAP_Hdr, ) +bind_layers( L2CAP_Hdr, L2CAP_CmdHdr, cid=1) +bind_layers( L2CAP_CmdHdr, L2CAP_CmdRej, code=1) +bind_layers( L2CAP_CmdHdr, L2CAP_ConnReq, code=2) +bind_layers( L2CAP_CmdHdr, L2CAP_ConnResp, code=3) +bind_layers( L2CAP_CmdHdr, L2CAP_ConfReq, code=4) +bind_layers( L2CAP_CmdHdr, L2CAP_ConfResp, code=5) +bind_layers( L2CAP_CmdHdr, L2CAP_DisconnReq, code=6) +bind_layers( L2CAP_CmdHdr, L2CAP_DisconnResp, code=7) +bind_layers( L2CAP_CmdHdr, L2CAP_InfoReq, code=10) +bind_layers( L2CAP_CmdHdr, L2CAP_InfoResp, code=11) + +class BluetoothL2CAPSocket(SuperSocket): + def __init__(self, peer): + s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, + socket.BTPROTO_L2CAP) + s.connect((peer,0)) + + self.ins = self.outs = s + + def recv(self, x): + return L2CAP_CmdHdr(self.ins.recv(x)) + + +class BluetoothHCISocket(SuperSocket): + def __init__(self, iface=0x10000, type=None): + s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) + s.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR,1) + s.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP,1) + s.setsockopt(socket.SOL_HCI, socket.HCI_FILTER, struct.pack("IIIh2x", 0xffffffffL,0xffffffffL,0xffffffffL,0)) #type mask, event mask, event mask, opcode + s.bind((iface,)) + self.ins = self.outs = s +# s.connect((peer,0)) + + + def recv(self, x): + return HCI_Hdr(self.ins.recv(x)) + +## Bluetooth + + +def srbt(peer, pkts, inter=0.1, *args, **kargs): + s = conf.BTsocket(peer=peer) + a,b,c=sndrcv(s,pkts,inter=inter,*args,**kargs) + s.close() + return a,b + +def srbt1(peer, pkts, *args, **kargs): + a,b = srbt(peer, pkts, *args, **kargs) + if len(a) > 0: + return a[0][1] + + + +conf.BTsocket = BluetoothL2CAPSocket diff --git a/scapy/layers/dhcp.py b/scapy/layers/dhcp.py new file mode 100644 index 0000000000000000000000000000000000000000..84c12da2c2c2c071de22313d9b78ca1398aed3a4 --- /dev/null +++ b/scapy/layers/dhcp.py @@ -0,0 +1,337 @@ +import struct + +from scapy.packet import * +from scapy.fields import * +from scapy.ansmachine import * +from scapy.layers.inet import UDP + +dhcpmagic="c\x82Sc" + + +class BOOTP(Packet): + name = "BOOTP" + fields_desc = [ ByteEnumField("op",1, {1:"BOOTREQUEST", 2:"BOOTREPLY"}), + ByteField("htype",1), + ByteField("hlen",6), + ByteField("hops",0), + IntField("xid",0), + ShortField("secs",0), + FlagsField("flags", 0, 16, "???????????????B"), + IPField("ciaddr","0.0.0.0"), + IPField("yiaddr","0.0.0.0"), + IPField("siaddr","0.0.0.0"), + IPField("giaddr","0.0.0.0"), + Field("chaddr","", "16s"), + Field("sname","","64s"), + Field("file","","128s"), + StrField("options","") ] + def guess_payload_class(self, payload): + if self.options[:len(dhcpmagic)] == dhcpmagic: + return DHCP + else: + return Packet.guess_payload_class(self, payload) + def extract_padding(self,s): + if self.options[:len(dhcpmagic)] == dhcpmagic: + # set BOOTP options to DHCP magic cookie and make rest a payload of DHCP options + payload = self.options[len(dhcpmagic):] + self.options = self.options[:len(dhcpmagic)] + return payload, None + else: + return "", None + def hashret(self): + return struct.pack("L", self.xid) + def answers(self, other): + if not isinstance(other, BOOTP): + return 0 + return self.xid == other.xid + + + +#DHCP_UNKNOWN, DHCP_IP, DHCP_IPLIST, DHCP_TYPE \ +#= range(4) +# + +DHCPTypes = { + 1: "discover", + 2: "offer", + 3: "request", + 4: "decline", + 5: "ack", + 6: "nak", + 7: "release", + 8: "inform", + 9: "force_renew", + 10:"lease_query", + 11:"lease_unassigned", + 12:"lease_unknown", + 13:"lease_active", + } + +DHCPOptions = { + 0: "pad", + 1: IPField("subnet_mask", "0.0.0.0"), + 2: "time_zone", + 3: IPField("router","0.0.0.0"), + 4: IPField("time_server","0.0.0.0"), + 5: IPField("IEN_name_server","0.0.0.0"), + 6: IPField("name_server","0.0.0.0"), + 7: IPField("log_server","0.0.0.0"), + 8: IPField("cookie_server","0.0.0.0"), + 9: IPField("lpr_server","0.0.0.0"), + 12: "hostname", + 14: "dump_path", + 15: "domain", + 17: "root_disk_path", + 22: "max_dgram_reass_size", + 23: "default_ttl", + 24: "pmtu_timeout", + 28: IPField("broadcast_address","0.0.0.0"), + 35: "arp_cache_timeout", + 36: "ether_or_dot3", + 37: "tcp_ttl", + 38: "tcp_keepalive_interval", + 39: "tcp_keepalive_garbage", + 40: "NIS_domain", + 41: IPField("NIS_server","0.0.0.0"), + 42: IPField("NTP_server","0.0.0.0"), + 43: "vendor_specific", + 44: IPField("NetBIOS_server","0.0.0.0"), + 45: IPField("NetBIOS_dist_server","0.0.0.0"), + 50: IPField("requested_addr","0.0.0.0"), + 51: IntField("lease_time", 43200), + 54: IPField("server_id","0.0.0.0"), + 55: "param_req_list", + 57: ShortField("max_dhcp_size", 1500), + 58: IntField("renewal_time", 21600), + 59: IntField("rebinding_time", 37800), + 60: "vendor_class_id", + 61: "client_id", + + 64: "NISplus_domain", + 65: IPField("NISplus_server","0.0.0.0"), + 69: IPField("SMTP_server","0.0.0.0"), + 70: IPField("POP3_server","0.0.0.0"), + 71: IPField("NNTP_server","0.0.0.0"), + 72: IPField("WWW_server","0.0.0.0"), + 73: IPField("Finger_server","0.0.0.0"), + 74: IPField("IRC_server","0.0.0.0"), + 75: IPField("StreetTalk_server","0.0.0.0"), + 76: "StreetTalk_Dir_Assistance", + 82: "relay_agent_Information", + 53: ByteEnumField("message-type", 1, DHCPTypes), + # 55: DHCPRequestListField("request-list"), + 255: "end" + } + +DHCPRevOptions = {} + +for k,v in DHCPOptions.iteritems(): + if type(v) is str: + n = v + v = None + else: + n = v.name + DHCPRevOptions[n] = (k,v) +del(n) +del(v) +del(k) + + + + + +class DHCPOptionsField(StrField): + islist=1 + def i2repr(self,pkt,x): + s = [] + for v in x: + if type(v) is tuple and len(v) in [2,3]: + if DHCPRevOptions.has_key(v[0]) and isinstance(DHCPRevOptions[v[0]][1],Field): + f = DHCPRevOptions[v[0]][1] + vv = f.i2repr(pkt,v[1]) + else: + vv = repr(v[1]) + r = "%s=%s" % (v[0],vv) + if len(v) > 2: + r += " (garbage=%r)" % v[2] + s.append(r) + else: + s.append(sane(v)) + return "[%s]" % (" ".join(s)) + + def getfield(self, pkt, s): + return "", self.m2i(pkt, s) + def m2i(self, pkt, x): + opt = [] + while x: + o = ord(x[0]) + if o == 255: + opt.append("end") + x = x[1:] + continue + if o == 0: + opt.append("pad") + x = x[1:] + continue + if len(x) < 2 or len(x) < ord(x[1])+2: + opt.append(x) + break + elif DHCPOptions.has_key(o): + f = DHCPOptions[o] + + if isinstance(f, str): + olen = ord(x[1]) + opt.append( (f,x[2:olen+2]) ) + x = x[olen+2:] + else: + olen = ord(x[1]) + try: + left, val = f.getfield(pkt,x[2:olen+2]) + except: + opt.append(x) + break + if left: + otuple = (f.name, val, left) + else: + otuple = (f.name, val) + opt.append(otuple) + x = x[olen+2:] + else: + olen = ord(x[1]) + opt.append((o, x[2:olen+2])) + x = x[olen+2:] + return opt + def i2m(self, pkt, x): + if type(x) is str: + return x + s = "" + for o in x: + if type(o) is tuple and len(o) in [2,3]: + name, val = o[:2] + + if isinstance(name, int): + onum, oval = name, val + elif DHCPRevOptions.has_key(name): + onum, f = DHCPRevOptions[name] + if f is None: + oval = val + else: + oval = f.addfield(pkt,"",f.any2i(pkt,val)) + else: + warning("Unknown field option %s" % name) + continue + if len(o) > 2: + oval += o[2] + + s += chr(onum) + s += chr(len(oval)) + s += oval + + elif (type(o) is str and DHCPRevOptions.has_key(o) and + DHCPRevOptions[o][1] == None): + s += chr(DHCPRevOptions[o][0]) + elif type(o) is int: + s += chr(o)+"\0" + elif type(o) is str: + s += o + else: + warning("Malformed option %s" % o) + return s + + +class DHCP(Packet): + name = "DHCP options" + fields_desc = [ DHCPOptionsField("options","") ] + + +bind_layers( UDP, BOOTP, dport=67, sport=68) +bind_layers( UDP, BOOTP, dport=68, sport=67) +bind_layers( BOOTP, DHCP, options='c\x82Sc') +def dhcp_request(iface=None,**kargs): + if conf.checkIPaddr != 0: + warning("conf.checkIPaddr is not 0, I may not be able to match the answer") + if iface is None: + iface = conf.iface + fam,hw = get_if_raw_hwaddr(iface) + return srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67) + /BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]),iface=iface,**kargs) + + +class BOOTP_am(AnsweringMachine): + function_name = "bootpd" + filter = "udp and port 68 and port 67" + send_function = staticmethod(sendp) + def parse_options(self, pool=Net("192.168.1.128/25"), network="192.168.1.0/24",gw="192.168.1.1", + domain="localnet", renewal_time=60, lease_time=1800): + if type(pool) is str: + poom = Net(pool) + self.domain = domain + netw,msk = (network.split("/")+["32"])[:2] + msk = itom(int(msk)) + self.netmask = ltoa(msk) + self.network = ltoa(atol(netw)&msk) + self.broadcast = ltoa( atol(self.network) | (0xffffffff&~msk) ) + self.gw = gw + if isinstance(pool,Gen): + pool = [k for k in pool if k not in [gw, self.network, self.broadcast]] + pool.reverse() + if len(pool) == 1: + pool, = pool + self.pool = pool + self.lease_time = lease_time + self.renewal_time = renewal_time + self.leases = {} + + def is_request(self, req): + if not req.haslayer(BOOTP): + return 0 + reqb = req.getlayer(BOOTP) + if reqb.op != 1: + return 0 + return 1 + + def print_reply(self, req, reply): + print "Reply %s to %s" % (reply.getlayer(IP).dst,reply.dst) + + def make_reply(self, req): + mac = req.src + if type(self.pool) is list: + if not self.leases.has_key(mac): + self.leases[mac] = self.pool.pop() + ip = self.leases[mac] + else: + ip = self.pool + + repb = req.getlayer(BOOTP).copy() + repb.op="BOOTREPLY" + repb.yiaddr = ip + repb.siaddr = self.gw + repb.ciaddr = self.gw + repb.giaddr = self.gw + del(repb.payload) + rep=Ether(dst=mac)/IP(dst=ip)/UDP(sport=req.dport,dport=req.sport)/repb + return rep + + +class DHCP_am(BOOTP_am): + function_name="dhcpd" + def make_reply(self, req): + resp = BOOTP_am.make_reply(self, req) + if DHCP in req: + dhcp_options = [(op[0],{1:2,3:5}.get(op[1],op[1])) + for op in req[DHCP].options + if type(op) is tuple and op[0] == "message-type"] + dhcp_options += [("server_id",self.gw), + ("domain", self.domain), + ("router", self.gw), + ("name_server", self.gw), + ("broadcast_address", self.broadcast), + ("subnet_mask", self.netmask), + ("renewal_time", self.renewal_time), + ("lease_time", self.lease_time), + "end" + ] + resp /= DHCP(options=dhcp_options) + return resp + + diff --git a/scapy/layers/dns.py b/scapy/layers/dns.py new file mode 100644 index 0000000000000000000000000000000000000000..c880700e3a42ebba3095d5a412433569f2392375 --- /dev/null +++ b/scapy/layers/dns.py @@ -0,0 +1,316 @@ +import socket,struct + +from scapy.packet import * +from scapy.fields import * +from scapy.ansmachine import * +from scapy.layers.inet import UDP + +class DNSStrField(StrField): + def i2m(self, pkt, x): + x = [k[:63] for k in x.split(".")] # Truncate chunks that cannont be encoded (more than 63 bytes..) + x = map(lambda y: chr(len(y))+y, x) + x = "".join(x) + if x[-1] != "\x00": + x += "\x00" + return x + def getfield(self, pkt, s): + n = "" + while 1: + l = ord(s[0]) + s = s[1:] + if not l: + break + if l & 0xc0: + raise Scapy_Exception("DNS message can't be compressed at this point!") + else: + n += s[:l]+"." + s = s[l:] + return s, n + + +class DNSRRCountField(ShortField): + holds_packets=1 + def __init__(self, name, default, rr): + ShortField.__init__(self, name, default) + self.rr = rr + def _countRR(self, pkt): + x = getattr(pkt,self.rr) + i = 0 + while isinstance(x, DNSRR) or isinstance(x, DNSQR): + x = x.payload + i += 1 + return i + + def i2m(self, pkt, x): + if x is None: + x = self._countRR(pkt) + return x + def i2h(self, pkt, x): + if x is None: + x = self._countRR(pkt) + return x + + +def DNSgetstr(s,p): + name = "" + q = 0 + jpath = [p] + while 1: + if p >= len(s): + warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s))) + break + l = ord(s[p]) + p += 1 + if l & 0xc0: + if not q: + q = p+1 + if p >= len(s): + warning("DNS incomplete jump token at (ofs=%i)" % p) + break + p = ((l & 0x3f) << 8) + ord(s[p]) - 12 + if p in jpath: + warning("DNS decompression loop detected") + break + jpath.append(p) + continue + elif l > 0: + name += s[p:p+l]+"." + p += l + continue + break + if q: + p = q + return name,p + + +class DNSRRField(StrField): + holds_packets=1 + def __init__(self, name, countfld, passon=1): + StrField.__init__(self, name, None) + self.countfld = countfld + self.passon = passon + def i2m(self, pkt, x): + if x is None: + return "" + return str(x) + def decodeRR(self, name, s, p): + ret = s[p:p+10] + type,cls,ttl,rdlen = struct.unpack("!HHIH", ret) + p += 10 + rr = DNSRR("\x00"+ret+s[p:p+rdlen]) + if rr.type in [2, 3, 4, 5]: + rr.rdata = DNSgetstr(s,p)[0] + del(rr.rdlen) + + p += rdlen + + rr.rrname = name + return rr,p + def getfield(self, pkt, s): + if type(s) is tuple : + s,p = s + else: + p = 0 + ret = None + c = getattr(pkt, self.countfld) + if c > len(s): + warning("wrong value: DNS.%s=%i" % (self.countfld,c)) + return s,"" + while c: + c -= 1 + name,p = DNSgetstr(s,p) + rr,p = self.decodeRR(name, s, p) + if ret is None: + ret = rr + else: + ret.add_payload(rr) + if self.passon: + return (s,p),ret + else: + return s[p:],ret + + +class DNSQRField(DNSRRField): + holds_packets=1 + def decodeRR(self, name, s, p): + ret = s[p:p+4] + p += 4 + rr = DNSQR("\x00"+ret) + rr.qname = name + return rr,p + + + +class RDataField(StrLenField): + def m2i(self, pkt, s): + family = None + if pkt.type == 1: + family = socket.AF_INET + elif pkt.type == 28: + family = socket.AF_INET6 + elif pkt.type == 12: + s = DNSgetstr(s, 0)[0] + if family is not None: + s = inet_ntop(family, s) + return s + def i2m(self, pkt, s): + if pkt.type == 1: + if s: + s = inet_aton(s) + elif pkt.type == 28: + if s: + s = inet_pton(socket.AF_INET6, s) + elif pkt.type in [2,3,4,5]: + s = "".join(map(lambda x: chr(len(x))+x, s.split("."))) + if ord(s[-1]): + s += "\x00" + return s + +class RDLenField(Field): + def __init__(self, name): + Field.__init__(self, name, None, "H") + def i2m(self, pkt, x): + if x is None: + rdataf = pkt.get_field("rdata") + x = len(rdataf.i2m(pkt, pkt.rdata)) + return x + def i2h(self, pkt, x): + if x is None: + rdataf = pkt.get_field("rdata") + x = len(rdataf.i2m(pkt, pkt.rdata)) + return x + + +class DNS(Packet): + name = "DNS" + fields_desc = [ ShortField("id",0), + BitField("qr",0, 1), + BitEnumField("opcode", 0, 4, {0:"QUERY",1:"IQUERY",2:"STATUS"}), + BitField("aa", 0, 1), + BitField("tc", 0, 1), + BitField("rd", 0, 1), + BitField("ra", 0 ,1), + BitField("z", 0, 3), + BitEnumField("rcode", 0, 4, {0:"ok", 1:"format-error", 2:"server-failure", 3:"name-error", 4:"not-implemented", 5:"refused"}), + DNSRRCountField("qdcount", None, "qd"), + DNSRRCountField("ancount", None, "an"), + DNSRRCountField("nscount", None, "ns"), + DNSRRCountField("arcount", None, "ar"), + DNSQRField("qd", "qdcount"), + DNSRRField("an", "ancount"), + DNSRRField("ns", "nscount"), + DNSRRField("ar", "arcount",0) ] + def answers(self, other): + return (isinstance(other, DNS) + and self.id == other.id + and self.qr == 1 + and other.qr == 0) + + def mysummary(self): + type = ["Qry","Ans"][self.qr] + name = "" + if self.qr: + type = "Ans" + if self.ancount > 0 and isinstance(self.an, DNSRR): + name = ' "%s"' % self.an.rdata + else: + type = "Qry" + if self.qdcount > 0 and isinstance(self.qd, DNSQR): + name = ' "%s"' % self.qd.qname + return 'DNS %s%s ' % (type, name) + +dnstypes = { 0:"ANY", 255:"ALL", + 1:"A", 2:"NS", 3:"MD", 4:"MD", 5:"CNAME", 6:"SOA", 7: "MB", 8:"MG", + 9:"MR",10:"NULL",11:"WKS",12:"PTR",13:"HINFO",14:"MINFO",15:"MX",16:"TXT", + 17:"RP",18:"AFSDB",28:"AAAA", 33:"SRV",38:"A6",39:"DNAME"} + +dnsqtypes = {251:"IXFR",252:"AXFR",253:"MAILB",254:"MAILA",255:"ALL"} +dnsqtypes.update(dnstypes) +dnsclasses = {1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY'} + + +class DNSQR(Packet): + name = "DNS Question Record" + show_indent=0 + fields_desc = [ DNSStrField("qname",""), + ShortEnumField("qtype", 1, dnsqtypes), + ShortEnumField("qclass", 1, dnsclasses) ] + + + +class DNSRR(Packet): + name = "DNS Resource Record" + show_indent=0 + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 1, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + RDLenField("rdlen"), + RDataField("rdata", "", length_from=lambda pkt:pkt.rdlen) ] + +bind_layers( UDP, DNS, dport=53) +bind_layers( UDP, DNS, sport=53) +def dyndns_add(nameserver, name, rdata, type="A", ttl=10): + """Send a DNS add message to a nameserver for "name" to have a new "rdata" +dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok) + +example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1") +RFC2136 +""" + zone = name[name.find(".")+1:] + r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, + qd=[DNSQR(qname=zone, qtype="SOA")], + ns=[DNSRR(rrname=name, type="A", + ttl=ttl, rdata=rdata)]), + verbose=0, timeout=5) + if r and r.haslayer(DNS): + return r.getlayer(DNS).rcode + else: + return -1 + + + + +def dyndns_del(nameserver, name, type="ALL", ttl=10): + """Send a DNS delete message to a nameserver for "name" +dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok) + +example: dyndns_del("ns1.toto.com", "dyn.toto.com") +RFC2136 +""" + zone = name[name.find(".")+1:] + r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, + qd=[DNSQR(qname=zone, qtype="SOA")], + ns=[DNSRR(rrname=name, type=type, + rclass="ANY", ttl=0, rdata="")]), + verbose=0, timeout=5) + if r and r.haslayer(DNS): + return r.getlayer(DNS).rcode + else: + return -1 + + +class DNS_am(AnsweringMachine): + function_name="dns_spoof" + filter = "udp port 53" + + def parse_options(self, joker="192.168.1.1", match=None): + if match is None: + self.match = {} + else: + self.match = match + self.joker=joker + + def is_request(self, req): + return req.haslayer(DNS) and req.getlayer(DNS).qr == 0 + + def make_reply(self, req): + ip = req.getlayer(IP) + dns = req.getlayer(DNS) + resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport) + rdata = self.match.get(dns.qd.qname, self.joker) + resp /= DNS(id=dns.id, qr=1, qd=dns.qd, + an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata)) + return resp + + diff --git a/scapy/layers/dot11.py b/scapy/layers/dot11.py new file mode 100644 index 0000000000000000000000000000000000000000..b09058fd2ffe77c896d20675ed5571823f36d842 --- /dev/null +++ b/scapy/layers/dot11.py @@ -0,0 +1,469 @@ +import re,struct + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.l2 import * + +class PrismHeader(Packet): + """ iwpriv wlan0 monitor 3 """ + name = "Prism header" + fields_desc = [ LEIntField("msgcode",68), + LEIntField("len",144), + StrFixedLenField("dev","",16), + LEIntField("hosttime_did",0), + LEShortField("hosttime_status",0), + LEShortField("hosttime_len",0), + LEIntField("hosttime",0), + LEIntField("mactime_did",0), + LEShortField("mactime_status",0), + LEShortField("mactime_len",0), + LEIntField("mactime",0), + LEIntField("channel_did",0), + LEShortField("channel_status",0), + LEShortField("channel_len",0), + LEIntField("channel",0), + LEIntField("rssi_did",0), + LEShortField("rssi_status",0), + LEShortField("rssi_len",0), + LEIntField("rssi",0), + LEIntField("sq_did",0), + LEShortField("sq_status",0), + LEShortField("sq_len",0), + LEIntField("sq",0), + LEIntField("signal_did",0), + LEShortField("signal_status",0), + LEShortField("signal_len",0), + LESignedIntField("signal",0), + LEIntField("noise_did",0), + LEShortField("noise_status",0), + LEShortField("noise_len",0), + LEIntField("noise",0), + LEIntField("rate_did",0), + LEShortField("rate_status",0), + LEShortField("rate_len",0), + LEIntField("rate",0), + LEIntField("istx_did",0), + LEShortField("istx_status",0), + LEShortField("istx_len",0), + LEIntField("istx",0), + LEIntField("frmlen_did",0), + LEShortField("frmlen_status",0), + LEShortField("frmlen_len",0), + LEIntField("frmlen",0), + ] + def answers(self, other): + if isinstance(other, PrismHeader): + return self.payload.answers(other.payload) + else: + return self.payload.answers(other) + +class RadioTap(Packet): + name = "RadioTap dummy" + fields_desc = [ ByteField('version', 0), + ByteField('pad', 0), + FieldLenField('len', None, 'notdecoded', '@H', adjust=lambda pkt,x:x+8), + FlagsField('present', None, -32, ['TSFT','Flags','Rate','Channel','FHSS','dBm_AntSignal', + 'dBm_AntNoise','Lock_Quality','TX_Attenuation','dB_TX_Attenuation', + 'dBm_TX_Power', 'Antenna', 'dB_AntSignal', 'dB_AntNoise', + 'b14', 'b15','b16','b17','b18','b19','b20','b21','b22','b23', + 'b24','b25','b26','b27','b28','b29','b30','Ext']), + StrLenField('notdecoded', "", length_from= lambda pkt:pkt.len-8) ] + + + +class Dot11SCField(LEShortField): + def is_applicable(self, pkt): + return pkt.type != 1 # control frame + def addfield(self, pkt, s, val): + if self.is_applicable(pkt): + return LEShortField.addfield(self, pkt, s, val) + else: + return s + def getfield(self, pkt, s): + if self.is_applicable(pkt): + return LEShortField.getfield(self, pkt, s) + else: + return s,None + +class Dot11(Packet): + name = "802.11" + fields_desc = [ + BitField("subtype", 0, 4), + BitEnumField("type", 0, 2, ["Management", "Control", "Data", "Reserved"]), + BitField("proto", 0, 2), + FlagsField("FCfield", 0, 8, ["to-DS", "from-DS", "MF", "retry", "pw-mgt", "MD", "wep", "order"]), + ShortField("ID",0), + MACField("addr1", ETHER_ANY), + Dot11Addr2MACField("addr2", ETHER_ANY), + Dot11Addr3MACField("addr3", ETHER_ANY), + Dot11SCField("SC", 0), + Dot11Addr4MACField("addr4", ETHER_ANY) + ] + def mysummary(self): + return self.sprintf("802.11 %Dot11.type% %Dot11.subtype% %Dot11.addr2% > %Dot11.addr1%") + def guess_payload_class(self, payload): + if self.type == 0x02 and (self.subtype >= 0x08 and self.subtype <=0xF and self.subtype != 0xD): + return Dot11QoS + elif self.FCfield & 0x40: + return Dot11WEP + else: + return Packet.guess_payload_class(self, payload) + def answers(self, other): + if isinstance(other,Dot11): + if self.type == 0: # management + if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA + return 0 + if (other.subtype,self.subtype) in [(0,1),(2,3),(4,5)]: + return 1 + if self.subtype == other.subtype == 11: # auth + return self.payload.answers(other.payload) + elif self.type == 1: # control + return 0 + elif self.type == 2: # data + return self.payload.answers(other.payload) + elif self.type == 3: # reserved + return 0 + return 0 + def unwep(self, key=None, warn=1): + if self.FCfield & 0x40 == 0: + if warn: + warning("No WEP to remove") + return + if isinstance(self.payload.payload, NoPayload): + if key or conf.wepkey: + self.payload.decrypt(key) + if isinstance(self.payload.payload, NoPayload): + if warn: + warning("Dot11 can't be decrypted. Check conf.wepkey.") + return + self.FCfield &= ~0x40 + self.payload=self.payload.payload + + +class Dot11QoS(Packet): + name = "802.11 QoS" + fields_desc = [ BitField("TID",None,4), + BitField("EOSP",None,1), + BitField("Ack Policy",None,2), + BitField("Reserved",None,1), + ByteField("TXOP",None) ] + def guess_payload_class(self, payload): + if isinstance(self.underlayer, Dot11): + if self.underlayer.FCfield & 0x40: + return Dot11WEP + return Packet.guess_payload_class(self, payload) + + +capability_list = [ "res8", "res9", "short-slot", "res11", + "res12", "DSSS-OFDM", "res14", "res15", + "ESS", "IBSS", "CFP", "CFP-req", + "privacy", "short-preamble", "PBCC", "agility"] + +reason_code = {0:"reserved",1:"unspec", 2:"auth-expired", + 3:"deauth-ST-leaving", + 4:"inactivity", 5:"AP-full", 6:"class2-from-nonauth", + 7:"class3-from-nonass", 8:"disas-ST-leaving", + 9:"ST-not-auth"} + +status_code = {0:"success", 1:"failure", 10:"cannot-support-all-cap", + 11:"inexist-asso", 12:"asso-denied", 13:"algo-unsupported", + 14:"bad-seq-num", 15:"challenge-failure", + 16:"timeout", 17:"AP-full",18:"rate-unsupported" } + +class Dot11Beacon(Packet): + name = "802.11 Beacon" + fields_desc = [ LELongField("timestamp", 0), + LEShortField("beacon_interval", 0x0064), + FlagsField("cap", 0, 16, capability_list) ] + + +class Dot11Elt(Packet): + name = "802.11 Information Element" + fields_desc = [ ByteEnumField("ID", 0, {0:"SSID", 1:"Rates", 2: "FHset", 3:"DSset", 4:"CFset", 5:"TIM", 6:"IBSSset", 16:"challenge", + 42:"ERPinfo", 46:"QoS Capability", 47:"ERPinfo", 48:"RSNinfo", 50:"ESRates",221:"vendor",68:"reserved"}), + FieldLenField("len", None, "info", "B"), + StrLenField("info", "", length_from=lambda x:x.len) ] + def mysummary(self): + if self.ID == 0: + return "SSID=%s"%repr(self.info),[Dot11] + else: + return "" + +class Dot11ATIM(Packet): + name = "802.11 ATIM" + +class Dot11Disas(Packet): + name = "802.11 Disassociation" + fields_desc = [ LEShortEnumField("reason", 1, reason_code) ] + +class Dot11AssoReq(Packet): + name = "802.11 Association Request" + fields_desc = [ FlagsField("cap", 0, 16, capability_list), + LEShortField("listen_interval", 0x00c8) ] + + +class Dot11AssoResp(Packet): + name = "802.11 Association Response" + fields_desc = [ FlagsField("cap", 0, 16, capability_list), + LEShortField("status", 0), + LEShortField("AID", 0) ] + +class Dot11ReassoReq(Packet): + name = "802.11 Reassociation Request" + fields_desc = [ FlagsField("cap", 0, 16, capability_list), + MACField("current_AP", ETHER_ANY), + LEShortField("listen_interval", 0x00c8) ] + + +class Dot11ReassoResp(Dot11AssoResp): + name = "802.11 Reassociation Response" + +class Dot11ProbeReq(Packet): + name = "802.11 Probe Request" + +class Dot11ProbeResp(Packet): + name = "802.11 Probe Response" + fields_desc = [ LELongField("timestamp", 0), + LEShortField("beacon_interval", 0x0064), + FlagsField("cap", 0, 16, capability_list) ] + +class Dot11Auth(Packet): + name = "802.11 Authentication" + fields_desc = [ LEShortEnumField("algo", 0, ["open", "sharedkey"]), + LEShortField("seqnum", 0), + LEShortEnumField("status", 0, status_code) ] + def answers(self, other): + if self.seqnum == other.seqnum+1: + return 1 + return 0 + +class Dot11Deauth(Packet): + name = "802.11 Deauthentication" + fields_desc = [ LEShortEnumField("reason", 1, reason_code) ] + + + +class Dot11WEP(Packet): + name = "802.11 WEP packet" + fields_desc = [ StrFixedLenField("iv", "\0\0\0", 3), + ByteField("keyid", 0), + StrField("wepdata",None,remain=4), + IntField("icv",None) ] + + def post_dissect(self, s): +# self.icv, = struct.unpack("!I",self.wepdata[-4:]) +# self.wepdata = self.wepdata[:-4] + self.decrypt() + + def build_payload(self): + if self.wepdata is None: + return Packet.build_payload(self) + return "" + + def post_build(self, p, pay): + if self.wepdata is None: + key = conf.wepkey + if key: + if self.icv is None: + pay += struct.pack("<I",crc32(pay)) + icv = "" + else: + icv = p[4:8] + c = ARC4.new(self.iv+key) + p = p[:4]+c.encrypt(pay)+icv + else: + warning("No WEP key set (conf.wepkey).. strange results expected..") + return p + + + def decrypt(self,key=None): + if key is None: + key = conf.wepkey + if key: + c = ARC4.new(self.iv+key) + self.add_payload(LLC(c.decrypt(self.wepdata))) + + +bind_layers( PrismHeader, Dot11, ) +bind_layers( RadioTap, Dot11, ) +bind_layers( Dot11, LLC, type=2) +bind_layers( Dot11QoS, LLC, ) +bind_layers( Dot11, Dot11AssoReq, subtype=0, type=0) +bind_layers( Dot11, Dot11AssoResp, subtype=1, type=0) +bind_layers( Dot11, Dot11ReassoReq, subtype=2, type=0) +bind_layers( Dot11, Dot11ReassoResp, subtype=3, type=0) +bind_layers( Dot11, Dot11ProbeReq, subtype=4, type=0) +bind_layers( Dot11, Dot11ProbeResp, subtype=5, type=0) +bind_layers( Dot11, Dot11Beacon, subtype=8, type=0) +bind_layers( Dot11, Dot11ATIM, subtype=9, type=0) +bind_layers( Dot11, Dot11Disas, subtype=10, type=0) +bind_layers( Dot11, Dot11Auth, subtype=11, type=0) +bind_layers( Dot11, Dot11Deauth, subtype=12, type=0) +bind_layers( Dot11Beacon, Dot11Elt, ) +bind_layers( Dot11AssoReq, Dot11Elt, ) +bind_layers( Dot11AssoResp, Dot11Elt, ) +bind_layers( Dot11ReassoReq, Dot11Elt, ) +bind_layers( Dot11ReassoResp, Dot11Elt, ) +bind_layers( Dot11ProbeReq, Dot11Elt, ) +bind_layers( Dot11ProbeResp, Dot11Elt, ) +bind_layers( Dot11Auth, Dot11Elt, ) +bind_layers( Dot11Elt, Dot11Elt, ) + + +conf.l2types.register(801, Dot11) +conf.l2types.register_num2layer(105, Dot11) +conf.l2types.register(802, PrismHeader) +conf.l2types.register(803, RadioTap) +conf.l2types.register_num2layer(127, RadioTap) + + +class WiFi_am(AnsweringMachine): + """Before using this, initialize "iffrom" and "ifto" interfaces: +iwconfig iffrom mode monitor +iwpriv orig_ifto hostapd 1 +ifconfig ifto up +note: if ifto=wlan0ap then orig_ifto=wlan0 +note: ifto and iffrom must be set on the same channel +ex: +ifconfig eth1 up +iwconfig eth1 mode monitor +iwconfig eth1 channel 11 +iwpriv wlan0 hostapd 1 +ifconfig wlan0ap up +iwconfig wlan0 channel 11 +iwconfig wlan0 essid dontexist +iwconfig wlan0 mode managed +""" + function_name = "airpwn" + filter = None + + def parse_options(iffrom, ifto, replace, pattern="", ignorepattern=""): + self.iffrom = iffrom + self.ifto = ifto + ptrn = re.compile(pattern) + iptrn = re.compile(ignorepattern) + + def is_request(self, pkt): + if not isinstance(pkt,Dot11): + return 0 + if not pkt.FCfield & 1: + return 0 + if not pkt.haslayer(TCP): + return 0 + ip = pkt.getlayer(IP) + tcp = pkt.getlayer(TCP) + pay = str(tcp.payload) + if not self.ptrn.match(pay): + return 0 + if self.iptrn.match(pay): + return 0 + + def make_reply(self, p): + ip = p.getlayer(IP) + tcp = p.getlayer(TCP) + pay = str(tcp.payload) + del(p.payload.payload.payload) + p.FCfield="from-DS" + p.addr1,p.addr2 = p.addr2,p.addr1 + p /= IP(src=ip.dst,dst=ip.src) + p /= TCP(sport=tcp.dport, dport=tcp.sport, + seq=tcp.ack, ack=tcp.seq+len(pay), + flags="PA") + q = p.copy() + p /= self.replace + q.ID += 1 + q.getlayer(TCP).flags="RA" + q.getlayer(TCP).seq+=len(replace) + return [p,q] + + def print_reply(self): + print p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%") + + def send_reply(self, reply): + sendp(reply, iface=self.ifto, **self.optsend) + + def sniff(self): + sniff(iface=self.iffrom, **self.optsniff) + + + +plst=[] +def get_toDS(): + global plst + while 1: + p,=sniff(iface="eth1",count=1) + if not isinstance(p,Dot11): + continue + if p.FCfield & 1: + plst.append(p) + print "." + + +# if not ifto.endswith("ap"): +# print "iwpriv %s hostapd 1" % ifto +# os.system("iwpriv %s hostapd 1" % ifto) +# ifto += "ap" +# +# os.system("iwconfig %s mode monitor" % iffrom) +# + +def airpwn(iffrom, ifto, replace, pattern="", ignorepattern=""): + """Before using this, initialize "iffrom" and "ifto" interfaces: +iwconfig iffrom mode monitor +iwpriv orig_ifto hostapd 1 +ifconfig ifto up +note: if ifto=wlan0ap then orig_ifto=wlan0 +note: ifto and iffrom must be set on the same channel +ex: +ifconfig eth1 up +iwconfig eth1 mode monitor +iwconfig eth1 channel 11 +iwpriv wlan0 hostapd 1 +ifconfig wlan0ap up +iwconfig wlan0 channel 11 +iwconfig wlan0 essid dontexist +iwconfig wlan0 mode managed +""" + + ptrn = re.compile(pattern) + iptrn = re.compile(ignorepattern) + def do_airpwn(p, ifto=ifto, replace=replace, ptrn=ptrn, iptrn=iptrn): + if not isinstance(p,Dot11): + return + if not p.FCfield & 1: + return + if not p.haslayer(TCP): + return + ip = p.getlayer(IP) + tcp = p.getlayer(TCP) + pay = str(tcp.payload) +# print "got tcp" + if not ptrn.match(pay): + return +# print "match 1" + if iptrn.match(pay): + return +# print "match 2" + del(p.payload.payload.payload) + p.FCfield="from-DS" + p.addr1,p.addr2 = p.addr2,p.addr1 + q = p.copy() + p /= IP(src=ip.dst,dst=ip.src) + p /= TCP(sport=tcp.dport, dport=tcp.sport, + seq=tcp.ack, ack=tcp.seq+len(pay), + flags="PA") + q = p.copy() + p /= replace + q.ID += 1 + q.getlayer(TCP).flags="RA" + q.getlayer(TCP).seq+=len(replace) + + sendp([p,q], iface=ifto, verbose=0) +# print "send",repr(p) +# print "send",repr(q) + print p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%") + + sniff(iface=iffrom,prn=do_airpwn) + + + diff --git a/scapy/layers/gprs.py b/scapy/layers/gprs.py new file mode 100644 index 0000000000000000000000000000000000000000..8686fcc1917a843d55b9b1bd0eadecc84d15e201 --- /dev/null +++ b/scapy/layers/gprs.py @@ -0,0 +1,8 @@ +class GPRS(Packet): + name = "GPRSdummy" + fields_desc = [ + StrStopField("dummy","","\x65\x00\x00",1) + ] + + +bind_layers( GPRS, IP, ) diff --git a/scapy/layers/hsrp.py b/scapy/layers/hsrp.py new file mode 100644 index 0000000000000000000000000000000000000000..b8b57fbdf3b08d5efee08aa6fa084f343fb7d8cf --- /dev/null +++ b/scapy/layers/hsrp.py @@ -0,0 +1,19 @@ +class HSRP(Packet): + name = "HSRP" + fields_desc = [ + ByteField("version", 0), + ByteEnumField("opcode", 0, { 0:"Hello"}), + ByteEnumField("state", 16, { 16:"Active"}), + ByteField("hellotime", 3), + ByteField("holdtime", 10), + ByteField("priority", 120), + ByteField("group", 1), + ByteField("reserved", 0), + StrFixedLenField("auth","cisco",8), + IPField("virtualIP","192.168.1.1") ] + + + + + +bind_layers( UDP, HSRP, dport=1985, sport=1985) diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py new file mode 100644 index 0000000000000000000000000000000000000000..98f47d8e8be3d7adbacca44c8b92b92ed698f285 --- /dev/null +++ b/scapy/layers/inet.py @@ -0,0 +1,818 @@ +import os,time,struct,re,socket +from select import select + +from scapy.layers.l2 import * +from scapy.fields import * +from scapy.volatile import * +from config import conf +from sendrecv import sr,sr1 + + + +#################### +## IP Tools class ## +#################### + +class IPTools: + """Add more powers to a class that have a "src" attribute.""" + def whois(self): + os.system("whois %s" % self.src) + def ottl(self): + t = [32,64,128,255]+[self.ttl] + t.sort() + return t[t.index(self.ttl)+1] + def hops(self): + return self.ottl()-self.ttl-1 + + + +class IPoptionsField(StrField): + def i2m(self, pkt, x): + return x+"\x00"*(3-((len(x)+3)%4)) + def getfield(self, pkt, s): + opsz = (pkt.ihl-5)*4 + if opsz < 0: + warning("bad ihl (%i). Assuming ihl=5"%pkt.ihl) + opsz = 0 + return s[opsz:],s[:opsz] + def randval(self): + return RandBin(RandNum(0,39)) + + +TCPOptions = ( + { 0 : ("EOL",None), + 1 : ("NOP",None), + 2 : ("MSS","!H"), + 3 : ("WScale","!B"), + 4 : ("SAckOK",None), + 5 : ("SAck","!"), + 8 : ("Timestamp","!II"), + 14 : ("AltChkSum","!BH"), + 15 : ("AltChkSumOpt",None) + }, + { "EOL":0, + "NOP":1, + "MSS":2, + "WScale":3, + "SAckOK":4, + "SAck":5, + "Timestamp":8, + "AltChkSum":14, + "AltChkSumOpt":15, + } ) + +class TCPOptionsField(StrField): + islist=1 + def getfield(self, pkt, s): + opsz = (pkt.dataofs-5)*4 + if opsz < 0: + warning("bad dataofs (%i). Assuming dataofs=5"%pkt.dataofs) + opsz = 0 + return s[opsz:],self.m2i(pkt,s[:opsz]) + def m2i(self, pkt, x): + opt = [] + while x: + onum = ord(x[0]) + if onum == 0: + opt.append(("EOL",None)) + x=x[1:] + break + if onum == 1: + opt.append(("NOP",None)) + x=x[1:] + continue + olen = ord(x[1]) + if olen < 2: + warning("Malformed TCP option (announced length is %i)" % olen) + olen = 2 + oval = x[2:olen] + if TCPOptions[0].has_key(onum): + oname, ofmt = TCPOptions[0][onum] + if onum == 5: #SAck + ofmt += "%iI" % (len(oval)/4) + if ofmt and struct.calcsize(ofmt) == len(oval): + oval = struct.unpack(ofmt, oval) + if len(oval) == 1: + oval = oval[0] + opt.append((oname, oval)) + else: + opt.append((onum, oval)) + x = x[olen:] + return opt + + def i2m(self, pkt, x): + opt = "" + for oname,oval in x: + if type(oname) is str: + if oname == "NOP": + opt += "\x01" + continue + elif oname == "EOL": + opt += "\x00" + continue + elif TCPOptions[1].has_key(oname): + onum = TCPOptions[1][oname] + ofmt = TCPOptions[0][onum][1] + if onum == 5: #SAck + ofmt += "%iI" % len(oval) + if ofmt is not None and (type(oval) is not str or "s" in ofmt): + if type(oval) is not tuple: + oval = (oval,) + oval = struct.pack(ofmt, *oval) + else: + warning("option [%s] unknown. Skipped."%oname) + continue + else: + onum = oname + if type(oval) is not str: + warning("option [%i] is not string."%onum) + continue + opt += chr(onum)+chr(2+len(oval))+oval + return opt+"\x00"*(3-((len(opt)+3)%4)) + def randval(self): + return [] # XXX + + +class ICMPTimeStampField(IntField): + re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") + def i2repr(self, pkt, val): + if val is None: + return "--" + else: + sec, milli = divmod(val, 1000) + min, sec = divmod(sec, 60) + hour, min = divmod(min, 60) + return "%d:%d:%d.%d" %(hour, min, sec, int(milli)) + def any2i(self, pkt, val): + if type(val) is str: + hmsms = self.re_hmsm.match(val) + if hmsms: + h,_,m,_,s,_,ms = hmsms = hmsms.groups() + ms = int(((ms or "")+"000")[:3]) + val = ((int(h)*60+int(m or 0))*60+int(s or 0))*1000+ms + else: + val = 0 + elif val is None: + val = int((time.time()%(24*60*60))*1000) + return val + + +class IP(Packet, IPTools): + name = "IP" + fields_desc = [ BitField("version" , 4 , 4), + BitField("ihl", None, 4), + XByteField("tos", 0), + ShortField("len", None), + ShortField("id", 1), + FlagsField("flags", 0, 3, ["MF","DF","evil"]), + BitField("frag", 0, 13), + ByteField("ttl", 64), + ByteEnumField("proto", 0, IP_PROTOS), + XShortField("chksum", None), + #IPField("src", "127.0.0.1"), + Emph(SourceIPField("src","dst")), + Emph(IPField("dst", "127.0.0.1")), + IPoptionsField("options", "") ] + def post_build(self, p, pay): + ihl = self.ihl + if ihl is None: + ihl = len(p)/4 + p = chr(((self.version&0xf)<<4) | ihl&0x0f)+p[1:] + if self.len is None: + l = len(p)+len(pay) + p = p[:2]+struct.pack("!H", l)+p[4:] + if self.chksum is None: + ck = checksum(p) + p = p[:10]+chr(ck>>8)+chr(ck&0xff)+p[12:] + return p+pay + + def extract_padding(self, s): + l = self.len - (self.ihl << 2) + return s[:l],s[l:] + + def send(self, s, slp=0): + for p in self: + try: + s.sendto(str(p), (p.dst,0)) + except socket.error, msg: + log_runtime.error(msg) + if slp: + time.sleep(slp) + def hashret(self): + if ( (self.proto == socket.IPPROTO_ICMP) + and (isinstance(self.payload, ICMP)) + and (self.payload.type in [3,4,5,11,12]) ): + return self.payload.payload.hashret() + else: + if conf.checkIPsrc and conf.checkIPaddr: + return strxor(inet_aton(self.src),inet_aton(self.dst))+struct.pack("B",self.proto)+self.payload.hashret() + else: + return struct.pack("B", self.proto)+self.payload.hashret() + def answers(self, other): + if not isinstance(other,IP): + return 0 + if conf.checkIPaddr and (self.dst != other.src): + return 0 + if ( (self.proto == socket.IPPROTO_ICMP) and + (isinstance(self.payload, ICMP)) and + (self.payload.type in [3,4,5,11,12]) ): + # ICMP error message + return self.payload.payload.answers(other) + + else: + if ( (conf.checkIPaddr and (self.src != other.dst)) or + (self.proto != other.proto) ): + return 0 + return self.payload.answers(other.payload) + def mysummary(self): + s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") + if self.frag: + s += " frag:%i" % self.frag + return s + + + +class TCP(Packet): + name = "TCP" + fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES), + ShortEnumField("dport", 80, TCP_SERVICES), + IntField("seq", 0), + IntField("ack", 0), + BitField("dataofs", None, 4), + BitField("reserved", 0, 4), + FlagsField("flags", 0x2, 8, "FSRPAUEC"), + ShortField("window", 8192), + XShortField("chksum", None), + ShortField("urgptr", 0), + TCPOptionsField("options", {}) ] + def post_build(self, p, pay): + p += pay + dataofs = self.dataofs + if dataofs is None: + dataofs = 5+((len(self.get_field("options").i2m(self,self.options))+3)/4) + p = p[:12]+chr((dataofs << 4) | ord(p[12])&0x0f)+p[13:] + if self.chksum is None: + if isinstance(self.underlayer, IP): + if self.underlayer.len is not None: + ln = self.underlayer.len-20 + else: + ln = len(p) + psdhdr = struct.pack("!4s4sHH", + inet_aton(self.underlayer.src), + inet_aton(self.underlayer.dst), + self.underlayer.proto, + ln) + ck=checksum(psdhdr+p) + p = p[:16]+struct.pack("!H", ck)+p[18:] + elif isinstance(self.underlayer, IPv6) or isinstance(self.underlayer, _IPv6OptionHeader): + ck = in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) + p = p[:16]+struct.pack("!H", ck)+p[18:] + else: + warning("No IP underlayer to compute checksum. Leaving null.") + return p + def hashret(self): + if conf.checkIPsrc: + return struct.pack("H",self.sport ^ self.dport)+self.payload.hashret() + else: + return self.payload.hashret() + def answers(self, other): + if not isinstance(other, TCP): + return 0 + if conf.checkIPsrc: + if not ((self.sport == other.dport) and + (self.dport == other.sport)): + return 0 + if (abs(other.seq-self.ack) > 2+len(other.payload)): + return 0 + return 1 + def mysummary(self): + if isinstance(self.underlayer, IP): + return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") + elif isinstance(self.underlayer, IPv6): + return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") + else: + return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") + +class UDP(Packet): + name = "UDP" + fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES), + ShortEnumField("dport", 53, UDP_SERVICES), + ShortField("len", None), + XShortField("chksum", None), ] + def post_build(self, p, pay): + p += pay + l = self.len + if l is None: + l = len(p) + p = p[:4]+struct.pack("!H",l)+p[6:] + if self.chksum is None: + if isinstance(self.underlayer, IP): + if self.underlayer.len is not None: + ln = self.underlayer.len-20 + else: + ln = len(p) + psdhdr = struct.pack("!4s4sHH", + inet_aton(self.underlayer.src), + inet_aton(self.underlayer.dst), + self.underlayer.proto, + ln) + ck=checksum(psdhdr+p) + p = p[:6]+struct.pack("!H", ck)+p[8:] + elif isinstance(self.underlayer, IPv6) or isinstance(self.underlayer, _IPv6OptionHeader): + ck = in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) + p = p[:6]+struct.pack("!H", ck)+p[8:] + else: + warning("No IP underlayer to compute checksum. Leaving null.") + return p + def extract_padding(self, s): + l = self.len - 8 + return s[:l],s[l:] + def hashret(self): + return self.payload.hashret() + def answers(self, other): + if not isinstance(other, UDP): + return 0 + if conf.checkIPsrc: + if self.dport != other.sport: + return 0 + return self.payload.answers(other.payload) + def mysummary(self): + if isinstance(self.underlayer, IP): + return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") + elif isinstance(self.underlayer, IPv6): + return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") + else: + return self.sprintf("UDP %UDP.sport% > %UDP.dport%") + +icmptypes = { 0 : "echo-reply", + 3 : "dest-unreach", + 4 : "source-quench", + 5 : "redirect", + 8 : "echo-request", + 9 : "router-advertisement", + 10 : "router-solicitation", + 11 : "time-exceeded", + 12 : "parameter-problem", + 13 : "timestamp-request", + 14 : "timestamp-reply", + 15 : "information-request", + 16 : "information-response", + 17 : "address-mask-request", + 18 : "address-mask-reply" } + +class ICMP(Packet): + name = "ICMP" + fields_desc = [ ByteEnumField("type",8, icmptypes), + ByteField("code",0), + XShortField("chksum", None), + ConditionalField(XShortField("id",0), lambda pkt:pkt.type in [0,8,13,14,15,16]), + ConditionalField(XShortField("seq",0), lambda pkt:pkt.type in [0,8,13,14,15,16]), + ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13,14]), + ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13,14]), + ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13,14]), + ConditionalField(IPField("gw","0.0.0.0"), lambda pkt:pkt.type==5), + ConditionalField(ByteField("ptr",0), lambda pkt:pkt.type==12), + ConditionalField(X3BytesField("reserved",0), lambda pkt:pkt.type==12), + ConditionalField(IntField("unused",0), lambda pkt:pkt.type not in [0,5,8,12,13,14,15,16]), + ] + def post_build(self, p, pay): + p += pay + if self.chksum is None: + ck = checksum(p) + p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] + return p + + def hashret(self): + return struct.pack("HH",self.id,self.seq)+self.payload.hashret() + def answers(self, other): + if not isinstance(other,ICMP): + return 0 + if ( (other.type,self.type) in [(8,0),(13,14),(15,16),(17,18)] and + self.id == other.id and + self.seq == other.seq ): + return 1 + return 0 + + def guess_payload_class(self, payload): + if self.type in [3,4,5,11,12]: + return IPerror + else: + return None + def mysummary(self): + if isinstance(self.underlayer, IP): + return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%") + else: + return self.sprintf("ICMP %ICMP.type% %ICMP.code%") + + + + + +class IPerror(IP): + name = "IP in ICMP" + def answers(self, other): + if not isinstance(other, IP): + return 0 + if not ( ((conf.checkIPsrc == 0) or (self.dst == other.dst)) and + (self.src == other.src) and + ( ((conf.checkIPID == 0) + or (self.id == other.id) + or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and + (self.proto == other.proto) ): + return 0 + return self.payload.answers(other.payload) + def mysummary(self): + return Packet.mysummary(self) + + +class TCPerror(TCP): + name = "TCP in ICMP" + def answers(self, other): + if not isinstance(other, TCP): + return 0 + if conf.checkIPsrc: + if not ((self.sport == other.sport) and + (self.dport == other.dport)): + return 0 + if conf.check_TCPerror_seqack: + if self.seq is not None: + if self.seq != other.seq: + return 0 + if self.ack is not None: + if self.ack != other.ack: + return 0 + return 1 + def mysummary(self): + return Packet.mysummary(self) + + +class UDPerror(UDP): + name = "UDP in ICMP" + def answers(self, other): + if not isinstance(other, UDP): + return 0 + if conf.checkIPsrc: + if not ((self.sport == other.sport) and + (self.dport == other.dport)): + return 0 + return 1 + def mysummary(self): + return Packet.mysummary(self) + + + +class ICMPerror(ICMP): + name = "ICMP in ICMP" + def answers(self, other): + if not isinstance(other,ICMP): + return 0 + if not ((self.type == other.type) and + (self.code == other.code)): + return 0 + if self.code in [0,8,13,14,17,18]: + if (self.id == other.id and + self.seq == other.seq): + return 1 + else: + return 0 + else: + return 1 + def mysummary(self): + return Packet.mysummary(self) + +bind_layers( Ether, IP, type=2048) +bind_layers( CookedLinux, IP, proto=2048) +bind_layers( GRE, IP, proto=2048) +bind_layers( SNAP, IP, code=2048) +bind_layers( IPerror, IPerror, frag=0, proto=4) +bind_layers( IPerror, ICMPerror, frag=0, proto=1) +bind_layers( IPerror, TCPerror, frag=0, proto=6) +bind_layers( IPerror, UDPerror, frag=0, proto=17) +bind_layers( IP, IP, frag=0, proto=4) +bind_layers( IP, ICMP, frag=0, proto=1) +bind_layers( IP, TCP, frag=0, proto=6) +bind_layers( IP, UDP, frag=0, proto=17) +bind_layers( IP, GRE, frag=0, proto=47) + +conf.l2types.register(101, IP) +conf.l2types.register_num2layer(12, IP) + +conf.l3types.register(ETH_P_IP, IP) +conf.l3types.register_num2layer(ETH_P_ALL, IP) + + +################### +## Fragmentation ## +################### + +def fragment(pkt, fragsize=1480): + fragsize = (fragsize+7)/8*8 + lst = [] + for p in pkt: + s = str(p[IP].payload) + nb = (len(s)+fragsize-1)/fragsize + for i in range(nb): + q = p.copy() + del(q[IP].payload) + del(q[IP].chksum) + del(q[IP].len) + if i == nb-1: + q[IP].flags &= ~1 + else: + q[IP].flags |= 1 + q[IP].frag = i*fragsize/8 + r = Raw(load=s[i*fragsize:(i+1)*fragsize]) + r.overload_fields = p[IP].payload.overload_fields.copy() + q.add_payload(r) + lst.append(q) + return lst + +def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): + if overlap_fragsize is None: + overlap_fragsize = fragsize + q = p.copy() + del(q[IP].payload) + q[IP].add_payload(overlap) + + qfrag = fragment(q, overlap_fragsize) + qfrag[-1][IP].flags |= 1 + return qfrag+fragment(p, fragsize) + +def defrag(plist): + """defrag(plist) -> ([not fragmented], [defragmented], + [ [bad fragments], [bad fragments], ... ])""" + frags = {} + nofrag = PacketList() + for p in plist: + ip = p[IP] + if IP not in p: + nofrag.append(p) + continue + if ip.frag == 0 and ip.flags & 1 == 0: + nofrag.append(p) + continue + uniq = (ip.id,ip.src,ip.dst,ip.proto) + if uniq in frags: + frags[uniq].append(p) + else: + frags[uniq] = PacketList([p]) + defrag = [] + missfrag = [] + for lst in frags.itervalues(): + lst.sort(lambda x,y:cmp(x.frag, y.frag)) + p = lst[0] + if p.frag > 0: + missfrag.append(lst) + continue + p = p.copy() + if Padding in p: + del(p[Padding].underlayer.payload) + ip = p[IP] + if ip.len is None or ip.ihl is None: + clen = len(ip.payload) + else: + clen = ip.len - (ip.ihl<<2) + txt = Raw() + for q in lst[1:]: + if clen != q.frag<<3: + if clen > q.frag<<3: + warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) + missfrag.append(lst) + txt = None + break + if q[IP].len is None or q[IP].ihl is None: + clen += len(q[IP].payload) + else: + clen += q[IP].len - (q[IP].ihl<<2) + if Padding in q: + del(q[Padding].underlayer.payload) + txt.add_payload(q[IP].payload.copy()) + + if txt is None: + continue + + ip.flags &= ~1 # !MF + del(ip.chksum) + del(ip.len) + p = p/txt + defrag.append(p) + defrag2=PacketList() + for p in defrag: + defrag2.append(p.__class__(str(p))) + return nofrag,defrag2,missfrag + +def defragment(plist): + """defrag(plist) -> plist defragmented as much as possible """ + frags = {} + final = [] + + pos = 0 + for p in plist: + p._defrag_pos = pos + pos += 1 + if IP in p: + ip = p[IP] + if ip.frag != 0 or ip.flags & 1: + ip = p[IP] + uniq = (ip.id,ip.src,ip.dst,ip.proto) + if uniq in frags: + frags[uniq].append(p) + else: + frags[uniq] = [p] + continue + final.append(p) + + defrag = [] + missfrag = [] + for lst in frags.itervalues(): + lst.sort(lambda x,y:cmp(x.frag, y.frag)) + p = lst[0] + if p.frag > 0: + missfrag += lst + continue + p = p.copy() + if Padding in p: + del(p[Padding].underlayer.payload) + ip = p[IP] + if ip.len is None or ip.ihl is None: + clen = len(ip.payload) + else: + clen = ip.len - (ip.ihl<<2) + txt = Raw() + for q in lst[1:]: + if clen != q.frag<<3: + if clen > q.frag<<3: + warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) + missfrag += lst + txt = None + break + if q[IP].len is None or q[IP].ihl is None: + clen += len(q[IP].payload) + else: + clen += q[IP].len - (q[IP].ihl<<2) + if Padding in q: + del(q[Padding].underlayer.payload) + txt.add_payload(q[IP].payload.copy()) + + if txt is None: + continue + + ip.flags &= ~1 # !MF + del(ip.chksum) + del(ip.len) + p = p/txt + p._defrag_pos = lst[-1]._defrag_pos + defrag.append(p) + defrag2=[] + for p in defrag: + q = p.__class__(str(p)) + q._defrag_pos = p._defrag_pos + defrag2.append(q) + final += defrag2 + final += missfrag + final.sort(lambda x,y: cmp(x._defrag_pos, y._defrag_pos)) + for p in final: + del(p._defrag_pos) + + if hasattr(plist, "listname"): + name = "Defragmented %s" % plist.listname + else: + name = "Defragmented" + + + return PacketList(final, name=name) + + + + +def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, filter=None, timeout=2, verbose=None, **kargs): + """Instant TCP traceroute +traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) -> None +""" + if verbose is None: + verbose = conf.verb + if filter is None: + # we only consider ICMP error packets and TCP packets with at + # least the ACK flag set *and* either the SYN or the RST flag + # set + filter="(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" + if l4 is None: + a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport), + timeout=timeout, filter=filter, verbose=verbose, **kargs) + else: + # this should always work + filter="ip" + a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/l4, + timeout=timeout, filter=filter, verbose=verbose, **kargs) + + a = TracerouteResult(a.res) + if verbose: + a.show() + return a,b + + + +##################### +## Reporting stuff ## +##################### + +def report_ports(target, ports): + """portscan a target and output a LaTeX table +report_ports(target, ports) -> string""" + ans,unans = sr(IP(dst=target)/TCP(dport=ports),timeout=5) + rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" + for s,r in ans: + if not r.haslayer(ICMP): + if r.payload.flags == 0x12: + rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") + rep += "\\hline\n" + for s,r in ans: + if r.haslayer(ICMP): + rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") + elif r.payload.flags != 0x12: + rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") + rep += "\\hline\n" + for i in unans: + rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") + rep += "\\hline\n\\end{tabular}\n" + return rep + + + +def IPID_count(lst, funcID=lambda x:x[1].id, funcpres=lambda x:x[1].summary()): + idlst = map(funcID, lst) + idlst.sort() + classes = [idlst[0]]+map(lambda x:x[1],filter(lambda (x,y): abs(x-y)>50, map(lambda x,y: (x,y),idlst[:-1], idlst[1:]))) + lst = map(lambda x:(funcID(x), funcpres(x)), lst) + lst.sort() + print "Probably %i classes:" % len(classes), classes + for id,pr in lst: + print "%5i" % id, pr + + +def fragleak(target,sport=123, dport=123, timeout=0.2, onlyasc=0): + load = "XXXXYYYYYYYYYY" +# getmacbyip(target) +# pkt = IP(dst=target, id=RandShort(), options="\x22"*40)/UDP()/load + pkt = IP(dst=target, id=RandShort(), options="\x00"*40, flags=1)/UDP(sport=sport, dport=sport)/load + s=conf.L3socket() + intr=0 + found={} + try: + while 1: + try: + if not intr: + s.send(pkt) + sin,sout,serr = select([s],[],[],timeout) + if not sin: + continue + ans=s.recv(1600) + if not isinstance(ans, IP): #TODO: IPv6 + continue + if not isinstance(ans.payload, ICMP): + continue + if not isinstance(ans.payload.payload, IPerror): + continue + if ans.payload.payload.dst != target: + continue + if ans.src != target: + print "leak from", ans.src, + + +# print repr(ans) + if not ans.haslayer(Padding): + continue + + +# print repr(ans.payload.payload.payload.payload) + +# if not isinstance(ans.payload.payload.payload.payload, Raw): +# continue +# leak = ans.payload.payload.payload.payload.load[len(load):] + leak = ans.getlayer(Padding).load + if leak not in found: + found[leak]=None + linehexdump(leak, onlyasc=onlyasc) + except KeyboardInterrupt: + if intr: + raise + intr=1 + except KeyboardInterrupt: + pass + +def fragleak2(target, timeout=0.4, onlyasc=0): + found={} + try: + while 1: + p = sr1(IP(dst=target, options="\x00"*40, proto=200)/"XXXXYYYYYYYYYYYY",timeout=timeout,verbose=0) + if not p: + continue + if Padding in p: + leak = p[Padding].load + if leak not in found: + found[leak]=None + linehexdump(leak,onlyasc=onlyasc) + except: + pass + + diff --git a/scapy/layers/ip6.py b/scapy/layers/ip6.py new file mode 100644 index 0000000000000000000000000000000000000000..9dd938cc2b5a42bf69f20bd778a087b460d0f566 --- /dev/null +++ b/scapy/layers/ip6.py @@ -0,0 +1,22 @@ +from scapy.error import log_interactive +from scapy.packet import * +from scapy.fields import * + + +class IPv6(Packet): + """See http://namabiiru.hongo.wide.ad.jp/scapy6""" + name = "IPv6 not implemented here." + def __init__(self, *args, **kargs): + log_interactive.error(self.name) + def __repr__(self): + return "<IPv6: ERROR not implemented>" + +class _IPv6OptionHeader(Packet): + """See http://namabiiru.hongo.wide.ad.jp/scapy6""" + name = "IPv6 not implemented here." + def __init__(self, *args, **kargs): + log_interactive.error(self.name) + def __repr__(self): + return "<IPv6: ERROR not implemented>" + + diff --git a/scapy/layers/ir.py b/scapy/layers/ir.py new file mode 100644 index 0000000000000000000000000000000000000000..e0e47e4b8a2b4574477c01cbe40f0eaa3e0506b2 --- /dev/null +++ b/scapy/layers/ir.py @@ -0,0 +1,35 @@ +from scapy.packet import * +from scapy.fields import * +from scapy.layers.l2 import CookedLinux + + + +# IR + +class IrLAPHead(Packet): + name = "IrDA Link Access Protocol Header" + fields_desc = [ XBitField("Address", 0x7f, 7), + BitEnumField("Type", 1, 1, {"Response":0, + "Command":1})] + +class IrLAPCommand(Packet): + name = "IrDA Link Access Protocol Command" + fields_desc = [ XByteField("Control", 0), + XByteField("Format identifier", 0), + XIntField("Source address", 0), + XIntField("Destination address", 0xffffffffL), + XByteField("Discovery flags", 0x1), + ByteEnumField("Slot number", 255, {"final":255}), + XByteField("Version", 0)] + + +class IrLMP(Packet): + name = "IrDA Link Management Protocol" + fields_desc = [ XShortField("Service hints", 0), + XByteField("Character set", 0), + StrField("Device name", "") ] + + +bind_layers( CookedLinux, IrLAPHead, proto=23) +bind_layers( IrLAPHead, IrLAPCommand, Type=1) +bind_layers( IrLAPCommand, IrLMP, ) diff --git a/scapy/layers/isakmp.py b/scapy/layers/isakmp.py new file mode 100644 index 0000000000000000000000000000000000000000..b7aafc82a3cdd365755eccead0bbfe772afb7329 --- /dev/null +++ b/scapy/layers/isakmp.py @@ -0,0 +1,338 @@ +import struct +from scapy.packet import * +from scapy.fields import * +from scapy.ansmachine import * +from scapy.layers.inet import UDP + + +# see http://www.iana.org/assignments/ipsec-registry for details +ISAKMPAttributeTypes= { "Encryption": (1, { "DES-CBC" : 1, + "IDEA-CBC" : 2, + "Blowfish-CBC" : 3, + "RC5-R16-B64-CBC" : 4, + "3DES-CBC" : 5, + "CAST-CBC" : 6, + "AES-CBC" : 7, + "CAMELLIA-CBC" : 8, }, 0), + "Hash": (2, { "MD5": 1, + "SHA": 2, + "Tiger": 3, + "SHA2-256": 4, + "SHA2-384": 5, + "SHA2-512": 6,}, 0), + "Authentication":(3, { "PSK": 1, + "DSS": 2, + "RSA Sig": 3, + "RSA Encryption": 4, + "RSA Encryption Revised": 5, + "ElGamal Encryption": 6, + "ElGamal Encryption Revised": 7, + "ECDSA Sig": 8, + "HybridInitRSA": 64221, + "HybridRespRSA": 64222, + "HybridInitDSS": 64223, + "HybridRespDSS": 64224, + "XAUTHInitPreShared": 65001, + "XAUTHRespPreShared": 65002, + "XAUTHInitDSS": 65003, + "XAUTHRespDSS": 65004, + "XAUTHInitRSA": 65005, + "XAUTHRespRSA": 65006, + "XAUTHInitRSAEncryption": 65007, + "XAUTHRespRSAEncryption": 65008, + "XAUTHInitRSARevisedEncryption": 65009, + "XAUTHRespRSARevisedEncryptio": 65010, }, 0), + "GroupDesc": (4, { "768MODPgr" : 1, + "1024MODPgr" : 2, + "EC2Ngr155" : 3, + "EC2Ngr185" : 4, + "1536MODPgr" : 5, + "2048MODPgr" : 14, + "3072MODPgr" : 15, + "4096MODPgr" : 16, + "6144MODPgr" : 17, + "8192MODPgr" : 18, }, 0), + "GroupType": (5, {"MODP": 1, + "ECP": 2, + "EC2N": 3}, 0), + "GroupPrime": (6, {}, 1), + "GroupGenerator1":(7, {}, 1), + "GroupGenerator2":(8, {}, 1), + "GroupCurveA": (9, {}, 1), + "GroupCurveB": (10, {}, 1), + "LifeType": (11, {"Seconds": 1, + "Kilobytes": 2, }, 0), + "LifeDuration": (12, {}, 1), + "PRF": (13, {}, 0), + "KeyLength": (14, {}, 0), + "FieldSize": (15, {}, 0), + "GroupOrder": (16, {}, 1), + } + +# the name 'ISAKMPTransformTypes' is actually a misnomer (since the table +# holds info for all ISAKMP Attribute types, not just transforms, but we'll +# keep it for backwards compatibility... for now at least +ISAKMPTransformTypes = ISAKMPAttributeTypes + +ISAKMPTransformNum = {} +for n in ISAKMPTransformTypes: + val = ISAKMPTransformTypes[n] + tmp = {} + for e in val[1]: + tmp[val[1][e]] = e + ISAKMPTransformNum[val[0]] = (n,tmp, val[2]) +del(n) +del(e) +del(tmp) +del(val) + + +class ISAKMPTransformSetField(StrLenField): + islist=1 + def type2num(self, (typ,val)): + type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typ, (typ,{},0)) + val = enc_dict.get(val, val) + s = "" + if (val & ~0xffff): + if not tlv: + warning("%r should not be TLV but is too big => using TLV encoding" % typ) + n = 0 + while val: + s = chr(val&0xff)+s + val >>= 8 + n += 1 + val = n + else: + type_val |= 0x8000 + return struct.pack("!HH",type_val, val)+s + def num2type(self, typ, enc): + val = ISAKMPTransformNum.get(typ,(typ,{})) + enc = val[1].get(enc,enc) + return (val[0],enc) + def i2m(self, pkt, i): + if i is None: + return "" + i = map(self.type2num, i) + return "".join(i) + def m2i(self, pkt, m): + # I try to ensure that we don't read off the end of our packet based + # on bad length fields we're provided in the packet. There are still + # conditions where struct.unpack() may not get enough packet data, but + # worst case that should result in broken attributes (which would + # be expected). (wam) + lst = [] + while len(m) >= 4: + trans_type, = struct.unpack("!H", m[:2]) + is_tlv = not (trans_type & 0x8000) + if is_tlv: + # We should probably check to make sure the attribute type we + # are looking at is allowed to have a TLV format and issue a + # warning if we're given an TLV on a basic attribute. + value_len, = struct.unpack("!H", m[2:4]) + if value_len+4 > len(m): + warning("Bad length for ISAKMP tranform type=%#6x" % trans_type) + value = m[4:4+value_len] + value = reduce(lambda x,y: (x<<8L)|y, struct.unpack("!%s" % ("B"*len(value),), value),0) + else: + trans_type &= 0x7fff + value_len=0 + value, = struct.unpack("!H", m[2:4]) + m=m[4+value_len:] + lst.append(self.num2type(trans_type, value)) + if len(m) > 0: + warning("Extra bytes after ISAKMP transform dissection [%r]" % m) + return lst + + +ISAKMP_payload_type = ["None","SA","Proposal","Transform","KE","ID","CERT","CR","Hash", + "SIG","Nonce","Notification","Delete","VendorID"] + +ISAKMP_exchange_type = ["None","base","identity prot.", + "auth only", "aggressive", "info"] + + +class ISAKMP_class(Packet): + def guess_payload_class(self, payload): + np = self.next_payload + if np == 0: + return Raw + elif np < len(ISAKMP_payload_type): + pt = ISAKMP_payload_type[np] + return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload) + else: + return ISAKMP_payload + + +class ISAKMP(ISAKMP_class): # rfc2408 + name = "ISAKMP" + fields_desc = [ + StrFixedLenField("init_cookie","",8), + StrFixedLenField("resp_cookie","",8), + ByteEnumField("next_payload",0,ISAKMP_payload_type), + XByteField("version",0x10), + ByteEnumField("exch_type",0,ISAKMP_exchange_type), + FlagsField("flags",0, 8, ["encryption","commit","auth_only","res3","res4","res5","res6","res7"]), # XXX use a Flag field + IntField("id",0), + IntField("length",None) + ] + + def guess_payload_class(self, payload): + if self.flags & 1: + return Raw + return ISAKMP_class.guess_payload_class(self, payload) + + def answers(self, other): + if isinstance(other, ISAKMP): + if other.init_cookie == self.init_cookie: + return 1 + return 0 + def post_build(self, p, pay): + p += pay + if self.length is None: + p = p[:24]+struct.pack("!I",len(p))+p[28:] + return p + + + + +class ISAKMP_payload_Transform(ISAKMP_class): + name = "IKE Transform" + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), +# ShortField("len",None), + ShortField("length",None), + ByteField("num",None), + ByteEnumField("id",1,{1:"KEY_IKE"}), + ShortField("res2",0), + ISAKMPTransformSetField("transforms",None,length_from=lambda x:x.length-8) +# XIntField("enc",0x80010005L), +# XIntField("hash",0x80020002L), +# XIntField("auth",0x80030001L), +# XIntField("group",0x80040002L), +# XIntField("life_type",0x800b0001L), +# XIntField("durationh",0x000c0004L), +# XIntField("durationl",0x00007080L), + ] + def post_build(self, p, pay): + if self.length is None: + l = len(p) + p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:] + p += pay + return p + + + + +class ISAKMP_payload_Proposal(ISAKMP_class): + name = "IKE proposal" +# ISAKMP_payload_type = 0 + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), + ByteField("proposal",1), + ByteEnumField("proto",1,{1:"ISAKMP"}), + FieldLenField("SPIsize",None,"SPI","B"), + ByteField("trans_nb",None), + StrLenField("SPI","",length_from=lambda x:x.SPIsize), + PacketLenField("trans",Raw(),ISAKMP_payload_Transform,length_from=lambda x:x.length-8), + ] + + +class ISAKMP_payload(ISAKMP_class): + name = "ISAKMP payload" + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + + +class ISAKMP_payload_VendorID(ISAKMP_class): + name = "ISAKMP Vendor ID" + overload_fields = { ISAKMP: { "next_payload":13 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), + StrLenField("vendorID","",length_from=lambda x:x.length-4), + ] + +class ISAKMP_payload_SA(ISAKMP_class): + name = "ISAKMP SA" + overload_fields = { ISAKMP: { "next_payload":1 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+12), + IntEnumField("DOI",1,{1:"IPSEC"}), + IntEnumField("situation",1,{1:"identity"}), + PacketLenField("prop",Raw(),ISAKMP_payload_Proposal,length_from=lambda x:x.length-12), + ] + +class ISAKMP_payload_Nonce(ISAKMP_class): + name = "ISAKMP Nonce" + overload_fields = { ISAKMP: { "next_payload":10 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + +class ISAKMP_payload_KE(ISAKMP_class): + name = "ISAKMP Key Exchange" + overload_fields = { ISAKMP: { "next_payload":4 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + +class ISAKMP_payload_ID(ISAKMP_class): + name = "ISAKMP Identification" + overload_fields = { ISAKMP: { "next_payload":5 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), + ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}), + ByteEnumField("ProtoID",0,{0:"Unused"}), + ShortEnumField("Port",0,{0:"Unused"}), +# IPField("IdentData","127.0.0.1"), + StrLenField("load","",length_from=lambda x:x.length-8), + ] + + + +class ISAKMP_payload_Hash(ISAKMP_class): + name = "ISAKMP Hash" + overload_fields = { ISAKMP: { "next_payload":8 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + + + +ISAKMP_payload_type_overload = {} +for i in range(len(ISAKMP_payload_type)): + name = "ISAKMP_payload_%s" % ISAKMP_payload_type[i] + if name in globals(): + ISAKMP_payload_type_overload[globals()[name]] = {"next_payload":i} + +del(i) +del(name) +ISAKMP_class.overload_fields = ISAKMP_payload_type_overload.copy() + + +bind_layers( UDP, ISAKMP, dport=500, sport=500) +def ikescan(ip): + return sr(IP(dst=ip)/UDP()/ISAKMP(init_cookie=RandString(8), + exch_type=2)/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())) + diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py new file mode 100644 index 0000000000000000000000000000000000000000..1e4cadf0f3a8321b7d9cbc6d34ad2cfa53b49002 --- /dev/null +++ b/scapy/layers/l2.py @@ -0,0 +1,360 @@ +import os,struct,time +from scapy.config import conf +from scapy.packet import * +from scapy.ansmachine import * + +class Ether_or_Dot3_metaclass(Packet_metaclass): + def __call__(self, _pkt=None, *args, **kargs): + cls = self + if _pkt and len(_pkt) >= 14: + if struct.unpack("!H", _pkt[12:14])[0] <= 1500: + cls = Dot3 + else: + cls = Ether + i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) + i.__init__(_pkt=_pkt, *args, **kargs) + return i + +class Ether(Packet): + __metaclass__ = Ether_or_Dot3_metaclass + name = "Ethernet" + fields_desc = [ DestMACField("dst"), + SourceMACField("src"), + XShortEnumField("type", 0x0000, ETHER_TYPES) ] + def hashret(self): + return struct.pack("H",self.type)+self.payload.hashret() + def answers(self, other): + if isinstance(other,Ether): + if self.type == other.type: + return self.payload.answers(other.payload) + return 0 + def mysummary(self): + return self.sprintf("%src% > %dst% (%type%)") + +class Dot3(Packet): + __metaclass__ = Ether_or_Dot3_metaclass + name = "802.3" + fields_desc = [ MACField("dst", ETHER_BROADCAST), + MACField("src", ETHER_ANY), + LenField("len", None, "H") ] + def extract_padding(self,s): + l = self.len + return s[:l],s[l:] + def answers(self, other): + if isinstance(other,Dot3): + return self.payload.answers(other.payload) + return 0 + def mysummary(self): + return "802.3 %s > %s" % (self.src, self.dst) + + +class LLC(Packet): + name = "LLC" + fields_desc = [ XByteField("dsap", 0x00), + XByteField("ssap", 0x00), + ByteField("ctrl", 0) ] + + +class CookedLinux(Packet): + name = "cooked linux" + fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast", + 4:"sent-by-us"}), #XXX incomplete + XShortField("lladdrtype",512), + ShortField("lladdrlen",0), + StrFixedLenField("src","",8), + XShortEnumField("proto",0x800,ETHER_TYPES) ] + + + +class SNAP(Packet): + name = "SNAP" + fields_desc = [ X3BytesField("OUI",0x000000), + XShortEnumField("code", 0x000, ETHER_TYPES) ] + + +class Dot1Q(Packet): + name = "802.1Q" + aliastypes = [ Ether ] + fields_desc = [ BitField("prio", 0, 3), + BitField("id", 0, 1), + BitField("vlan", 1, 12), + XShortEnumField("type", 0x0000, ETHER_TYPES) ] + def answers(self, other): + if isinstance(other,Dot1Q): + if ( (self.type == other.type) and + (self.vlan == other.vlan) ): + return self.payload.answers(other.payload) + else: + return self.payload.answers(other) + return 0 + def default_payload_class(self, pay): + if self.type <= 1500: + return LLC + return Raw + def extract_padding(self,s): + if self.type <= 1500: + return s[:self.type],s[self.type:] + return s,None + def mysummary(self): + if isinstance(self.underlayer, Ether): + return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%") + else: + return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%") + + + + +class STP(Packet): + name = "Spanning Tree Protocol" + fields_desc = [ ShortField("proto", 0), + ByteField("version", 0), + ByteField("bpdutype", 0), + ByteField("bpduflags", 0), + ShortField("rootid", 0), + MACField("rootmac", ETHER_ANY), + IntField("pathcost", 0), + ShortField("bridgeid", 0), + MACField("bridgemac", ETHER_ANY), + ShortField("portid", 0), + BCDFloatField("age", 1), + BCDFloatField("maxage", 20), + BCDFloatField("hellotime", 2), + BCDFloatField("fwddelay", 15) ] + + +class EAPOL(Packet): + name = "EAPOL" + fields_desc = [ ByteField("version", 1), + ByteEnumField("type", 0, ["EAP_PACKET", "START", "LOGOFF", "KEY", "ASF"]), + LenField("len", None, "H") ] + + EAP_PACKET= 0 + START = 1 + LOGOFF = 2 + KEY = 3 + ASF = 4 + def extract_padding(self, s): + l = self.len + return s[:l],s[l:] + def hashret(self): + return chr(self.type)+self.payload.hashret() + def answers(self, other): + if isinstance(other,EAPOL): + if ( (self.type == self.EAP_PACKET) and + (other.type == self.EAP_PACKET) ): + return self.payload.answers(other.payload) + return 0 + def mysummary(self): + return self.sprintf("EAPOL %EAPOL.type%") + + +class EAP(Packet): + name = "EAP" + fields_desc = [ ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}), + ByteField("id", 0), + ShortField("len",None), + ConditionalField(ByteEnumField("type",0, {1:"ID",4:"MD5"}), lambda pkt:pkt.code not in [EAP.SUCCESS, EAP.FAILURE]) + + ] + + REQUEST = 1 + RESPONSE = 2 + SUCCESS = 3 + FAILURE = 4 + TYPE_ID = 1 + TYPE_MD5 = 4 + def answers(self, other): + if isinstance(other,EAP): + if self.code == self.REQUEST: + return 0 + elif self.code == self.RESPONSE: + if ( (other.code == self.REQUEST) and + (other.type == self.type) ): + return 1 + elif other.code == self.RESPONSE: + return 1 + return 0 + + def post_build(self, p, pay): + if self.len is None: + l = len(p)+len(pay) + p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:] + return p+pay + + +class ARP(Packet): + name = "ARP" + fields_desc = [ XShortField("hwtype", 0x0001), + XShortEnumField("ptype", 0x0800, ETHER_TYPES), + ByteField("hwlen", 6), + ByteField("plen", 4), + ShortEnumField("op", 1, {"who-has":1, "is-at":2, "RARP-req":3, "RARP-rep":4, "Dyn-RARP-req":5, "Dyn-RAR-rep":6, "Dyn-RARP-err":7, "InARP-req":8, "InARP-rep":9}), + ARPSourceMACField("hwsrc"), + SourceIPField("psrc","pdst"), + MACField("hwdst", ETHER_ANY), + IPField("pdst", "0.0.0.0") ] + who_has = 1 + is_at = 2 + def answers(self, other): + if isinstance(other,ARP): + if ( (self.op == self.is_at) and + (other.op == self.who_has) and + (self.psrc == other.pdst) ): + return 1 + return 0 + def extract_padding(self, s): + return "",s + def mysummary(self): + if self.op == self.is_at: + return "ARP is at %s says %s" % (self.hwsrc, self.psrc) + elif self.op == self.who_has: + return "ARP who has %s says %s" % (self.pdst, self.psrc) + else: + return "ARP %ARP.op% %ARP.psrc% > %ARP.pdst%" + + + +class GRE(Packet): + name = "GRE" + fields_desc = [ BitField("chksumpresent",0,1), + BitField("reserved0",0,12), + BitField("version",0,3), + XShortEnumField("proto", 0x0000, ETHER_TYPES), + ConditionalField(XShortField("chksum",None),lambda pkt:pkt.chksumpresent==1), + ConditionalField(XShortField("reserved1",None),lambda pkt:pkt.chksumpresent==1), + ] + def post_build(self, p, pay): + p += pay + if self.chksumpresent and self.chksum is None: + c = checksum(p) + p = p[:4]+chr((c>>8)&0xff)+chr(c&0xff)+p[6:] + return p + + + + +bind_layers( Dot3, LLC, ) +bind_layers( Ether, LLC, type=122) +bind_layers( Ether, Dot1Q, type=33024) +bind_layers( Ether, Ether, type=1) +bind_layers( Ether, ARP, type=2054) +bind_layers( Ether, EAPOL, type=34958) +bind_layers( Ether, EAPOL, dst='01:80:c2:00:00:03', type=34958) +bind_layers( CookedLinux, LLC, proto=122) +bind_layers( CookedLinux, Dot1Q, proto=33024) +bind_layers( CookedLinux, Ether, proto=1) +bind_layers( CookedLinux, ARP, proto=2054) +bind_layers( CookedLinux, EAPOL, proto=34958) +bind_layers( GRE, LLC, proto=122) +bind_layers( GRE, Dot1Q, proto=33024) +bind_layers( GRE, Ether, proto=1) +bind_layers( GRE, ARP, proto=2054) +bind_layers( GRE, EAPOL, proto=34958) +bind_layers( EAPOL, EAP, type=0) +bind_layers( LLC, STP, dsap=66, ssap=66, ctrl=3) +bind_layers( LLC, SNAP, dsap=170, ssap=170, ctrl=3) +bind_layers( SNAP, Dot1Q, code=33024) +bind_layers( SNAP, Ether, code=1) +bind_layers( SNAP, ARP, code=2054) +bind_layers( SNAP, EAPOL, code=34958) +bind_layers( SNAP, STP, code=267) + +conf.l2types.register(ARPHDR_ETHER, Ether) +conf.l2types.register_num2layer(ARPHDR_METRICOM, Ether) +conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether) +conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3) +conf.l2types.register(113, CookedLinux) +conf.l2types.register(144, CookedLinux) # called LINUX_IRDA, similar to CookedLinux + +conf.l3types.register(ETH_P_ARP, ARP) + +def arpcachepoison(target, victim, interval=60): + """Poison target's cache with (your MAC,victim's IP) couple +arpcachepoison(target, victim, [interval=60]) -> None +""" + tmac = getmacbyip(target) + p = Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target) + try: + while 1: + sendp(p, iface_hint=target) + if conf.verb > 1: + os.write(1,".") + time.sleep(interval) + except KeyboardInterrupt: + pass + + +def arping(net, timeout=2, cache=0, verbose=None, **kargs): + """Send ARP who-has requests to determine which hosts are up +arping(net, [cache=0,] [iface=conf.iface,] [verbose=conf.verb]) -> None +Set cache=True if you want arping to modify internal ARP-Cache""" + if verbose is None: + verbose = conf.verb + ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net), verbose=verbose, + filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) + ans = ARPingResult(ans.res) + + if cache and ans is not None: + for pair in ans: + arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time()) + if verbose: + ans.show() + return ans,unans + +def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00",**kargs): + """Try to guess if target is in Promisc mode. The target is provided by its ip.""" + + responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip),type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0,**kargs) + + return responses is not None + +def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs): + """Send ARP who-has requests to determine which hosts are in promiscuous mode + promiscping(net, iface=conf.iface)""" + ans,unans = srp(Ether(dst=fake_bcast)/ARP(pdst=net), + filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) + ans = ARPingResult(ans.res, name="PROMISCPing") + + ans.display() + return ans,unans + + +class ARP_am(AnsweringMachine): + function_name="farpd" + filter = "arp" + send_function = staticmethod(sendp) + + def parse_options(self, IP_addr=None, iface=None, ARP_addr=None): + self.IP_addr=IP_addr + self.iface=iface + self.ARP_addr=ARP_addr + + def is_request(self, req): + return (req.haslayer(ARP) and + req.getlayer(ARP).op == 1 and + (self.IP_addr == None or self.IP_addr == req.getlayer(ARP).pdst)) + + def make_reply(self, req): + ether = req.getlayer(Ether) + arp = req.getlayer(ARP) + iff,a,gw = conf.route.route(arp.psrc) + if self.iface != None: + iff = iface + ARP_addr = self.ARP_addr + IP_addr = arp.pdst + resp = Ether(dst=ether.src, + src=ARP_addr)/ARP(op="is-at", + hwsrc=ARP_addr, + psrc=IP_addr, + hwdst=arp.hwsrc, + pdst=arp.pdst) + return resp + + def sniff(self): + sniff(iface=self.iface, **self.optsniff) + +def etherleak(target, **kargs): + return srpflood(Ether()/ARP(pdst=target), prn=lambda (s,r): Padding in r and hexstr(r[Padding].load), + filter="arp", **kargs) + + diff --git a/scapy/layers/l2tp.py b/scapy/layers/l2tp.py new file mode 100644 index 0000000000000000000000000000000000000000..b5d001a8bf99945b41cbfd673570f72c8931f264 --- /dev/null +++ b/scapy/layers/l2tp.py @@ -0,0 +1,25 @@ +import struct + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP +from scapy.layers.ppp import PPP + +class L2TP(Packet): + fields_desc = [ ShortEnumField("pkt_type",2,{2:"data"}), + ShortField("len", None), + ShortField("tunnel_id", 0), + ShortField("session_id", 0), + ShortField("ns", 0), + ShortField("nr", 0), + ShortField("offset", 0) ] + + def post_build(self, pkt, pay): + if self.len is None: + l = len(pkt)+len(pay) + pkt = pkt[:2]+struct.pack("!H", l)+pkt[4:] + return pkt+pay + + +bind_layers( UDP, L2TP, sport=1701, dport=1701) +bind_layers( L2TP, PPP, ) diff --git a/scapy/layers/mgcp.py b/scapy/layers/mgcp.py new file mode 100644 index 0000000000000000000000000000000000000000..fec4dbf52e48dc15c1f271f2a12c9732eac6797c --- /dev/null +++ b/scapy/layers/mgcp.py @@ -0,0 +1,34 @@ +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP + +class MGCP(Packet): + name = "MGCP" + longname = "Media Gateway Control Protocol" + fields_desc = [ StrStopField("verb","AUEP"," ", -1), + StrFixedLenField("sep1"," ",1), + StrStopField("transaction_id","1234567"," ", -1), + StrFixedLenField("sep2"," ",1), + StrStopField("endpoint","dummy@dummy.net"," ", -1), + StrFixedLenField("sep3"," ",1), + StrStopField("version","MGCP 1.0 NCS 1.0","\x0a", -1), + StrFixedLenField("sep4","\x0a",1), + ] + + +#class MGCP(Packet): +# name = "MGCP" +# longname = "Media Gateway Control Protocol" +# fields_desc = [ ByteEnumField("type",0, ["request","response","others"]), +# ByteField("code0",0), +# ByteField("code1",0), +# ByteField("code2",0), +# ByteField("code3",0), +# ByteField("code4",0), +# IntField("trasid",0), +# IntField("req_time",0), +# ByteField("is_duplicate",0), +# ByteField("req_available",0) ] +# +bind_layers( UDP, MGCP, dport=2727) +bind_layers( UDP, MGCP, sport=2727) diff --git a/scapy/layers/mobileip.py b/scapy/layers/mobileip.py new file mode 100644 index 0000000000000000000000000000000000000000..a455525e68cd837f06e07f59b05d490b9d98d910 --- /dev/null +++ b/scapy/layers/mobileip.py @@ -0,0 +1,33 @@ +class MobileIP(Packet): + name = "Mobile IP (RFC3344)" + fields_desc = [ ByteEnumField("type", 1, {1:"RRQ", 3:"RRP"}) ] + +class MobileIPRRQ(Packet): + name = "Mobile IP Registration Request (RFC3344)" + fields_desc = [ XByteField("flags", 0), + ShortField("lifetime", 180), + IPField("homeaddr", "0.0.0.0"), + IPField("haaddr", "0.0.0.0"), + IPField("coaddr", "0.0.0.0"), + Field("id", "", "64s") ] + +class MobileIPRRP(Packet): + name = "Mobile IP Registration Reply (RFC3344)" + fields_desc = [ ByteField("code", 0), + ShortField("lifetime", 180), + IPField("homeaddr", "0.0.0.0"), + IPField("haaddr", "0.0.0.0"), + Field("id", "", "64s") ] + +class MobileIPTunnelData(Packet): + name = "Mobile IP Tunnel Data Message (RFC3519)" + fields_desc = [ ByteField("nexthdr", 4), + ShortField("res", 0) ] + + +bind_layers( UDP, MobileIP, sport=434) +bind_layers( UDP, MobileIP, dport=434) +bind_layers( MobileIP, MobileIPRRQ, type=1) +bind_layers( MobileIP, MobileIPRRP, type=3) +bind_layers( MobileIP, MobileIPTunnelData, type=4) +bind_layers( MobileIPTunnelData, IP, nexthdr=4) diff --git a/scapy/layers/netbios.py b/scapy/layers/netbios.py new file mode 100644 index 0000000000000000000000000000000000000000..fdfa4215a9702e10e9f1a9d53d55e8f0a0f0351a --- /dev/null +++ b/scapy/layers/netbios.py @@ -0,0 +1,210 @@ +import struct +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP,TCP + +class NetBIOS_DS(Packet): + name = "NetBIOS datagram service" + fields_desc = [ + ByteEnumField("type",17, {17:"direct_group"}), + ByteField("flags",0), + XShortField("id",0), + IPField("src","127.0.0.1"), + ShortField("sport",138), + ShortField("len",None), + ShortField("ofs",0), + NetBIOSNameField("srcname",""), + NetBIOSNameField("dstname",""), + ] + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-14 + p = p[:10]+struct.pack("!H", l)+p[12:] + return p + +# ShortField("length",0), +# ShortField("Delimitor",0), +# ByteField("command",0), +# ByteField("data1",0), +# ShortField("data2",0), +# ShortField("XMIt",0), +# ShortField("RSPCor",0), +# StrFixedLenField("dest","",16), +# StrFixedLenField("source","",16), +# +# ] +# + +#NetBIOS + + +# Name Query Request +# Node Status Request +class NBNSQueryRequest(Packet): + name="NBNS query request" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x0110), + ShortField("QDCOUNT",1), + ShortField("ANCOUNT",0), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("QUESTION_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"})] + +# Name Registration Request +# Name Refresh Request +# Name Release Request or Demand +class NBNSRequest(Packet): + name="NBNS request" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x2910), + ShortField("QDCOUNT",1), + ShortField("ANCOUNT",0), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",1), + NetBIOSNameField("QUESTION_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), + ShortEnumField("RR_NAME",0xC00C,{0xC00C:"Label String Pointer to QUESTION_NAME"}), + ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), + IntField("TTL", 0), + ShortField("RDLENGTH", 6), + BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), + BitEnumField("OWNER NODE TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}), + BitEnumField("UNUSED",0,13,{0:"Unused"}), + IPField("NB_ADDRESS", "127.0.0.1")] + +# Name Query Response +# Name Registration Response +class NBNSQueryResponse(Packet): + name="NBNS query response" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x8500), + ShortField("QDCOUNT",0), + ShortField("ANCOUNT",1), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("RR_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), + IntField("TTL", 0x493e0), + ShortField("RDLENGTH", 6), + ShortField("NB_FLAGS", 0), + IPField("NB_ADDRESS", "127.0.0.1")] + +# Name Query Response (negative) +# Name Release Response +class NBNSQueryResponseNegative(Packet): + name="NBNS query response (negative)" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x8506), + ShortField("QDCOUNT",0), + ShortField("ANCOUNT",1), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("RR_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), + IntField("TTL",0), + ShortField("RDLENGTH",6), + BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), + BitEnumField("OWNER NODE TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}), + BitEnumField("UNUSED",0,13,{0:"Unused"}), + IPField("NB_ADDRESS", "127.0.0.1")] + +# Node Status Response +class NBNSNodeStatusResponse(Packet): + name="NBNS Node Status Response" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x8500), + ShortField("QDCOUNT",0), + ShortField("ANCOUNT",1), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("RR_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("RR_TYPE",0x21, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), + IntField("TTL",0), + ShortField("RDLENGTH",83), + ByteField("NUM_NAMES",1)] + +# Service for Node Status Response +class NBNSNodeStatusResponseService(Packet): + name="NBNS Node Status Response Service" + fields_desc = [StrFixedLenField("NETBIOS_NAME","WINDOWS ",15), + ByteEnumField("SUFFIX",0,{0:"workstation",0x03:"messenger service",0x20:"file server service",0x1b:"domain master browser",0x1c:"domain controller", 0x1e:"browser election service"}), + ByteField("NAME_FLAGS",0x4), + ByteEnumField("UNUSED",0,{0:"unused"})] + +# End of Node Status Response packet +class NBNSNodeStatusResponseEnd(Packet): + name="NBNS Node Status Response" + fields_desc = [SourceMACField("MAC_ADDRESS"), + BitField("STATISTICS",0,57*8)] + +# Wait for Acknowledgement Response +class NBNSWackResponse(Packet): + name="NBNS Wait for Acknowledgement Response" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0xBC07), + ShortField("QDCOUNT",0), + ShortField("ANCOUNT",1), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("RR_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), + IntField("TTL", 2), + ShortField("RDLENGTH",2), + BitField("RDATA",10512,16)] #10512=0010100100010000 + +class NBTDatagram(Packet): + name="NBT Datagram Packet" + fields_desc= [ByteField("Type", 0x10), + ByteField("Flags", 0x02), + ShortField("ID", 0), + IPField("SourceIP", "127.0.0.1"), + ShortField("SourcePort", 138), + ShortField("Length", 272), + ShortField("Offset", 0), + NetBIOSNameField("SourceName","windows"), + ShortEnumField("SUFFIX1",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + NetBIOSNameField("DestinationName","windows"), + ShortEnumField("SUFFIX2",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0)] + + +class NBTSession(Packet): + name="NBT Session Packet" + fields_desc= [ByteEnumField("TYPE",0,{0x00:"Session Message",0x81:"Session Request",0x82:"Positive Session Response",0x83:"Negative Session Response",0x84:"Retarget Session Response",0x85:"Session Keepalive"}), + BitField("RESERVED",0x00,7), + BitField("LENGTH",0,17)] + +bind_layers( UDP, NBNSQueryRequest, dport=137) +bind_layers( UDP, NBNSRequest, dport=137) +bind_layers( UDP, NBNSQueryResponse, sport=137) +bind_layers( UDP, NBNSQueryResponseNegative, sport=137) +bind_layers( UDP, NBNSNodeStatusResponse, sport=137) +bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) +bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) +bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseService, ) +bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseEnd, ) +bind_layers( UDP, NBNSWackResponse, sport=137) +bind_layers( UDP, NBTDatagram, dport=138) +bind_layers( TCP, NBTSession, dport=139) diff --git a/scapy/layers/netflow.py b/scapy/layers/netflow.py new file mode 100644 index 0000000000000000000000000000000000000000..aa652e205def1f3d37356d0686bb009b3ab0d44d --- /dev/null +++ b/scapy/layers/netflow.py @@ -0,0 +1,35 @@ +# Cisco Netflow Protocol version 1 +class NetflowHeader(Packet): + name = "Netflow Header" + fields_desc = [ ShortField("version", 1) ] + +class NetflowHeaderV1(Packet): + name = "Netflow Header V1" + fields_desc = [ ShortField("count", 0), + IntField("sysUptime", 0), + IntField("unixSecs", 0), + IntField("unixNanoSeconds", 0) ] + + +class NetflowRecordV1(Packet): + name = "Netflow Record" + fields_desc = [ IPField("ipsrc", "0.0.0.0"), + IPField("ipdst", "0.0.0.0"), + IPField("nexthop", "0.0.0.0"), + ShortField("inputIfIndex", 0), + ShortField("outpuIfIndex", 0), + IntField("dpkts", 0), + IntField("dbytes", 0), + IntField("starttime", 0), + IntField("endtime", 0), + ShortField("srcport", 0), + ShortField("dstport", 0), + ShortField("padding", 0), + ByteField("proto", 0), + ByteField("tos", 0), + IntField("padding1", 0), + IntField("padding2", 0) ] + + +bind_layers( NetflowHeader, NetflowHeaderV1, version=1) +bind_layers( NetflowHeaderV1, NetflowRecordV1, ) diff --git a/scapy/layers/ntp.py b/scapy/layers/ntp.py new file mode 100644 index 0000000000000000000000000000000000000000..5f07bb9f9ac105e60360c92b0028ab0ea7430d5d --- /dev/null +++ b/scapy/layers/ntp.py @@ -0,0 +1,77 @@ +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP + +# seconds between 01-01-1900 and 01-01-1970 +ntp_basetime = 2208988800 + +class TimeStampField(BitField): + def __init__(self, name, default, size): + BitField.__init__(self, name, default, size) + self.size = size + def getfield(self, pkt, s): + s,timestamp = BitField.getfield(self, pkt, s) + + if timestamp: + # timestamp is a 64 bits field : + # + first 32 bits : number of seconds since 1900 + # + last 32 bits : fraction part + timestamp >>= 32 + timestamp -= ntp_basetime + + from time import gmtime, strftime + b = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(timestamp)) + else: + b = 'None' + + return s, b + def addfield(self, pkt, s, val): + t = -1 + if type(val) is str: + from time import strptime, mktime + t = int(mktime(strptime(val))) + ntp_basetime + 3600 + else: + if val == -1: + from time import time + t = int(time()) + ntp_basetime + else: + t = val + t <<= 32 + return BitField.addfield(self,pkt,s, t) + + +class NTP(Packet): + # RFC 1769 + name = "NTP" + fields_desc = [ + BitEnumField('leap', 0, 2, + { 0: 'nowarning', + 1: 'longminute', + 2: 'shortminute', + 3: 'notsync'}), + BitField('version', 3, 3), + BitEnumField('mode', 3, 3, + { 0: 'reserved', + 1: 'sym_active', + 2: 'sym_passive', + 3: 'client', + 4: 'server', + 5: 'broadcast', + 6: 'control', + 7: 'private'}), + BitField('stratum', 2, 8), + BitField('poll', 0xa, 8), ### XXX : it's a signed int + BitField('precision', 0, 8), ### XXX : it's a signed int + FloatField('delay', 0, 32), + FloatField('dispersion', 0, 32), + IPField('id', "127.0.0.1"), + TimeStampField('ref', 0, 64), + TimeStampField('orig', -1, 64), # -1 means current time + TimeStampField('recv', 0, 64), + TimeStampField('sent', -1, 64) + ] + def mysummary(self): + return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%") + + +bind_layers( UDP, NTP, dport=123, sport=123) diff --git a/scapy/layers/ppp.py b/scapy/layers/ppp.py new file mode 100644 index 0000000000000000000000000000000000000000..cec4e18db2db95a35b11b30651e78b4ff9b8a437 --- /dev/null +++ b/scapy/layers/ppp.py @@ -0,0 +1,362 @@ +import struct +from scapy.packet import * +from scapy.layers.l2 import * +from scapy.layers.inet import * +from scapy.fields import * + +class PPPoE(Packet): + name = "PPP over Ethernet" + fields_desc = [ BitField("version", 1, 4), + BitField("type", 1, 4), + ByteEnumField("code", 0, {0:"Session"}), + XShortField("sessionid", 0x0), + ShortField("len", None) ] + + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-6 + p = p[:4]+struct.pack("!H", l)+p[6:] + return p + +class PPPoED(PPPoE): + name = "PPP over Ethernet Discovery" + fields_desc = [ BitField("version", 1, 4), + BitField("type", 1, 4), + ByteEnumField("code", 0x09, {0x09:"PADI",0x07:"PADO",0x19:"PADR",0x65:"PADS",0xa7:"PADT"}), + XShortField("sessionid", 0x0), + ShortField("len", None) ] + + +_PPP_proto = { 0x0001: "Padding Protocol", + 0x0003: "ROHC small-CID [RFC3095]", + 0x0005: "ROHC large-CID [RFC3095]", + 0x0021: "Internet Protocol version 4", + 0x0023: "OSI Network Layer", + 0x0025: "Xerox NS IDP", + 0x0027: "DECnet Phase IV", + 0x0029: "Appletalk", + 0x002b: "Novell IPX", + 0x002d: "Van Jacobson Compressed TCP/IP", + 0x002f: "Van Jacobson Uncompressed TCP/IP", + 0x0031: "Bridging PDU", + 0x0033: "Stream Protocol (ST-II)", + 0x0035: "Banyan Vines", + 0x0037: "reserved (until 1993) [Typo in RFC1172]", + 0x0039: "AppleTalk EDDP", + 0x003b: "AppleTalk SmartBuffered", + 0x003d: "Multi-Link [RFC1717]", + 0x003f: "NETBIOS Framing", + 0x0041: "Cisco Systems", + 0x0043: "Ascom Timeplex", + 0x0045: "Fujitsu Link Backup and Load Balancing (LBLB)", + 0x0047: "DCA Remote Lan", + 0x0049: "Serial Data Transport Protocol (PPP-SDTP)", + 0x004b: "SNA over 802.2", + 0x004d: "SNA", + 0x004f: "IPv6 Header Compression", + 0x0051: "KNX Bridging Data [ianp]", + 0x0053: "Encryption [Meyer]", + 0x0055: "Individual Link Encryption [Meyer]", + 0x0057: "Internet Protocol version 6 [Hinden]", + 0x0059: "PPP Muxing [RFC3153]", + 0x005b: "Vendor-Specific Network Protocol (VSNP) [RFC3772]", + 0x0061: "RTP IPHC Full Header [RFC3544]", + 0x0063: "RTP IPHC Compressed TCP [RFC3544]", + 0x0065: "RTP IPHC Compressed Non TCP [RFC3544]", + 0x0067: "RTP IPHC Compressed UDP 8 [RFC3544]", + 0x0069: "RTP IPHC Compressed RTP 8 [RFC3544]", + 0x006f: "Stampede Bridging", + 0x0071: "Reserved [Fox]", + 0x0073: "MP+ Protocol [Smith]", + 0x007d: "reserved (Control Escape) [RFC1661]", + 0x007f: "reserved (compression inefficient [RFC1662]", + 0x0081: "Reserved Until 20-Oct-2000 [IANA]", + 0x0083: "Reserved Until 20-Oct-2000 [IANA]", + 0x00c1: "NTCITS IPI [Ungar]", + 0x00cf: "reserved (PPP NLID)", + 0x00fb: "single link compression in multilink [RFC1962]", + 0x00fd: "compressed datagram [RFC1962]", + 0x00ff: "reserved (compression inefficient)", + 0x0201: "802.1d Hello Packets", + 0x0203: "IBM Source Routing BPDU", + 0x0205: "DEC LANBridge100 Spanning Tree", + 0x0207: "Cisco Discovery Protocol [Sastry]", + 0x0209: "Netcs Twin Routing [Korfmacher]", + 0x020b: "STP - Scheduled Transfer Protocol [Segal]", + 0x020d: "EDP - Extreme Discovery Protocol [Grosser]", + 0x0211: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", + 0x0213: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", + 0x0231: "Luxcom", + 0x0233: "Sigma Network Systems", + 0x0235: "Apple Client Server Protocol [Ridenour]", + 0x0281: "MPLS Unicast [RFC3032] ", + 0x0283: "MPLS Multicast [RFC3032]", + 0x0285: "IEEE p1284.4 standard - data packets [Batchelder]", + 0x0287: "ETSI TETRA Network Protocol Type 1 [Nieminen]", + 0x0289: "Multichannel Flow Treatment Protocol [McCann]", + 0x2063: "RTP IPHC Compressed TCP No Delta [RFC3544]", + 0x2065: "RTP IPHC Context State [RFC3544]", + 0x2067: "RTP IPHC Compressed UDP 16 [RFC3544]", + 0x2069: "RTP IPHC Compressed RTP 16 [RFC3544]", + 0x4001: "Cray Communications Control Protocol [Stage]", + 0x4003: "CDPD Mobile Network Registration Protocol [Quick]", + 0x4005: "Expand accelerator protocol [Rachmani]", + 0x4007: "ODSICP NCP [Arvind]", + 0x4009: "DOCSIS DLL [Gaedtke]", + 0x400B: "Cetacean Network Detection Protocol [Siller]", + 0x4021: "Stacker LZS [Simpson]", + 0x4023: "RefTek Protocol [Banfill]", + 0x4025: "Fibre Channel [Rajagopal]", + 0x4027: "EMIT Protocols [Eastham]", + 0x405b: "Vendor-Specific Protocol (VSP) [RFC3772]", + 0x8021: "Internet Protocol Control Protocol", + 0x8023: "OSI Network Layer Control Protocol", + 0x8025: "Xerox NS IDP Control Protocol", + 0x8027: "DECnet Phase IV Control Protocol", + 0x8029: "Appletalk Control Protocol", + 0x802b: "Novell IPX Control Protocol", + 0x802d: "reserved", + 0x802f: "reserved", + 0x8031: "Bridging NCP", + 0x8033: "Stream Protocol Control Protocol", + 0x8035: "Banyan Vines Control Protocol", + 0x8037: "reserved (until 1993)", + 0x8039: "reserved", + 0x803b: "reserved", + 0x803d: "Multi-Link Control Protocol", + 0x803f: "NETBIOS Framing Control Protocol", + 0x8041: "Cisco Systems Control Protocol", + 0x8043: "Ascom Timeplex", + 0x8045: "Fujitsu LBLB Control Protocol", + 0x8047: "DCA Remote Lan Network Control Protocol (RLNCP)", + 0x8049: "Serial Data Control Protocol (PPP-SDCP)", + 0x804b: "SNA over 802.2 Control Protocol", + 0x804d: "SNA Control Protocol", + 0x804f: "IP6 Header Compression Control Protocol", + 0x8051: "KNX Bridging Control Protocol [ianp]", + 0x8053: "Encryption Control Protocol [Meyer]", + 0x8055: "Individual Link Encryption Control Protocol [Meyer]", + 0x8057: "IPv6 Control Protovol [Hinden]", + 0x8059: "PPP Muxing Control Protocol [RFC3153]", + 0x805b: "Vendor-Specific Network Control Protocol (VSNCP) [RFC3772]", + 0x806f: "Stampede Bridging Control Protocol", + 0x8073: "MP+ Control Protocol [Smith]", + 0x8071: "Reserved [Fox]", + 0x807d: "Not Used - reserved [RFC1661]", + 0x8081: "Reserved Until 20-Oct-2000 [IANA]", + 0x8083: "Reserved Until 20-Oct-2000 [IANA]", + 0x80c1: "NTCITS IPI Control Protocol [Ungar]", + 0x80cf: "Not Used - reserved [RFC1661]", + 0x80fb: "single link compression in multilink control [RFC1962]", + 0x80fd: "Compression Control Protocol [RFC1962]", + 0x80ff: "Not Used - reserved [RFC1661]", + 0x8207: "Cisco Discovery Protocol Control [Sastry]", + 0x8209: "Netcs Twin Routing [Korfmacher]", + 0x820b: "STP - Control Protocol [Segal]", + 0x820d: "EDPCP - Extreme Discovery Protocol Ctrl Prtcl [Grosser]", + 0x8235: "Apple Client Server Protocol Control [Ridenour]", + 0x8281: "MPLSCP [RFC3032]", + 0x8285: "IEEE p1284.4 standard - Protocol Control [Batchelder]", + 0x8287: "ETSI TETRA TNP1 Control Protocol [Nieminen]", + 0x8289: "Multichannel Flow Treatment Protocol [McCann]", + 0xc021: "Link Control Protocol", + 0xc023: "Password Authentication Protocol", + 0xc025: "Link Quality Report", + 0xc027: "Shiva Password Authentication Protocol", + 0xc029: "CallBack Control Protocol (CBCP)", + 0xc02b: "BACP Bandwidth Allocation Control Protocol [RFC2125]", + 0xc02d: "BAP [RFC2125]", + 0xc05b: "Vendor-Specific Authentication Protocol (VSAP) [RFC3772]", + 0xc081: "Container Control Protocol [KEN]", + 0xc223: "Challenge Handshake Authentication Protocol", + 0xc225: "RSA Authentication Protocol [Narayana]", + 0xc227: "Extensible Authentication Protocol [RFC2284]", + 0xc229: "Mitsubishi Security Info Exch Ptcl (SIEP) [Seno]", + 0xc26f: "Stampede Bridging Authorization Protocol", + 0xc281: "Proprietary Authentication Protocol [KEN]", + 0xc283: "Proprietary Authentication Protocol [Tackabury]", + 0xc481: "Proprietary Node ID Authentication Protocol [KEN]"} + + +class HDLC(Packet): + fields_desc = [ XByteField("address",0xff), + XByteField("control",0x03) ] + +class PPP_metaclass(Packet_metaclass): + def __call__(self, _pkt=None, *args, **kargs): + cls = self + if _pkt and _pkt[0] == '\xff': + cls = HDLC + i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) + i.__init__(_pkt=_pkt, *args, **kargs) + return i + + +class PPP(Packet): + __metaclass__ = PPP_metaclass + name = "PPP Link Layer" + fields_desc = [ ShortEnumField("proto", 0x0021, _PPP_proto) ] + +_PPP_conftypes = { 1:"Configure-Request", + 2:"Configure-Ack", + 3:"Configure-Nak", + 4:"Configure-Reject", + 5:"Terminate-Request", + 6:"Terminate-Ack", + 7:"Code-Reject", + 8:"Protocol-Reject", + 9:"Echo-Request", + 10:"Echo-Reply", + 11:"Discard-Request", + 14:"Reset-Request", + 15:"Reset-Ack", + } + +class PPP_Option_metaclass(Packet_metaclass): + _known_options={} + def __call__(self, _pkt=None, *args, **kargs): + cls = self + if _pkt: + t = ord(_pkt[0]) + cls = self._known_options.get(t,self) + i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) + i.__init__(_pkt=_pkt, *args, **kargs) + return i + def _register(self, cls): + self._known_options[cls.fields_desc[0].default] = cls + + +### PPP IPCP stuff (RFC 1332) + +# All IPCP options are defined below (names and associated classes) +_PPP_ipcpopttypes = { 1:"IP-Addresses (Deprecated)", + 2:"IP-Compression-Protocol", + 3:"IP-Address", + 4:"Mobile-IPv4", # not implemented, present for completeness + 129:"Primary-DNS-Address", + 130:"Primary-NBNS-Address", + 131:"Secondary-DNS-Address", + 132:"Secondary-NBNS-Address"} + + + +class PPP_IPCP_Option_metaclass(PPP_Option_metaclass): + _known_options={} + + +class PPP_IPCP_Option(Packet): + __metaclass__=PPP_IPCP_Option_metaclass + name = "PPP IPCP Option" + fields_desc = [ ByteEnumField("type" , None , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] + def extract_padding(self, pay): + return "",pay + +class PPP_IPCP_Specific_Option_metaclass(PPP_IPCP_Option_metaclass): + def __new__(cls, name, bases, dct): + newcls = super(PPP_IPCP_Specific_Option_metaclass, cls).__new__(cls, name, bases, dct) + PPP_IPCP_Option._register(newcls) + + +class PPP_IPCP_Option_IPAddress(PPP_IPCP_Option): + __metaclass__=PPP_IPCP_Specific_Option_metaclass + name = "PPP IPCP Option: IP Address" + fields_desc = [ ByteEnumField("type" , 3 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + +class PPP_IPCP_Option_DNS1(PPP_IPCP_Option): + __metaclass__=PPP_IPCP_Specific_Option_metaclass + name = "PPP IPCP Option: DNS1 Address" + fields_desc = [ ByteEnumField("type" , 129 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + +class PPP_IPCP_Option_DNS2(PPP_IPCP_Option): + __metaclass__=PPP_IPCP_Specific_Option_metaclass + name = "PPP IPCP Option: DNS2 Address" + fields_desc = [ ByteEnumField("type" , 131 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + +class PPP_IPCP_Option_NBNS1(PPP_IPCP_Option): + __metaclass__=PPP_IPCP_Specific_Option_metaclass + name = "PPP IPCP Option: NBNS1 Address" + fields_desc = [ ByteEnumField("type" , 130 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + +class PPP_IPCP_Option_NBNS2(PPP_IPCP_Option): + __metaclass__=PPP_IPCP_Specific_Option_metaclass + name = "PPP IPCP Option: NBNS2 Address" + fields_desc = [ ByteEnumField("type" , 132 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + + + + +class PPP_IPCP(Packet): + fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), + XByteField("id", 0 ), + FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), + PacketListField("options", [], PPP_IPCP_Option, length_from=lambda p:p.len-4,) ] + + +### ECP + +_PPP_ecpopttypes = { 0:"OUI", + 1:"DESE", } + +class PPP_ECP_Option_metaclass(PPP_Option_metaclass): + _known_options={} + + + +class PPP_ECP_Option(Packet): + __metaclass__=PPP_ECP_Option_metaclass + name = "PPP ECP Option" + fields_desc = [ ByteEnumField("type" , None , _PPP_ecpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] + def extract_padding(self, pay): + return "",pay + +class PPP_ECP_Specific_Option_metaclass(PPP_ECP_Option_metaclass): + def __new__(cls, name, bases, dct): + newcls = super(PPP_ECP_Specific_Option_metaclass, cls).__new__(cls, name, bases, dct) + PPP_ECP_Option._register(newcls) + + +class PPP_ECP_Option_OUI(PPP_ECP_Option): + __metaclass__=PPP_ECP_Specific_Option_metaclass + fields_desc = [ ByteEnumField("type" , 0 , _PPP_ecpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + StrFixedLenField("oui","",3), + ByteField("subtype",0), + StrLenField("data", "", length_from=lambda p:p.len-6) ] + + + +class PPP_ECP(Packet): + fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), + XByteField("id", 0 ), + FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), + PacketListField("options", [], PPP_ECP_Option, length_from=lambda p:p.len-4,) ] + +bind_layers( Ether, PPPoED, type=34915) +bind_layers( Ether, PPPoE, type=34916) +bind_layers( CookedLinux, PPPoED, proto=34915) +bind_layers( CookedLinux, PPPoE, proto=34916) +bind_layers( PPPoE, PPP, code=0) +bind_layers( HDLC, PPP, ) +bind_layers( PPP, IP, proto=33) +bind_layers( PPP, PPP_IPCP, proto=0x8021) +bind_layers( PPP, PPP_ECP, proto=0x8053) diff --git a/scapy/layers/radius.py b/scapy/layers/radius.py new file mode 100644 index 0000000000000000000000000000000000000000..13946b9a85a675a9f99b96c4932aeb84d4f06839 --- /dev/null +++ b/scapy/layers/radius.py @@ -0,0 +1,56 @@ +import struct +from scapy.packet import * +from scapy.fields import * + +class Radius(Packet): + name = "Radius" + fields_desc = [ ByteEnumField("code", 1, {1: "Access-Request", + 2: "Access-Accept", + 3: "Access-Reject", + 4: "Accounting-Request", + 5: "Accounting-Accept", + 6: "Accounting-Status", + 7: "Password-Request", + 8: "Password-Ack", + 9: "Password-Reject", + 10: "Accounting-Message", + 11: "Access-Challenge", + 12: "Status-Server", + 13: "Status-Client", + 21: "Resource-Free-Request", + 22: "Resource-Free-Response", + 23: "Resource-Query-Request", + 24: "Resource-Query-Response", + 25: "Alternate-Resource-Reclaim-Request", + 26: "NAS-Reboot-Request", + 27: "NAS-Reboot-Response", + 29: "Next-Passcode", + 30: "New-Pin", + 31: "Terminate-Session", + 32: "Password-Expired", + 33: "Event-Request", + 34: "Event-Response", + 40: "Disconnect-Request", + 41: "Disconnect-ACK", + 42: "Disconnect-NAK", + 43: "CoA-Request", + 44: "CoA-ACK", + 45: "CoA-NAK", + 50: "IP-Address-Allocate", + 51: "IP-Address-Release", + 253: "Experimental-use", + 254: "Reserved", + 255: "Reserved"} ), + ByteField("id", 0), + ShortField("len", None), + StrFixedLenField("authenticator","",16) ] + def post_build(self, p, pay): + p += pay + l = self.len + if l is None: + l = len(p) + p = p[:2]+struct.pack("!H",l)+p[4:] + return p + + + diff --git a/scapy/layers/rip.py b/scapy/layers/rip.py new file mode 100644 index 0000000000000000000000000000000000000000..f41b611cc29a62079f2c6c896042355f3d7842ab --- /dev/null +++ b/scapy/layers/rip.py @@ -0,0 +1,31 @@ +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP + +class RIP(Packet): + name = "RIP header" + fields_desc = [ + ByteEnumField("command",1,{1:"req",2:"resp",3:"traceOn",4:"traceOff",5:"sun", + 6:"trigReq",7:"trigResp",8:"trigAck",9:"updateReq", + 10:"updateResp",11:"updateAck"}), + ByteField("version",1), + ShortField("null",0), + ] + +class RIPEntry(Packet): + name = "RIP entry" + fields_desc = [ + ShortEnumField("AF",2,{2:"IP"}), + ShortField("RouteTag",0), + IPField("addr","0.0.0.0"), + IPField("mask","0.0.0.0"), + IPField("nextHop","0.0.0.0"), + IntEnumField("metric",1,{16:"Unreach"}), + ] + + + +bind_layers( UDP, RIP, sport=520) +bind_layers( UDP, RIP, dport=520) +bind_layers( RIP, RIPEntry, ) +bind_layers( RIPEntry, RIPEntry, ) diff --git a/scapy/layers/rtp.py b/scapy/layers/rtp.py new file mode 100644 index 0000000000000000000000000000000000000000..f926f2aa5da0dc9ca6c66be7d2edf03031162884 --- /dev/null +++ b/scapy/layers/rtp.py @@ -0,0 +1,31 @@ +from scapy.packet import * +from scapy.fields import * + +_rtp_payload_types = { + # http://www.iana.org/assignments/rtp-parameters + 0: 'G.711 PCMU', 3: 'GSM', + 4: 'G723', 5: 'DVI4', + 6: 'DVI4', 7: 'LPC', + 8: 'PCMA', 9: 'G722', + 10: 'L16', 11: 'L16', + 12: 'QCELP', 13: 'CN', + 14: 'MPA', 15: 'G728', + 16: 'DVI4', 17: 'DVI4', + 18: 'G729', 25: 'CelB', + 26: 'JPEG', 28: 'nv', + 31: 'H261', 32: 'MPV', + 33: 'MP2T', 34: 'H263' } + +class RTP(Packet): + name="RTP" + fields_desc = [ BitField('version', 2, 2), + BitField('padding', 0, 1), + BitField('extension', 0, 1), + BitFieldLenField('numsync', None, 4, count_of='sync'), + BitField('marker', 0, 1), + BitEnumField('payload', 0, 7, _rtp_payload_types), + ShortField('sequence', 0), + IntField('timestamp', 0), + IntField('sourcesync', 0), + FieldListField('sync', [], IntField("id",0), count_from=lambda pkt:pkt.numsync) ] + diff --git a/scapy/layers/sebek.py b/scapy/layers/sebek.py new file mode 100644 index 0000000000000000000000000000000000000000..a22138c18401a4dcc3164f4fa275ca0ddd7c1a7c --- /dev/null +++ b/scapy/layers/sebek.py @@ -0,0 +1,95 @@ +### SEBEK + + +class SebekHead(Packet): + name = "Sebek header" + fields_desc = [ XIntField("magic", 0xd0d0d0), + ShortField("version", 1), + ShortEnumField("type", 0, {"read":0, "write":1, + "socket":2, "open":3}), + IntField("counter", 0), + IntField("time_sec", 0), + IntField("time_usec", 0) ] + def mysummary(self): + return self.sprintf("Sebek Header v%SebekHead.version% %SebekHead.type%") + +# we need this because Sebek headers differ between v1 and v3, and +# between v3 type socket and v3 others + +class SebekV1(Packet): + name = "Sebek v1" + fields_desc = [ IntField("pid", 0), + IntField("uid", 0), + IntField("fd", 0), + StrFixedLenField("command", "", 12), + FieldLenField("data_length", None, "data",fmt="I"), + StrLenField("data", "", length_from=lambda x:x.data_length) ] + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v1 %SebekHead.type% (%SebekV1.command%)") + else: + return self.sprintf("Sebek v1 (%SebekV1.command%)") + +class SebekV3(Packet): + name = "Sebek v3" + fields_desc = [ IntField("parent_pid", 0), + IntField("pid", 0), + IntField("uid", 0), + IntField("fd", 0), + IntField("inode", 0), + StrFixedLenField("command", "", 12), + FieldLenField("data_length", None, "data",fmt="I"), + StrLenField("data", "", length_from=lambda x:x.data_length) ] + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3.command%)") + else: + return self.sprintf("Sebek v3 (%SebekV3.command%)") + +class SebekV2(SebekV3): + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.command%)") + else: + return self.sprintf("Sebek v2 (%SebekV2.command%)") + +class SebekV3Sock(Packet): + name = "Sebek v2 socket" + fields_desc = [ IntField("parent_pid", 0), + IntField("pid", 0), + IntField("uid", 0), + IntField("fd", 0), + IntField("inode", 0), + StrFixedLenField("command", "", 12), + IntField("data_length", 15), + IPField("dip", "127.0.0.1"), + ShortField("dport", 0), + IPField("sip", "127.0.0.1"), + ShortField("sport", 0), + ShortEnumField("call", 0, { "bind":2, + "connect":3, "listen":4, + "accept":5, "sendmsg":16, + "recvmsg":17, "sendto":11, + "recvfrom":12}), + ByteEnumField("proto", 0, IP_PROTOS) ] + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3Sock.command%)") + else: + return self.sprintf("Sebek v3 socket (%SebekV3Sock.command%)") + +class SebekV2Sock(SebekV3Sock): + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.command%)") + else: + return self.sprintf("Sebek v2 socket (%SebekV2Sock.command%)") + +bind_layers( UDP, SebekHead, sport=1101) +bind_layers( UDP, SebekHead, dport=1101) +bind_layers( UDP, SebekHead, dport=1101, sport=1101) +bind_layers( SebekHead, SebekV1, version=1) +bind_layers( SebekHead, SebekV2Sock, version=2, type=2) +bind_layers( SebekHead, SebekV2, version=2) +bind_layers( SebekHead, SebekV3Sock, version=3, type=2) +bind_layers( SebekHead, SebekV3, version=3) diff --git a/scapy/layers/skinny.py b/scapy/layers/skinny.py new file mode 100644 index 0000000000000000000000000000000000000000..d8eb918ffcec18246c60b9ca4293dce04c9f15d8 --- /dev/null +++ b/scapy/layers/skinny.py @@ -0,0 +1,154 @@ +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import TCP + +# Cisco Skinny protocol + +# shamelessly ripped from Ethereal dissector +skinny_messages = { +# Station -> Callmanager + 0x0000: "KeepAliveMessage", + 0x0001: "RegisterMessage", + 0x0002: "IpPortMessage", + 0x0003: "KeypadButtonMessage", + 0x0004: "EnblocCallMessage", + 0x0005: "StimulusMessage", + 0x0006: "OffHookMessage", + 0x0007: "OnHookMessage", + 0x0008: "HookFlashMessage", + 0x0009: "ForwardStatReqMessage", + 0x000A: "SpeedDialStatReqMessage", + 0x000B: "LineStatReqMessage", + 0x000C: "ConfigStatReqMessage", + 0x000D: "TimeDateReqMessage", + 0x000E: "ButtonTemplateReqMessage", + 0x000F: "VersionReqMessage", + 0x0010: "CapabilitiesResMessage", + 0x0011: "MediaPortListMessage", + 0x0012: "ServerReqMessage", + 0x0020: "AlarmMessage", + 0x0021: "MulticastMediaReceptionAck", + 0x0022: "OpenReceiveChannelAck", + 0x0023: "ConnectionStatisticsRes", + 0x0024: "OffHookWithCgpnMessage", + 0x0025: "SoftKeySetReqMessage", + 0x0026: "SoftKeyEventMessage", + 0x0027: "UnregisterMessage", + 0x0028: "SoftKeyTemplateReqMessage", + 0x0029: "RegisterTokenReq", + 0x002A: "MediaTransmissionFailure", + 0x002B: "HeadsetStatusMessage", + 0x002C: "MediaResourceNotification", + 0x002D: "RegisterAvailableLinesMessage", + 0x002E: "DeviceToUserDataMessage", + 0x002F: "DeviceToUserDataResponseMessage", + 0x0030: "UpdateCapabilitiesMessage", + 0x0031: "OpenMultiMediaReceiveChannelAckMessage", + 0x0032: "ClearConferenceMessage", + 0x0033: "ServiceURLStatReqMessage", + 0x0034: "FeatureStatReqMessage", + 0x0035: "CreateConferenceResMessage", + 0x0036: "DeleteConferenceResMessage", + 0x0037: "ModifyConferenceResMessage", + 0x0038: "AddParticipantResMessage", + 0x0039: "AuditConferenceResMessage", + 0x0040: "AuditParticipantResMessage", + 0x0041: "DeviceToUserDataVersion1Message", +# Callmanager -> Station */ + 0x0081: "RegisterAckMessage", + 0x0082: "StartToneMessage", + 0x0083: "StopToneMessage", + 0x0085: "SetRingerMessage", + 0x0086: "SetLampMessage", + 0x0087: "SetHkFDetectMessage", + 0x0088: "SetSpeakerModeMessage", + 0x0089: "SetMicroModeMessage", + 0x008A: "StartMediaTransmission", + 0x008B: "StopMediaTransmission", + 0x008C: "StartMediaReception", + 0x008D: "StopMediaReception", + 0x008F: "CallInfoMessage", + 0x0090: "ForwardStatMessage", + 0x0091: "SpeedDialStatMessage", + 0x0092: "LineStatMessage", + 0x0093: "ConfigStatMessage", + 0x0094: "DefineTimeDate", + 0x0095: "StartSessionTransmission", + 0x0096: "StopSessionTransmission", + 0x0097: "ButtonTemplateMessage", + 0x0098: "VersionMessage", + 0x0099: "DisplayTextMessage", + 0x009A: "ClearDisplay", + 0x009B: "CapabilitiesReqMessage", + 0x009C: "EnunciatorCommandMessage", + 0x009D: "RegisterRejectMessage", + 0x009E: "ServerResMessage", + 0x009F: "Reset", + 0x0100: "KeepAliveAckMessage", + 0x0101: "StartMulticastMediaReception", + 0x0102: "StartMulticastMediaTransmission", + 0x0103: "StopMulticastMediaReception", + 0x0104: "StopMulticastMediaTransmission", + 0x0105: "OpenReceiveChannel", + 0x0106: "CloseReceiveChannel", + 0x0107: "ConnectionStatisticsReq", + 0x0108: "SoftKeyTemplateResMessage", + 0x0109: "SoftKeySetResMessage", + 0x0110: "SelectSoftKeysMessage", + 0x0111: "CallStateMessage", + 0x0112: "DisplayPromptStatusMessage", + 0x0113: "ClearPromptStatusMessage", + 0x0114: "DisplayNotifyMessage", + 0x0115: "ClearNotifyMessage", + 0x0116: "ActivateCallPlaneMessage", + 0x0117: "DeactivateCallPlaneMessage", + 0x0118: "UnregisterAckMessage", + 0x0119: "BackSpaceReqMessage", + 0x011A: "RegisterTokenAck", + 0x011B: "RegisterTokenReject", + 0x0042: "DeviceToUserDataResponseVersion1Message", + 0x011C: "StartMediaFailureDetection", + 0x011D: "DialedNumberMessage", + 0x011E: "UserToDeviceDataMessage", + 0x011F: "FeatureStatMessage", + 0x0120: "DisplayPriNotifyMessage", + 0x0121: "ClearPriNotifyMessage", + 0x0122: "StartAnnouncementMessage", + 0x0123: "StopAnnouncementMessage", + 0x0124: "AnnouncementFinishMessage", + 0x0127: "NotifyDtmfToneMessage", + 0x0128: "SendDtmfToneMessage", + 0x0129: "SubscribeDtmfPayloadReqMessage", + 0x012A: "SubscribeDtmfPayloadResMessage", + 0x012B: "SubscribeDtmfPayloadErrMessage", + 0x012C: "UnSubscribeDtmfPayloadReqMessage", + 0x012D: "UnSubscribeDtmfPayloadResMessage", + 0x012E: "UnSubscribeDtmfPayloadErrMessage", + 0x012F: "ServiceURLStatMessage", + 0x0130: "CallSelectStatMessage", + 0x0131: "OpenMultiMediaChannelMessage", + 0x0132: "StartMultiMediaTransmission", + 0x0133: "StopMultiMediaTransmission", + 0x0134: "MiscellaneousCommandMessage", + 0x0135: "FlowControlCommandMessage", + 0x0136: "CloseMultiMediaReceiveChannel", + 0x0137: "CreateConferenceReqMessage", + 0x0138: "DeleteConferenceReqMessage", + 0x0139: "ModifyConferenceReqMessage", + 0x013A: "AddParticipantReqMessage", + 0x013B: "DropParticipantReqMessage", + 0x013C: "AuditConferenceReqMessage", + 0x013D: "AuditParticipantReqMessage", + 0x013F: "UserToDeviceDataVersion1Message", + } + + + +class Skinny(Packet): + name="Skinny" + fields_desc = [ LEIntField("len",0), + LEIntField("res",0), + LEIntEnumField("msg",0,skinny_messages) ] + +bind_layers( TCP, Skinny, dport=2000) +bind_layers( TCP, Skinny, sport=2000) diff --git a/scapy/layers/smb.py b/scapy/layers/smb.py new file mode 100644 index 0000000000000000000000000000000000000000..d999c360bb13e6d6e0769cc59484759404a42d6d --- /dev/null +++ b/scapy/layers/smb.py @@ -0,0 +1,345 @@ +from scapy.packet import * +from scapy.fields import * +from scapy.layers.netbios import NBTSession + + +# SMB NetLogon Response Header +class SMBNetlogon_Protocol_Response_Header(Packet): + name="SMBNetlogon Protocol Response Header" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x25,{0x25:"Trans"}), + ByteField("Error_Class",0x02), + ByteField("Reserved",0), + LEShortField("Error_code",4), + ByteField("Flags",0), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",0), + LEShortField("UID",0), + LEShortField("MID",0), + ByteField("WordCount",17), + LEShortField("TotalParamCount",0), + LEShortField("TotalDataCount",112), + LEShortField("MaxParamCount",0), + LEShortField("MaxDataCount",0), + ByteField("MaxSetupCount",0), + ByteField("unused2",0), + LEShortField("Flags3",0), + ByteField("TimeOut1",0xe8), + ByteField("TimeOut2",0x03), + LEShortField("unused3",0), + LEShortField("unused4",0), + LEShortField("ParamCount2",0), + LEShortField("ParamOffset",0), + LEShortField("DataCount",112), + LEShortField("DataOffset",92), + ByteField("SetupCount", 3), + ByteField("unused5", 0)] + +# SMB MailSlot Protocol +class SMBMailSlot(Packet): + name = "SMB Mail Slot Protocol" + fields_desc = [LEShortField("opcode", 1), + LEShortField("priority", 1), + LEShortField("class", 2), + LEShortField("size", 135), + StrNullField("name","\MAILSLOT\NET\GETDC660")] + +# SMB NetLogon Protocol Response Tail SAM +class SMBNetlogon_Protocol_Response_Tail_SAM(Packet): + name = "SMB Netlogon Protocol Response Tail SAM" + fields_desc = [ByteEnumField("Command", 0x17, {0x12:"SAM logon request", 0x17:"SAM Active directory Response"}), + ByteField("unused", 0), + ShortField("Data1", 0), + ShortField("Data2", 0xfd01), + ShortField("Data3", 0), + ShortField("Data4", 0xacde), + ShortField("Data5", 0x0fe5), + ShortField("Data6", 0xd10a), + ShortField("Data7", 0x374c), + ShortField("Data8", 0x83e2), + ShortField("Data9", 0x7dd9), + ShortField("Data10", 0x3a16), + ShortField("Data11", 0x73ff), + ByteField("Data12", 0x04), + StrFixedLenField("Data13", "rmff", 4), + ByteField("Data14", 0x0), + ShortField("Data16", 0xc018), + ByteField("Data18", 0x0a), + StrFixedLenField("Data20", "rmff-win2k", 10), + ByteField("Data21", 0xc0), + ShortField("Data22", 0x18c0), + ShortField("Data23", 0x180a), + StrFixedLenField("Data24", "RMFF-WIN2K", 10), + ShortField("Data25", 0), + ByteField("Data26", 0x17), + StrFixedLenField("Data27", "Default-First-Site-Name", 23), + ShortField("Data28", 0x00c0), + ShortField("Data29", 0x3c10), + ShortField("Data30", 0x00c0), + ShortField("Data31", 0x0200), + ShortField("Data32", 0x0), + ShortField("Data33", 0xac14), + ShortField("Data34", 0x0064), + ShortField("Data35", 0x0), + ShortField("Data36", 0x0), + ShortField("Data37", 0x0), + ShortField("Data38", 0x0), + ShortField("Data39", 0x0d00), + ShortField("Data40", 0x0), + ShortField("Data41", 0xffff)] + +# SMB NetLogon Protocol Response Tail LM2.0 +class SMBNetlogon_Protocol_Response_Tail_LM20(Packet): + name = "SMB Netlogon Protocol Response Tail LM20" + fields_desc = [ByteEnumField("Command",0x06,{0x06:"LM 2.0 Response to logon request"}), + ByteField("unused", 0), + StrFixedLenField("DblSlash", "\\\\", 2), + StrNullField("ServerName","WIN"), + LEShortField("LM20Token", 0xffff)] + +# SMBNegociate Protocol Request Header +class SMBNegociate_Protocol_Request_Header(Packet): + name="SMBNegociate Protocol Request Header" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_code",0), + ByteField("Flags",0x18), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",0), + LEShortField("ByteCount",12)] + +# SMB Negociate Protocol Request Tail +class SMBNegociate_Protocol_Request_Tail(Packet): + name="SMB Negociate Protocol Request Tail" + fields_desc=[ByteField("BufferFormat",0x02), + StrNullField("BufferData","NT LM 0.12")] + +# SMBNegociate Protocol Response Advanced Security +class SMBNegociate_Protocol_Response_Advanced_Security(Packet): + name="SMBNegociate Protocol Response Advanced Security" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x98), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",17), + LEShortField("DialectIndex",7), + ByteField("SecurityMode",0x03), + LEShortField("MaxMpxCount",50), + LEShortField("MaxNumberVC",1), + LEIntField("MaxBufferSize",16144), + LEIntField("MaxRawSize",65536), + LEIntField("SessionKey",0x0000), + LEShortField("ServerCapabilities",0xf3f9), + BitField("UnixExtensions",0,1), + BitField("Reserved2",0,7), + BitField("ExtendedSecurity",1,1), + BitField("CompBulk",0,2), + BitField("Reserved3",0,5), +# There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. + LEIntField("ServerTimeHigh",0xD6228000L), + LEIntField("ServerTimeLow",0x1C4EF94), + LEShortField("ServerTimeZone",0x3c), + ByteField("EncryptionKeyLength",0), + LEFieldLenField("ByteCount", None, "SecurityBlob", adjust=lambda pkt,x:x-16), + BitField("GUID",0,128), + StrLenField("SecurityBlob", "", length_from=lambda x:x.ByteCount+16)] + +# SMBNegociate Protocol Response No Security +# When using no security, with EncryptionKeyLength=8, you must have an EncryptionKey before the DomainName +class SMBNegociate_Protocol_Response_No_Security(Packet): + name="SMBNegociate Protocol Response No Security" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x98), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",17), + LEShortField("DialectIndex",7), + ByteField("SecurityMode",0x03), + LEShortField("MaxMpxCount",50), + LEShortField("MaxNumberVC",1), + LEIntField("MaxBufferSize",16144), + LEIntField("MaxRawSize",65536), + LEIntField("SessionKey",0x0000), + LEShortField("ServerCapabilities",0xf3f9), + BitField("UnixExtensions",0,1), + BitField("Reserved2",0,7), + BitField("ExtendedSecurity",0,1), + FlagsField("CompBulk",0,2,"CB"), + BitField("Reserved3",0,5), + # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. + LEIntField("ServerTimeHigh",0xD6228000L), + LEIntField("ServerTimeLow",0x1C4EF94), + LEShortField("ServerTimeZone",0x3c), + ByteField("EncryptionKeyLength",8), + LEShortField("ByteCount",24), + BitField("EncryptionKey",0,64), + StrNullField("DomainName","WORKGROUP"), + StrNullField("ServerName","RMFF1")] + +# SMBNegociate Protocol Response No Security No Key +class SMBNegociate_Protocol_Response_No_Security_No_Key(Packet): + namez="SMBNegociate Protocol Response No Security No Key" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x98), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",17), + LEShortField("DialectIndex",7), + ByteField("SecurityMode",0x03), + LEShortField("MaxMpxCount",50), + LEShortField("MaxNumberVC",1), + LEIntField("MaxBufferSize",16144), + LEIntField("MaxRawSize",65536), + LEIntField("SessionKey",0x0000), + LEShortField("ServerCapabilities",0xf3f9), + BitField("UnixExtensions",0,1), + BitField("Reserved2",0,7), + BitField("ExtendedSecurity",0,1), + FlagsField("CompBulk",0,2,"CB"), + BitField("Reserved3",0,5), + # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. + LEIntField("ServerTimeHigh",0xD6228000L), + LEIntField("ServerTimeLow",0x1C4EF94), + LEShortField("ServerTimeZone",0x3c), + ByteField("EncryptionKeyLength",0), + LEShortField("ByteCount",16), + StrNullField("DomainName","WORKGROUP"), + StrNullField("ServerName","RMFF1")] + +# Session Setup AndX Request +class SMBSession_Setup_AndX_Request(Packet): + name="Session Setup AndX Request" + fields_desc=[StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x18), + LEShortField("Flags2",0x0001), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",13), + ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), + ByteField("Reserved2",0), + LEShortField("AndXOffset",96), + LEShortField("MaxBufferS",2920), + LEShortField("MaxMPXCount",50), + LEShortField("VCNumber",0), + LEIntField("SessionKey",0), + LEFieldLenField("ANSIPasswordLength",None,"ANSIPassword"), + LEShortField("UnicodePasswordLength",0), + LEIntField("Reserved3",0), + LEShortField("ServerCapabilities",0x05), + BitField("UnixExtensions",0,1), + BitField("Reserved4",0,7), + BitField("ExtendedSecurity",0,1), + BitField("CompBulk",0,2), + BitField("Reserved5",0,5), + LEShortField("ByteCount",35), + StrLenField("ANSIPassword", "Pass",length_from=lambda x:x.ANSIPasswordLength), + StrNullField("Account","GUEST"), + StrNullField("PrimaryDomain", ""), + StrNullField("NativeOS","Windows 4.0"), + StrNullField("NativeLanManager","Windows 4.0"), + ByteField("WordCount2",4), + ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), + ByteField("Reserved6",0), + LEShortField("AndXOffset2",0), + LEShortField("Flags3",0x2), + LEShortField("PasswordLength",0x1), + LEShortField("ByteCount2",18), + ByteField("Password",0), + StrNullField("Path","\\\\WIN2K\\IPC$"), + StrNullField("Service","IPC")] + +# Session Setup AndX Response +class SMBSession_Setup_AndX_Response(Packet): + name="Session Setup AndX Response" + fields_desc=[StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x90), + LEShortField("Flags2",0x1001), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",3), + ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), + ByteField("Reserved2",0), + LEShortField("AndXOffset",66), + LEShortField("Action",0), + LEShortField("ByteCount",25), + StrNullField("NativeOS","Windows 4.0"), + StrNullField("NativeLanManager","Windows 4.0"), + StrNullField("PrimaryDomain",""), + ByteField("WordCount2",3), + ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), + ByteField("Reserved3",0), + LEShortField("AndXOffset2",80), + LEShortField("OptionalSupport",0x01), + LEShortField("ByteCount2",5), + StrNullField("Service","IPC"), + StrNullField("NativeFileSystem","")] + +bind_layers( NBTSession, SMBNegociate_Protocol_Request_Header, ) +bind_layers( NBTSession, SMBNegociate_Protocol_Response_Advanced_Security, ExtendedSecurity=1) +bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security, ExtendedSecurity=0, EncryptionKeyLength=8) +bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security_No_Key, ExtendedSecurity=0, EncryptionKeyLength=0) +bind_layers( NBTSession, SMBSession_Setup_AndX_Request, ) +bind_layers( NBTSession, SMBSession_Setup_AndX_Response, ) +bind_layers( SMBNegociate_Protocol_Request_Header, SMBNegociate_Protocol_Request_Tail, ) +bind_layers( SMBNegociate_Protocol_Request_Tail, SMBNegociate_Protocol_Request_Tail, ) diff --git a/scapy/layers/snmp.py b/scapy/layers/snmp.py new file mode 100644 index 0000000000000000000000000000000000000000..58559cd5d278b32d7861e6396d81f8b3df81645f --- /dev/null +++ b/scapy/layers/snmp.py @@ -0,0 +1,244 @@ +from scapy.asn1packet import * +from scapy.asn1fields import * +from scapy.layers.inet import UDP + +########## +## SNMP ## +########## + +######[ ASN1 class ]###### + +class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): + name="SNMP" + PDU_GET = 0xa0 + PDU_NEXT = 0xa1 + PDU_RESPONSE = 0xa2 + PDU_SET = 0xa3 + PDU_TRAPv1 = 0xa4 + PDU_BULK = 0xa5 + PDU_INFORM = 0xa6 + PDU_TRAPv2 = 0xa7 + + +class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_GET + +class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_NEXT + +class ASN1_SNMP_PDU_RESPONSE(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_RESPONSE + +class ASN1_SNMP_PDU_SET(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_SET + +class ASN1_SNMP_PDU_TRAPv1(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_TRAPv1 + +class ASN1_SNMP_PDU_BULK(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_BULK + +class ASN1_SNMP_PDU_INFORM(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_INFORM + +class ASN1_SNMP_PDU_TRAPv2(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_TRAPv2 + + +######[ BER codecs ]####### + +class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_GET + +class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_NEXT + +class BERcodec_SNMP_PDU_RESPONSE(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_RESPONSE + +class BERcodec_SNMP_PDU_SET(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_SET + +class BERcodec_SNMP_PDU_TRAPv1(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_TRAPv1 + +class BERcodec_SNMP_PDU_BULK(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_BULK + +class BERcodec_SNMP_PDU_INFORM(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_INFORM + +class BERcodec_SNMP_PDU_TRAPv2(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_TRAPv2 + + + +######[ ASN1 fields ]###### + +class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_GET + +class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_NEXT + +class ASN1F_SNMP_PDU_RESPONSE(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_RESPONSE + +class ASN1F_SNMP_PDU_SET(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_SET + +class ASN1F_SNMP_PDU_TRAPv1(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv1 + +class ASN1F_SNMP_PDU_BULK(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_BULK + +class ASN1F_SNMP_PDU_INFORM(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_INFORM + +class ASN1F_SNMP_PDU_TRAPv2(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv2 + + + +######[ SNMP Packet ]###### + +SNMP_error = { 0: "no_error", + 1: "too_big", + 2: "no_such_name", + 3: "bad_value", + 4: "read_only", + 5: "generic_error", + 6: "no_access", + 7: "wrong_type", + 8: "wrong_length", + 9: "wrong_encoding", + 10: "wrong_value", + 11: "no_creation", + 12: "inconsistent_value", + 13: "ressource_unavailable", + 14: "commit_failed", + 15: "undo_failed", + 16: "authorization_error", + 17: "not_writable", + 18: "inconsistent_name", + } + +SNMP_trap_types = { 0: "cold_start", + 1: "warm_start", + 2: "link_down", + 3: "link_up", + 4: "auth_failure", + 5: "egp_neigh_loss", + 6: "enterprise_specific", + } + +class SNMPvarbind(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"), + ASN1F_field("value",ASN1_NULL(0)) + ) + + +class SNMPget(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPnext(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPresponse(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_RESPONSE( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPset(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_SET( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPtrapv1(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_TRAPv1( ASN1F_INTEGER("id",0), + ASN1F_OID("enterprise", "1.3"), + ASN1F_STRING("agent_addr",""), + ASN1F_enum_INTEGER("generic_trap", 0, SNMP_trap_types), + ASN1F_INTEGER("specific_trap", 0), + ASN1F_INTEGER("time_stamp", IntAutoTime()), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPbulk(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_BULK( ASN1F_INTEGER("id",0), + ASN1F_INTEGER("non_repeaters",0), + ASN1F_INTEGER("max_repetitions",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPinform(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_INFORM( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPtrapv2(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_TRAPv2( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + + +class SNMP(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SEQUENCE( + ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}), + ASN1F_STRING("community","public"), + ASN1F_CHOICE("PDU", SNMPget(), + SNMPget, SNMPnext, SNMPresponse, SNMPset, + SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) + ) + def answers(self, other): + return ( isinstance(self.PDU, SNMPresponse) and + ( isinstance(other.PDU, SNMPget) or + isinstance(other.PDU, SNMPnext) or + isinstance(other.PDU, SNMPset) ) and + self.PDU.id == other.PDU.id ) + +bind_layers( UDP, SNMP, sport=161) +bind_layers( UDP, SNMP, dport=161) +def snmpwalk(dst, oid="1", community="public"): + try: + while 1: + r = sr1(IP(dst=dst)/UDP(sport=RandShort())/SNMP(community=community, PDU=SNMPnext(varbindlist=[SNMPvarbind(oid=oid)])),timeout=2, chainCC=1, verbose=0, retry=2) + if ICMP in r: + print repr(r) + break + if r is None: + print "No answers" + break + print "%-40s: %r" % (r[SNMPvarbind].oid.val,r[SNMPvarbind].value) + oid = r[SNMPvarbind].oid + + except KeyboardInterrupt: + pass + diff --git a/scapy/layers/tftp.py b/scapy/layers/tftp.py new file mode 100644 index 0000000000000000000000000000000000000000..5b3c879916dc97f52e534e281135793afbd80c16 --- /dev/null +++ b/scapy/layers/tftp.py @@ -0,0 +1,468 @@ +import os,random +from scapy.packet import * +from scapy.fields import * +from scapy.automaton import * +from scapy.layers.inet import UDP + + + +TFTP_operations = { 1:"RRQ",2:"WRQ",3:"DATA",4:"ACK",5:"ERROR",6:"OACK" } + + +class TFTP(Packet): + name = "TFTP opcode" + fields_desc = [ ShortEnumField("op", 1, TFTP_operations), ] + + + +class TFTP_RRQ(Packet): + name = "TFTP Read Request" + fields_desc = [ StrNullField("filename", ""), + StrNullField("mode", "octet") ] + def answers(self, other): + return 0 + def mysummary(self): + return self.sprintf("RRQ %filename%"),[UDP] + + +class TFTP_WRQ(Packet): + name = "TFTP Write Request" + fields_desc = [ StrNullField("filename", ""), + StrNullField("mode", "octet") ] + def answers(self, other): + return 0 + def mysummary(self): + return self.sprintf("WRQ %filename%"),[UDP] + +class TFTP_DATA(Packet): + name = "TFTP Data" + fields_desc = [ ShortField("block", 0) ] + def answers(self, other): + return self.block == 1 and isinstance(other, TFTP_RRQ) + def mysummary(self): + return self.sprintf("DATA %block%"),[UDP] + +class TFTP_Option(Packet): + fields_desc = [ StrNullField("oname",""), + StrNullField("value","") ] + def extract_padding(self, pkt): + return "",pkt + +class TFTP_Options(Packet): + fields_desc = [ PacketListField("options", [], TFTP_Option, length_from=lambda x:None) ] + + +class TFTP_ACK(Packet): + name = "TFTP Ack" + fields_desc = [ ShortField("block", 0) ] + def answers(self, other): + if isinstance(other, TFTP_DATA): + return self.block == other.block + elif isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_OACK): + return self.block == 0 + return 0 + def mysummary(self): + return self.sprintf("ACK %block%"),[UDP] + +TFTP_Error_Codes = { 0: "Not defined", + 1: "File not found", + 2: "Access violation", + 3: "Disk full or allocation exceeded", + 4: "Illegal TFTP operation", + 5: "Unknown transfer ID", + 6: "File already exists", + 7: "No such user", + 8: "Terminate transfer due to option negotiation", + } + +class TFTP_ERROR(Packet): + name = "TFTP Error" + fields_desc = [ ShortEnumField("errorcode", 0, TFTP_Error_Codes), + StrNullField("errormsg", "")] + def answers(self, other): + return (isinstance(other, TFTP_DATA) or + isinstance(other, TFTP_RRQ) or + isinstance(other, TFTP_WRQ) or + isinstance(other, TFTP_ACK)) + def mysummary(self): + return self.sprintf("ERROR %errorcode%: %errormsg%"),[UDP] + + +class TFTP_OACK(Packet): + name = "TFTP Option Ack" + fields_desc = [ ] + def answers(self, other): + return isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_RRQ) + + +bind_layers(UDP, TFTP, dport=69) +bind_layers(TFTP, TFTP_RRQ, op=1) +bind_layers(TFTP, TFTP_WRQ, op=2) +bind_layers(TFTP, TFTP_DATA, op=3) +bind_layers(TFTP, TFTP_ACK, op=4) +bind_layers(TFTP, TFTP_ERROR, op=5) +bind_layers(TFTP, TFTP_OACK, op=6) +bind_layers(TFTP_RRQ, TFTP_Options) +bind_layers(TFTP_WRQ, TFTP_Options) +bind_layers(TFTP_OACK, TFTP_Options) + + +class TFTP_read(Automaton): + def parse_args(self, filename, server, sport = None, port=69, **kargs): + Automaton.parse_args(self, **kargs) + self.filename = filename + self.server = server + self.port = port + self.sport = sport + + + def master_filter(self, pkt): + return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt + and pkt[UDP].dport == self.my_tid + and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) + + # BEGIN + @ATMT.state(initial=1) + def BEGIN(self): + self.blocksize=512 + self.my_tid = self.sport or RandShort()._fix() + bind_bottom_up(UDP, TFTP, dport=self.my_tid) + self.server_tid = None + self.res = "" + + self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() + self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet") + self.send(self.last_packet) + self.awaiting=1 + + raise self.WAITING() + + # WAITING + @ATMT.state() + def WAITING(self): + pass + + + @ATMT.receive_condition(WAITING) + def receive_data(self, pkt): + if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: + if self.server_tid is None: + self.server_tid = pkt[UDP].sport + self.l3[UDP].dport = self.server_tid + raise self.RECEIVING(pkt) + + @ATMT.receive_condition(WAITING, prio=1) + def receive_error(self, pkt): + if TFTP_ERROR in pkt: + raise self.ERROR(pkt) + + + @ATMT.timeout(WAITING, 3) + def timeout_waiting(self): + raise self.WAITING() + @ATMT.action(timeout_waiting) + def retransmit_last_packet(self): + self.send(self.last_packet) + + @ATMT.action(receive_data) +# @ATMT.action(receive_error) + def send_ack(self): + self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting) + self.send(self.last_packet) + + + # RECEIVED + @ATMT.state() + def RECEIVING(self, pkt): + if Raw in pkt: + recvd = pkt[Raw].load + else: + recvd = "" + self.res += recvd + self.awaiting += 1 + if len(recvd) == self.blocksize: + raise self.WAITING() + raise self.END() + + # ERROR + @ATMT.state(error=1) + def ERROR(self,pkt): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + return pkt[TFTP_ERROR].summary() + + #END + @ATMT.state(final=1) + def END(self): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + return self.res + + + + +class TFTP_write(Automaton): + def parse_args(self, filename, data, server, sport=None, port=69,**kargs): + Automaton.parse_args(self, **kargs) + self.filename = filename + self.server = server + self.port = port + self.sport = sport + self.blocksize = 512 + self.origdata = data + + def master_filter(self, pkt): + return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt + and pkt[UDP].dport == self.my_tid + and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) + + + # BEGIN + @ATMT.state(initial=1) + def BEGIN(self): + self.data = [ self.origdata[i*self.blocksize:(i+1)*self.blocksize] + for i in range( len(self.origdata)/self.blocksize+1) ] + self.my_tid = self.sport or RandShort()._fix() + bind_bottom_up(UDP, TFTP, dport=self.my_tid) + self.server_tid = None + + self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() + self.last_packet = self.l3/TFTP_WRQ(filename=self.filename, mode="octet") + self.send(self.last_packet) + self.res = "" + self.awaiting=0 + + raise self.WAITING_ACK() + + # WAITING_ACK + @ATMT.state() + def WAITING_ACK(self): + pass + + @ATMT.receive_condition(WAITING_ACK) + def received_ack(self,pkt): + if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.awaiting: + if self.server_tid is None: + self.server_tid = pkt[UDP].sport + self.l3[UDP].dport = self.server_tid + raise self.SEND_DATA() + + @ATMT.receive_condition(WAITING_ACK) + def received_error(self, pkt): + if TFTP_ERROR in pkt: + raise self.ERROR(pkt) + + @ATMT.timeout(WAITING_ACK, 3) + def timeout_waiting(self): + raise self.WAITING_ACK() + @ATMT.action(timeout_waiting) + def retransmit_last_packet(self): + self.send(self.last_packet) + + # SEND_DATA + @ATMT.state() + def SEND_DATA(self): + self.awaiting += 1 + self.last_packet = self.l3/TFTP_DATA(block=self.awaiting)/self.data.pop(0) + self.send(self.last_packet) + if self.data: + raise self.WAITING_ACK() + raise self.END() + + + # ERROR + @ATMT.state(error=1) + def ERROR(self,pkt): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + return pkt[TFTP_ERROR].summary() + + # END + @ATMT.state(final=1) + def END(self): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + + +class TFTP_WRQ_server(Automaton): + + def parse_args(self, ip=None, sport=None, *args, **kargs): + Automaton.parse_args(self, *args, **kargs) + self.ip = ip + self.sport = sport + + def master_filter(self, pkt): + return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) + + @ATMT.state(initial=1) + def BEGIN(self): + self.blksize=512 + self.blk=1 + self.filedata="" + self.my_tid = self.sport or random.randint(10000,65500) + bind_bottom_up(UDP, TFTP, dport=self.my_tid) + + @ATMT.receive_condition(BEGIN) + def receive_WRQ(self,pkt): + if TFTP_WRQ in pkt: + raise self.WAIT_DATA().action_parameters(pkt) + + @ATMT.action(receive_WRQ) + def ack_WRQ(self, pkt): + ip = pkt[IP] + self.ip = ip.dst + self.dst = ip.src + self.filename = pkt[TFTP_WRQ].filename + options = pkt[TFTP_Options] + self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=pkt.sport)/TFTP() + if options is None: + self.last_packet = self.l3/TFTP_ACK(block=0) + self.send(self.last_packet) + else: + opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] + if opt: + self.blksize = int(opt[0].value) + self.debug(2,"Negotiated new blksize at %i" % self.blksize) + self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) + self.send(self.last_packet) + + @ATMT.state() + def WAIT_DATA(self): + pass + + @ATMT.timeout(WAIT_DATA, 1) + def resend_ack(self): + self.send(self.last_packet) + raise self.WAIT_DATA() + + @ATMT.receive_condition(WAIT_DATA) + def receive_data(self, pkt): + if TFTP_DATA in pkt: + data = pkt[TFTP_DATA] + if data.block == self.blk: + raise self.DATA(data) + + @ATMT.action(receive_data) + def ack_data(self): + self.last_packet = self.l3/TFTP_ACK(block = self.blk) + self.send(self.last_packet) + + @ATMT.state() + def DATA(self, data): + self.filedata += data.load + if len(data.load) < self.blksize: + raise self.END() + self.blk += 1 + raise self.WAIT_DATA() + + @ATMT.state(final=1) + def END(self): + return self.filename,self.filedata + split_bottom_up(UDP, TFTP, dport=self.my_tid) + + +class TFTP_RRQ_server(Automaton): + def parse_args(self, store=None, joker=None, dir=None, ip=None, sport=None, serve_one=False, **kargs): + Automaton.parse_args(self,**kargs) + if store is None: + store = {} + if dir is not None: + self.dir = os.path.join(os.path.abspath(dir),"") + else: + self.dir = None + self.store = store + self.joker = joker + self.ip = ip + self.sport = sport + self.serve_one = serve_one + self.my_tid = self.sport or random.randint(10000,65500) + bind_bottom_up(UDP, TFTP, dport=self.my_tid) + + def master_filter(self, pkt): + return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) + + @ATMT.state(initial=1) + def WAIT_RRQ(self): + self.blksize=512 + self.blk=0 + + @ATMT.receive_condition(WAIT_RRQ) + def receive_rrq(self, pkt): + if TFTP_RRQ in pkt: + raise self.RECEIVED_RRQ(pkt) + + + @ATMT.state() + def RECEIVED_RRQ(self, pkt): + ip = pkt[IP] + options = pkt[TFTP_Options] + self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=ip.sport)/TFTP() + self.filename = pkt[TFTP_RRQ].filename + self.blk=1 + self.data = None + if self.filename in self.store: + self.data = self.store[self.filename] + elif self.dir is not None: + fn = os.path.abspath(os.path.join(self.dir, self.filename)) + if fn.startswith(self.dir): # Check we're still in the server's directory + try: + self.data=open(fn).read() + except IOError: + pass + if self.data is None: + self.data = self.joker + + if options: + opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] + if opt: + self.blksize = int(opt[0].value) + self.debug(2,"Negotiated new blksize at %i" % self.blksize) + self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) + self.send(self.last_packet) + + + + + @ATMT.condition(RECEIVED_RRQ) + def file_in_store(self): + if self.data is not None: + self.blknb = len(self.data)/self.blksize+1 + raise self.SEND_FILE() + + @ATMT.condition(RECEIVED_RRQ) + def file_not_found(self): + if self.data is None: + raise self.WAIT_RRQ() + @ATMT.action(file_not_found) + def send_error(self): + self.send(self.l3/TFTP_ERROR(errorcode=1, errormsg=TFTP_Error_Codes[1])) + + @ATMT.state() + def SEND_FILE(self): + self.send(self.l3/TFTP_DATA(block=self.blk)/self.data[(self.blk-1)*self.blksize:self.blk*self.blksize]) + + @ATMT.timeout(SEND_FILE, 3) + def timeout_waiting_ack(self): + raise self.SEND_FILE() + + @ATMT.receive_condition(SEND_FILE) + def received_ack(self, pkt): + if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.blk: + raise self.RECEIVED_ACK() + @ATMT.state() + def RECEIVED_ACK(self): + self.blk += 1 + + @ATMT.condition(RECEIVED_ACK) + def no_more_data(self): + if self.blk > self.blknb: + if self.serve_one: + raise self.END() + raise self.WAIT_RRQ() + @ATMT.condition(RECEIVED_ACK, prio=2) + def data_remaining(self): + raise self.SEND_FILE() + + @ATMT.state(final=1) + def END(self): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + + + + diff --git a/scapy/layers/x509.py b/scapy/layers/x509.py new file mode 100644 index 0000000000000000000000000000000000000000..4c3e8182d9e4d2d60f05e83fc6cab83f45182b45 --- /dev/null +++ b/scapy/layers/x509.py @@ -0,0 +1,99 @@ +from scapy.asn1packet import * +from scapy.asn1fields import * + +########## +## X509 ## +########## + +######[ ASN1 class ]###### + +class ASN1_Class_X509(ASN1_Class_UNIVERSAL): + name="X509" + CONT0 = 0xa0 + CONT1 = 0xa1 + CONT2 = 0xa2 + CONT3 = 0xa3 + +class ASN1_X509_CONT0(ASN1_SEQUENCE): + tag = ASN1_Class_X509.CONT0 + +class ASN1_X509_CONT1(ASN1_SEQUENCE): + tag = ASN1_Class_X509.CONT1 + +class ASN1_X509_CONT2(ASN1_SEQUENCE): + tag = ASN1_Class_X509.CONT2 + +class ASN1_X509_CONT3(ASN1_SEQUENCE): + tag = ASN1_Class_X509.CONT3 + +######[ BER codecs ]####### + +class BERcodec_X509_CONT0(BERcodec_SEQUENCE): + tag = ASN1_Class_X509.CONT0 + +class BERcodec_X509_CONT1(BERcodec_SEQUENCE): + tag = ASN1_Class_X509.CONT1 + +class BERcodec_X509_CONT2(BERcodec_SEQUENCE): + tag = ASN1_Class_X509.CONT2 + +class BERcodec_X509_CONT3(BERcodec_SEQUENCE): + tag = ASN1_Class_X509.CONT3 + +######[ ASN1 fields ]###### + +class ASN1F_X509_CONT0(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_X509.CONT0 + +class ASN1F_X509_CONT1(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_X509.CONT1 + +class ASN1F_X509_CONT2(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_X509.CONT2 + +class ASN1F_X509_CONT3(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_X509.CONT3 + +######[ X509 packets ]###### + +class X509RDN(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SET( + ASN1F_SEQUENCE( ASN1F_OID("oid","2.5.4.6"), + ASN1F_PRINTABLE_STRING("value","") + ) + ) + +class X509v3Ext(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_field("val",ASN1_NULL(0)) + + +class X509Cert(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SEQUENCE( + ASN1F_SEQUENCE( + ASN1F_optionnal(ASN1F_X509_CONT0(ASN1F_INTEGER("version",3))), + ASN1F_INTEGER("sn",1), + ASN1F_SEQUENCE(ASN1F_OID("sign_algo","1.2.840.113549.1.1.5"), + ASN1F_field("sa_value",ASN1_NULL(0))), + ASN1F_SEQUENCE_OF("issuer",[],X509RDN), + ASN1F_SEQUENCE(ASN1F_UTC_TIME("not_before",ZuluTime(-600)), # ten minutes ago + ASN1F_UTC_TIME("not_after",ZuluTime(+86400))), # for 24h + ASN1F_SEQUENCE_OF("subject",[],X509RDN), + ASN1F_SEQUENCE( + ASN1F_SEQUENCE(ASN1F_OID("pubkey_algo","1.2.840.113549.1.1.1"), + ASN1F_field("pk_value",ASN1_NULL(0))), + ASN1F_BIT_STRING("pubkey","") + ), + ASN1F_optionnal(ASN1F_X509_CONT3(ASN1F_SEQUENCE_OF("x509v3ext",[],X509v3Ext))), + + ), + ASN1F_SEQUENCE(ASN1F_OID("sign_algo2","1.2.840.113549.1.1.5"), + ASN1F_field("sa2_value",ASN1_NULL(0))), + ASN1F_BIT_STRING("signature","") + ) + + + + diff --git a/scapy/main.py b/scapy/main.py new file mode 100644 index 0000000000000000000000000000000000000000..86de2d2c979f37de93b92c8028bb9f5fa3387ee7 --- /dev/null +++ b/scapy/main.py @@ -0,0 +1,265 @@ + +from __future__ import generators +import os,sys + +VERSION = "1.2.0.2" + +DEFAULT_CONFIG_FILE = os.path.join(os.environ["HOME"], ".scapy_startup.py") + +try: + os.stat(DEFAULT_CONFIG_FILE) +except OSError: + DEFAULT_CONFIG_FILE = None + +def usage(): + print """Usage: scapy.py [-s sessionfile] [-c new_startup_file] [-C] + -C: do not read startup file""" + sys.exit(0) + + +from config import conf +from themes import ColorPrompt + + +###################### +## Extension system ## +###################### + + +def load_extension(filename): + import imp + paths = conf.extensions_paths + if type(paths) is not list: + paths = [paths] + + name = os.path.realpath(os.path.expanduser(filename)) + thepath = os.path.dirname(name) + thename = os.path.basename(name) + if thename.endswith(".py"): + thename = thename[:-3] + + paths.insert(0, thepath) + cwd=syspath=None + try: + cwd = os.getcwd() + os.chdir(thepath) + syspath = sys.path[:] + sys.path += paths + try: + extf = imp.find_module(thename, paths) + except ImportError: + log_runtime.error("Module [%s] not found. Check conf.extensions_paths ?" % filename) + else: + ext = imp.load_module(thename, *extf) + import __builtin__ + __builtin__.__dict__.update(ext.__dict__) + finally: + if syspath: + sys.path=syspath + if cwd: + os.chdir(cwd) + + +################ +##### Main ##### +################ + +def scapy_write_history_file(readline): + if conf.histfile: + try: + readline.write_history_file(conf.histfile) + except IOError,e: + try: + warning("Could not write history to [%s]\n\t (%s)" % (conf.histfile,e)) + tmp = os.tempnam("","scapy") + readline.write_history_file(tmp) + warning("Wrote history to [%s]" % tmp) + except: + warning("Cound not write history to [%s]. Discarded" % tmp) + + +def interact(mydict=None,argv=None,mybanner=None,loglevel=1): + global session + import code,sys,cPickle,types,os,imp,getopt,logging,re + + logging.getLogger("scapy").setLevel(loglevel) + + the_banner = "Welcome to Scapy (%s)" + if mybanner is not None: + the_banner += "\n" + the_banner += mybanner + + if argv is None: + argv = sys.argv + +# scapy_module = argv[0][argv[0].rfind("/")+1:] +# if not scapy_module: +# scapy_module = "scapy" +# else: +# if scapy_module.endswith(".py"): +# scapy_module = scapy_module[:-3] +# +# scapy=imp.load_module("scapy",*imp.find_module(scapy_module)) + + +# __builtin__.__dict__.update(scapy.__dict__) + import __builtin__ + scapy_builtins = {} + for m in [ "ansmachine","arch","automaton","autorun","config","dadict","data","error","fields","main", + "mib","packet","plist","route","sendrecv","supersocket","themes","utils","volatile","asn1","asn1f" ]: + mod = __import__("scapy."+m,globals(),locals(),".") + scapy_builtins.update(mod.__dict__) + for m in ["l2","inet","dot11","dhcp","dns","ip6","isakmp","l2tp","mgcp","netbios","ntp","ppp","radius","rip", + "skinny","smb","rtp","tftp","snmp","x509" ]: # "ir","bluetooth","hsrp","gprs" + mod = __import__("scapy.layers."+m,globals(),locals(),".") + scapy_builtins.update(mod.__dict__) + __builtin__.__dict__.update(scapy_builtins) + globkeys = scapy_builtins.keys() + globkeys.append("scapy_session") + scapy_builtins=None # XXX replace with "with" statement + if mydict is not None: + __builtin__.__dict__.update(mydict) + globkeys += mydict.keys() + + import re, atexit + try: + import rlcompleter,readline + except ImportError: + log_loading.info("Can't load Python libreadline or completer") + READLINE=0 + else: + READLINE=1 + class ScapyCompleter(rlcompleter.Completer): + def global_matches(self, text): + matches = [] + n = len(text) + for lst in [dir(__builtin__), session.keys()]: + for word in lst: + if word[:n] == text and word != "__builtins__": + matches.append(word) + return matches + + + def attr_matches(self, text): + m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) + if not m: + return + expr, attr = m.group(1, 3) + try: + object = eval(expr) + except: + object = eval(expr, session) + if isinstance(object, Packet) or isinstance(object, Packet_metaclass): + words = filter(lambda x: x[0]!="_",dir(object)) + words += [x.name for x in object.fields_desc] + else: + words = dir(object) + if hasattr( object,"__class__" ): + words = words + rlcompleter.get_class_members(object.__class__) + matches = [] + n = len(attr) + for word in words: + if word[:n] == attr and word != "__builtins__": + matches.append("%s.%s" % (expr, word)) + return matches + + readline.set_completer(ScapyCompleter().complete) + readline.parse_and_bind("C-o: operate-and-get-next") + readline.parse_and_bind("tab: complete") + + + session=None + session_name="" + CONFIG_FILE = DEFAULT_CONFIG_FILE + + iface = None + try: + opts=getopt.getopt(argv[1:], "hs:Cc:") + for opt, parm in opts[0]: + if opt == "-h": + usage() + elif opt == "-s": + session_name = parm + elif opt == "-c": + CONFIG_FILE = parm + elif opt == "-C": + CONFIG_FILE = None + + if len(opts[1]) > 0: + raise getopt.GetoptError("Too many parameters : [%s]" % " ".join(opts[1])) + + + except getopt.GetoptError, msg: + log_loading.error(msg) + sys.exit(1) + + + if CONFIG_FILE: + read_config_file(CONFIG_FILE) + + if session_name: + try: + os.stat(session_name) + except OSError: + log_loading.info("New session [%s]" % session_name) + else: + try: + try: + session = cPickle.load(gzip.open(session_name,"rb")) + except IOError: + session = cPickle.load(open(session_name,"rb")) + log_loading.info("Using session [%s]" % session_name) + except EOFError: + log_loading.error("Error opening session [%s]" % session_name) + except AttributeError: + log_loading.error("Error opening session [%s]. Attribute missing" % session_name) + + if session: + if "conf" in session: + conf.configure(session["conf"]) + session["conf"] = conf + else: + conf.session = session_name + session={"conf":conf} + + else: + session={"conf": conf} + + __builtin__.__dict__["scapy_session"] = session + + + if READLINE: + if conf.histfile: + try: + readline.read_history_file(conf.histfile) + except IOError: + pass + atexit.register(scapy_write_history_file,readline) + + sys.ps1 = ColorPrompt() + code.interact(banner = the_banner % (VERSION), local=session) + + if conf.session: + save_session(conf.session, session) + + + for k in globkeys: + try: + del(__builtin__.__dict__[k]) + except: + pass + +def read_config_file(configfile): + try: + execfile(configfile) + except IOError,e: + log_loading.warning("Cannot read config file [%s] [%s]" % (configfile,e)) + except Exception,e: + log_loading.exception("Error during evaluation of config file [%s]" % configfile) + + +if __name__ == "__main__": + interact() +else: + if DEFAULT_CONFIG_FILE: + read_config_file(DEFAULT_CONFIG_FILE) diff --git a/scapy/mib.py b/scapy/mib.py new file mode 100644 index 0000000000000000000000000000000000000000..3e5d7d733171eec7199a2ce1ec0c587ddcd0e815 --- /dev/null +++ b/scapy/mib.py @@ -0,0 +1,134 @@ +import re +from glob import glob +from dadict import DADict +from config import conf + +################# +## MIB parsing ## +################# + +_mib_re_integer = re.compile("^[0-9]+$") +_mib_re_both = re.compile("^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$") +_mib_re_oiddecl = re.compile("$\s*([a-zA-Z0-9_-]+)\s+OBJECT[^:]+::=\s*\{([^\}]+)\}",re.M) +_mib_re_strings = re.compile('"[^"]*"') +_mib_re_comments = re.compile('--.*(\r|\n)') + +class MIBDict(DADict): + def _findroot(self, x): + if x.startswith("."): + x = x[1:] + if not x.endswith("."): + x += "." + max=0 + root="." + for k in self.keys(): + if x.startswith(self[k]+"."): + if max < len(self[k]): + max = len(self[k]) + root = k + return root, x[max:-1] + def _oidname(self, x): + root,remainder = self._findroot(x) + return root+remainder + def _oid(self, x): + xl = x.strip(".").split(".") + p = len(xl)-1 + while p >= 0 and _mib_re_integer.match(xl[p]): + p -= 1 + if p != 0 or xl[p] not in self: + return x + xl[p] = self[xl[p]] + return ".".join(xl[p:]) + def _make_graph(self, other_keys=[], **kargs): + nodes = [(k,self[k]) for k in self.keys()] + oids = [self[k] for k in self.keys()] + for k in other_keys: + if k not in oids: + nodes.append(self.oidname(k),k) + s = 'digraph "mib" {\n\trankdir=LR;\n\n' + for k,o in nodes: + s += '\t"%s" [ label="%s" ];\n' % (o,k) + s += "\n" + for k,o in nodes: + parent,remainder = self._findroot(o[:-1]) + remainder = remainder[1:]+o[-1] + if parent != ".": + parent = self[parent] + s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o,remainder) + s += "}\n" + do_graph(s, **kargs) + + +def mib_register(ident, value, the_mib, unresolved): + if ident in the_mib or ident in unresolved: + return ident in the_mib + resval = [] + not_resolved = 0 + for v in value: + if _mib_re_integer.match(v): + resval.append(v) + else: + v = fixname(v) + if v not in the_mib: + not_resolved = 1 + if v in the_mib: + v = the_mib[v] + elif v in unresolved: + v = unresolved[v] + if type(v) is list: + resval += v + else: + resval.append(v) + if not_resolved: + unresolved[ident] = resval + return False + else: + the_mib[ident] = resval + keys = unresolved.keys() + i = 0 + while i < len(keys): + k = keys[i] + if mib_register(k,unresolved[k], the_mib, {}): + del(unresolved[k]) + del(keys[i]) + i = 0 + else: + i += 1 + + return True + + +def load_mib(filenames): + the_mib = {'iso': ['1']} + unresolved = {} + for k in conf.mib.keys(): + mib_register(k, conf.mib[k].split("."), the_mib, unresolved) + + if type(filenames) is str: + filenames = [filenames] + for fnames in filenames: + for fname in glob(fnames): + f = open(fname) + text = f.read() + cleantext = " ".join(_mib_re_strings.split(" ".join(_mib_re_comments.split(text)))) + for m in _mib_re_oiddecl.finditer(cleantext): + ident,oid = m.groups() + ident=fixname(ident) + oid = oid.split() + for i in range(len(oid)): + m = _mib_re_both.match(oid[i]) + if m: + oid[i] = m.groups()[1] + mib_register(ident, oid, the_mib, unresolved) + + newmib = MIBDict(_name="MIB") + for k,o in the_mib.iteritems(): + newmib[k]=".".join(o) + for k,o in unresolved.iteritems(): + newmib[k]=".".join(o) + + conf.mib=newmib + + + +conf.mib = MIBDict(_name="MIB") diff --git a/scapy/nmap.py b/scapy/nmap.py new file mode 100644 index 0000000000000000000000000000000000000000..5b4911931ee0adbf37ebea6a7cf2e372a3bc01f7 --- /dev/null +++ b/scapy/nmap.py @@ -0,0 +1,195 @@ +from scapy.data import KnowledgeBase +from scapy.config import conf + +###################### +## nmap OS fp stuff ## +###################### + + +class NmapKnowledgeBase(KnowledgeBase): + def lazy_init(self): + try: + f=open(self.filename) + except IOError: + return + + self.base = [] + name = None + try: + for l in f: + l = l.strip() + if not l or l[0] == "#": + continue + if l[:12] == "Fingerprint ": + if name is not None: + self.base.append((name,sig)) + name = l[12:].strip() + sig={} + p = self.base + continue + elif l[:6] == "Class ": + continue + op = l.find("(") + cl = l.find(")") + if op < 0 or cl < 0: + warning("error reading nmap os fp base file") + continue + test = l[:op] + s = map(lambda x: x.split("="), l[op+1:cl].split("%")) + si = {} + for n,v in s: + si[n] = v + sig[test]=si + if name is not None: + self.base.append((name,sig)) + except: + self.base = None + warning("Can't read nmap database [%s](new nmap version ?)" % self.filename) + f.close() + +nmap_kdb = NmapKnowledgeBase(conf.nmap_base) + +def TCPflags2str(f): + fl="FSRPAUEC" + s="" + for i in range(len(fl)): + if f & 1: + s = fl[i]+s + f >>= 1 + return s + +def nmap_tcppacket_sig(pkt): + r = {} + if pkt is not None: +# r["Resp"] = "Y" + r["DF"] = (pkt.flags & 2) and "Y" or "N" + r["W"] = "%X" % pkt.window + r["ACK"] = pkt.ack==2 and "S++" or pkt.ack==1 and "S" or "O" + r["Flags"] = TCPflags2str(pkt.payload.flags) + r["Ops"] = "".join(map(lambda x: x[0][0],pkt.payload.options)) + else: + r["Resp"] = "N" + return r + + +def nmap_udppacket_sig(S,T): + r={} + if T is None: + r["Resp"] = "N" + else: + r["DF"] = (T.flags & 2) and "Y" or "N" + r["TOS"] = "%X" % T.tos + r["IPLEN"] = "%X" % T.len + r["RIPTL"] = "%X" % T.payload.payload.len + r["RID"] = S.id == T.payload.payload.id and "E" or "F" + r["RIPCK"] = S.chksum == T.getlayer(IPerror).chksum and "E" or T.getlayer(IPerror).chksum == 0 and "0" or "F" + r["UCK"] = S.payload.chksum == T.getlayer(UDPerror).chksum and "E" or T.getlayer(UDPerror).chksum ==0 and "0" or "F" + r["ULEN"] = "%X" % T.getlayer(UDPerror).len + r["DAT"] = T.getlayer(Raw) is None and "E" or S.getlayer(Raw).load == T.getlayer(Raw).load and "E" or "F" + return r + + + +def nmap_match_one_sig(seen, ref): + c = 0 + for k in seen.keys(): + if ref.has_key(k): + if seen[k] in ref[k].split("|"): + c += 1 + if c == 0 and seen.get("Resp") == "N": + return 0.7 + else: + return 1.0*c/len(seen.keys()) + + + +def nmap_sig(target, oport=80, cport=81, ucport=1): + res = {} + + tcpopt = [ ("WScale", 10), + ("NOP",None), + ("MSS", 256), + ("Timestamp",(123,0)) ] + tests = [ IP(dst=target, id=1)/TCP(seq=1, sport=5001, dport=oport, options=tcpopt, flags="CS"), + IP(dst=target, id=1)/TCP(seq=1, sport=5002, dport=oport, options=tcpopt, flags=0), + IP(dst=target, id=1)/TCP(seq=1, sport=5003, dport=oport, options=tcpopt, flags="SFUP"), + IP(dst=target, id=1)/TCP(seq=1, sport=5004, dport=oport, options=tcpopt, flags="A"), + IP(dst=target, id=1)/TCP(seq=1, sport=5005, dport=cport, options=tcpopt, flags="S"), + IP(dst=target, id=1)/TCP(seq=1, sport=5006, dport=cport, options=tcpopt, flags="A"), + IP(dst=target, id=1)/TCP(seq=1, sport=5007, dport=cport, options=tcpopt, flags="FPU"), + IP(str(IP(dst=target)/UDP(sport=5008,dport=ucport)/(300*"i"))) ] + + ans, unans = sr(tests, timeout=2) + ans += map(lambda x: (x,None), unans) + + for S,T in ans: + if S.sport == 5008: + res["PU"] = nmap_udppacket_sig(S,T) + else: + t = "T%i" % (S.sport-5000) + if T is not None and T.haslayer(ICMP): + warning("Test %s answered by an ICMP" % t) + T=None + res[t] = nmap_tcppacket_sig(T) + + return res + +def nmap_probes2sig(tests): + tests=tests.copy() + res = {} + if "PU" in tests: + res["PU"] = nmap_udppacket_sig(*tests["PU"]) + del(tests["PU"]) + for k in tests: + res[k] = nmap_tcppacket_sig(tests[k]) + return res + + +def nmap_search(sigs): + guess = 0,[] + for os,fp in nmap_kdb.get_base(): + c = 0.0 + for t in sigs.keys(): + if t in fp: + c += nmap_match_one_sig(sigs[t], fp[t]) + c /= len(sigs.keys()) + if c > guess[0]: + guess = c,[ os ] + elif c == guess[0]: + guess[1].append(os) + return guess + + +def nmap_fp(target, oport=80, cport=81): + """nmap fingerprinting +nmap_fp(target, [oport=80,] [cport=81,]) -> list of best guesses with accuracy +""" + sigs = nmap_sig(target, oport, cport) + return nmap_search(sigs) + + +def nmap_sig2txt(sig): + torder = ["TSeq","T1","T2","T3","T4","T5","T6","T7","PU"] + korder = ["Class", "gcd", "SI", "IPID", "TS", + "Resp", "DF", "W", "ACK", "Flags", "Ops", + "TOS", "IPLEN", "RIPTL", "RID", "RIPCK", "UCK", "ULEN", "DAT" ] + txt=[] + for i in sig.keys(): + if i not in torder: + torder.append(i) + for t in torder: + sl = sig.get(t) + if sl is None: + continue + s = [] + for k in korder: + v = sl.get(k) + if v is None: + continue + s.append("%s=%s"%(k,v)) + txt.append("%s(%s)" % (t, "%".join(s))) + return "\n".join(txt) + + + + diff --git a/scapy/p0f.py b/scapy/p0f.py new file mode 100644 index 0000000000000000000000000000000000000000..05118e9289933ace65cb01c6bbc00b3e33031fb4 --- /dev/null +++ b/scapy/p0f.py @@ -0,0 +1,247 @@ +from scapy.data import KnowledgeBase +from scapy.config import conf + +############### +## p0f stuff ## +############### + +# File format (according to p0f.fp) : +# +# wwww:ttt:D:ss:OOO...:QQ:OS:Details +# +# wwww - window size +# ttt - initial TTL +# D - don't fragment bit (0=unset, 1=set) +# ss - overall SYN packet size +# OOO - option value and order specification +# QQ - quirks list +# OS - OS genre +# details - OS description + + + +class p0fKnowledgeBase(KnowledgeBase): + def __init__(self, filename): + KnowledgeBase.__init__(self, filename) + #self.ttl_range=[255] + def lazy_init(self): + try: + f=open(self.filename) + except IOError: + warning("Can't open base %s" % self.filename) + return + try: + self.base = [] + for l in f: + if l[0] in ["#","\n"]: + continue + l = tuple(l.split(":")) + if len(l) < 8: + continue + li = map(int,l[1:4]) + #if li[0] not in self.ttl_range: + # self.ttl_range.append(li[0]) + # self.ttl_range.sort() + self.base.append((l[0], li[0], li[1], li[2], l[4], l[5], l[6], l[7][:-1])) + except: + warning("Can't parse p0f database (new p0f version ?)") + self.base = None + f.close() + +p0f_kdb = p0fKnowledgeBase(conf.p0f_base) + + +def packet2p0f(pkt): + while pkt.haslayer(IP) and pkt.haslayer(TCP): + pkt = pkt.getlayer(IP) + if isinstance(pkt.payload, TCP): + break + pkt = pkt.payload + + if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): + raise TypeError("Not a TCP/IP packet") + if pkt.payload.flags & 0x13 != 0x02: #S,!A,!F + raise TypeError("Not a syn packet") + + #t = p0f_kdb.ttl_range[:] + #t += [pkt.ttl] + #t.sort() + #ttl=t[t.index(pkt.ttl)+1] + ttl = pkt.ttl + + df = (pkt.flags & 2) / 2 + ss = len(pkt) + # from p0f/config.h : PACKET_BIG = 100 + if ss > 100: + ss = 0 + + ooo = "" + mss = -1 + qqT = False + qqP = False + #qqBroken = False + ilen = (pkt[TCP].dataofs << 2) - 20 # from p0f.c + for option in pkt.payload.options: + ilen -= 1 + if option[0] == "MSS": + ooo += "M" + str(option[1]) + "," + mss = option[1] + # FIXME: qqBroken + ilen -= 3 + elif option[0] == "WScale": + ooo += "W" + str(option[1]) + "," + # FIXME: qqBroken + ilen -= 2 + elif option[0] == "Timestamp": + if option[1][0] == 0: + ooo += "T0," + else: + ooo += "T," + if option[1][1] != 0: + qqT = True + ilen -= 9 + elif option[0] == "SAckOK": + ooo += "S," + ilen -= 1 + elif option[0] == "NOP": + ooo += "N," + elif option[0] == "EOL": + ooo += "E," + if ilen > 0: + qqP = True + else: + ooo += "?," + # FIXME: ilen + ooo = ooo[:-1] + if ooo == "": ooo = "." + + win = pkt.payload.window + if mss != -1: + if win % mss == 0: + win = "S" + str(win/mss) + elif win % (mss + 40) == 0: + win = "T" + str(win/(mss+40)) + win = str(win) + + qq = "" + + if qqP: + qq += "P" + if pkt[IP].id == 0: + qq += "Z" + if pkt[IP].options != '': + qq += "I" + if pkt[TCP].urgptr != 0: + qq += "U" + if pkt[TCP].reserved != 0: + qq += "X" + if pkt[TCP].ack != 0: + qq += "A" + if qqT: + qq += "T" + if pkt[TCP].flags & 40 != 0: + # U or P + qq += "F" + if not isinstance(pkt[TCP].payload, NoPayload): + qq += "D" + # FIXME : "!" - broken options segment + + if qq == "": + qq = "." + + return (win, + ttl, + df, + ss, + ooo, + qq) + +def p0f_correl(x,y): + d = 0 + # wwww can be "*" or "%nn" + d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0)) + # ttl + d += (y[1] >= x[1] and y[1] - x[1] < 32) + for i in [2, 3, 5]: + d += (x[i] == y[i]) + xopt = x[4].split(",") + yopt = y[4].split(",") + if len(xopt) == len(yopt): + same = True + for i in range(len(xopt)): + if not (xopt[i] == yopt[i] or + (len(yopt[i]) == 2 and len(xopt[i]) > 1 and + yopt[i][1] == "*" and xopt[i][0] == yopt[i][0]) or + (len(yopt[i]) > 2 and len(xopt[i]) > 1 and + yopt[i][1] == "%" and xopt[i][0] == yopt[i][0] and + int(xopt[i][1:]) % int(yopt[i][2:]) == 0)): + same = False + break + if same: + d += len(xopt) + return d + + +def p0f(pkt): + """Passive OS fingerprinting: which OS emitted this TCP SYN ? +p0f(packet) -> accuracy, [list of guesses] +""" + pb = p0f_kdb.get_base() + if not pb: + warning("p0f base empty.") + return [] + s = len(pb[0][0]) + r = [] + sig = packet2p0f(pkt) + max = len(sig[4].split(",")) + 5 + for b in pb: + d = p0f_correl(sig,b) + if d == max: + r.append((b[6], b[7], b[1] - pkt[IP].ttl)) + return r + + +def prnp0f(pkt): + try: + r = p0f(pkt) + except: + return + if r == []: + r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt))) + ":?:?]", None) + else: + r = r[0] + uptime = None + try: + uptime = pkt2uptime(pkt) + except: + pass + if uptime == 0: + uptime = None + res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) + if uptime is not None: + res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n -> %IP.dst%:%TCP.dport%") + else: + res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport%") + if r[2] is not None: + res += " (distance " + str(r[2]) + ")" + print res + + +def pkt2uptime(pkt, HZ=100): + """Calculate the date the machine which emitted the packet booted using TCP timestamp +pkt2uptime(pkt, [HZ=100])""" + if not isinstance(pkt, Packet): + raise TypeError("Not a TCP packet") + if isinstance(pkt,NoPayload): + raise TypeError("Not a TCP packet") + if not isinstance(pkt, TCP): + return pkt2uptime(pkt.payload) + for opt in pkt.options: + if opt[0] == "Timestamp": + #t = pkt.time - opt[1][0] * 1.0/HZ + #return time.ctime(t) + t = opt[1][0] / HZ + return t + raise TypeError("No timestamp option") + + diff --git a/scapy/packet.py b/scapy/packet.py new file mode 100644 index 0000000000000000000000000000000000000000..0957fb72c59d9150fe7acecd749e1d7391568a25 --- /dev/null +++ b/scapy/packet.py @@ -0,0 +1,1293 @@ +import re,time,itertools,os,random,socket +from fields import * + + +################ +## Generators ## +################ + +class Gen(object): + def __iter__(self): + return iter([]) + +class SetGen(Gen): + def __init__(self, set, _iterpacket=1): + self._iterpacket=_iterpacket + if type(set) is list: + self.set = set + elif isinstance(set, PacketList): + self.set = list(set) + else: + self.set = [set] + def transf(self, element): + return element + def __iter__(self): + for i in self.set: + if (type(i) is tuple) and (len(i) == 2) and type(i[0]) is int and type(i[1]) is int: + if (i[0] <= i[1]): + j=i[0] + while j <= i[1]: + yield j + j += 1 + elif isinstance(i, Gen) and (self._iterpacket or not isinstance(i,Packet)): + for j in i: + yield j + else: + yield i + def __repr__(self): + return "<SetGen %s>" % self.set.__repr__() + +class Net(Gen): + """Generate a list of IPs from a network address or a name""" + name = "ip" + ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") + def __init__(self, net): + self.repr=net + + tmp=net.split('/')+["32"] + if not self.ipaddress.match(net): + tmp[0]=socket.gethostbyname(tmp[0]) + netmask = int(tmp[1]) + + def parse_digit(a,netmask): + netmask = min(8,max(netmask,0)) + if a == "*": + a = (0,256) + elif a.find("-") >= 0: + x,y = map(int,a.split("-")) + if x > y: + y = x + a = (x & (0xffL<<netmask) , max(y, (x | (0xffL>>(8-netmask))))+1) + else: + a = (int(a) & (0xffL<<netmask),(int(a) | (0xffL>>(8-netmask)))+1) + return a + + self.parsed = map(lambda x,y: parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))) + + def __iter__(self): + for d in xrange(*self.parsed[3]): + for c in xrange(*self.parsed[2]): + for b in xrange(*self.parsed[1]): + for a in xrange(*self.parsed[0]): + yield "%i.%i.%i.%i" % (a,b,c,d) + def choice(self): + ip = [] + for v in self.parsed: + ip.append(str(random.randint(v[0],v[1]-1))) + return ".".join(ip) + + def __repr__(self): + return "Net(%r)" % self.repr + +class OID(Gen): + name = "OID" + def __init__(self, oid): + self.oid = oid + self.cmpt = [] + fmt = [] + for i in oid.split("."): + if "-" in i: + fmt.append("%i") + self.cmpt.append(tuple(map(int, i.split("-")))) + else: + fmt.append(i) + self.fmt = ".".join(fmt) + def __repr__(self): + return "OID(%r)" % self.oid + def __iter__(self): + ii = [k[0] for k in self.cmpt] + while 1: + yield self.fmt % tuple(ii) + i = 0 + while 1: + if i >= len(ii): + raise StopIteration + if ii[i] < self.cmpt[i][1]: + ii[i]+=1 + break + else: + ii[i] = self.cmpt[i][0] + i += 1 + +########################### +## Packet abstract class ## +########################### + +class Packet_metaclass(type): + def __new__(cls, name, bases, dct): + newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct) + for f in newcls.fields_desc: + f.register_owner(newcls) + return newcls + def __getattr__(self, attr): + for k in self.fields_desc: + if k.name == attr: + return k + raise AttributeError(attr) + +class NewDefaultValues(Packet_metaclass): + """NewDefaultValues metaclass. Example usage: + class MyPacket(Packet): + fields_desc = [ StrField("my_field", "my default value"), ] + + class MyPacket_variant(MyPacket): + __metaclass__ = NewDefaultValues + my_field = "my new default value" + """ + def __new__(cls, name, bases, dct): + fields = None + for b in bases: + if hasattr(b,"fields_desc"): + fields = b.fields_desc + break + if fields is None: + raise Scapy_Exception("No fields_desc in superclasses") + + new_fields = [] + for f in fields: + if f.name in dct: + f = f.copy() + f.default = dct[f.name] + del(dct[f.name]) + new_fields.append(f) + dct["fields_desc"] = new_fields + return super(NewDefaultValues, cls).__new__(cls, name, bases, dct) + +class Packet(Gen): + __metaclass__ = Packet_metaclass + name=None + + fields_desc = [] + + aliastypes = [] + overload_fields = {} + + underlayer = None + + payload_guess = [] + initialized = 0 + show_indent=1 + explicit = 0 + + @classmethod + def from_hexcap(cls): + return cls(import_hexcap()) + + @classmethod + def upper_bonds(self): + for fval,upper in self.payload_guess: + print "%-20s %s" % (upper.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.iteritems())) + + @classmethod + def lower_bonds(self): + for lower,fval in self.overload_fields.iteritems(): + print "%-20s %s" % (lower.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.iteritems())) + + def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields): + self.time = time.time() + self.sent_time = 0 + if self.name is None: + self.name = self.__class__.__name__ + self.aliastypes = [ self.__class__ ] + self.aliastypes + self.default_fields = {} + self.overloaded_fields = {} + self.fields={} + self.fieldtype={} + self.packetfields=[] + self.__dict__["payload"] = NoPayload() + self.init_fields() + self.underlayer = _underlayer + self.initialized = 1 + if _pkt: + self.dissect(_pkt) + if not _internal: + self.dissection_done(self) + for f in fields.keys(): + self.fields[f] = self.get_field(f).any2i(self,fields[f]) + if type(post_transform) is list: + self.post_transforms = post_transform + elif post_transform is None: + self.post_transforms = [] + else: + self.post_transforms = [post_transform] + + def init_fields(self): + self.do_init_fields(self.fields_desc) + + def do_init_fields(self, flist): + for f in flist: + self.default_fields[f.name] = f.default + self.fieldtype[f.name] = f + if f.holds_packets: + self.packetfields.append(f) + + def dissection_done(self,pkt): + """DEV: will be called after a dissection is completed""" + self.post_dissection(pkt) + self.payload.dissection_done(pkt) + + def post_dissection(self, pkt): + """DEV: is called after the dissection of the whole packet""" + pass + + def get_field(self, fld): + """DEV: returns the field instance from the name of the field""" + return self.fieldtype[fld] + + def add_payload(self, payload): + if payload is None: + return + elif not isinstance(self.payload, NoPayload): + self.payload.add_payload(payload) + else: + if isinstance(payload, Packet): + self.__dict__["payload"] = payload + payload.add_underlayer(self) + for t in self.aliastypes: + if payload.overload_fields.has_key(t): + self.overloaded_fields = payload.overload_fields[t] + break + elif type(payload) is str: + self.__dict__["payload"] = Raw(load=payload) + else: + raise TypeError("payload must be either 'Packet' or 'str', not [%s]" % repr(payload)) + def remove_payload(self): + self.payload.remove_underlayer(self) + self.__dict__["payload"] = NoPayload() + self.overloaded_fields = {} + def add_underlayer(self, underlayer): + self.underlayer = underlayer + def remove_underlayer(self,other): + self.underlayer = None + def copy(self): + """Returns a deep copy of the instance.""" + clone = self.__class__() + clone.fields = self.fields.copy() + for k in clone.fields: + clone.fields[k]=self.get_field(k).do_copy(clone.fields[k]) + clone.default_fields = self.default_fields.copy() + clone.overloaded_fields = self.overloaded_fields.copy() + clone.overload_fields = self.overload_fields.copy() + clone.underlayer=self.underlayer + clone.explicit=self.explicit + clone.post_transforms=self.post_transforms[:] + clone.__dict__["payload"] = self.payload.copy() + clone.payload.add_underlayer(clone) + return clone + + def getfieldval(self, attr): + if attr in self.fields: + return self.fields[attr] + if attr in self.overloaded_fields: + return self.overloaded_fields[attr] + if attr in self.default_fields: + return self.default_fields[attr] + return self.payload.getfieldval(attr) + + def getfield_and_val(self, attr): + if attr in self.fields: + return self.get_field(attr),self.fields[attr] + if attr in self.overloaded_fields: + return self.get_field(attr),self.overloaded_fields[attr] + if attr in self.default_fields: + return self.get_field(attr),self.default_fields[attr] + return self.payload.getfield_and_val(attr) + + def __getattr__(self, attr): + if self.initialized: + fld,v = self.getfield_and_val(attr) + if fld is not None: + return fld.i2h(self, v) + return v + raise AttributeError(attr) + + def setfieldval(self, attr, val): + if self.default_fields.has_key(attr): + fld = self.get_field(attr) + if fld is None: + any2i = lambda x,y: y + else: + any2i = fld.any2i + self.fields[attr] = any2i(self, val) + self.explicit=0 + elif attr == "payload": + self.remove_payload() + self.add_payload(val) + else: + self.payload.setfieldval(attr,val) + + def __setattr__(self, attr, val): + if self.initialized: + try: + self.setfieldval(attr,val) + except AttributeError: + pass + else: + return + self.__dict__[attr] = val + + def delfieldval(self, attr): + if self.fields.has_key(attr): + del(self.fields[attr]) + self.explicit=0 # in case a default value must be explicited + elif self.default_fields.has_key(attr): + pass + elif attr == "payload": + self.remove_payload() + else: + self.payload.delfieldval(attr) + + def __delattr__(self, attr): + if self.initialized: + try: + self.delfieldval(attr) + except AttributeError: + pass + else: + return + if self.__dict__.has_key(attr): + del(self.__dict__[attr]) + else: + raise AttributeError(attr) + + def __repr__(self): + s = "" + ct = conf.color_theme + for f in self.fields_desc: + if isinstance(f, ConditionalField) and not f._evalcond(self): + continue + if f.name in self.fields: + val = f.i2repr(self, self.fields[f.name]) + elif f.name in self.overloaded_fields: + val = f.i2repr(self, self.overloaded_fields[f.name]) + else: + continue + if isinstance(f, Emph): + ncol = ct.emph_field_name + vcol = ct.emph_field_value + else: + ncol = ct.field_name + vcol = ct.field_value + + + s += " %s%s%s" % (ncol(f.name), + ct.punct("="), + vcol(val)) + return "%s%s %s %s%s%s"% (ct.punct("<"), + ct.layer_name(self.__class__.__name__), + s, + ct.punct("|"), + repr(self.payload), + ct.punct(">")) + def __str__(self): + return self.build() + def __div__(self, other): + if isinstance(other, Packet): + cloneA = self.copy() + cloneB = other.copy() + cloneA.add_payload(cloneB) + return cloneA + elif type(other) is str: + return self/Raw(load=other) + else: + return other.__rdiv__(self) + def __rdiv__(self, other): + if type(other) is str: + return Raw(load=other)/self + else: + raise TypeError + def __mul__(self, other): + if type(other) is int: + return [self]*other + else: + raise TypeError + def __rmul__(self,other): + return self.__mul__(other) + + def __nonzero__(self): + return True + def __len__(self): + return len(self.__str__()) + def do_build(self): + p="" + for f in self.fields_desc: + p = f.addfield(self, p, self.getfieldval(f.name)) + return p + + def post_build(self, pkt, pay): + """DEV: called right after the current layer is build.""" + return pkt+pay + + def build_payload(self): + return self.payload.build(internal=1) + + def build(self,internal=0): + if not self.explicit: + self = self.__iter__().next() + pkt = self.do_build() + for t in self.post_transforms: + pkt = t(pkt) + pay = self.build_payload() + try: + p = self.post_build(pkt,pay) + except TypeError: + log_runtime.error("API changed! post_build() now takes 2 arguments. Compatibility is only assured for a short transition time") + p = self.post_build(pkt+pay) + if not internal: + pad = self.payload.getlayer(Padding) + if pad: + p += pad.build() + p = self.build_done(p) + return p + + def build_done(self, p): + return self.payload.build_done(p) + + def do_build_ps(self): + p="" + pl = [] + q="" + for f in self.fields_desc: + p = f.addfield(self, p, self.getfieldval(f.name) ) + if type(p) is str: + r = p[len(q):] + q = p + else: + r = "" + pl.append( (f, f.i2repr(self,self.getfieldval(f.name)), r) ) + + pkt,lst = self.payload.build_ps(internal=1) + p += pkt + lst.append( (self, pl) ) + + return p,lst + + def build_ps(self,internal=0): + p,lst = self.do_build_ps() +# if not internal: +# pkt = self +# while pkt.haslayer(Padding): +# pkt = pkt.getlayer(Padding) +# lst.append( (pkt, [ ("loakjkjd", pkt.load, pkt.load) ] ) ) +# p += pkt.load +# pkt = pkt.payload + return p,lst + + + def psdump(self, filename=None, **kargs): + """psdump(filename=None, layer_shift=0, rebuild=1) +Creates an EPS file describing a packet. If filename is not provided a temporary file is created and gs is called.""" + canvas = self.canvas_dump(**kargs) + if filename is None: + fname = "/tmp/scapy.%i"%os.getpid() + canvas.writeEPSfile(fname) + os.system("%s '%s.eps' &" % (conf.prog.psreader,fname)) + else: + canvas.writeEPSfile(filename) + + def pdfdump(self, filename=None, **kargs): + """pdfdump(filename=None, layer_shift=0, rebuild=1) + Creates a PDF file describing a packet. If filename is not provided a temporary file is created and xpdf is called.""" + canvas = self.canvas_dump(**kargs) + if filename is None: + fname = "/tmp/scapy.%i"%os.getpid() + canvas.writePDFfile(fname) + os.system("%s '%s.pdf' &" % (conf.prog.pdfreader,fname)) + else: + canvas.writePDFfile(filename) + + + def canvas_dump(self, layer_shift=0, rebuild=1): + canvas = pyx.canvas.canvas() + if rebuild: + p,t = self.__class__(str(self)).build_ps() + else: + p,t = self.build_ps() + YTXT=len(t) + for n,l in t: + YTXT += len(l) + YTXT = float(YTXT) + YDUMP=YTXT + + XSTART = 1 + XDSTART = 10 + y = 0.0 + yd = 0.0 + xd = 0 + XMUL= 0.55 + YMUL = 0.4 + + backcolor=colgen(0.6, 0.8, 1.0, trans=pyx.color.rgb) + forecolor=colgen(0.2, 0.5, 0.8, trans=pyx.color.rgb) +# backcolor=makecol(0.376, 0.729, 0.525, 1.0) + + + def hexstr(x): + s = [] + for c in x: + s.append("%02x" % ord(c)) + return " ".join(s) + + + def make_dump_txt(x,y,txt): + return pyx.text.text(XDSTART+x*XMUL, (YDUMP-y)*YMUL, r"\tt{%s}"%hexstr(txt), [pyx.text.size.Large]) + + def make_box(o): + return pyx.box.rect(o.left(), o.bottom(), o.width(), o.height(), relcenter=(0.5,0.5)) + + def make_frame(lst): + if len(lst) == 1: + b = lst[0].bbox() + b.enlarge(pyx.unit.u_pt) + return b.path() + else: + fb = lst[0].bbox() + fb.enlarge(pyx.unit.u_pt) + lb = lst[-1].bbox() + lb.enlarge(pyx.unit.u_pt) + if len(lst) == 2 and fb.left() > lb.right(): + return pyx.path.path(pyx.path.moveto(fb.right(), fb.top()), + pyx.path.lineto(fb.left(), fb.top()), + pyx.path.lineto(fb.left(), fb.bottom()), + pyx.path.lineto(fb.right(), fb.bottom()), + pyx.path.moveto(lb.left(), lb.top()), + pyx.path.lineto(lb.right(), lb.top()), + pyx.path.lineto(lb.right(), lb.bottom()), + pyx.path.lineto(lb.left(), lb.bottom())) + else: + # XXX + gb = lst[1].bbox() + if gb != lb: + gb.enlarge(pyx.unit.u_pt) + kb = lst[-2].bbox() + if kb != gb and kb != lb: + kb.enlarge(pyx.unit.u_pt) + return pyx.path.path(pyx.path.moveto(fb.left(), fb.top()), + pyx.path.lineto(fb.right(), fb.top()), + pyx.path.lineto(fb.right(), kb.bottom()), + pyx.path.lineto(lb.right(), kb.bottom()), + pyx.path.lineto(lb.right(), lb.bottom()), + pyx.path.lineto(lb.left(), lb.bottom()), + pyx.path.lineto(lb.left(), gb.top()), + pyx.path.lineto(fb.left(), gb.top()), + pyx.path.closepath(),) + + + def make_dump(s, shift=0, y=0, col=None, bkcol=None, larg=16): + c = pyx.canvas.canvas() + tlist = [] + while s: + dmp,s = s[:larg-shift],s[larg-shift:] + txt = make_dump_txt(shift, y, dmp) + tlist.append(txt) + shift += len(dmp) + if shift >= 16: + shift = 0 + y += 1 + if col is None: + col = pyx.color.rgb.red + if bkcol is None: + col = pyx.color.rgb.white + c.stroke(make_frame(tlist),[col,pyx.deco.filled([bkcol]),pyx.style.linewidth.Thick]) + for txt in tlist: + c.insert(txt) + return c, tlist[-1].bbox(), shift, y + + + last_shift,last_y=0,0.0 + while t: + bkcol = backcolor.next() + proto,fields = t.pop() + y += 0.5 + pt = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % proto.name, [ pyx.text.size.Large]) + y += 1 + ptbb=pt.bbox() + ptbb.enlarge(pyx.unit.u_pt*2) + canvas.stroke(ptbb.path(),[pyx.color.rgb.black, pyx.deco.filled([bkcol])]) + canvas.insert(pt) + for fname, fval, fdump in fields: + col = forecolor.next() + ft = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fname.name)) + if fval is not None: + if len(fval) > 18: + fval = fval[:18]+"[...]" + else: + fval="" + vt = pyx.text.text(XSTART+3, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fval)) + y += 1.0 + if fdump: + dt,target,last_shift,last_y = make_dump(fdump, last_shift, last_y, col, bkcol) + + dtb = dt.bbox() + dtb=target + vtb = vt.bbox() + bxvt = make_box(vtb) + bxdt = make_box(dtb) + dtb.enlarge(pyx.unit.u_pt) + try: + if yd < 0: + cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=-90) + else: + cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=90) + except: + pass + else: + canvas.stroke(cnx,[pyx.style.linewidth.thin,pyx.deco.earrow.small,col]) + + canvas.insert(dt) + + canvas.insert(ft) + canvas.insert(vt) + last_y += layer_shift + + return canvas + + + + def extract_padding(self, s): + """DEV: to be overloaded to extract current layer's padding. Return a couple of strings (actual layer, padding)""" + return s,None + + def post_dissect(self, s): + """DEV: is called right after the current layer has been dissected""" + return s + + def pre_dissect(self, s): + """DEV: is called right before the current layer is dissected""" + return s + + def do_dissect(self, s): + flist = self.fields_desc[:] + flist.reverse() + while s and flist: + f = flist.pop() + s,fval = f.getfield(self, s) + self.fields[f.name] = fval + + return s + + def do_dissect_payload(self, s): + if s: + cls = self.guess_payload_class(s) + try: + p = cls(s, _internal=1, _underlayer=self) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + if isinstance(cls,type) and issubclass(cls,Packet): + log_runtime.error("%s dissector failed" % cls.name) + else: + log_runtime.error("%s.guess_payload_class() returned [%s]" % (self.__class__.__name__,repr(cls))) + if cls is not None: + raise + p = Raw(s, _internal=1, _underlayer=self) + self.add_payload(p) + + def dissect(self, s): + s = self.pre_dissect(s) + + s = self.do_dissect(s) + + s = self.post_dissect(s) + + payl,pad = self.extract_padding(s) + self.do_dissect_payload(payl) + if pad and conf.padding: + self.add_payload(Padding(pad)) + + + def guess_payload_class(self, payload): + """DEV: Guesses the next payload class from layer bonds. Can be overloaded to use a different mechanism.""" + for t in self.aliastypes: + for fval, cls in t.payload_guess: + ok = 1 + for k in fval.keys(): + if not hasattr(self, k) or fval[k] != self.getfieldval(k): + ok = 0 + break + if ok: + return cls + return self.default_payload_class(payload) + + def default_payload_class(self, payload): + """DEV: Returns the default payload class if nothing has been found by the guess_payload_class() method.""" + return Raw + + def hide_defaults(self): + """Removes fields' values that are the same as default values.""" + for k in self.fields.keys(): + if self.default_fields.has_key(k): + if self.default_fields[k] == self.fields[k]: + del(self.fields[k]) + self.payload.hide_defaults() + + def clone_with(self, payload=None, **kargs): + pkt = self.__class__() + pkt.explicit = 1 + pkt.fields = kargs + pkt.time = self.time + pkt.underlayer = self.underlayer + pkt.overload_fields = self.overload_fields.copy() + pkt.post_transforms = self.post_transforms + if payload is not None: + pkt.add_payload(payload) + return pkt + + + def __iter__(self): + def loop(todo, done, self=self): + if todo: + eltname = todo.pop() + elt = self.getfieldval(eltname) + if not isinstance(elt, Gen): + if self.get_field(eltname).islist: + elt = SetGen([elt]) + else: + elt = SetGen(elt) + for e in elt: + done[eltname]=e + for x in loop(todo[:], done): + yield x + else: + if isinstance(self.payload,NoPayload): + payloads = [None] + else: + payloads = self.payload + for payl in payloads: + done2=done.copy() + for k in done2: + if isinstance(done2[k], VolatileValue): + done2[k] = done2[k]._fix() + pkt = self.clone_with(payload=payl, **done2) + yield pkt + + if self.explicit: + todo = [] + done = self.fields + else: + todo = [ k for (k,v) in itertools.chain(self.default_fields.iteritems(), + self.overloaded_fields.iteritems()) + if isinstance(v, VolatileValue) ] + self.fields.keys() + done = {} + return loop(todo, done) + + def __gt__(self, other): + """True if other is an answer from self (self ==> other).""" + if isinstance(other, Packet): + return other < self + elif type(other) is str: + return 1 + else: + raise TypeError((self, other)) + def __lt__(self, other): + """True if self is an answer from other (other ==> self).""" + if isinstance(other, Packet): + return self.answers(other) + elif type(other) is str: + return 1 + else: + raise TypeError((self, other)) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + for f in self.fields_desc: + if f not in other.fields_desc: + return False + if self.getfieldval(f.name) != other.getfieldval(f.name): + return False + return self.payload == other.payload + + def __ne__(self, other): + return not self.__eq__(other) + + def hashret(self): + """DEV: returns a string that has the same value for a request and its answer.""" + return self.payload.hashret() + def answers(self, other): + """DEV: true if self is an answer from other""" + if other.__class__ == self.__class__: + return self.payload.answers(other.payload) + return 0 + + def haslayer(self, cls): + """true if self has a layer that is an instance of cls. Superseded by "cls in self" syntax.""" + if self.__class__ == cls or self.__class__.__name__ == cls: + return 1 + for f in self.packetfields: + fvalue_gen = self.getfieldval(f.name) + if fvalue_gen is None: + continue + if not f.islist: + fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) + for fvalue in fvalue_gen: + if isinstance(fvalue, Packet): + ret = fvalue.haslayer(cls) + if ret: + return ret + return self.payload.haslayer(cls) + def getlayer(self, cls, nb=1, _track=None): + """Return the nb^th layer that is an instance of cls.""" + if type(cls) is str and "." in cls: + ccls,fld = cls.split(".",1) + else: + ccls,fld = cls,None + if self.__class__ == cls or self.__class__.name == ccls: + if nb == 1: + if fld is None: + return self + else: + return self.getfieldval(fld) + else: + nb -=1 + for f in self.packetfields: + fvalue_gen = self.getfieldval(f.name) + if fvalue_gen is None: + continue + if not f.islist: + fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) + for fvalue in fvalue_gen: + if isinstance(fvalue, Packet): + track=[] + ret = fvalue.getlayer(cls, nb, _track=track) + if ret is not None: + return ret + nb = track[0] + return self.payload.getlayer(cls,nb,_track=_track) + + def __getitem__(self, cls): + if type(cls) is slice: + if cls.stop: + ret = self.getlayer(cls.start, cls.stop) + else: + ret = self.getlayer(cls.start) + if ret is None and cls.step is not None: + ret = cls.step + return ret + else: + return self.getlayer(cls) + + def __contains__(self, cls): + """"cls in self" returns true if self has a layer which is an instance of cls.""" + return self.haslayer(cls) + + + + def display(self,*args,**kargs): # Deprecated. Use show() + """Deprecated. Use show() method.""" + self.show(*args,**kargs) + def show(self, indent=3, lvl="", label_lvl=""): + """Prints a hierarchical view of the packet. "indent" gives the size of indentation for each layer.""" + ct = conf.color_theme + print "%s%s %s %s" % (label_lvl, + ct.punct("###["), + ct.layer_name(self.name), + ct.punct("]###")) + for f in self.fields_desc: + if isinstance(f, ConditionalField) and not f._evalcond(self): + continue + if isinstance(f, Emph): + ncol = ct.emph_field_name + vcol = ct.emph_field_value + else: + ncol = ct.field_name + vcol = ct.field_value + fvalue = self.getfieldval(f.name) + if isinstance(fvalue, Packet) or (f.islist and f.holds_packets and type(fvalue) is list): + print "%s \\%-10s\\" % (label_lvl+lvl, ncol(f.name)) + fvalue_gen = SetGen(fvalue,_iterpacket=0) + for fvalue in fvalue_gen: + fvalue.show(indent=indent, label_lvl=label_lvl+lvl+" |") + else: + print "%s %-10s%s %s" % (label_lvl+lvl, + ncol(f.name), + ct.punct("="), + vcol(f.i2repr(self,fvalue))) + self.payload.show(indent=indent, lvl=lvl+(" "*indent*self.show_indent), label_lvl=label_lvl) + def show2(self): + """Prints a hierarchical view of an assembled version of the packet, so that automatic fields are calculated (checksums, etc.)""" + self.__class__(str(self)).show() + + def sprintf(self, fmt, relax=1): + """sprintf(format, [relax=1]) -> str +where format is a string that can include directives. A directive begins and +ends by % and has the following format %[fmt[r],][cls[:nb].]field%. + +fmt is a classic printf directive, "r" can be appended for raw substitution +(ex: IP.flags=0x18 instead of SA), nb is the number of the layer we want +(ex: for IP/IP packets, IP:2.src is the src of the upper IP layer). +Special case : "%.time%" is the creation time. +Ex : p.sprintf("%.time% %-15s,IP.src% -> %-15s,IP.dst% %IP.chksum% " + "%03xr,IP.proto% %r,TCP.flags%") + +Moreover, the format string can include conditionnal statements. A conditionnal +statement looks like : {layer:string} where layer is a layer name, and string +is the string to insert in place of the condition if it is true, i.e. if layer +is present. If layer is preceded by a "!", the result si inverted. Conditions +can be imbricated. A valid statement can be : + p.sprintf("This is a{TCP: TCP}{UDP: UDP}{ICMP:n ICMP} packet") + p.sprintf("{IP:%IP.dst% {ICMP:%ICMP.type%}{TCP:%TCP.dport%}}") + +A side effect is that, to obtain "{" and "}" characters, you must use +"%(" and "%)". +""" + + escape = { "%": "%", + "(": "{", + ")": "}" } + + + # Evaluate conditions + while "{" in fmt: + i = fmt.rindex("{") + j = fmt[i+1:].index("}") + cond = fmt[i+1:i+j+1] + k = cond.find(":") + if k < 0: + raise Scapy_Exception("Bad condition in format string: [%s] (read sprintf doc!)"%cond) + cond,format = cond[:k],cond[k+1:] + res = False + if cond[0] == "!": + res = True + cond = cond[1:] + if self.haslayer(cond): + res = not res + if not res: + format = "" + fmt = fmt[:i]+format+fmt[i+j+2:] + + # Evaluate directives + s = "" + while "%" in fmt: + i = fmt.index("%") + s += fmt[:i] + fmt = fmt[i+1:] + if fmt and fmt[0] in escape: + s += escape[fmt[0]] + fmt = fmt[1:] + continue + try: + i = fmt.index("%") + sfclsfld = fmt[:i] + fclsfld = sfclsfld.split(",") + if len(fclsfld) == 1: + f = "s" + clsfld = fclsfld[0] + elif len(fclsfld) == 2: + f,clsfld = fclsfld + else: + raise Scapy_Exception + if "." in clsfld: + cls,fld = clsfld.split(".") + else: + cls = self.__class__.__name__ + fld = clsfld + num = 1 + if ":" in cls: + cls,num = cls.split(":") + num = int(num) + fmt = fmt[i+1:] + except: + raise Scapy_Exception("Bad format string [%%%s%s]" % (fmt[:25], fmt[25:] and "...")) + else: + if fld == "time": + val = time.strftime("%H:%M:%S.%%06i", time.localtime(self.time)) % int((self.time-int(self.time))*1000000) + elif cls == self.__class__.__name__ and hasattr(self, fld): + if num > 1: + val = self.payload.sprintf("%%%s,%s:%s.%s%%" % (f,cls,num-1,fld), relax) + f = "s" + elif f[-1] == "r": # Raw field value + val = getattr(self,fld) + f = f[:-1] + if not f: + f = "s" + else: + val = getattr(self,fld) + if fld in self.fieldtype: + val = self.fieldtype[fld].i2repr(self,val) + else: + val = self.payload.sprintf("%%%s%%" % sfclsfld, relax) + f = "s" + s += ("%"+f) % val + + s += fmt + return s + + def mysummary(self): + """DEV: can be overloaded to return a string that summarizes the layer. + Only one mysummary() is used in a whole packet summary: the one of the upper layer, + except if a mysummary() also returns (as a couple) a list of layers whose + mysummary() must be called if they are present.""" + return "" + + def summary(self, intern=0): + """Prints a one line summary of a packet.""" + found,s,needed = self.payload.summary(intern=1) + if s: + s = " / "+s + ret = "" + if not found or self.__class__ in needed: + ret = self.mysummary() + if type(ret) is tuple: + ret,n = ret + needed += n + if ret or needed: + found = 1 + if not ret: + ret = self.__class__.__name__ + ret = "%s%s" % (ret,s) + if intern: + return found,ret,needed + else: + return ret + + def lastlayer(self,layer=None): + """Returns the uppest layer of the packet""" + return self.payload.lastlayer(self) + + def decode_payload_as(self,cls): + """Reassembles the payload and decode it using another packet class""" + s = str(self.payload) + self.payload = cls(s) + + def libnet(self): + """Not ready yet. Should give the necessary C code that interfaces with libnet to recreate the packet""" + print "libnet_build_%s(" % self.__class__.name.lower() + det = self.__class__(str(self)) + for f in self.fields_desc: + val = det.getfieldval(f.name) + if val is None: + val = 0 + elif type(val) is int: + val = str(val) + else: + val = '"%s"' % str(val) + print "\t%s, \t\t/* %s */" % (val,f.name) + print ");" + def command(self): + """Returns a string representing the command you have to type to obtain the same packet""" + f = [] + for fn,fv in self.fields.items(): + fld = self.get_field(fn) + if isinstance(fv, Packet): + fv = fv.command() + elif fld.islist and fld.holds_packets and type(fv) is list: + fv = "[%s]" % ",".join( map(Packet.command, fv)) + else: + fv = repr(fv) + f.append("%s=%s" % (fn, fv)) + c = "%s(%s)" % (self.__class__.__name__, ", ".join(f)) + pc = self.payload.command() + if pc: + c += "/"+pc + return c + +class NoPayload(Packet,object): + def __new__(cls, *args, **kargs): + singl = cls.__dict__.get("__singl__") + if singl is None: + cls.__singl__ = singl = object.__new__(cls) + Packet.__init__(singl, *args, **kargs) + return singl + def __init__(self, *args, **kargs): + pass + def dissection_done(self,pkt): + return + def add_payload(self, payload): + raise Scapy_Exception("Can't add payload to NoPayload instance") + def remove_payload(self): + pass + def add_underlayer(self,underlayer): + pass + def remove_underlayer(self,other): + pass + def copy(self): + return self + def __repr__(self): + return "" + def __str__(self): + return "" + def __nonzero__(self): + return False + def build(self, internal=0): + return "" + def build_done(self, p): + return p + def build_ps(self, internal=0): + return "",[] + def getfieldval(self, attr): + raise AttributeError(attr) + def getfield_and_val(self, attr): + raise AttributeError(attr) + def setfieldval(self, attr, val): + raise AttributeError(attr) + def delfieldval(self, attr): + raise AttributeError(attr) + def __getattr__(self, attr): + if attr in self.__dict__: + return self.__dict__[attr] + elif attr in self.__class__.__dict__: + return self.__class__.__dict__[attr] + else: + raise AttributeError, attr + def hide_defaults(self): + pass + def __iter__(self): + return iter([]) + def __eq__(self, other): + if isinstance(other, NoPayload): + return True + return False + def hashret(self): + return "" + def answers(self, other): + return isinstance(other, NoPayload) or isinstance(other, Padding) + def haslayer(self, cls): + return 0 + def getlayer(self, cls, nb=1, _track=None): + if _track is not None: + _track.append(nb) + return None + def show(self, indent=3, lvl="", label_lvl=""): + pass + def sprintf(self, fmt, relax): + if relax: + return "??" + else: + raise Scapy_Exception("Format not found [%s]"%fmt) + def summary(self, intern=0): + return 0,"",[] + def lastlayer(self,layer): + return layer + def command(self): + return "" + +#################### +## packet classes ## +#################### + + +class Raw(Packet): + name = "Raw" + fields_desc = [ StrField("load", "") ] + def answers(self, other): + return 1 +# s = str(other) +# t = self.load +# l = min(len(s), len(t)) +# return s[:l] == t[:l] + +class Padding(Raw): + name = "Padding" + def build(self, internal=0): + if internal: + return "" + else: + return Raw.build(self) + +################# +## Bind layers ## +################# + + +def bind_bottom_up(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + lower.payload_guess = lower.payload_guess[:] + lower.payload_guess.append((fval, upper)) + + +def bind_top_down(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + upper.overload_fields = upper.overload_fields.copy() + upper.overload_fields[lower] = fval + +def bind_layers(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + bind_top_down(lower, upper, **fval) + bind_bottom_up(lower, upper, **fval) + +def split_bottom_up(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + def do_filter((f,u),upper=upper,fval=fval): + if u != upper: + return True + for k in fval: + if k not in f or f[k] != fval[k]: + return True + return False + lower.payload_guess = filter(do_filter, lower.payload_guess) + +def split_top_down(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + if lower in upper.overload_fields: + ofval = upper.overload_fields[lower] + for k in fval: + if k not in ofval or ofval[k] != fval[k]: + return + upper.overload_fields = upper.overload_fields.copy() + del(upper.overload_fields[lower]) + +def split_layers(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + split_bottom_up(lower, upper, **fval) + split_top_down(lower, upper, **fval) + + +def ls(obj=None): + """List available layers, or infos on a given layer""" + if obj is None: + import __builtin__ + all = __builtin__.__dict__.copy() + all.update(globals()) + objlst = filter(lambda (n,o): isinstance(o,type) and issubclass(o,Packet), all.items()) + objlst.sort(lambda x,y:cmp(x[0],y[0])) + for n,o in objlst: + print "%-10s : %s" %(n,o.name) + else: + if isinstance(obj, type) and issubclass(obj, Packet): + for f in obj.fields_desc: + print "%-10s : %-20s = (%s)" % (f.name, f.__class__.__name__, repr(f.default)) + elif isinstance(obj, Packet): + for f in obj.fields_desc: + print "%-10s : %-20s = %-15s (%s)" % (f.name, f.__class__.__name__, repr(getattr(obj,f.name)), repr(f.default)) + if not isinstance(obj.payload, NoPayload): + print "--" + ls(obj.payload) + + + else: + print "Not a packet class. Type 'ls()' to list packet classes." + + + +############# +## Fuzzing ## +############# + + +def fuzz(p, _inplace=0): + if not _inplace: + p = p.copy() + q = p + while not isinstance(q, NoPayload): + for f in q.fields_desc: + if isinstance(f, PacketListField): + for r in getattr(q, f.name): + print "fuzzing", repr(r) + fuzz(r, _inplace=1) + elif f.default is not None: + rnd = f.randval() + if rnd is not None: + q.default_fields[f.name] = rnd + q = q.payload + return p + + + diff --git a/scapy/plist.py b/scapy/plist.py new file mode 100644 index 0000000000000000000000000000000000000000..7560b095b1ab7df78ac9ea5e122c6be6f32969f6 --- /dev/null +++ b/scapy/plist.py @@ -0,0 +1,912 @@ +import os,socket +from config import conf + +############# +## Results ## +############# + +class PacketList: + res = [] + def __init__(self, res=None, name="PacketList", stats=None): + """create a packet list from a list of packets + res: the list of packets + stats: a list of classes that will appear in the stats (defaults to [TCP,UDP,ICMP])""" + if stats is None: + stats = [ TCP,UDP,ICMP ] + self.stats = stats + if res is None: + res = [] + if isinstance(res, PacketList): + res = res.res + self.res = res + self.listname = name + def _elt2pkt(self, elt): + return elt + def _elt2sum(self, elt): + return elt.summary() + def _elt2show(self, elt): + return self._elt2sum(elt) + def __repr__(self): +# stats=dict.fromkeys(self.stats,0) ## needs python >= 2.3 :( + stats = dict(map(lambda x: (x,0), self.stats)) + other = 0 + for r in self.res: + f = 0 + for p in stats: + if self._elt2pkt(r).haslayer(p): + stats[p] += 1 + f = 1 + break + if not f: + other += 1 + s = "" + ct = conf.color_theme + for p in self.stats: + s += " %s%s%s" % (ct.packetlist_proto(p.name), + ct.punct(":"), + ct.packetlist_value(stats[p])) + s += " %s%s%s" % (ct.packetlist_proto("Other"), + ct.punct(":"), + ct.packetlist_value(other)) + return "%s%s%s%s%s" % (ct.punct("<"), + ct.packetlist_name(self.listname), + ct.punct(":"), + s, + ct.punct(">")) + def __getattr__(self, attr): + return getattr(self.res, attr) + def __getitem__(self, item): + if isinstance(item,type) and issubclass(item,Packet): + return self.__class__(filter(lambda x: item in self._elt2pkt(x),self.res), + name="%s from %s"%(item.__name__,self.listname)) + if type(item) is slice: + return self.__class__(self.res.__getitem__(item), + name = "mod %s" % self.listname) + return self.res.__getitem__(item) + def __getslice__(self, *args, **kargs): + return self.__class__(self.res.__getslice__(*args, **kargs), + name="mod %s"%self.listname) + def __add__(self, other): + return self.__class__(self.res+other.res, + name="%s+%s"%(self.listname,other.listname)) + def summary(self, prn=None, lfilter=None): + """prints a summary of each packet +prn: function to apply to each packet instead of lambda x:x.summary() +lfilter: truth function to apply to each packet to decide whether it will be displayed""" + for r in self.res: + if lfilter is not None: + if not lfilter(r): + continue + if prn is None: + print self._elt2sum(r) + else: + print prn(r) + def nsummary(self,prn=None, lfilter=None): + """prints a summary of each packet with the packet's number +prn: function to apply to each packet instead of lambda x:x.summary() +lfilter: truth function to apply to each packet to decide whether it will be displayed""" + for i in range(len(self.res)): + if lfilter is not None: + if not lfilter(self.res[i]): + continue + print conf.color_theme.id(i,"%04i"), + if prn is None: + print self._elt2sum(self.res[i]) + else: + print prn(self.res[i]) + def display(self): # Deprecated. Use show() + """deprecated. is show()""" + self.show() + def show(self, *args, **kargs): + """Best way to display the packet list. Defaults to nsummary() method""" + return self.nsummary(*args, **kargs) + + def filter(self, func): + """Returns a packet list filtered by a truth function""" + return self.__class__(filter(func,self.res), + name="filtered %s"%self.listname) + def make_table(self, *args, **kargs): + """Prints a table using a function that returs for each packet its head column value, head row value and displayed value + ex: p.make_table(lambda x:(x[IP].dst, x[TCP].dport, x[TCP].sprintf("%flags%")) """ + return make_table(self.res, *args, **kargs) + def make_lined_table(self, *args, **kargs): + """Same as make_table, but print a table with lines""" + return make_lined_table(self.res, *args, **kargs) + def make_tex_table(self, *args, **kargs): + """Same as make_table, but print a table with LaTeX syntax""" + return make_tex_table(self.res, *args, **kargs) + + def plot(self, f, lfilter=None,**kargs): + """Applies a function to each packet to get a value that will be plotted with GnuPlot. A gnuplot object is returned + lfilter: a truth function that decides whether a packet must be ploted""" + g=Gnuplot.Gnuplot() + l = self.res + if lfilter is not None: + l = filter(lfilter, l) + l = map(f,l) + g.plot(Gnuplot.Data(l, **kargs)) + return g + + def diffplot(self, f, delay=1, lfilter=None, **kargs): + """diffplot(f, delay=1, lfilter=None) + Applies a function to couples (l[i],l[i+delay])""" + g = Gnuplot.Gnuplot() + l = self.res + if lfilter is not None: + l = filter(lfilter, l) + l = map(f,l[:-delay],l[delay:]) + g.plot(Gnuplot.Data(l, **kargs)) + return g + + def multiplot(self, f, lfilter=None, **kargs): + """Uses a function that returns a label and a value for this label, then plots all the values label by label""" + g=Gnuplot.Gnuplot() + l = self.res + if lfilter is not None: + l = filter(lfilter, l) + + d={} + for e in l: + k,v = f(e) + if k in d: + d[k].append(v) + else: + d[k] = [v] + data=[] + for k in d: + data.append(Gnuplot.Data(d[k], title=k, **kargs)) + + g.plot(*data) + return g + + + def rawhexdump(self): + """Prints an hexadecimal dump of each packet in the list""" + for p in self: + hexdump(self._elt2pkt(p)) + + def hexraw(self, lfilter=None): + """Same as nsummary(), except that if a packet has a Raw layer, it will be hexdumped + lfilter: a truth function that decides whether a packet must be displayed""" + for i in range(len(self.res)): + p = self._elt2pkt(self.res[i]) + if lfilter is not None and not lfilter(p): + continue + print "%s %s %s" % (conf.color_theme.id(i,"%04i"), + p.sprintf("%.time%"), + self._elt2sum(self.res[i])) + if p.haslayer(Raw): + hexdump(p.getlayer(Raw).load) + + def hexdump(self, lfilter=None): + """Same as nsummary(), except that packets are also hexdumped + lfilter: a truth function that decides whether a packet must be displayed""" + for i in range(len(self.res)): + p = self._elt2pkt(self.res[i]) + if lfilter is not None and not lfilter(p): + continue + print "%s %s %s" % (conf.color_theme.id(i,"%04i"), + p.sprintf("%.time%"), + self._elt2sum(self.res[i])) + hexdump(p) + + def padding(self, lfilter=None): + """Same as hexraw(), for Padding layer""" + for i in range(len(self.res)): + p = self._elt2pkt(self.res[i]) + if p.haslayer(Padding): + if lfilter is None or lfilter(p): + print "%s %s %s" % (conf.color_theme.id(i,"%04i"), + p.sprintf("%.time%"), + self._elt2sum(self.res[i])) + hexdump(p.getlayer(Padding).load) + + def nzpadding(self, lfilter=None): + """Same as padding() but only non null padding""" + for i in range(len(self.res)): + p = self._elt2pkt(self.res[i]) + if p.haslayer(Padding): + pad = p.getlayer(Padding).load + if pad == pad[0]*len(pad): + continue + if lfilter is None or lfilter(p): + print "%s %s %s" % (conf.color_theme.id(i,"%04i"), + p.sprintf("%.time%"), + self._elt2sum(self.res[i])) + hexdump(p.getlayer(Padding).load) + + + def conversations(self, getsrcdst=None,**kargs): + """Graphes a conversations between sources and destinations and display it + (using graphviz and imagemagick) + getsrcdst: a function that takes an element of the list and return the source and dest + by defaults, return source and destination IP + type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option + target: filename or redirect. Defaults pipe to Imagemagick's display program + prog: which graphviz program to use""" + if getsrcdst is None: + getsrcdst = lambda x:(x[IP].src, x[IP].dst) + conv = {} + for p in self.res: + p = self._elt2pkt(p) + try: + c = getsrcdst(p) + except: + #XXX warning() + continue + conv[c] = conv.get(c,0)+1 + gr = 'digraph "conv" {\n' + for s,d in conv: + gr += '\t "%s" -> "%s"\n' % (s,d) + gr += "}\n" + return do_graph(gr, **kargs) + + def afterglow(self, src=None, event=None, dst=None, **kargs): + """Experimental clone attempt of http://sourceforge.net/projects/afterglow + each datum is reduced as src -> event -> dst and the data are graphed. + by default we have IP.src -> IP.dport -> IP.dst""" + if src is None: + src = lambda x: x[IP].src + if event is None: + event = lambda x: x[IP].dport + if dst is None: + dst = lambda x: x[IP].dst + sl = {} + el = {} + dl = {} + for i in self.res: + try: + s,e,d = src(i),event(i),dst(i) + if s in sl: + n,l = sl[s] + n += 1 + if e not in l: + l.append(e) + sl[s] = (n,l) + else: + sl[s] = (1,[e]) + if e in el: + n,l = el[e] + n+=1 + if d not in l: + l.append(d) + el[e] = (n,l) + else: + el[e] = (1,[d]) + dl[d] = dl.get(d,0)+1 + except: + continue + + import math + def normalize(n): + return 2+math.log(n)/4.0 + + def minmax(x): + m,M = min(x),max(x) + if m == M: + m = 0 + if M == 0: + M = 1 + return m,M + + mins,maxs = minmax(map(lambda (x,y): x, sl.values())) + mine,maxe = minmax(map(lambda (x,y): x, el.values())) + mind,maxd = minmax(dl.values()) + + gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n' + + gr += "# src nodes\n" + for s in sl: + n,l = sl[s]; n = 1+float(n-mins)/(maxs-mins) + gr += '"src.%s" [label = "%s", shape=box, fillcolor="#FF0000", style=filled, fixedsize=1, height=%.2f,width=%.2f];\n' % (`s`,`s`,n,n) + gr += "# event nodes\n" + for e in el: + n,l = el[e]; n = n = 1+float(n-mine)/(maxe-mine) + gr += '"evt.%s" [label = "%s", shape=circle, fillcolor="#00FFFF", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`e`,`e`,n,n) + for d in dl: + n = dl[d]; n = n = 1+float(n-mind)/(maxd-mind) + gr += '"dst.%s" [label = "%s", shape=triangle, fillcolor="#0000ff", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`d`,`d`,n,n) + + gr += "###\n" + for s in sl: + n,l = sl[s] + for e in l: + gr += ' "src.%s" -> "evt.%s";\n' % (`s`,`e`) + for e in el: + n,l = el[e] + for d in l: + gr += ' "evt.%s" -> "dst.%s";\n' % (`e`,`d`) + + gr += "}" + open("/tmp/aze","w").write(gr) + return do_graph(gr, **kargs) + + + + def timeskew_graph(self, ip, **kargs): + """Tries to graph the timeskew between the timestamps and real time for a given ip""" + res = map(lambda x: self._elt2pkt(x), self.res) + b = filter(lambda x:x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), res) + c = [] + for p in b: + opts = p.getlayer(TCP).options + for o in opts: + if o[0] == "Timestamp": + c.append((p.time,o[1][0])) + if not c: + warning("No timestamps found in packet list") + return + d = map(lambda (x,y): (x%2000,((x-c[0][0])-((y-c[0][1])/1000.0))),c) + g = Gnuplot.Gnuplot() + g.plot(Gnuplot.Data(d,**kargs)) + return g + + def _dump_document(self, **kargs): + d = pyx.document.document() + l = len(self.res) + for i in range(len(self.res)): + elt = self.res[i] + c = self._elt2pkt(elt).canvas_dump(**kargs) + cbb = c.bbox() + c.text(cbb.left(),cbb.top()+1,r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i,l),[pyx.text.size.LARGE]) + if conf.verb >= 2: + os.write(1,".") + d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4, + margin=1*pyx.unit.t_cm, + fittosize=1)) + return d + + + + def psdump(self, filename = None, **kargs): + """Creates a multipage poscript file with a psdump of every packet + filename: name of the file to write to. If empty, a temporary file is used and + conf.prog.psreader is called""" + d = self._dump_document(**kargs) + if filename is None: + filename = "/tmp/scapy.psd.%i" % os.getpid() + d.writePSfile(filename) + os.system("%s %s.ps &" % (conf.prog.psreader,filename)) + else: + d.writePSfile(filename) + print + + def pdfdump(self, filename = None, **kargs): + """Creates a PDF file with a psdump of every packet + filename: name of the file to write to. If empty, a temporary file is used and + conf.prog.pdfreader is called""" + d = self._dump_document(**kargs) + if filename is None: + filename = "/tmp/scapy.psd.%i" % os.getpid() + d.writePDFfile(filename) + os.system("%s %s.pdf &" % (conf.prog.pdfreader,filename)) + else: + d.writePDFfile(filename) + print + + def sr(self,multi=0): + """sr([multi=1]) -> (SndRcvList, PacketList) + Matches packets in the list and return ( (matched couples), (unmatched packets) )""" + remain = self.res[:] + sr = [] + i = 0 + while i < len(remain): + s = remain[i] + j = i + while j < len(remain)-1: + j += 1 + r = remain[j] + if r.answers(s): + sr.append((s,r)) + if multi: + remain[i]._answered=1 + remain[j]._answered=2 + continue + del(remain[j]) + del(remain[i]) + i -= 1 + break + i += 1 + if multi: + remain = filter(lambda x:not hasattr(x,"_answered"), remain) + return SndRcvList(sr),PacketList(remain) + + + + + + +class Dot11PacketList(PacketList): + def __init__(self, res=None, name="Dot11List", stats=None): + if stats is None: + stats = [Dot11WEP, Dot11Beacon, UDP, ICMP, TCP] + + PacketList.__init__(self, res, name, stats) + def toEthernet(self): + data = map(lambda x:x.getlayer(Dot11), filter(lambda x : x.haslayer(Dot11) and x.type == 2, self.res)) + r2 = [] + for p in data: + q = p.copy() + q.unwep() + r2.append(Ether()/q.payload.payload.payload) #Dot11/LLC/SNAP/IP + return PacketList(r2,name="Ether from %s"%self.listname) + + + +class SndRcvList(PacketList): + def __init__(self, res=None, name="Results", stats=None): + PacketList.__init__(self, res, name, stats) + def _elt2pkt(self, elt): + return elt[1] + def _elt2sum(self, elt): + return "%s ==> %s" % (elt[0].summary(),elt[1].summary()) + + +class ARPingResult(SndRcvList): + def __init__(self, res=None, name="ARPing", stats=None): + PacketList.__init__(self, res, name, stats) + + def show(self): + for s,r in self.res: + print r.sprintf("%Ether.src% %ARP.psrc%") + + +class AS_resolver: + server = None + options = "-k" + def __init__(self, server=None, port=43, options=None): + if server is not None: + self.server = server + self.port = port + if options is not None: + self.options = options + + def _start(self): + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.connect((self.server,self.port)) + if self.options: + self.s.send(self.options+"\n") + self.s.recv(8192) + def _stop(self): + self.s.close() + + def _parse_whois(self, txt): + asn,desc = None,"" + for l in txt.splitlines(): + if not asn and l.startswith("origin:"): + asn = l[7:].strip() + if l.startswith("descr:"): + if desc: + desc += r"\n" + desc += l[6:].strip() + if asn is not None and desc: + break + return asn,desc.strip() + + def _resolve_one(self, ip): + self.s.send("%s\n" % ip) + x = "" + while not ("%" in x or "source" in x): + x += self.s.recv(8192) + asn, desc = self._parse_whois(x) + return ip,asn,desc + def resolve(self, *ips): + self._start() + ret = [] + for ip in ips: + ip,asn,desc = self._resolve_one(ip) + if asn is not None: + ret.append((ip,asn,desc)) + self._stop() + return ret + +class AS_resolver_riswhois(AS_resolver): + server = "riswhois.ripe.net" + options = "-k -M -1" + + +class AS_resolver_radb(AS_resolver): + server = "whois.ra.net" + options = "-k -M" + + +class AS_resolver_cymru(AS_resolver): + server = "whois.cymru.com" + options = None + def resolve(self, *ips): + ASNlist = [] + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((self.server,self.port)) + s.send("begin\r\n"+"\r\n".join(ips)+"\r\nend\r\n") + r = "" + while 1: + l = s.recv(8192) + if l == "": + break + r += l + s.close() + for l in r.splitlines()[1:]: + if "|" not in l: + continue + asn,ip,desc = map(str.strip, l.split("|")) + if asn == "NA": + continue + asn = int(asn) + ASNlist.append((ip,asn,desc)) + return ASNlist + +class AS_resolver_multi(AS_resolver): + resolvers_list = ( AS_resolver_cymru(),AS_resolver_riswhois(),AS_resolver_radb() ) + def __init__(self, *reslist): + if reslist: + self.resolvers_list = reslist + def resolve(self, *ips): + todo = ips + ret = [] + for ASres in self.resolvers_list: + res = ASres.resolve(*todo) + resolved = [ ip for ip,asn,desc in res ] + todo = [ ip for ip in todo if ip not in resolved ] + ret += res + return ret + + + +class TracerouteResult(SndRcvList): + def __init__(self, res=None, name="Traceroute", stats=None): + PacketList.__init__(self, res, name, stats) + self.graphdef = None + self.graphASres = 0 + self.padding = 0 + self.hloc = None + self.nloc = None + + def show(self): + return self.make_table(lambda (s,r): (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), + s.ttl, + r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) + + + def get_trace(self): + trace = {} + for s,r in self.res: + if IP not in s: + continue + d = s[IP].dst + if d not in trace: + trace[d] = {} + trace[d][s[IP].ttl] = r[IP].src, ICMP not in r + for k in trace.values(): + m = filter(lambda x:k[x][1], k.keys()) + if not m: + continue + m = min(m) + for l in k.keys(): + if l > m: + del(k[l]) + return trace + + def trace3D(self): + """Give a 3D representation of the traceroute. + right button: rotate the scene + middle button: zoom + left button: move the scene + left button on a ball: toggle IP displaying + ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" + trace = self.get_trace() + import visual + + class IPsphere(visual.sphere): + def __init__(self, ip, **kargs): + visual.sphere.__init__(self, **kargs) + self.ip=ip + self.label=None + self.setlabel(self.ip) + def setlabel(self, txt,visible=None): + if self.label is not None: + if visible is None: + visible = self.label.visible + self.label.visible = 0 + elif visible is None: + visible=0 + self.label=visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) + def action(self): + self.label.visible ^= 1 + + visual.scene = visual.display() + visual.scene.exit_on_close(0) + start = visual.box() + rings={} + tr3d = {} + for i in trace: + tr = trace[i] + tr3d[i] = [] + ttl = tr.keys() + for t in range(1,max(ttl)+1): + if t not in rings: + rings[t] = [] + if t in tr: + if tr[t] not in rings[t]: + rings[t].append(tr[t]) + tr3d[i].append(rings[t].index(tr[t])) + else: + rings[t].append(("unk",-1)) + tr3d[i].append(len(rings[t])-1) + for t in rings: + r = rings[t] + l = len(r) + for i in range(l): + if r[i][1] == -1: + col = (0.75,0.75,0.75) + elif r[i][1]: + col = visual.color.green + else: + col = visual.color.blue + + s = IPsphere(pos=((l-1)*visual.cos(2*i*visual.pi/l),(l-1)*visual.sin(2*i*visual.pi/l),2*t), + ip = r[i][0], + color = col) + for trlst in tr3d.values(): + if t <= len(trlst): + if trlst[t-1] == i: + trlst[t-1] = s + forecol = colgen(0.625, 0.4375, 0.25, 0.125) + for trlst in tr3d.values(): + col = forecol.next() + start = (0,0,0) + for ip in trlst: + visual.cylinder(pos=start,axis=ip.pos-start,color=col,radius=0.2) + start = ip.pos + + movcenter=None + while 1: + if visual.scene.kb.keys: + k = visual.scene.kb.getkey() + if k == "esc": + break + if visual.scene.mouse.events: + ev = visual.scene.mouse.getevent() + if ev.press == "left": + o = ev.pick + if o: + if ev.ctrl: + if o.ip == "unk": + continue + savcolor = o.color + o.color = (1,0,0) + a,b=sr(IP(dst=o.ip)/TCP(dport=[21,22,23,25,80,443]),timeout=2) + o.color = savcolor + if len(a) == 0: + txt = "%s:\nno results" % o.ip + else: + txt = "%s:\n" % o.ip + for s,r in a: + txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") + o.setlabel(txt, visible=1) + else: + if hasattr(o, "action"): + o.action() + elif ev.drag == "left": + movcenter = ev.pos + elif ev.drop == "left": + movcenter = None + if movcenter: + visual.scene.center -= visual.scene.mouse.pos-movcenter + movcenter = visual.scene.mouse.pos + + + def world_trace(self): + ips = {} + rt = {} + ports_done = {} + for s,r in self.res: + ips[r.src] = None + if s.haslayer(TCP) or s.haslayer(UDP): + trace_id = (s.src,s.dst,s.proto,s.dport) + elif s.haslayer(ICMP): + trace_id = (s.src,s.dst,s.proto,s.type) + else: + trace_id = (s.src,s.dst,s.proto,0) + trace = rt.get(trace_id,{}) + if not r.haslayer(ICMP) or r.type != 11: + if ports_done.has_key(trace_id): + continue + ports_done[trace_id] = None + trace[s.ttl] = r.src + rt[trace_id] = trace + + trt = {} + for trace_id in rt: + trace = rt[trace_id] + loctrace = [] + for i in range(max(trace.keys())): + ip = trace.get(i,None) + if ip is None: + continue + loc = locate_ip(ip) + if loc is None: + continue +# loctrace.append((ip,loc)) # no labels yet + loctrace.append(loc) + if loctrace: + trt[trace_id] = loctrace + + tr = map(lambda x: Gnuplot.Data(x,with="lines"), trt.values()) + g = Gnuplot.Gnuplot() + world = Gnuplot.File(conf.gnuplot_world,with="lines") + g.plot(world,*tr) + return g + + def make_graph(self,ASres=None,padding=0): + if ASres is None: + ASres = conf.AS_resolver + self.graphASres = ASres + self.graphpadding = padding + ips = {} + rt = {} + ports = {} + ports_done = {} + for s,r in self.res: + r = r[IP] or r[IPv6] or r + s = s[IP] or s[IPv6] or s + ips[r.src] = None + if TCP in s: + trace_id = (s.src,s.dst,6,s.dport) + elif UDP in s: + trace_id = (s.src,s.dst,17,s.dport) + elif ICMP in s: + trace_id = (s.src,s.dst,1,s.type) + else: + trace_id = (s.src,s.dst,s.proto,0) + trace = rt.get(trace_id,{}) + ttl = IPv6 in s and s.hlim or s.ttl + if not (ICMP in r and r[ICMP].type == 11) and not (IPv6 in r and ICMPv6TimeExceeded in r): + if trace_id in ports_done: + continue + ports_done[trace_id] = None + p = ports.get(r.src,[]) + if TCP in r: + p.append(r.sprintf("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%")) + trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') + elif UDP in r: + p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%")) + trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') + elif ICMP in r: + p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%")) + trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') + else: + p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> IPv6 %nh%}")) + trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') + ports[r.src] = p + else: + trace[ttl] = r.sprintf('"%r,src%"') + rt[trace_id] = trace + + # Fill holes with unk%i nodes + unknown_label = incremental_label("unk%i") + blackholes = [] + bhip = {} + for rtk in rt: + trace = rt[rtk] + k = trace.keys() + for n in range(min(k), max(k)): + if not trace.has_key(n): + trace[n] = unknown_label.next() + if not ports_done.has_key(rtk): + if rtk[2] == 1: #ICMP + bh = "%s %i/icmp" % (rtk[1],rtk[3]) + elif rtk[2] == 6: #TCP + bh = "%s %i/tcp" % (rtk[1],rtk[3]) + elif rtk[2] == 17: #UDP + bh = '%s %i/udp' % (rtk[1],rtk[3]) + else: + bh = '%s %i/proto' % (rtk[1],rtk[2]) + ips[bh] = None + bhip[rtk[1]] = bh + bh = '"%s"' % bh + trace[max(k)+1] = bh + blackholes.append(bh) + + # Find AS numbers + ASN_query_list = dict.fromkeys(map(lambda x:x.rsplit(" ",1)[0],ips)).keys() + if ASres is None: + ASNlist = [] + else: + ASNlist = ASres.resolve(*ASN_query_list) + + ASNs = {} + ASDs = {} + for ip,asn,desc, in ASNlist: + if asn is None: + continue + iplist = ASNs.get(asn,[]) + if ip in bhip: + if ip in ports: + iplist.append(ip) + iplist.append(bhip[ip]) + else: + iplist.append(ip) + ASNs[asn] = iplist + ASDs[asn] = desc + + + backcolorlist=colgen("60","86","ba","ff") + forecolorlist=colgen("a0","70","40","20") + + s = "digraph trace {\n" + + s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" + + s += "\n#ASN clustering\n" + for asn in ASNs: + s += '\tsubgraph cluster_%s {\n' % asn + col = backcolorlist.next() + s += '\t\tcolor="#%s%s%s";' % col + s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col + s += '\t\tfontsize = 10;' + s += '\t\tlabel = "%s\\n[%s]"\n' % (asn,ASDs[asn]) + for ip in ASNs[asn]: + + s += '\t\t"%s";\n'%ip + s += "\t}\n" + + + + + s += "#endpoints\n" + for p in ports: + s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p,p,"|".join(ports[p])) + + s += "\n#Blackholes\n" + for bh in blackholes: + s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh + + if padding: + s += "\n#Padding\n" + pad={} + for snd,rcv in self.res: + if rcv.src not in ports and rcv.haslayer(Padding): + p = rcv.getlayer(Padding).load + if p != "\x00"*len(p): + pad[rcv.src]=None + for rcv in pad: + s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv + + + + s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" + + + for rtk in rt: + s += "#---[%s\n" % `rtk` + s += '\t\tedge [color="#%s%s%s"];\n' % forecolorlist.next() + trace = rt[rtk] + k = trace.keys() + for n in range(min(k), max(k)): + s += '\t%s ->\n' % trace[n] + s += '\t%s;\n' % trace[max(k)] + + s += "}\n"; + self.graphdef = s + + def graph(self, ASres=None, padding=0, **kargs): + """x.graph(ASres=conf.AS_resolver, other args): + ASres=None : no AS resolver => no clustering + ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) + ASres=AS_resolver_cymru(): use whois.cymru.com whois database + ASres=AS_resolver(server="whois.ra.net") + type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option + target: filename or redirect. Defaults pipe to Imagemagick's display program + prog: which graphviz program to use""" + if ASres is None: + ASres = conf.AS_resolver + if (self.graphdef is None or + self.graphASres != ASres or + self.graphpadding != padding): + self.make_graph(ASres,padding) + + return do_graph(self.graphdef, **kargs) + + +conf.AS_resolver = AS_resolver_multi() + diff --git a/scapy/queso.py b/scapy/queso.py new file mode 100644 index 0000000000000000000000000000000000000000..92daa32e2eb16adcd58b99800db68aacdb8bb2b6 --- /dev/null +++ b/scapy/queso.py @@ -0,0 +1,98 @@ +from scapy.data import KnowledgeBase +from scapy.config import conf + +################# +## Queso stuff ## +################# + + +def quesoTCPflags(flags): + if flags == "-": + return "-" + flv = "FSRPAUXY" + v = 0 + for i in flags: + v |= 2**flv.index(i) + return "%x" % v + +class QuesoKnowledgeBase(KnowledgeBase): + def lazy_init(self): + try: + f = open(self.filename) + except IOError: + return + self.base = {} + p = None + try: + for l in f: + l = l.strip() + if not l or l[0] == ';': + continue + if l[0] == '*': + if p is not None: + p[""] = name + name = l[1:].strip() + p = self.base + continue + if l[0] not in list("0123456"): + continue + res = l[2:].split() + res[-1] = quesoTCPflags(res[-1]) + res = " ".join(res) + if not p.has_key(res): + p[res] = {} + p = p[res] + if p is not None: + p[""] = name + except: + self.base = None + warning("Can't load queso base [%s]", self.filename) + f.close() + + +queso_kdb = QuesoKnowledgeBase(conf.queso_base) + + +def queso_sig(target, dport=80, timeout=3): + p = queso_kdb.get_base() + ret = [] + for flags in ["S", "SA", "F", "FA", "SF", "P", "SEC"]: + ans, unans = sr(IP(dst=target)/TCP(dport=dport,flags=flags,seq=RandInt()), + timeout=timeout, verbose=0) + if len(ans) == 0: + rs = "- - - -" + else: + s,r = ans[0] + rs = "%i" % (r.seq != 0) + if not r.ack: + r += " 0" + elif r.ack-s.seq > 666: + rs += " R" % 0 + else: + rs += " +%i" % (r.ack-s.seq) + rs += " %X" % r.window + rs += " %x" % r.payload.flags + ret.append(rs) + return ret + +def queso_search(sig): + p = queso_kdb.get_base() + sig.reverse() + ret = [] + try: + while sig: + s = sig.pop() + p = p[s] + if p.has_key(""): + ret.append(p[""]) + except KeyError: + pass + return ret + + +def queso(*args,**kargs): + """Queso OS fingerprinting +queso(target, dport=80, timeout=3)""" + return queso_search(queso_sig(*args, **kargs)) + + diff --git a/scapy/route.py b/scapy/route.py new file mode 100644 index 0000000000000000000000000000000000000000..d7cd7646610816dc66c161028b076ffa4cb04d57 --- /dev/null +++ b/scapy/route.py @@ -0,0 +1,157 @@ +import socket +from arch import read_routes +from utils import atol +from config import conf + +############################## +## Routing/Interfaces stuff ## +############################## + +class Route: + def __init__(self): + self.resync() + self.s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.cache = {} + + def invalidate_cache(self): + self.cache = {} + + def resync(self): + self.invalidate_cache() + self.routes = read_routes() + + def __repr__(self): + rt = "Network Netmask Gateway Iface Output IP\n" + for net,msk,gw,iface,addr in self.routes: + rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net), + ltoa(msk), + gw, + iface, + addr) + return rt + + def make_route(self, host=None, net=None, gw=None, dev=None): + if host is not None: + thenet,msk = host,32 + elif net is not None: + thenet,msk = net.split("/") + msk = int(msk) + else: + raise Scapy_Exception("make_route: Incorrect parameters. You should specify a host or a net") + if gw is None: + gw="0.0.0.0" + if dev is None: + if gw: + nhop = gw + else: + nhop = thenet + dev,ifaddr,x = self.route(nhop) + else: + ifaddr = get_if_addr(dev) + return (atol(thenet), itom(msk), gw, dev, ifaddr) + + def add(self, *args, **kargs): + """Ex: + add(net="192.168.1.0/24",gw="1.2.3.4") + """ + self.invalidate_cache() + self.routes.append(self.make_route(*args,**kargs)) + + + def delt(self, *args, **kargs): + """delt(host|net, gw|dev)""" + self.invalidate_cache() + route = self.make_route(*args,**kargs) + try: + i=self.routes.index(route) + del(self.routes[i]) + except ValueError: + warning("no matching route found") + + def ifchange(self, iff, addr): + self.invalidate_cache() + the_addr,the_msk = (addr.split("/")+["32"])[:2] + the_msk = itom(int(the_msk)) + the_rawaddr = atol(the_addr) + the_net = the_rawaddr & the_msk + + + for i in range(len(self.routes)): + net,msk,gw,iface,addr = self.routes[i] + if iface != iff: + continue + if gw == '0.0.0.0': + self.routes[i] = (the_net,the_msk,gw,iface,the_addr) + else: + self.routes[i] = (net,msk,gw,iface,the_addr) + for i in arp_cache.keys(): + del(arp_cache[i]) + + + + def ifdel(self, iff): + self.invalidate_cache() + new_routes=[] + for rt in self.routes: + if rt[3] != iff: + new_routes.append(rt) + self.routes=new_routes + + def ifadd(self, iff, addr): + self.invalidate_cache() + the_addr,the_msk = (addr.split("/")+["32"])[:2] + the_msk = itom(int(the_msk)) + the_rawaddr = atol(the_addr) + the_net = the_rawaddr & the_msk + self.routes.append((the_net,the_msk,'0.0.0.0',iff,the_addr)) + + + def route(self,dest,verbose=None): + if dest in self.cache: + return self.cache[dest] + if verbose is None: + verbose=conf.verb + # Transform "192.168.*.1-5" to one IP of the set + dst = dest.split("/")[0] + dst = dst.replace("*","0") + while 1: + l = dst.find("-") + if l < 0: + break + m = (dst[l:]+".").find(".") + dst = dst[:l]+dst[l+m:] + + + dst = atol(dst) + pathes=[] + for d,m,gw,i,a in self.routes: + aa = atol(a) + if aa == dst: + pathes.append((0xffffffffL,("lo",a,"0.0.0.0"))) + if (dst & m) == (d & m): + pathes.append((m,(i,a,gw))) + if not pathes: + if verbose: + warning("No route found (no default route?)") + return "lo","0.0.0.0","0.0.0.0" #XXX linux specific! + # Choose the more specific route (greatest netmask). + # XXX: we don't care about metrics + pathes.sort() + ret = pathes[-1][1] + self.cache[dest] = ret + return ret + + def get_if_bcast(self, iff): + for net, msk, gw, iface, addr in self.routes: + if (iff == iface and net != 0L): + bcast = atol(addr)|(~msk&0xffffffffL); # FIXME: check error in atol() + return ltoa(bcast); + warning("No broadcast address found for iface %s\n" % iff); + +conf.route=Route() + +#XXX use "with" +_betteriface = conf.route.route("0.0.0.0", verbose=0)[0] +if _betteriface != "lo": #XXX linux specific... + conf.iface = _betteriface +del(_betteriface) diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py new file mode 100644 index 0000000000000000000000000000000000000000..03ce3658921fe7faedc164e7c0fe2c6a4d0243b5 --- /dev/null +++ b/scapy/sendrecv.py @@ -0,0 +1,547 @@ +import cPickle,os,sys,time +from select import select +from data import * +from config import conf + + +################# +## Debug class ## +################# + +class debug: + recv=[] + sent=[] + match=[] + + +#################### +## Send / Receive ## +#################### + + + + +def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0): + if not isinstance(pkt, Gen): + pkt = SetGen(pkt) + + if verbose is None: + verbose = conf.verb + debug.recv = PacketList([],"Unanswered") + debug.sent = PacketList([],"Sent") + debug.match = SndRcvList([]) + nbrecv=0 + ans = [] + # do it here to fix random fields, so that parent and child have the same + all_stimuli = tobesent = [p for p in pkt] + notans = len(tobesent) + + hsent={} + for i in tobesent: + h = i.hashret() + if h in hsent: + hsent[h].append(i) + else: + hsent[h] = [i] + if retry < 0: + retry = -retry + autostop=retry + else: + autostop=0 + + + while retry >= 0: + found=0 + + if timeout < 0: + timeout = None + + rdpipe,wrpipe = os.pipe() + rdpipe=os.fdopen(rdpipe) + wrpipe=os.fdopen(wrpipe,"w") + + pid=1 + try: + pid = os.fork() + if pid == 0: + try: + sys.stdin.close() + rdpipe.close() + try: + i = 0 + if verbose: + print "Begin emission:" + for p in tobesent: + pks.send(p) + i += 1 + time.sleep(inter) + if verbose: + print "Finished to send %i packets." % i + except SystemExit: + pass + except KeyboardInterrupt: + pass + except: + log_runtime.exception("--- Error in child %i" % os.getpid()) + log_runtime.info("--- Error in child %i" % os.getpid()) + finally: + try: + os.setpgrp() # Chance process group to avoid ctrl-C + sent_times = [p.sent_time for p in all_stimuli if p.sent_time] + cPickle.dump( (arp_cache,sent_times), wrpipe ) + wrpipe.close() + except: + pass + elif pid < 0: + log_runtime.error("fork error") + else: + wrpipe.close() + stoptime = 0 + remaintime = None + inmask = [rdpipe,pks] + try: + try: + while 1: + if stoptime: + remaintime = stoptime-time.time() + if remaintime <= 0: + break + r = None + if FREEBSD or DARWIN: + inp, out, err = select(inmask,[],[], 0.05) + if len(inp) == 0 or pks in inp: + r = pks.nonblock_recv() + else: + inp, out, err = select(inmask,[],[], remaintime) + if len(inp) == 0: + break + if pks in inp: + r = pks.recv(MTU) + if rdpipe in inp: + if timeout: + stoptime = time.time()+timeout + del(inmask[inmask.index(rdpipe)]) + if r is None: + continue + ok = 0 + h = r.hashret() + if h in hsent: + hlst = hsent[h] + for i in range(len(hlst)): + if r.answers(hlst[i]): + ans.append((hlst[i],r)) + if verbose > 1: + os.write(1, "*") + ok = 1 + if not multi: + del(hlst[i]) + notans -= 1; + else: + if not hasattr(hlst[i], '_answered'): + notans -= 1; + hlst[i]._answered = 1; + break + if notans == 0 and not multi: + break + if not ok: + if verbose > 1: + os.write(1, ".") + nbrecv += 1 + if conf.debug_match: + debug.recv.append(r) + except KeyboardInterrupt: + if chainCC: + raise + finally: + try: + ac,sent_times = cPickle.load(rdpipe) + except EOFError: + warning("Child died unexpectedly. Packets may have not been sent %i"%os.getpid()) + else: + arp_cache.update(ac) + for p,t in zip(all_stimuli, sent_times): + p.sent_time = t + os.waitpid(pid,0) + finally: + if pid == 0: + os._exit(0) + + remain = reduce(list.__add__, hsent.values(), []) + if multi: + remain = filter(lambda p: not hasattr(p, '_answered'), remain); + + if autostop and len(remain) > 0 and len(remain) != len(tobesent): + retry = autostop + + tobesent = remain + if len(tobesent) == 0: + break + retry -= 1 + + if conf.debug_match: + debug.sent=PacketList(remain[:],"Sent") + debug.match=SndRcvList(ans[:]) + + #clean the ans list to delete the field _answered + if (multi): + for s,r in ans: + if hasattr(s, '_answered'): + del(s._answered) + + if verbose: + print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans) + return SndRcvList(ans),PacketList(remain,"Unanswered"),debug.recv + + +def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs): + if not isinstance(x, Gen): + x = SetGen(x) + if verbose is None: + verbose = conf.verb + n = 0 + if count is not None: + loop = -count + elif not loop: + loop=-1 + try: + while loop: + for p in x: + s.send(p) + n += 1 + if verbose: + os.write(1,".") + time.sleep(inter) + if loop < 0: + loop += 1 + except KeyboardInterrupt: + pass + s.close() + if verbose: + print "\nSent %i packets." % n + +def send(x, inter=0, loop=0, count=None, verbose=None, *args, **kargs): + """Send packets at layer 3 +send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" + __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose) + +def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, *args, **kargs): + """Send packets at layer 2 +sendp(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" + if iface is None and iface_hint is not None: + iface = conf.route.route(iface_hint)[0] + __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose) + +def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, iface=None): + """Send packets at layer 2 using tcpreplay for performance + pps: packets per second + mpbs: MBits per second + realtime: use packet's timestamp, bending time with realtime value + loop: number of times to process the packet list + iface: output interface """ + if iface is None: + iface = conf.iface + options = ["--intf1=%s" % iface ] + if pps is not None: + options.append("--pps=%i" % pps) + elif mbps is not None: + options.append("--mbps=%i" % mbps) + elif realtime is not None: + options.append("--multiplier=%i" % realtime) + else: + options.append("--topspeed") + + if loop: + options.append("--loop=%i" % loop) + + f = os.tempnam("scapy") + options.append(f) + wrpcap(f, x) + try: + try: + os.spawnlp(os.P_WAIT, conf.prog.tcpreplay, conf.prog.tcpreplay, *options) + except KeyboardInterrupt: + log_interactive.info("Interrupted by user") + finally: + os.unlink(f) + + + + + +def sr(x,filter=None, iface=None, nofilter=0, *args,**kargs): + """Send and receive packets at layer 3 +nofilter: put 1 to avoid use of bpf filters +retry: if positive, how many times to resend unanswered packets + if negative, how many times to retry when no more packets are answered +timeout: how much time to wait after the last packet has been sent +verbose: set verbosity level +multi: whether to accept multiple answers for the same stimulus +filter: provide a BPF filter +iface: listen answers only on the given interface""" + if not kargs.has_key("timeout"): + kargs["timeout"] = -1 + s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) + a,b,c=sndrcv(s,x,*args,**kargs) + s.close() + return a,b + +def sr1(x,filter=None,iface=None, nofilter=0, *args,**kargs): + """Send packets at layer 3 and return only the first answer +nofilter: put 1 to avoid use of bpf filters +retry: if positive, how many times to resend unanswered packets + if negative, how many times to retry when no more packets are answered +timeout: how much time to wait after the last packet has been sent +verbose: set verbosity level +multi: whether to accept multiple answers for the same stimulus +filter: provide a BPF filter +iface: listen answers only on the given interface""" + if not kargs.has_key("timeout"): + kargs["timeout"] = -1 + s=conf.L3socket(filter=filter, nofilter=nofilter, iface=iface) + a,b,c=sndrcv(s,x,*args,**kargs) + s.close() + if len(a) > 0: + return a[0][1] + else: + return None + +def srp(x,iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args,**kargs): + """Send and receive packets at layer 2 +nofilter: put 1 to avoid use of bpf filters +retry: if positive, how many times to resend unanswered packets + if negative, how many times to retry when no more packets are answered +timeout: how much time to wait after the last packet has been sent +verbose: set verbosity level +multi: whether to accept multiple answers for the same stimulus +filter: provide a BPF filter +iface: work only on the given interface""" + if not kargs.has_key("timeout"): + kargs["timeout"] = -1 + if iface is None and iface_hint is not None: + iface = conf.route.route(iface_hint)[0] + s = conf.L2socket(iface=iface, filter=filter, nofilter=nofilter, type=type) + a,b,c=sndrcv(s ,x,*args,**kargs) + s.close() + return a,b + +def srp1(*args,**kargs): + """Send and receive packets at layer 2 and return only the first answer +nofilter: put 1 to avoid use of bpf filters +retry: if positive, how many times to resend unanswered packets + if negative, how many times to retry when no more packets are answered +timeout: how much time to wait after the last packet has been sent +verbose: set verbosity level +multi: whether to accept multiple answers for the same stimulus +filter: provide a BPF filter +iface: work only on the given interface""" + if not kargs.has_key("timeout"): + kargs["timeout"] = -1 + a,b=srp(*args,**kargs) + if len(a) > 0: + return a[0][1] + else: + return None + +def __sr_loop(srfunc, pkts, prn=lambda x:x[1].summary(), prnfail=lambda x:x.summary(), inter=1, timeout=None, count=None, verbose=None, store=1, *args, **kargs): + n = 0 + r = 0 + ct = conf.color_theme + if verbose is None: + verbose = conf.verb + parity = 0 + ans=[] + unans=[] + if timeout is None: + timeout = min(2*inter, 5) + try: + while 1: + parity ^= 1 + col = [ct.even,ct.odd][parity] + if count is not None: + if count == 0: + break + count -= 1 + start = time.time() + print "\rsend...\r", + res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=1, *args, **kargs) + n += len(res[0])+len(res[1]) + r += len(res[0]) + if verbose > 1 and prn and len(res[0]) > 0: + msg = "RECV %i:" % len(res[0]) + print "\r"+ct.success(msg), + for p in res[0]: + print col(prn(p)) + print " "*len(msg), + if verbose > 1 and prnfail and len(res[1]) > 0: + msg = "fail %i:" % len(res[1]) + print "\r"+ct.fail(msg), + for p in res[1]: + print col(prnfail(p)) + print " "*len(msg), + if verbose > 1 and not (prn or prnfail): + print "recv:%i fail:%i" % tuple(map(len, res[:2])) + if store: + ans += res[0] + unans += res[1] + end=time.time() + if end-start < inter: + time.sleep(inter+start-end) + except KeyboardInterrupt: + pass + + if verbose and n>0: + print ct.normal("\nSent %i packets, received %i packets. %3.1f%% hits." % (n,r,100.0*r/n)) + return SndRcvList(ans),PacketList(unans) + +def srloop(pkts, *args, **kargs): + """Send a packet at layer 3 in loop and print the answer each time +srloop(pkts, [prn], [inter], [count], ...) --> None""" + return __sr_loop(sr, pkts, *args, **kargs) + +def srploop(pkts, *args, **kargs): + """Send a packet at layer 2 in loop and print the answer each time +srloop(pkts, [prn], [inter], [count], ...) --> None""" + return __sr_loop(srp, pkts, *args, **kargs) + + +def sndrcvflood(pks, pkt, prn=lambda (s,r):r.summary(), chainCC=0, store=1, unique=0): + if not isinstance(pkt, Gen): + pkt = SetGen(pkt) + tobesent = [p for p in pkt] + received = SndRcvList() + seen = {} + + hsent={} + for i in tobesent: + h = i.hashret() + if h in hsent: + hsent[h].append(i) + else: + hsent[h] = [i] + + def send_in_loop(tobesent): + while 1: + for p in tobesent: + yield p + + packets_to_send = send_in_loop(tobesent) + + ssock = rsock = pks.fileno() + + try: + while 1: + readyr,readys,_ = select([rsock],[ssock],[]) + if ssock in readys: + pks.send(packets_to_send.next()) + + if rsock in readyr: + p = pks.recv(MTU) + if p is None: + continue + h = p.hashret() + if h in hsent: + hlst = hsent[h] + for i in hlst: + if p.answers(i): + res = prn((i,p)) + if unique: + if res in seen: + continue + seen[res] = None + if res is not None: + print res + if store: + received.append((i,p)) + except KeyboardInterrupt: + if chainCC: + raise + return received + +def srflood(x,filter=None, iface=None, nofilter=None, *args,**kargs): + """Flood and receive packets at layer 3 +prn: function applied to packets received. Ret val is printed if not None +store: if 1 (default), store answers and return them +unique: only consider packets whose print +nofilter: put 1 to avoid use of bpf filters +filter: provide a BPF filter +iface: listen answers only on the given interface""" + s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) + r=sndrcvflood(s,x,*args,**kargs) + s.close() + return r + +def srpflood(x,filter=None, iface=None, iface_hint=None, nofilter=None, *args,**kargs): + """Flood and receive packets at layer 2 +prn: function applied to packets received. Ret val is printed if not None +store: if 1 (default), store answers and return them +unique: only consider packets whose print +nofilter: put 1 to avoid use of bpf filters +filter: provide a BPF filter +iface: listen answers only on the given interface""" + if iface is None and iface_hint is not None: + iface = conf.route.route(iface_hint)[0] + s = conf.L2socket(filter=filter, iface=iface, nofilter=nofilter) + r=sndrcvflood(s,x,*args,**kargs) + s.close() + return r + + + + +def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg): + """Sniff packets +sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets + + count: number of packets to capture. 0 means infinity + store: wether to store sniffed packets or discard them + prn: function to apply to each packet. If something is returned, + it is displayed. Ex: + ex: prn = lambda x: x.summary() +lfilter: python function applied to each packet to determine + if further action may be done + ex: lfilter = lambda x: x.haslayer(Padding) +offline: pcap file to read packets from, instead of sniffing them +timeout: stop sniffing after a given time (default: None) +L2socket: use the provided L2socket + """ + c = 0 + + if offline is None: + if L2socket is None: + L2socket = conf.L2listen + s = L2socket(type=ETH_P_ALL, *arg, **karg) + else: + s = PcapReader(offline) + + lst = [] + if timeout is not None: + stoptime = time.time()+timeout + remain = None + while 1: + try: + if timeout is not None: + remain = stoptime-time.time() + if remain <= 0: + break + sel = select([s],[],[],remain) + if s in sel[0]: + p = s.recv(MTU) + if p is None: + break + if lfilter and not lfilter(p): + continue + if store: + lst.append(p) + c += 1 + if prn: + r = prn(p) + if r is not None: + print r + if count > 0 and c >= count: + break + except KeyboardInterrupt: + break + s.close() + return PacketList(lst,"Sniffed") + + diff --git a/scapy/supersocket.py b/scapy/supersocket.py new file mode 100644 index 0000000000000000000000000000000000000000..f9406018ea21586241e7ab35a2abe33a1fbf7dfd --- /dev/null +++ b/scapy/supersocket.py @@ -0,0 +1,513 @@ +import socket,time,os,struct +from select import select +from packet import Raw +from data import * +from config import conf +from arch import PCAP,DNET + +def flush_fd(fd): + if type(fd) is not int: + fd = fd.fileno() + while 1: + r,w,e = select([fd],[],[],0) + if r: + os.read(fd,MTU) + else: + break + +class SuperSocket: + closed=0 + def __init__(self, family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0): + self.ins = socket.socket(family, type, proto) + self.outs = self.ins + self.promisc=None + def send(self, x): + sx = str(x) + x.sent_time = time.time() + return self.outs.send(sx) + def recv(self, x): + return Raw(self.ins.recv(x)) + def fileno(self): + return self.ins.fileno() + def close(self): + if self.closed: + return + self.closed=1 + if self.ins != self.outs: + if self.outs and self.outs.fileno() != -1: + self.outs.close() + if self.ins and self.ins.fileno() != -1: + self.ins.close() + def bind_in(self, addr): + self.ins.bind(addr) + def bind_out(self, addr): + self.outs.bind(addr) + + +class L3RawSocket(SuperSocket): + def __init__(self, type = ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0): + self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) + self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + def recv(self, x): + return Ether(self.ins.recv(x)).payload + def send(self, x): + try: + sx = str(x) + x.sent_time = time.time() + self.outs.sendto(sx,(x.dst,0)) + except socket.error,msg: + log_runtime.error(msg) + + + +class L3PacketSocket(SuperSocket): + def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): + self.type = type + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) + flush_fd(self.ins) + if iface: + self.ins.bind((iface, type)) + if not nofilter: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if filter is not None: + attach_filter(self.ins, filter) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) + self.outs = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) + if promisc is None: + promisc = conf.promisc + self.promisc = promisc + if self.promisc: + if iface is None: + self.iff = get_if_list() + else: + if iface.__class__ is list: + self.iff = iface + else: + self.iff = [iface] + for i in self.iff: + set_promisc(self.ins, i) + def close(self): + if self.closed: + return + self.closed=1 + if self.promisc: + for i in self.iff: + set_promisc(self.ins, i, 0) + SuperSocket.close(self) + def recv(self, x): + pkt, sa_ll = self.ins.recvfrom(x) + if sa_ll[2] == socket.PACKET_OUTGOING: + return None + if sa_ll[3] in conf.l2types: + cls = conf.l2types[sa_ll[3]] + lvl = 2 + elif sa_ll[1] in conf.l3types: + cls = conf.l3types[sa_ll[1]] + lvl = 3 + else: + warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3])) + cls = Ether + lvl = 2 + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = Raw(pkt) + if lvl == 2: + pkt = pkt.payload + + if pkt is not None: + pkt.time = get_last_packet_timestamp(self.ins) + return pkt + + def send(self, x): + if isinstance(x, IPv6): + iff,a,gw = conf.route6.route(x.dst) + elif hasattr(x,"dst"): + iff,a,gw = conf.route.route(x.dst) + else: + iff = conf.iface + sdto = (iff, self.type) + self.outs.bind(sdto) + sn = self.outs.getsockname() + ll = lambda x:x + if sn[3] in (ARPHDR_PPP,ARPHDR_TUN): + sdto = (iff, ETH_P_IP) + if sn[3] in conf.l2types: + ll = lambda x:conf.l2types[sn[3]]()/x + try: + sx = str(ll(x)) + x.sent_time = time.time() + self.outs.sendto(sx, sdto) + except socket.error,msg: + x.sent_time = time.time() # bad approximation + if conf.auto_fragment and msg[0] == 90: + for p in fragment(x): + self.outs.sendto(str(ll(p)), sdto) + else: + raise + + + + +class L2Socket(SuperSocket): + def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): + if iface is None: + iface = conf.iface + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) + flush_fd(self.ins) + if not nofilter: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if filter is not None: + attach_filter(self.ins, filter) + self.ins.bind((iface, type)) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) + self.outs = self.ins + self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) + sa_ll = self.outs.getsockname() + if sa_ll[3] in conf.l2types: + self.LL = conf.l2types[sa_ll[3]] + elif sa_ll[1] in conf.l3types: + self.LL = conf.l3types[sa_ll[1]] + else: + warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3])) + self.LL = Ether + + def recv(self, x): + pkt, sa_ll = self.ins.recvfrom(x) + if sa_ll[2] == socket.PACKET_OUTGOING: + return None + try: + q = self.LL(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + q = Raw(pkt) + q.time = get_last_packet_timestamp(self.ins) + return q + + +class L2ListenSocket(SuperSocket): + def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None, nofilter=0): + self.type = type + self.outs = None + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) + flush_fd(self.ins) + if iface is not None: + self.ins.bind((iface, type)) + if not nofilter: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if filter is not None: + attach_filter(self.ins, filter) + if promisc is None: + promisc = conf.sniff_promisc + self.promisc = promisc + if iface is None: + self.iff = get_if_list() + else: + if iface.__class__ is list: + self.iff = iface + else: + self.iff = [iface] + if self.promisc: + for i in self.iff: + set_promisc(self.ins, i) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) + def close(self): + if self.promisc: + for i in self.iff: + set_promisc(self.ins, i, 0) + SuperSocket.close(self) + + def recv(self, x): + pkt, sa_ll = self.ins.recvfrom(x) + if sa_ll[3] in conf.l2types : + cls = conf.l2types[sa_ll[3]] + elif sa_ll[1] in conf.l3types: + cls = conf.l3types[sa_ll[1]] + else: + warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3])) + cls = Ether + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = Raw(pkt) + pkt.time = get_last_packet_timestamp(self.ins) + return pkt + + def send(self, x): + raise Scapy_Exception("Can't send anything with L2ListenSocket") + + + +class L3dnetSocket(SuperSocket): + def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): + self.iflist = {} + self.ins = pcap.pcapObject() + if iface is None: + iface = conf.iface + self.iface = iface + self.ins.open_live(iface, 1600, 0, 100) + try: + ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) + except: + pass + if nofilter: + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + filter = "ether proto %i" % type + else: + filter = None + else: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + if filter: + filter = "(ether proto %i) and (%s)" % (type,filter) + else: + filter = "ether proto %i" % type + if filter: + self.ins.setfilter(filter, 0, 0) + def send(self, x): + if isinstance(x, IPv6): + iff,a,gw = conf.route6.route(x.dst) + elif hasattr(x,"dst"): + iff,a,gw = conf.route.route(x.dst) + else: + iff = conf.iface + ifs = self.iflist.get(iff) + if ifs is None: + self.iflist[iff] = ifs = dnet.eth(iff) + sx = str(Ether()/x) + x.sent_time = time.time() + ifs.send(sx) + def recv(self,x=MTU): + ll = self.ins.datalink() + if ll in conf.l2types: + cls = conf.l2types[ll] + else: + warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll)) + cls = Ether + + pkt = self.ins.next() + if pkt is not None: + l,pkt,ts = pkt + if pkt is None: + return + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = Raw(pkt) + pkt.time = ts + return pkt.payload + + def nonblock_recv(self): + self.ins.setnonblock(1) + p = self.recv() + self.ins.setnonblock(0) + return p + + def close(self): + if hasattr(self, "ins"): + del(self.ins) + if hasattr(self, "outs"): + del(self.outs) + +class L2dnetSocket(SuperSocket): + def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): + if iface is None: + iface = conf.iface + self.iface = iface + self.ins = pcap.pcapObject() + self.ins.open_live(iface, 1600, 0, 100) + try: + ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) + except: + pass + if nofilter: + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + filter = "ether proto %i" % type + else: + filter = None + else: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + if filter: + filter = "(ether proto %i) and (%s)" % (type,filter) + else: + filter = "ether proto %i" % type + if filter: + self.ins.setfilter(filter, 0, 0) + self.outs = dnet.eth(iface) + def recv(self,x): + ll = self.ins.datalink() + if ll in conf.l2types: + cls = conf.l2types[ll] + else: + warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll)) + cls = Ether + + pkt = self.ins.next() + if pkt is not None: + l,pkt,ts = pkt + if pkt is None: + return + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = Raw(pkt) + pkt.time = ts + return pkt + + def nonblock_recv(self): + self.ins.setnonblock(1) + p = self.recv(MTU) + self.ins.setnonblock(0) + return p + + def close(self): + if hasattr(self, "ins"): + del(self.ins) + if hasattr(self, "outs"): + del(self.outs) + + + + + +class L2pcapListenSocket(SuperSocket): + def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None): + self.type = type + self.outs = None + self.ins = pcap.pcapObject() + self.iface = iface + if iface is None: + iface = conf.iface + if promisc is None: + promisc = conf.sniff_promisc + self.promisc = promisc + self.ins.open_live(iface, 1600, self.promisc, 100) + try: + ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) + except: + pass + if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if filter: + self.ins.setfilter(filter, 0, 0) + + def close(self): + del(self.ins) + + def recv(self, x): + ll = self.ins.datalink() + if ll in conf.l2types: + cls = conf.l2types[ll] + else: + warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll)) + cls = Ether + + pkt = None + while pkt is None: + pkt = self.ins.next() + if pkt is not None: + l,pkt,ts = pkt + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = Raw(pkt) + pkt.time = ts + return pkt + + def send(self, x): + raise Scapy_Exception("Can't send anything with L2pcapListenSocket") + + +class SimpleSocket(SuperSocket): + def __init__(self, sock): + self.ins = sock + self.outs = sock + + +class StreamSocket(SimpleSocket): + def __init__(self, sock, basecls=Raw): + SimpleSocket.__init__(self, sock) + self.basecls = basecls + + def recv(self, x=MTU): + pkt = self.ins.recv(x, socket.MSG_PEEK) + x = len(pkt) + pkt = self.basecls(pkt) + pad = pkt[Padding] + if pad is not None and pad.underlayer is not None: + del(pad.underlayer.payload) + while pad is not None and not isinstance(pad, NoPayload): + x -= len(pad.load) + pad = pad.payload + self.ins.recv(x) + return pkt + + +conf.L3socket = L3PacketSocket +conf.L2socket = L2Socket +conf.L2listen = L2ListenSocket + +if PCAP: + conf.L2listen=L2pcapListenSocket + if DNET: + conf.L3socket=L3dnetSocket + conf.L2socket=L2dnetSocket + diff --git a/scapy/themes.py b/scapy/themes.py new file mode 100644 index 0000000000000000000000000000000000000000..64035799cb23dab34f06046b9ef30c6d3a1fbaf3 --- /dev/null +++ b/scapy/themes.py @@ -0,0 +1,247 @@ + +################## +## Color themes ## +################## + +class Color: + normal = "\033[0m" + black = "\033[30m" + red = "\033[31m" + green = "\033[32m" + yellow = "\033[33m" + blue = "\033[34m" + purple = "\033[35m" + cyan = "\033[36m" + grey = "\033[37m" + + bold = "\033[1m" + uline = "\033[4m" + blink = "\033[5m" + invert = "\033[7m" + + +class ColorTheme: + def __repr__(self): + return "<%s>" % self.__class__.__name__ + def __getattr__(self, attr): + return lambda x:x + + +class NoTheme(ColorTheme): + pass + + +class AnsiColorTheme(ColorTheme): + def __getattr__(self, attr): + if attr.startswith("__"): + raise AttributeError(attr) + s = "style_%s" % attr + if s in self.__class__.__dict__: + before = getattr(self, s) + after = self.style_normal + else: + before = after = "" + + def do_style(val, fmt=None, before=before, after=after): + if fmt is None: + if type(val) is not str: + val = str(val) + else: + val = fmt % val + return before+val+after + return do_style + + + style_normal = "" + style_prompt = "" + style_punct = "" + style_id = "" + style_not_printable = "" + style_layer_name = "" + style_field_name = "" + style_field_value = "" + style_emph_field_name = "" + style_emph_field_value = "" + style_packetlist_name = "" + style_packetlist_proto = "" + style_packetlist_value = "" + style_fail = "" + style_success = "" + style_odd = "" + style_even = "" + style_opening = "" + style_active = "" + style_closed = "" + style_left = "" + style_right = "" + +class BlackAndWhite(AnsiColorTheme): + pass + +class DefaultTheme(AnsiColorTheme): + style_normal = Color.normal + style_prompt = Color.blue+Color.bold + style_punct = Color.normal + style_id = Color.blue+Color.bold + style_not_printable = Color.grey + style_layer_name = Color.red+Color.bold + style_field_name = Color.blue + style_field_value = Color.purple + style_emph_field_name = Color.blue+Color.uline+Color.bold + style_emph_field_value = Color.purple+Color.uline+Color.bold + style_packetlist_name = Color.red+Color.bold + style_packetlist_proto = Color.blue + style_packetlist_value = Color.purple + style_fail = Color.red+Color.bold + style_success = Color.blue+Color.bold + style_even = Color.black+Color.bold + style_odd = Color.black + style_opening = Color.yellow + style_active = Color.black + style_closed = Color.grey + style_left = Color.blue+Color.invert + style_right = Color.red+Color.invert + +class BrightTheme(AnsiColorTheme): + style_normal = Color.normal + style_punct = Color.normal + style_id = Color.yellow+Color.bold + style_layer_name = Color.red+Color.bold + style_field_name = Color.yellow+Color.bold + style_field_value = Color.purple+Color.bold + style_emph_field_name = Color.yellow+Color.bold + style_emph_field_value = Color.green+Color.bold + style_packetlist_name = Color.red+Color.bold + style_packetlist_proto = Color.yellow+Color.bold + style_packetlist_value = Color.purple+Color.bold + style_fail = Color.red+Color.bold + style_success = Color.blue+Color.bold + style_even = Color.black+Color.bold + style_odd = Color.black + style_left = Color.cyan+Color.invert + style_right = Color.purple+Color.invert + + +class RastaTheme(AnsiColorTheme): + style_normal = Color.normal+Color.green+Color.bold + style_prompt = Color.yellow+Color.bold + style_punct = Color.red + style_id = Color.green+Color.bold + style_not_printable = Color.green + style_layer_name = Color.red+Color.bold + style_field_name = Color.yellow+Color.bold + style_field_value = Color.green+Color.bold + style_emph_field_name = Color.green + style_emph_field_value = Color.green + style_packetlist_name = Color.red+Color.bold + style_packetlist_proto = Color.yellow+Color.bold + style_packetlist_value = Color.green+Color.bold + style_fail = Color.red + style_success = Color.red+Color.bold + style_even = Color.yellow + style_odd = Color.green + style_left = Color.yellow+Color.invert + style_right = Color.red+Color.invert + + +class FormatTheme(ColorTheme): + def __getattr__(self, attr): + if attr.startswith("__"): + raise AttributeError(attr) + col = self.__class__.__dict__.get("style_%s" % attr, "%s") + def do_style(val, fmt=None, col=col): + if fmt is None: + if type(val) is not str: + val = str(val) + else: + val = fmt % val + return col % val + return do_style + + +class LatexTheme(FormatTheme): + style_prompt = r"\textcolor{blue}{%s}" + style_not_printable = r"\textcolor{gray}{%s}" + style_layer_name = r"\textcolor{red}{\bf %s}" + style_field_name = r"\textcolor{blue}{%s}" + style_field_value = r"\textcolor{purple}{%s}" + style_emph_field_name = r"\textcolor{blue}{\underline{%s}}" #ul + style_emph_field_value = r"\textcolor{purple}{\underline{%s}}" #ul + style_packetlist_name = r"\textcolor{red}{\bf %s}" + style_packetlist_proto = r"\textcolor{blue}{%s}" + style_packetlist_value = r"\textcolor{purple}{%s}" + style_fail = r"\textcolor{red}{\bf %s}" + style_success = r"\textcolor{blue}{\bf %s}" + style_left = r"\textcolor{blue}{%s}" + style_right = r"\textcolor{red}{%s}" +# style_even = r"}{\bf " +# style_odd = "" + +class LatexTheme2(FormatTheme): + style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@" + style_not_printable = r"@`@textcolor@[@gray@]@@[@%s@]@" + style_layer_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" + style_field_name = r"@`@textcolor@[@blue@]@@[@%s@]@" + style_field_value = r"@`@textcolor@[@purple@]@@[@%s@]@" + style_emph_field_name = r"@`@textcolor@[@blue@]@@[@@`@underline@[@%s@]@@]@" + style_emph_field_value = r"@`@textcolor@[@purple@]@@[@@`@underline@[@%s@]@@]@" + style_packetlist_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" + style_packetlist_proto = r"@`@textcolor@[@blue@]@@[@%s@]@" + style_packetlist_value = r"@`@textcolor@[@purple@]@@[@%s@]@" + style_fail = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" + style_success = r"@`@textcolor@[@blue@]@@[@@`@bfserices@[@@]@%s@]@" + style_even = r"@`@textcolor@[@gray@]@@[@@`@bfseries@[@@]@%s@]@" +# style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@" + style_left = r"@`@textcolor@[@blue@]@@[@%s@]@" + style_right = r"@`@textcolor@[@red@]@@[@%s@]@" + +class HTMLTheme(FormatTheme): + style_prompt = "<span class=prompt>%s</span>" + style_not_printable = "<span class=not_printable>%s</span>" + style_layer_name = "<span class=layer_name>%s</span>" + style_field_name = "<span class=field_name>%s</span>" + style_field_value = "<span class=field_value>%s</span>" + style_emph_field_name = "<span class=emph_field_name>%s</span>" + style_emph_field_value = "<span class=emph_field_value>%s</span>" + style_packetlist_name = "<span class=packetlist_name>%s</span>" + style_packetlist_proto = "<span class=packetlist_proto>%s</span>" + style_packetlist_value = "<span class=packetlist_value>%s</span>" + style_fail = "<span class=fail>%s</span>" + style_success = "<span class=success>%s</span>" + style_even = "<span class=even>%s</span>" + style_odd = "<span class=odd>%s</span>" + style_left = "<span class=left>%s</span>" + style_right = "<span class=right>%s</span>" + +class HTMLTheme2(HTMLTheme): + style_prompt = "#[#span class=prompt#]#%s#[#/span#]#" + style_not_printable = "#[#span class=not_printable#]#%s#[#/span#]#" + style_layer_name = "#[#span class=layer_name#]#%s#[#/span#]#" + style_field_name = "#[#span class=field_name#]#%s#[#/span#]#" + style_field_value = "#[#span class=field_value#]#%s#[#/span#]#" + style_emph_field_name = "#[#span class=emph_field_name#]#%s#[#/span#]#" + style_emph_field_value = "#[#span class=emph_field_value#]#%s#[#/span#]#" + style_packetlist_name = "#[#span class=packetlist_name#]#%s#[#/span#]#" + style_packetlist_proto = "#[#span class=packetlist_proto#]#%s#[#/span#]#" + style_packetlist_value = "#[#span class=packetlist_value#]#%s#[#/span#]#" + style_fail = "#[#span class=fail#]#%s#[#/span#]#" + style_success = "#[#span class=success#]#%s#[#/span#]#" + style_even = "#[#span class=even#]#%s#[#/span#]#" + style_odd = "#[#span class=odd#]#%s#[#/span#]#" + style_left = "#[#span class=left#]#%s#[#/span#]#" + style_right = "#[#span class=right#]#%s#[#/span#]#" + + +class ColorPrompt: + __prompt = ">>> " + def __str__(self): + try: + ct = conf.color_theme + if isinstance(ct, AnsiColorTheme): + ## ^A and ^B delimit invisible caracters for readline to count right + return "\001%s\002" % ct.prompt("\002"+conf.prompt+"\001") + else: + return ct.prompt(conf.prompt) + except: + return self.__prompt + diff --git a/scapy/utils.py b/scapy/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a5f6704108237a55513bd8dbdc4360f0f513555f --- /dev/null +++ b/scapy/utils.py @@ -0,0 +1,746 @@ +import os,sys,socket,types +import random +import gzip,zlib,cPickle +import re,struct,array +from arch import * + +########### +## Tools ## +########### + +def sane_color(x): + r="" + for i in x: + j = ord(i) + if (j < 32) or (j >= 127): + r=r+conf.color_theme.not_printable(".") + else: + r=r+i + return r + +def sane(x): + r="" + for i in x: + j = ord(i) + if (j < 32) or (j >= 127): + r=r+"." + else: + r=r+i + return r + +def lhex(x): + if type(x) in (int,long): + return hex(x) + elif type(x) is tuple: + return "(%s)" % ", ".join(map(lhex, x)) + elif type(x) is list: + return "[%s]" % ", ".join(map(lhex, x)) + else: + return x + +def hexdump(x): + x=str(x) + l = len(x) + i = 0 + while i < l: + print "%04x " % i, + for j in range(16): + if i+j < l: + print "%02X" % ord(x[i+j]), + else: + print " ", + if j%16 == 7: + print "", + print " ", + print sane_color(x[i:i+16]) + i += 16 + +def linehexdump(x, onlyasc=0, onlyhex=0): + x = str(x) + l = len(x) + if not onlyasc: + for i in range(l): + print "%02X" % ord(x[i]), + print "", + if not onlyhex: + print sane_color(x) + +def chexdump(x): + x=str(x) + print ", ".join(map(lambda x: "%#04x"%ord(x), x)) + +def hexstr(x, onlyasc=0, onlyhex=0): + s = [] + if not onlyasc: + s.append(" ".join(map(lambda x:"%02x"%ord(x), x))) + if not onlyhex: + s.append(sane(x)) + return " ".join(s) + + +def hexdiff(x,y): + x=str(x)[::-1] + y=str(y)[::-1] + SUBST=1 + INSERT=1 + d={} + d[-1,-1] = 0,(-1,-1) + for j in range(len(y)): + d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1) + for i in range(len(x)): + d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1) + + for j in range(len(y)): + for i in range(len(x)): + d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ), + ( d[i-1,j][0]+INSERT, (i-1,j) ), + ( d[i,j-1][0]+INSERT, (i,j-1) ) ) + + + backtrackx = [] + backtracky = [] + i=len(x)-1 + j=len(y)-1 + while not (i == j == -1): + i2,j2 = d[i,j][1] + backtrackx.append(x[i2+1:i+1]) + backtracky.append(y[j2+1:j+1]) + i,j = i2,j2 + + + + x = y = i = 0 + colorize = { 0: lambda x:x, + -1: conf.color_theme.left, + 1: conf.color_theme.right } + + dox=1 + doy=0 + l = len(backtrackx) + while i < l: + separate=0 + linex = backtrackx[i:i+16] + liney = backtracky[i:i+16] + xx = sum(len(k) for k in linex) + yy = sum(len(k) for k in liney) + if dox and not xx: + dox = 0 + doy = 1 + if dox and linex == liney: + doy=1 + + if dox: + xd = y + j = 0 + while not linex[j]: + j += 1 + xd -= 1 + print colorize[doy-dox]("%04x" % xd), + x += xx + line=linex + else: + print " ", + if doy: + yd = y + j = 0 + while not liney[j]: + j += 1 + yd -= 1 + print colorize[doy-dox]("%04x" % yd), + y += yy + line=liney + else: + print " ", + + print " ", + + cl = "" + for j in range(16): + if i+j < l: + if line[j]: + col = colorize[(linex[j]!=liney[j])*(doy-dox)] + print col("%02X" % ord(line[j])), + if linex[j]==liney[j]: + cl += sane_color(line[j]) + else: + cl += col(sane(line[j])) + else: + print " ", + cl += " " + else: + print " ", + if j == 7: + print "", + + + print " ",cl + + if doy or not yy: + doy=0 + dox=1 + i += 16 + else: + if yy: + dox=0 + doy=1 + else: + i += 16 + + +crc32 = zlib.crc32 + +if BIG_ENDIAN: + def checksum(pkt): + if len(pkt) % 2 == 1: + pkt += "\0" + s = sum(array.array("H", pkt)) + s = (s >> 16) + (s & 0xffff) + s += s >> 16 + s = ~s + return s & 0xffff +else: + def checksum(pkt): + if len(pkt) % 2 == 1: + pkt += "\0" + s = sum(array.array("H", pkt)) + s = (s >> 16) + (s & 0xffff) + s += s >> 16 + s = ~s + return (((s>>8)&0xff)|s<<8) & 0xffff + +def warning(x): + log_runtime.warning(x) + +def mac2str(mac): + return "".join(map(lambda x: chr(int(x,16)), mac.split(":"))) + +def str2mac(s): + return ("%02x:"*6)[:-1] % tuple(map(ord, s)) + +def strxor(x,y): + return "".join(map(lambda x,y:chr(ord(x)^ord(y)),x,y)) + +def atol(x): + try: + ip = inet_aton(x) + except socket.error: + ip = inet_aton(socket.gethostbyname(x)) + return struct.unpack("!I", ip)[0] +def ltoa(x): + return inet_ntoa(struct.pack("!I", x)) + +def itom(x): + return (0xffffffff00000000L>>x)&0xffffffffL + +def do_graph(graph,prog=None,format="svg",target=None, type=None,string=None,options=None): + """do_graph(graph, prog=conf.prog.dot, format="svg", + target="| conf.prog.display", options=None, [string=1]): + string: if not None, simply return the graph string + graph: GraphViz graph description + format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option + target: filename or redirect. Defaults pipe to Imagemagick's display program + prog: which graphviz program to use + options: options to be passed to prog""" + + + if string: + return graph + if type is not None: + format=type + if prog is None: + prog = conf.prog.dot + if target is None: + target = "| %s" % conf.prog.display + if format is not None: + format = "-T %s" % format + w,r = os.popen2("%s %s %s %s" % (prog,options or "", format or "", target)) + w.write(graph) + w.close() + +_TEX_TR = { + "{":"{\\tt\\char123}", + "}":"{\\tt\\char125}", + "\\":"{\\tt\\char92}", + "^":"\\^{}", + "$":"\\$", + "#":"\\#", + "~":"\\~", + "_":"\\_", + "&":"\\&", + "%":"\\%", + "|":"{\\tt\\char124}", + "~":"{\\tt\\char126}", + "<":"{\\tt\\char60}", + ">":"{\\tt\\char62}", + } + +def tex_escape(x): + s = "" + for c in x: + s += _TEX_TR.get(c,c) + return s + +def colgen(*lstcol,**kargs): + """Returns a generator that mixes provided quantities forever + trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default""" + if len(lstcol) < 2: + lstcol *= 2 + trans = kargs.get("trans", lambda x,y,z: (x,y,z)) + while 1: + for i in range(len(lstcol)): + for j in range(len(lstcol)): + for k in range(len(lstcol)): + if i != j or j != k or k != i: + yield trans(lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)]) + +def incremental_label(label="tag%05i", start=0): + while True: + yield label % start + start += 1 + +######################### +#### Enum management #### +######################### + +class EnumElement: + _value=None + def __init__(self, key, value): + self._key = key + self._value = value + def __repr__(self): + return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value) + def __getattr__(self, attr): + return getattr(self._value, attr) + def __str__(self): + return self._key + def __eq__(self, other): + return self._value == int(other) + + +class Enum_metaclass(type): + element_class = EnumElement + def __new__(cls, name, bases, dct): + rdict={} + for k,v in dct.iteritems(): + if type(v) is int: + v = cls.element_class(k,v) + dct[k] = v + rdict[v] = k + dct["__rdict__"] = rdict + return super(Enum_metaclass, cls).__new__(cls, name, bases, dct) + def __getitem__(self, attr): + return self.__rdict__[attr] + def __contains__(self, val): + return val in self.__rdict__ + def get(self, attr, val=None): + return self._rdict__.get(attr, val) + def __repr__(self): + return "<%s>" % self.__dict__.get("name", self.__name__) + + + + +############################## +## Session saving/restoring ## +############################## + + +def save_session(fname, session=None, pickleProto=-1): + if session is None: + session = scapy_session + + to_be_saved = session.copy() + + if to_be_saved.has_key("__builtins__"): + del(to_be_saved["__builtins__"]) + + for k in to_be_saved.keys(): + if type(to_be_saved[k]) in [types.TypeType, types.ClassType, types.ModuleType]: + log_interactive.error("[%s] (%s) can't be saved." % (k, type(to_be_saved[k]))) + del(to_be_saved[k]) + + try: + os.rename(fname, fname+".bak") + except OSError: + pass + f=gzip.open(fname,"wb") + cPickle.dump(to_be_saved, f, pickleProto) + f.close() + +def load_session(fname): + try: + s = cPickle.load(gzip.open(fname,"rb")) + except IOError: + s = cPickle.load(open(fname,"rb")) + scapy_session.clear() + scapy_session.update(s) + +def update_session(fname): + try: + s = cPickle.load(gzip.open(fname,"rb")) + except IOError: + s = cPickle.load(open(fname,"rb")) + scapy_session.update(s) + + +def export_object(obj): + print gzip.zlib.compress(cPickle.dumps(obj,2),9).encode("base64") + +def import_object(obj=None): + if obj is None: + obj = sys.stdin.read() + return cPickle.loads(gzip.zlib.decompress(obj.strip().decode("base64"))) + + +def save_object(fname, obj): + cPickle.dump(obj,gzip.open(fname,"wb")) + +def load_object(fname): + return cPickle.load(gzip.open(fname,"rb")) + +def corrupt_bytes(s, p=0.01, n=None): + s = array.array("B",str(s)) + l = len(s) + if n is None: + n = max(1,int(l*p)) + for i in random.sample(xrange(l), n): + s[i] = random.randint(0,255) + return s.tostring() + +def corrupt_bits(s, p=0.01, n=None): + s = array.array("B",str(s)) + l = len(s)*8 + if n is None: + n = max(1,int(l*p)) + for i in random.sample(xrange(l), n): + s[i/8] ^= 1 << (i%8) + return s.tostring() + + + + +############################# +## pcap capture file stuff ## +############################# + +def wrpcap(filename, pkt, *args, **kargs): + """Write a list of packets to a pcap file +gz: set to 1 to save a gzipped capture +linktype: force linktype value +endianness: "<" or ">", force endianness""" + PcapWriter(filename, *args, **kargs).write(pkt) + +def rdpcap(filename, count=-1): + """Read a pcap file and return a packet list +count: read only <count> packets""" + return PcapReader(filename).read_all(count=count) + +class PcapReader: + """A stateful pcap reader + + Based entirely on scapy.rdpcap(), this class allows for packets + to be dispatched without having to be loaded into memory all at + once + """ + + def __init__(self, filename): + self.filename = filename + try: + self.f = gzip.open(filename,"rb") + magic = self.f.read(4) + except IOError: + self.f = open(filename,"rb") + magic = self.f.read(4) + if magic == "\xa1\xb2\xc3\xd4": #big endian + self.endian = ">" + elif magic == "\xd4\xc3\xb2\xa1": #little endian + self.endian = "<" + else: + raise RuntimeWarning, "Not a pcap capture file (bad magic)" + hdr = self.f.read(20) + if len(hdr)<20: + raise RuntimeWarning, "Invalid pcap file (too short)" + vermaj,vermin,tz,sig,snaplen,linktype = struct.unpack(self.endian+"HHIIII",hdr) + self.LLcls = conf.l2types.get(linktype, Raw) + if self.LLcls == Raw: + warning("PcapReader: unkonwon LL type [%i]/[%#x]. Using Raw packets" % (linktype,linktype)) + + def __iter__(self): + return self + + def next(self): + """impliment the iterator protocol on a set of packets in a + pcap file + """ + pkt = self.read_packet() + if pkt == None: + raise StopIteration + return pkt + + + def read_packet(self): + """return a single packet read from the file + + returns None when no more packets are available + """ + hdr = self.f.read(16) + if len(hdr) < 16: + return None + sec,usec,caplen,olen = struct.unpack(self.endian+"IIII", hdr) + s = self.f.read(caplen) + try: + p = self.LLcls(s) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + p = Raw(s) + p.time = sec+0.000001*usec + return p + + def dispatch(self, callback): + """call the specified callback routine for each packet read + + This is just a convienience function for the main loop + that allows for easy launching of packet processing in a + thread. + """ + p = self.read_packet() + while p != None: + callback(p) + p = self.read_packet() + + def read_all(self,count=-1): + """return a list of all packets in the pcap file + """ + res=[] + while count != 0: + count -= 1 + p = self.read_packet() + if p is None: + break + res.append(p) + return PacketList(res,name = os.path.basename(self.filename)) + + def recv(self, size): + """ Emulate a socket + """ + return self.read_packet() + + def fileno(self): + return self.f.fileno() + + + +class PcapWriter: + """A stream PCAP writer with more control than wrpcap()""" + def __init__(self, filename, linktype=None, gz=False, endianness="", append=False, sync=False): + """ + linktype: force linktype to a given value. If None, linktype is taken + from the first writter packet + gz: compress the capture on the fly + endianness: force an endianness (little:"<", big:">"). Default is native + append: append packets to the capture file instead of truncating it + sync: do not bufferize writes to the capture file + """ + + self.linktype = linktype + self.header_present = 0 + self.append=append + self.gz = gz + self.endian = endianness + self.filename=filename + self.sync=sync + bufsz=4096 + if sync: + bufsz=0 + + self.f = [open,gzip.open][gz](filename,append and "ab" or "wb", gz and 9 or bufsz) + + + + def fileno(self): + return self.f.fileno() + + def _write_header(self, pkt): + self.header_present=1 + + if self.linktype == None: + if type(pkt) is list or type(pkt) is tuple: + pkt = pkt[0] + self.linktype = conf.l2types.get(pkt.__class__,1) + + if self.append: + # Even if prone to race conditions, this seems to be + # safest way to tell whether the header is already present + # because we have to handle compressed streams that + # are not as flexible as basic files + g = [open,gzip.open][self.gz](self.filename,"rb") + if g.read(16): + return + + self.f.write(struct.pack(self.endian+"IHHIIII", 0xa1b2c3d4L, + 2, 4, 0, 0, MTU, self.linktype)) + self.f.flush() + + + def write(self, pkt): + """accepts a either a single packet or a list of packets + to be written to the dumpfile + """ + if not self.header_present: + self._write_header(pkt) + for p in pkt: + self._write_packet(p) + + def _write_packet(self, packet): + """writes a single packet to the pcap file + """ + s = str(packet) + l = len(s) + sec = int(packet.time) + usec = int((packet.time-sec)*1000000) + self.f.write(struct.pack(self.endian+"IIII", sec, usec, l, l)) + self.f.write(s) + if self.gz and self.sync: + self.f.flush() + + def flush(self): + return self.f.flush() + def close(self): + return self.f.close() + + +re_extract_hexcap = re.compile("^(0x[0-9a-fA-F]{2,}[ :\t]|(0x)?[0-9a-fA-F]{2,}:|(0x)?[0-9a-fA-F]{3,}[: \t]|) *(([0-9a-fA-F]{2} {,2}){,16})") + +def import_hexcap(): + p = "" + try: + while 1: + l = raw_input().strip() + try: + p += re_extract_hexcap.match(l).groups()[3] + except: + warning("Parsing error during hexcap") + continue + except EOFError: + pass + + p = p.replace(" ","") + p2="" + for i in range(len(p)/2): + p2 += chr(int(p[2*i:2*i+2],16)) + return p2 + + + +def wireshark(pktlist): + f = os.tempnam("scapy") + wrpcap(f, pktlist) + os.spawnlp(os.P_NOWAIT, conf.prog.wireshark, conf.prog.wireshark, "-r", f) + +def hexedit(x): + x = str(x) + f = os.tempnam("scapy") + open(f,"w").write(x) + os.spawnlp(os.P_WAIT, conf.prog.hexedit, conf.prog.hexedit, f) + x = open(f).read() + os.unlink(f) + return x + +def __make_table(yfmtfunc, fmtfunc, endline, list, fxyz, sortx=None, sorty=None, seplinefunc=None): + vx = {} + vy = {} + vz = {} + vxf = {} + vyf = {} + l = 0 + for e in list: + xx,yy,zz = map(str, fxyz(e)) + l = max(len(yy),l) + vx[xx] = max(vx.get(xx,0), len(xx), len(zz)) + vy[yy] = None + vz[(xx,yy)] = zz + + vxk = vx.keys() + vyk = vy.keys() + if sortx: + vxk.sort(sortx) + else: + try: + vxk.sort(lambda x,y:int(x)-int(y)) + except: + try: + vxk.sort(lambda x,y: cmp(atol(x),atol(y))) + except: + vxk.sort() + if sorty: + vyk.sort(sorty) + else: + try: + vyk.sort(lambda x,y:int(x)-int(y)) + except: + try: + vyk.sort(lambda x,y: cmp(atol(x),atol(y))) + except: + vyk.sort() + + + if seplinefunc: + sepline = seplinefunc(l, map(lambda x:vx[x],vxk)) + print sepline + + fmt = yfmtfunc(l) + print fmt % "", + for x in vxk: + vxf[x] = fmtfunc(vx[x]) + print vxf[x] % x, + print endline + if seplinefunc: + print sepline + for y in vyk: + print fmt % y, + for x in vxk: + print vxf[x] % vz.get((x,y), "-"), + print endline + if seplinefunc: + print sepline + +def make_table(*args, **kargs): + __make_table(lambda l:"%%-%is" % l, lambda l:"%%-%is" % l, "", *args, **kargs) + +def make_lined_table(*args, **kargs): + __make_table(lambda l:"%%-%is |" % l, lambda l:"%%-%is |" % l, "", + seplinefunc=lambda a,x:"+".join(map(lambda y:"-"*(y+2), [a-1]+x+[-2])), + *args, **kargs) + +def make_tex_table(*args, **kargs): + __make_table(lambda l: "%s", lambda l: "& %s", "\\\\", seplinefunc=lambda a,x:"\\hline", *args, **kargs) + + +###################### +## Online doc stuff ## +###################### + + +def lsc(cmd=None): + """List user commands""" + if cmd is None: + for c in user_commands: + doc = "No doc. available" + if c.__doc__: + doc = c.__doc__.split("\n")[0] + + print "%-16s : %s" % (c.__name__, doc) + else: + print cmd.__doc__ + + +#XXX user_commands = [ sr, sr1, srp, srp1, srloop, srploop, sniff, p0f, arpcachepoison, send, sendp, traceroute, arping, ls, lsc, queso, nmap_fp, report_ports, dyndns_add, dyndns_del, is_promisc, promiscping ] + + + +def tethereal(*args,**kargs): + sniff(prn=lambda x: x.display(),*args,**kargs) + diff --git a/scapy/voip.py b/scapy/voip.py new file mode 100644 index 0000000000000000000000000000000000000000..df2e4f8c5bcae5e0bd67c1a388817ee9f74a0980 --- /dev/null +++ b/scapy/voip.py @@ -0,0 +1,134 @@ +import os +################### +## Testing stuff ## +################### + + + +def merge(x,y): + if len(x) > len(y): + y += "\x00"*(len(x)-len(y)) + elif len(x) < len(y): + x += "\x00"*(len(y)-len(x)) + m = "" + for i in range(len(x)/ss): + m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)] + return m +# return "".join(map(str.__add__, x, y)) + + +def voip_play(s1,list=None,**kargs): + FIFO="/tmp/conv1.%i.%%i" % os.getpid() + FIFO1=FIFO % 1 + FIFO2=FIFO % 2 + + os.mkfifo(FIFO1) + os.mkfifo(FIFO2) + try: + os.system("soxmix -t .ul %s -t .ul %s -t ossdsp /dev/dsp &" % (FIFO1,FIFO2)) + + c1=open(FIFO1,"w", 4096) + c2=open(FIFO2,"w", 4096) + fcntl.fcntl(c1.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) + fcntl.fcntl(c2.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) + + # dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") + def play(pkt,last=[]): + if not pkt: + return + if not pkt.haslayer(UDP): + return + ip=pkt.getlayer(IP) + if s1 in [ip.src, ip.dst]: + if not last: + last.append(pkt) + return + load=last.pop() + # x1 = load.load[12:] + c1.write(load.load[12:]) + if load.getlayer(IP).src == ip.src: + # x2 = "" + c2.write("\x00"*len(load.load[12:])) + last.append(pkt) + else: + # x2 = pkt.load[:12] + c2.write(pkt.load[12:]) + # dsp.write(merge(x1,x2)) + + if list is None: + sniff(store=0, prn=play, **kargs) + else: + for p in list: + play(p) + finally: + os.unlink(FIFO1) + os.unlink(FIFO2) + + + +def voip_play1(s1,list=None,**kargs): + + + dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") + def play(pkt): + if not pkt: + return + if not pkt.haslayer(UDP): + return + ip=pkt.getlayer(IP) + if s1 in [ip.src, ip.dst]: + dsp.write(pkt.getlayer(Raw).load[12:]) + try: + if list is None: + sniff(store=0, prn=play, **kargs) + else: + for p in list: + play(p) + finally: + dsp.close() + rd.close() + +def voip_play2(s1,**kargs): + dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") + def play(pkt,last=[]): + if not pkt: + return + if not pkt.haslayer(UDP): + return + ip=pkt.getlayer(IP) + if s1 in [ip.src, ip.dst]: + if not last: + last.append(pkt) + return + load=last.pop() + x1 = load.load[12:] +# c1.write(load.load[12:]) + if load.getlayer(IP).src == ip.src: + x2 = "" +# c2.write("\x00"*len(load.load[12:])) + last.append(pkt) + else: + x2 = pkt.load[:12] +# c2.write(pkt.load[12:]) + dsp.write(merge(x1,x2)) + + sniff(store=0, prn=play, **kargs) + +def voip_play3(lst=None,**kargs): + dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") + try: + def play(pkt, dsp=dsp): + if pkt and pkt.haslayer(UDP) and pkt.haslayer(Raw): + dsp.write(pkt.getlayer(RTP).load) + if lst is None: + sniff(store=0, prn=play, **kargs) + else: + for p in lst: + play(p) + finally: + try: + dsp.close() + rd.close() + except: + pass + diff --git a/scapy/volatile.py b/scapy/volatile.py new file mode 100644 index 0000000000000000000000000000000000000000..8a1b831f9b79ccf0a028660baedcf50e637491c8 --- /dev/null +++ b/scapy/volatile.py @@ -0,0 +1,280 @@ +import random,time + +#################### +## Random numbers ## +#################### + +def randseq(inf, sup, seed=None, forever=1, renewkeys=0): + """iterate through a sequence in random order. + When all the values have been drawn, if forever=1, the drawing is done again. + If renewkeys=0, the draw will be in the same order, guaranteeing that the same + number will be drawn in not less than the number of integers of the sequence""" + rnd = random.Random(seed) + sbox_size = 256 + + top = sup-inf+1 + + n=0 + while (1<<n) < top: + n += 1 + + fs = min(3,(n+1)/2) + fsmask = 2**fs-1 + rounds = max(n,3) + turns = 0 + + while 1: + if turns == 0 or renewkeys: + sbox = [rnd.randint(0,fsmask) for k in xrange(sbox_size)] + turns += 1 + i = 0 + while i < 2**n: + ct = i + i += 1 + for k in range(rounds): # Unbalanced Feistel Network + lsb = ct & fsmask + ct >>= fs + lsb ^= sbox[ct%sbox_size] + ct |= lsb << (n-fs) + + if ct < top: + yield inf+ct + if not forever: + break + + +class VolatileValue: + def __repr__(self): + return "<%s>" % self.__class__.__name__ + def __getattr__(self, attr): + if attr == "__setstate__": + raise AttributeError(attr) + return getattr(self._fix(),attr) + def _fix(self): + return None + + +class RandField(VolatileValue): + pass + + +class RandNum(RandField): + min = 0 + max = 0 + def __init__(self, min, max): + self.seq = randseq(min,max) + def _fix(self): + return self.seq.next() + +class RandNumGamma(RandField): + def __init__(self, alpha, beta): + self.alpha = alpha + self.beta = beta + def _fix(self): + return int(round(random.gammavariate(self.alpha, self.beta))) + +class RandNumGauss(RandField): + def __init__(self, mu, sigma): + self.mu = mu + self.sigma = sigma + def _fix(self): + return int(round(random.gauss(self.mu, self.sigma))) + +class RandNumExpo(RandField): + def __init__(self, lambd): + self.lambd = lambd + def _fix(self): + return int(round(random.expovariate(self.lambd))) + +class RandByte(RandNum): + def __init__(self): + RandNum.__init__(self, 0, 2L**8-1) + +class RandShort(RandNum): + def __init__(self): + RandNum.__init__(self, 0, 2L**16-1) + +class RandInt(RandNum): + def __init__(self): + RandNum.__init__(self, 0, 2L**32-1) + +class RandSInt(RandNum): + def __init__(self): + RandNum.__init__(self, -2L**31, 2L**31-1) + +class RandLong(RandNum): + def __init__(self): + RandNum.__init__(self, 0, 2L**64-1) + +class RandSLong(RandNum): + def __init__(self): + RandNum.__init__(self, -2L**63, 2L**63-1) + +class RandChoice(RandField): + def __init__(self, *args): + if not args: + raise TypeError("RandChoice needs at least one choice") + self._choice = args + def _fix(self): + return random.choice(self._choice) + +class RandString(RandField): + def __init__(self, size, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"): + self.chars = chars + self.size = size + def _fix(self): + s = "" + for i in range(self.size): + s += random.choice(self.chars) + return s + +class RandBin(RandString): + def __init__(self, size): + RandString.__init__(self, size, "".join(map(chr,range(256)))) + + +class RandTermString(RandString): + def __init__(self, size, term): + RandString.__init__(self, size, "".join(map(chr,range(1,256)))) + self.term = term + def _fix(self): + return RandString._fix(self)+self.term + + + +class RandIP(RandString): + def __init__(self, iptemplate="0.0.0.0/0"): + self.ip = Net(iptemplate) + def _fix(self): + return self.ip.choice() + +class RandMAC(RandString): + def __init__(self, template="*"): + template += ":*:*:*:*:*" + template = template.split(":") + self.mac = () + for i in range(6): + if template[i] == "*": + v = RandByte() + elif "-" in template[i]: + x,y = template[i].split("-") + v = RandNum(int(x,16), int(y,16)) + else: + v = int(template[i],16) + self.mac += (v,) + def _fix(self): + return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac + + +class RandOID(RandString): + def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)): + self.ori_fmt = fmt + if fmt is not None: + fmt = fmt.split(".") + for i in range(len(fmt)): + if "-" in fmt[i]: + fmt[i] = tuple(map(int, fmt[i].split("-"))) + self.fmt = fmt + self.depth = depth + self.idnum = idnum + def __repr__(self): + if self.ori_fmt is None: + return "<%s>" % self.__class__.__name__ + else: + return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt) + def _fix(self): + if self.fmt is None: + return ".".join(map(str, [self.idnum for i in xrange(1+self.depth)])) + else: + oid = [] + for i in self.fmt: + if i == "*": + oid.append(str(self.idnum)) + elif i == "**": + oid += map(str, [self.idnum for i in xrange(1+self.depth)]) + elif type(i) is tuple: + oid.append(str(random.randrange(*i))) + else: + oid.append(i) + return ".".join(oid) + + + +class RandDHCPOptions(RandField): + def __init__(self, size=None, rndstr=None): + if size is None: + size = RandNumExpo(0.05) + self.size = size + if rndstr is None: + rndstr = RandBin(RandNum(0,255)) + self.rndstr=rndstr + self._opts = DHCPOptions.values() + self._opts.remove("pad") + self._opts.remove("end") + def _fix(self): + op = [] + for k in range(self.size): + o = random.choice(self._opts) + if type(o) is str: + op.append((o,self.rndstr*1)) + else: + op.append((o.name, o.randval()._fix())) + return op + + +# Automatic timestamp + +class AutoTime(VolatileValue): + def __init__(self, base=None): + if base == None: + self.diff = 0 + else: + self.diff = time.time()-base + def _fix(self): + return time.time()-self.diff + +class IntAutoTime(AutoTime): + def _fix(self): + return int(time.time()-self.diff) + + +class ZuluTime(AutoTime): + def __init__(self, diff=None): + self.diff=diff + def _fix(self): + return time.strftime("%y%m%d%H%M%SZ",time.gmtime(time.time()+self.diff)) + + +class DelayedEval(VolatileValue): + """ Exemple of usage: DelayedEval("time.time()") """ + def __init__(self, expr): + self.expr = expr + def _fix(self): + return eval(self.expr) + + +class IncrementalValue(VolatileValue): + def __init__(self, start=0, step=1, restart=-1): + self.start = self.val = start + self.step = step + self.restart = restart + def _fix(self): + v = self.val + if self.val == self.restart : + self.val = self.start + else: + self.val += self.step + return v + +class CorruptedBytes(VolatileValue): + def __init__(self, s, p=0.01, n=None): + self.s = s + self.p = p + self.n = n + def _fix(self): + return corrupt_bytes(self.s, self.p, self.n) + +class CorruptedBits(CorruptedBytes): + def _fix(self): + return corrupt_bits(self.s, self.p, self.n) +