1## This file is part of Scapy 2## See http://www.secdev.org/projects/scapy for more informations 3## Copyright (C) Philippe Biondi <phil@secdev.org> 4## This program is published under a GPLv2 license 5 6""" 7Implementation of the configuration object. 8""" 9 10from __future__ import absolute_import 11from __future__ import print_function 12import os,time,socket,sys 13 14from scapy import VERSION 15from scapy.data import * 16from scapy import base_classes 17from scapy.themes import NoTheme, apply_ipython_style 18from scapy.error import log_scapy 19import scapy.modules.six as six 20 21############ 22## Config ## 23############ 24 25class 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 45class 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 62class 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 75class 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 97class Emphasize(ConfigFieldList): 98 pass 99 100class Resolve(ConfigFieldList): 101 pass 102 103 104class 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 148class 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 157class 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 171def lsc(): 172 print(repr(conf.commands)) 173 174class 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 260class 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 286class 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 294def 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 307def 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 329def 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 337def _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 348class Conf(ConfClass): 349 """This object contains the configuration of Scapy. 350session : filename where the session will be saved 351interactive_shell : can be "ipython", "python" or "auto". Default: Auto 352stealth : if 1, prevents any unwanted packet to go out (ARP, DNS, ...) 353checkIPID: 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 356checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks) 357checkIPinIP: if True, checks that IP-in-IP layers match. If False, do not 358 check IP layers that encapsulates another IP layer 359check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation 360iff : selects the default output interface for srp() and sendp(). default:"eth0") 361verb : level of verbosity, from 0 (almost mute) to 3 (verbose) 362promisc : default mode for listening socket (to get answers if you spoof on a lan) 363sniff_promisc : default mode for sniff() 364filter : bpf filter added to every sniffing socket to exclude traffic from analysis 365histfile : history file 366padding : includes padding in disassembled packets 367except_filter : BPF filter for packets to ignore 368debug_match : when 1, store received packet that are not matched into debug.recv 369route : holds the Scapy routing table and provides methods to manipulate it 370warning_threshold : how much time between warnings from the same place 371ASN1_default_codec: Codec used by default for ASN1 objects 372mib : holds MIB direct access dictionary 373resolve : holds list of fields for which resolution should be done 374noenum : holds list of enum fields for which conversion to string should NOT be done 375AS_resolver: choose the AS resolver class to use 376extensions_paths: path or list of paths where extensions are to be looked for 377contribs : a dict which can be used by contrib layers to store local configuration 378debug_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 461if 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 467if 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 471conf=Conf() 472conf.logLevel=30 # 30=Warning 473 474 475def 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