Home | History | Annotate | Download | only in scapy
      1 ## This file is part of Scapy
      2 ## See http://www.secdev.org/projects/scapy for more informations
      3 ## Copyright (C) Philippe Biondi <phil (at] secdev.org>
      4 ## This program is published under a GPLv2 license
      5 
      6 """
      7 Implementation of the configuration object.
      8 """
      9 
     10 from __future__ import absolute_import
     11 from __future__ import print_function
     12 import os,time,socket,sys
     13 
     14 from scapy import VERSION
     15 from scapy.data import *
     16 from scapy import base_classes
     17 from scapy.themes import NoTheme, apply_ipython_style
     18 from scapy.error import log_scapy
     19 import scapy.modules.six as six
     20 
     21 ############
     22 ## Config ##
     23 ############
     24 
     25 class ConfClass(object):
     26     def configure(self, cnf):
     27         self.__dict__ = cnf.__dict__.copy()
     28     def __repr__(self):
     29         return str(self)
     30     def __str__(self):
     31         s = ""
     32         keys = self.__class__.__dict__.copy()
     33         keys.update(self.__dict__)
     34         keys = sorted(keys)
     35         for i in keys:
     36             if i[0] != "_":
     37                 r = repr(getattr(self, i))
     38                 r = " ".join(r.split())
     39                 wlen = 76-max(len(i),10)
     40                 if len(r) > wlen:
     41                     r = r[:wlen-3]+"..."
     42                 s += "%-10s = %s\n" % (i, r)
     43         return s[:-1]
     44 
     45 class Interceptor(object):
     46     def __init__(self, name, default, hook, args=None, kargs=None):
     47         self.name = name
     48         self.intname = "_intercepted_%s" % name
     49         self.default=default
     50         self.hook = hook
     51         self.args = args if args is not None else []
     52         self.kargs = kargs if kargs is not None else {}
     53     def __get__(self, obj, typ=None):
     54         if not hasattr(obj, self.intname):
     55             setattr(obj, self.intname, self.default)
     56         return getattr(obj, self.intname)
     57     def __set__(self, obj, val):
     58         setattr(obj, self.intname, val)
     59         self.hook(self.name, val, *self.args, **self.kargs)
     60 
     61     
     62 class ProgPath(ConfClass):
     63     pdfreader = "acroread"
     64     psreader = "gv"
     65     dot = "dot"
     66     display = "display"
     67     tcpdump = "tcpdump"
     68     tcpreplay = "tcpreplay"
     69     hexedit = "hexer"
     70     tshark = "tshark"
     71     wireshark = "wireshark"
     72     ifconfig = "ifconfig"
     73 
     74 
     75 class ConfigFieldList:
     76     def __init__(self):
     77         self.fields = set()
     78         self.layers = set()
     79     @staticmethod
     80     def _is_field(f):
     81         return hasattr(f, "owners")
     82     def _recalc_layer_list(self):
     83         self.layers = {owner for f in self.fields for owner in f.owners}
     84     def add(self, *flds):
     85         self.fields |= {f for f in flds if self._is_field(f)}
     86         self._recalc_layer_list()
     87     def remove(self, *flds):
     88         self.fields -= set(flds)
     89         self._recalc_layer_list()
     90     def __contains__(self, elt):
     91         if isinstance(elt, base_classes.Packet_metaclass):
     92             return elt in self.layers
     93         return elt in self.fields
     94     def __repr__(self):
     95         return "<%s [%s]>" %  (self.__class__.__name__," ".join(str(x) for x in self.fields))
     96 
     97 class Emphasize(ConfigFieldList):
     98     pass
     99 
    100 class Resolve(ConfigFieldList):
    101     pass
    102     
    103 
    104 class Num2Layer:
    105     def __init__(self):
    106         self.num2layer = {}
    107         self.layer2num = {}
    108         
    109     def register(self, num, layer):
    110         self.register_num2layer(num, layer)
    111         self.register_layer2num(num, layer)
    112         
    113     def register_num2layer(self, num, layer):
    114         self.num2layer[num] = layer
    115     def register_layer2num(self, num, layer):
    116         self.layer2num[layer] = num
    117 
    118     def __getitem__(self, item):
    119         if isinstance(item, base_classes.Packet_metaclass):
    120             return self.layer2num[item]
    121         return self.num2layer[item]
    122     def __contains__(self, item):
    123         if isinstance(item, base_classes.Packet_metaclass):
    124             return item in self.layer2num
    125         return item in self.num2layer
    126     def get(self, item, default=None):
    127         if item in self:
    128             return self[item]
    129         return default
    130     
    131     def __repr__(self):
    132         lst = []
    133         for num,layer in six.iteritems(self.num2layer):
    134             if layer in self.layer2num and self.layer2num[layer] == num:
    135                 dir = "<->"
    136             else:
    137                 dir = " ->"
    138             lst.append((num,"%#6x %s %-20s (%s)" % (num, dir, layer.__name__,
    139                                                     layer._name)))
    140         for layer,num in six.iteritems(self.layer2num):
    141             if num not in self.num2layer or self.num2layer[num] != layer:
    142                 lst.append((num,"%#6x <-  %-20s (%s)" % (num, layer.__name__,
    143                                                          layer._name)))
    144         lst.sort()
    145         return "\n".join(y for x,y in lst)
    146 
    147 
    148 class LayersList(list):
    149     def __repr__(self):
    150         s=[]
    151         for l in self:
    152             s.append("%-20s: %s" % (l.__name__,l.name))
    153         return "\n".join(s)
    154     def register(self, layer):
    155         self.append(layer)
    156 
    157 class CommandsList(list):
    158     def __repr__(self):
    159         s=[]
    160         for l in sorted(self,key=lambda x:x.__name__):
    161             if l.__doc__:
    162                 doc = l.__doc__.split("\n")[0]
    163             else:
    164                 doc = "--"
    165             s.append("%-20s: %s" % (l.__name__,doc))
    166         return "\n".join(s)
    167     def register(self, cmd):
    168         self.append(cmd)
    169         return cmd # return cmd so that method can be used as a decorator
    170 
    171 def lsc():
    172     print(repr(conf.commands))
    173 
    174 class CacheInstance(dict, object):
    175     __slots__ = ["timeout", "name", "_timetable", "__dict__"]
    176     def __init__(self, name="noname", timeout=None):
    177         self.timeout = timeout
    178         self.name = name
    179         self._timetable = {}
    180     def flush(self):
    181         self.__init__(name=self.name, timeout=self.timeout)
    182     def __getitem__(self, item):
    183         if item in self.__slots__:
    184             return object.__getattribute__(self, item)
    185         val = dict.__getitem__(self,item)
    186         if self.timeout is not None:
    187             t = self._timetable[item]
    188             if time.time()-t > self.timeout:
    189                 raise KeyError(item)
    190         return val
    191     def get(self, item, default=None):
    192         # overloading this method is needed to force the dict to go through
    193         # the timetable check
    194         try:
    195             return self[item]
    196         except KeyError:
    197             return default
    198     def __setitem__(self, item, v):
    199         if item in self.__slots__:
    200             return object.__setattr__(self, item, v)
    201         self._timetable[item] = time.time()
    202         dict.__setitem__(self, item,v)
    203     def update(self, other):
    204         for key, value in other.iteritems():
    205             # We only update an element from `other` either if it does
    206             # not exist in `self` or if the entry in `self` is older.
    207             if key not in self or self._timetable[key] < other._timetable[key]:
    208                 dict.__setitem__(self, key, value)
    209                 self._timetable[key] = other._timetable[key]
    210     def iteritems(self):
    211         if self.timeout is None:
    212             return six.iteritems(self.__dict__)
    213         t0=time.time()
    214         return ((k,v) for (k,v) in six.iteritems(self.__dict__) if t0-self._timetable[k] < self.timeout)
    215     def iterkeys(self):
    216         if self.timeout is None:
    217             return six.iterkeys(self.__dict__)
    218         t0=time.time()
    219         return (k for k in six.iterkeys(self.__dict__) if t0-self._timetable[k] < self.timeout)
    220     def __iter__(self):
    221         return six.iterkeys(self.__dict__)
    222     def itervalues(self):
    223         if self.timeout is None:
    224             return six.itervalues(self.__dict__)
    225         t0=time.time()
    226         return (v for (k,v) in six.iteritems(self.__dict__) if t0-self._timetable[k] < self.timeout)
    227     def items(self):
    228         if self.timeout is None:
    229             return dict.items(self)
    230         t0=time.time()
    231         return [(k,v) for (k,v) in six.iteritems(self.__dict__) if t0-self._timetable[k] < self.timeout]
    232     def keys(self):
    233         if self.timeout is None:
    234             return dict.keys(self)
    235         t0=time.time()
    236         return [k for k in six.iterkeys(self.__dict__) if t0-self._timetable[k] < self.timeout]
    237     def values(self):
    238         if self.timeout is None:
    239             return six.values(self)
    240         t0=time.time()
    241         return [v for (k,v) in six.iteritems(self.__dict__) if t0-self._timetable[k] < self.timeout]
    242     def __len__(self):
    243         if self.timeout is None:
    244             return dict.__len__(self)
    245         return len(self.keys())
    246     def summary(self):
    247         return "%s: %i valid items. Timeout=%rs" % (self.name, len(self), self.timeout)
    248     def __repr__(self):
    249         s = []
    250         if self:
    251             mk = max(len(k) for k in six.iterkeys(self.__dict__))
    252             fmt = "%%-%is %%s" % (mk+1)
    253             for item in six.iteritems(self.__dict__):
    254                 s.append(fmt % item)
    255         return "\n".join(s)
    256             
    257             
    258 
    259 
    260 class NetCache:
    261     def __init__(self):
    262         self._caches_list = []
    263 
    264 
    265     def add_cache(self, cache):
    266         self._caches_list.append(cache)
    267         setattr(self,cache.name,cache)
    268     def new_cache(self, name, timeout=None):
    269         c = CacheInstance(name=name, timeout=timeout)
    270         self.add_cache(c)
    271     def __delattr__(self, attr):
    272         raise AttributeError("Cannot delete attributes")
    273     def update(self, other):
    274         for co in other._caches_list:
    275             if hasattr(self, co.name):
    276                 getattr(self,co.name).update(co)
    277             else:
    278                 self.add_cache(co.copy())
    279     def flush(self):
    280         for c in self._caches_list:
    281             c.flush()
    282     def __repr__(self):
    283         return "\n".join(c.summary() for c in self._caches_list)
    284         
    285 
    286 class LogLevel(object):
    287     def __get__(self, obj, otype):
    288         return obj._logLevel
    289     def __set__(self,obj,val):
    290         log_scapy.setLevel(val)
    291         obj._logLevel = val
    292         
    293 
    294 def isCryptographyValid():
    295     """
    296     Check if the cryptography library is present, and if it is recent enough
    297     for most usages in scapy (v1.7 or later).
    298     """
    299     try:
    300         import cryptography
    301     except ImportError:
    302         return False
    303     from distutils.version import LooseVersion
    304     return LooseVersion(cryptography.__version__) >= LooseVersion("1.7")
    305 
    306 
    307 def isCryptographyAdvanced():
    308     """
    309     Check if the cryptography library is present, and if it supports X25519,
    310     ChaCha20Poly1305 and such (v2.0 or later).
    311     """
    312     try:
    313         import cryptography
    314     except ImportError:
    315         return False
    316     from distutils.version import LooseVersion
    317     lib_valid = LooseVersion(cryptography.__version__) >= LooseVersion("2.0")
    318     if not lib_valid:
    319         return False
    320 
    321     try:
    322         from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
    323         X25519PrivateKey.generate()
    324     except:
    325         return False
    326     else:
    327         return True
    328 
    329 def isPyPy():
    330     """Returns either scapy is running under PyPy or not"""
    331     try:
    332         import __pypy__
    333         return True
    334     except ImportError:
    335         return False
    336 
    337 def _prompt_changer(attr, val):
    338     """Change the current prompt theme"""
    339     try:
    340         sys.ps1 = conf.color_theme.prompt(conf.prompt)
    341     except:
    342         pass
    343     try:
    344         apply_ipython_style(get_ipython())
    345     except NameError:
    346         pass
    347 
    348 class Conf(ConfClass):
    349     """This object contains the configuration of Scapy.
    350 session  : filename where the session will be saved
    351 interactive_shell : can be "ipython", "python" or "auto". Default: Auto
    352 stealth  : if 1, prevents any unwanted packet to go out (ARP, DNS, ...)
    353 checkIPID: if 0, doesn't check that IPID matches between IP sent and ICMP IP citation received
    354            if 1, checks that they either are equal or byte swapped equals (bug in some IP stacks)
    355            if 2, strictly checks that they are equals
    356 checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks)
    357 checkIPinIP: if True, checks that IP-in-IP layers match. If False, do not
    358              check IP layers that encapsulates another IP layer
    359 check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation
    360 iff      : selects the default output interface for srp() and sendp(). default:"eth0")
    361 verb     : level of verbosity, from 0 (almost mute) to 3 (verbose)
    362 promisc  : default mode for listening socket (to get answers if you spoof on a lan)
    363 sniff_promisc : default mode for sniff()
    364 filter   : bpf filter added to every sniffing socket to exclude traffic from analysis
    365 histfile : history file
    366 padding  : includes padding in disassembled packets
    367 except_filter : BPF filter for packets to ignore
    368 debug_match : when 1, store received packet that are not matched into debug.recv
    369 route    : holds the Scapy routing table and provides methods to manipulate it
    370 warning_threshold : how much time between warnings from the same place
    371 ASN1_default_codec: Codec used by default for ASN1 objects
    372 mib      : holds MIB direct access dictionary
    373 resolve  : holds list of fields for which resolution should be done
    374 noenum   : holds list of enum fields for which conversion to string should NOT be done
    375 AS_resolver: choose the AS resolver class to use
    376 extensions_paths: path or list of paths where extensions are to be looked for
    377 contribs : a dict which can be used by contrib layers to store local configuration
    378 debug_tls:When 1, print some TLS session secrets when they are computed.
    379 """
    380     version = VERSION
    381     session = ""
    382     interactive = False
    383     interactive_shell = ""
    384     stealth = "not implemented"
    385     iface = None
    386     iface6 = None
    387     layers = LayersList()
    388     commands = CommandsList()
    389     logLevel = LogLevel()
    390     checkIPID = 0
    391     checkIPsrc = 1
    392     checkIPaddr = 1
    393     checkIPinIP = True
    394     check_TCPerror_seqack = 0
    395     verb = 2
    396     prompt = Interceptor("prompt", ">>> ", _prompt_changer)
    397     promisc = 1
    398     sniff_promisc = 1
    399     raw_layer = None
    400     raw_summary = False
    401     default_l2 = None
    402     l2types = Num2Layer()
    403     l3types = Num2Layer()
    404     L3socket = None
    405     L2socket = None
    406     L2listen = None
    407     BTsocket = None
    408     min_pkt_size = 60
    409     histfile = os.getenv('SCAPY_HISTFILE',
    410                          os.path.join(os.path.expanduser("~"),
    411                                       ".scapy_history"))
    412     padding = 1
    413     except_filter = ""
    414     debug_match = 0
    415     debug_tls = 0
    416     wepkey = ""
    417     cache_iflist = {}
    418     cache_ipaddrs = {}
    419     route = None # Filed by route.py
    420     route6 = None # Filed by route6.py
    421     auto_fragment = 1
    422     debug_dissector = 0
    423     color_theme = Interceptor("color_theme", NoTheme(), _prompt_changer)
    424     warning_threshold = 5
    425     warning_next_only_once = False
    426     prog = ProgPath()
    427     resolve = Resolve()
    428     noenum = Resolve()
    429     emph = Emphasize()
    430     use_pypy = isPyPy()
    431     use_pcap = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y")
    432     use_dnet = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y")
    433     use_bpf = False
    434     use_winpcapy = False
    435     use_npcap = False
    436     ipv6_enabled = socket.has_ipv6
    437     ethertypes = ETHER_TYPES
    438     protocols = IP_PROTOS
    439     services_tcp = TCP_SERVICES
    440     services_udp = UDP_SERVICES
    441     extensions_paths = "."
    442     manufdb = MANUFDB
    443     stats_classic_protocols = []
    444     stats_dot11_protocols = []
    445     temp_files = []
    446     netcache = NetCache()
    447     geoip_city = '/usr/share/GeoIP/GeoIPCity.dat'
    448     geoip_city_ipv6 = '/usr/share/GeoIP/GeoIPCityv6.dat'
    449     load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs",
    450                    "hsrp", "inet6", "ir", "isakmp", "l2tp", "mgcp",
    451                    "mobileip", "netbios", "netflow", "ntp", "ppp", "pptp",
    452                    "radius", "rip", "rtp", "skinny", "smb", "snmp",
    453                    "tftp", "x509", "bluetooth", "dhcp6", "llmnr",
    454                    "sctp", "vrrp", "ipsec", "lltd", "vxlan", "eap"]
    455     contribs = dict()
    456     crypto_valid = isCryptographyValid()
    457     crypto_valid_advanced = isCryptographyAdvanced()
    458     fancy_prompt = True
    459 
    460 
    461 if not Conf.ipv6_enabled:
    462     log_scapy.warning("IPv6 support disabled in Python. Cannot load Scapy IPv6 layers.")
    463     for m in ["inet6","dhcp6"]:
    464         if m in Conf.load_layers:
    465             Conf.load_layers.remove(m)
    466     
    467 if not Conf.crypto_valid:
    468     log_scapy.warning("Crypto-related methods disabled for IPsec, Dot11 "
    469                       "and TLS layers (needs python-cryptography v1.7+).")
    470 
    471 conf=Conf()
    472 conf.logLevel=30 # 30=Warning
    473 
    474 
    475 def crypto_validator(func):
    476     """
    477     This a decorator to be used for any method relying on the cryptography library.
    478     Its behaviour depends on the 'crypto_valid' attribute of the global 'conf'.
    479     """
    480     def func_in(*args, **kwargs):
    481         if not conf.crypto_valid:
    482             raise ImportError("Cannot execute crypto-related method! "
    483                               "Please install python-cryptography v1.7 or later.")
    484         return func(*args, **kwargs)
    485     return func_in
    486