Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/main.py: 15%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
6"""
7Main module for interactive startup.
8"""
11import builtins
12import pathlib
13import sys
14import os
15import getopt
16import code
17import gzip
18import glob
19import importlib
20import io
21from itertools import zip_longest
22import logging
23import pickle
24import types
25import warnings
26from random import choice
28# Never add any global import, in main.py, that would trigger a
29# warning message before the console handlers gets added in interact()
30from scapy.error import (
31 log_interactive,
32 log_loading,
33 Scapy_Exception,
34)
35from scapy.themes import DefaultTheme, BlackAndWhite, apply_ipython_style
36from scapy.consts import WINDOWS
38from typing import (
39 Any,
40 Dict,
41 List,
42 Optional,
43 Union,
44 overload,
45)
46from scapy.compat import (
47 Literal,
48)
50LAYER_ALIASES = {
51 "tls": "tls.all",
52 "msrpce": "msrpce.all",
53}
55QUOTES = [
56 ("Craft packets like it is your last day on earth.", "Lao-Tze"),
57 ("Craft packets like I craft my beer.", "Jean De Clerck"),
58 ("Craft packets before they craft you.", "Socrate"),
59 ("Craft me if you can.", "IPv6 layer"),
60 ("To craft a packet, you have to be a packet, and learn how to swim in "
61 "the wires and in the waves.", "Jean-Claude Van Damme"),
62 ("We are in France, we say Skappee. OK? Merci.", "Sebastien Chabal"),
63 ("Wanna support scapy? Star us on GitHub!", "Satoshi Nakamoto"),
64 ("I'll be back.", "Python 2"),
65]
68def _probe_xdg_folder(var, default, *cf):
69 # type: (str, str, *str) -> Optional[pathlib.Path]
70 path = pathlib.Path(os.environ.get(var, default))
71 try:
72 if not path.exists():
73 # ~ folder doesn't exist. Create according to spec
74 # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
75 # "If, when attempting to write a file, the destination directory is
76 # non-existent an attempt should be made to create it with permission 0700."
77 path.mkdir(mode=0o700, exist_ok=True)
78 except Exception:
79 # There is a gazillion ways this can fail. Most notably, a read-only fs or no
80 # permissions to even check for folder to exist (e.x. privileges were dropped
81 # before scapy was started).
82 return None
83 return path.joinpath(*cf).resolve()
86def _probe_config_folder(*cf):
87 # type: (str) -> Optional[pathlib.Path]
88 return _probe_xdg_folder(
89 "XDG_CONFIG_HOME",
90 os.path.join(os.path.expanduser("~"), ".config"),
91 *cf
92 )
95def _probe_cache_folder(*cf):
96 # type: (str) -> Optional[pathlib.Path]
97 return _probe_xdg_folder(
98 "XDG_CACHE_HOME",
99 os.path.join(os.path.expanduser("~"), ".cache"),
100 *cf
101 )
104def _check_perms(file: Union[pathlib.Path, str]) -> None:
105 """
106 Checks that the permissions of a file are properly user-specific, if sudo is used.
107 """
108 if (
109 not WINDOWS and
110 "SUDO_UID" in os.environ and
111 "SUDO_GID" in os.environ
112 ):
113 # Was started with sudo. Still, chown to the user.
114 try:
115 os.chown(
116 file,
117 int(os.environ["SUDO_UID"]),
118 int(os.environ["SUDO_GID"]),
119 )
120 except Exception:
121 pass
124def _read_config_file(cf, _globals=globals(), _locals=locals(),
125 interactive=True, default=None):
126 # type: (str, Dict[str, Any], Dict[str, Any], bool, Optional[str]) -> None
127 """Read a config file: execute a python file while loading scapy, that
128 may contain some pre-configured values.
130 If _globals or _locals are specified, they will be updated with
131 the loaded vars. This allows an external program to use the
132 function. Otherwise, vars are only available from inside the scapy
133 console.
135 Parameters:
137 :param _globals: the globals() vars
138 :param _locals: the locals() vars
139 :param interactive: specified whether or not errors should be printed
140 using the scapy console or raised.
141 :param default: if provided, set a default value for the config file
143 ex, content of a config.py file:
144 'conf.verb = 42\n'
145 Manual loading:
146 >>> _read_config_file("./config.py"))
147 >>> conf.verb
148 2
150 """
151 cf_path = pathlib.Path(cf)
152 if not cf_path.exists():
153 log_loading.debug("Config file [%s] does not exist.", cf)
154 if default is None:
155 return
156 # We have a default ! set it
157 try:
158 if not cf_path.parent.exists():
159 cf_path.parent.mkdir(parents=True, exist_ok=True)
160 _check_perms(cf_path.parent)
162 with cf_path.open("w") as fd:
163 fd.write(default)
165 _check_perms(cf_path)
166 log_loading.debug("Config file [%s] created with default.", cf)
167 except OSError:
168 log_loading.warning("Config file [%s] could not be created.", cf,
169 exc_info=True)
170 return
171 log_loading.debug("Loading config file [%s]", cf)
172 try:
173 with open(cf) as cfgf:
174 exec(
175 compile(cfgf.read(), cf, 'exec'),
176 _globals, _locals
177 )
178 except IOError as e:
179 if interactive:
180 raise
181 log_loading.warning("Cannot read config file [%s] [%s]", cf, e)
182 except Exception:
183 if interactive:
184 raise
185 log_loading.exception("Error during evaluation of config file [%s]",
186 cf)
189def _validate_local(k):
190 # type: (str) -> bool
191 """Returns whether or not a variable should be imported."""
192 return k[0] != "_" and k not in ["range", "map"]
195# This is ~/.config/scapy
196SCAPY_CONFIG_FOLDER = _probe_config_folder("scapy")
197SCAPY_CACHE_FOLDER = _probe_cache_folder("scapy")
199if SCAPY_CONFIG_FOLDER:
200 DEFAULT_PRESTART_FILE: Optional[str] = str(SCAPY_CONFIG_FOLDER / "prestart.py")
201 DEFAULT_STARTUP_FILE: Optional[str] = str(SCAPY_CONFIG_FOLDER / "startup.py")
202else:
203 DEFAULT_PRESTART_FILE = None
204 DEFAULT_STARTUP_FILE = None
206# Default scapy prestart.py config file
208DEFAULT_PRESTART = """
209# Scapy CLI 'pre-start' config file
210# see https://scapy.readthedocs.io/en/latest/api/scapy.config.html#scapy.config.Conf
211# for all available options
213# default interpreter
214conf.interactive_shell = "auto"
216# color theme (DefaultTheme, BrightTheme, ColorOnBlackTheme, BlackAndWhite, ...)
217conf.color_theme = DefaultTheme()
219# disable INFO: tags related to dependencies missing
220# log_loading.setLevel(logging.WARNING)
222# force-use libpcap
223# conf.use_pcap = True
224""".strip()
227def _usage():
228 # type: () -> None
229 print(
230 "Usage: scapy.py [-s sessionfile] [-c new_startup_file] "
231 "[-p new_prestart_file] [-C] [-P] [-H]\n"
232 "Args:\n"
233 "\t-H: header-less start\n"
234 "\t-C: do not read startup file\n"
235 "\t-P: do not read pre-startup file\n"
236 )
237 sys.exit(0)
240######################
241# Extension system #
242######################
245def _load(module, globals_dict=None, symb_list=None):
246 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None
247 """Loads a Python module to make variables, objects and functions
248available globally.
250 The idea is to load the module using importlib, then copy the
251symbols to the global symbol table.
253 """
254 if globals_dict is None:
255 globals_dict = builtins.__dict__
256 try:
257 mod = importlib.import_module(module)
258 if '__all__' in mod.__dict__:
259 # import listed symbols
260 for name in mod.__dict__['__all__']:
261 if symb_list is not None:
262 symb_list.append(name)
263 globals_dict[name] = mod.__dict__[name]
264 else:
265 # only import non-private symbols
266 for name, sym in mod.__dict__.items():
267 if _validate_local(name):
268 if symb_list is not None:
269 symb_list.append(name)
270 globals_dict[name] = sym
271 except Exception:
272 log_interactive.error("Loading module %s", module, exc_info=True)
275def load_module(name, globals_dict=None, symb_list=None):
276 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None
277 """Loads a Scapy module to make variables, objects and functions
278 available globally.
280 """
281 _load("scapy.modules." + name,
282 globals_dict=globals_dict, symb_list=symb_list)
285def load_layer(name, globals_dict=None, symb_list=None):
286 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None
287 """Loads a Scapy layer module to make variables, objects and functions
288 available globally.
290 """
291 _load("scapy.layers." + LAYER_ALIASES.get(name, name),
292 globals_dict=globals_dict, symb_list=symb_list)
295def load_contrib(name, globals_dict=None, symb_list=None):
296 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None
297 """Loads a Scapy contrib module to make variables, objects and
298 functions available globally.
300 If no contrib module can be found with the given name, try to find
301 a layer module, since a contrib module may become a layer module.
303 """
304 try:
305 importlib.import_module("scapy.contrib." + name)
306 _load("scapy.contrib." + name,
307 globals_dict=globals_dict, symb_list=symb_list)
308 except ImportError as e:
309 # if layer not found in contrib, try in layers
310 try:
311 load_layer(name,
312 globals_dict=globals_dict, symb_list=symb_list)
313 except ImportError:
314 raise e # Let's raise the original error to avoid confusion
317def list_contrib(name=None, # type: Optional[str]
318 ret=False, # type: bool
319 _debug=False # type: bool
320 ):
321 # type: (...) -> Optional[List[Dict[str, str]]]
322 """Show the list of all existing contribs.
324 :param name: filter to search the contribs
325 :param ret: whether the function should return a dict instead of
326 printing it
327 :returns: None or a dictionary containing the results if ret=True
328 """
329 # _debug: checks that all contrib modules have correctly defined:
330 # # scapy.contrib.description = [...]
331 # # scapy.contrib.status = [...]
332 # # scapy.contrib.name = [...] (optional)
333 # or set the flag:
334 # # scapy.contrib.description = skip
335 # to skip the file
336 if name is None:
337 name = "*.py"
338 elif "*" not in name and "?" not in name and not name.endswith(".py"):
339 name += ".py"
340 results = [] # type: List[Dict[str, str]]
341 dir_path = os.path.join(os.path.dirname(__file__), "contrib")
342 if sys.version_info >= (3, 5):
343 name = os.path.join(dir_path, "**", name)
344 iterator = glob.iglob(name, recursive=True)
345 else:
346 name = os.path.join(dir_path, name)
347 iterator = glob.iglob(name)
348 for f in iterator:
349 mod = f.replace(os.path.sep, ".").partition("contrib.")[2]
350 if mod.startswith("__"):
351 continue
352 if mod.endswith(".py"):
353 mod = mod[:-3]
354 desc = {"description": "", "status": "", "name": mod}
355 with io.open(f, errors="replace") as fd:
356 for line in fd:
357 if line[0] != "#":
358 continue
359 p = line.find("scapy.contrib.")
360 if p >= 0:
361 p += 14
362 q = line.find("=", p)
363 key = line[p:q].strip()
364 value = line[q + 1:].strip()
365 desc[key] = value
366 if desc["status"] == "skip":
367 break
368 if desc["description"] and desc["status"]:
369 results.append(desc)
370 break
371 if _debug:
372 if desc["status"] == "skip":
373 pass
374 elif not desc["description"] or not desc["status"]:
375 raise Scapy_Exception("Module %s is missing its "
376 "contrib infos !" % mod)
377 results.sort(key=lambda x: x["name"])
378 if ret:
379 return results
380 else:
381 for desc in results:
382 print("%(name)-20s: %(description)-40s status=%(status)s" % desc)
383 return None
386##############################
387# Session saving/restoring #
388##############################
390def update_ipython_session(session):
391 # type: (Dict[str, Any]) -> None
392 """Updates IPython session with a custom one"""
393 if "_oh" not in session:
394 session["_oh"] = session["Out"] = {}
395 session["In"] = {}
396 try:
397 from IPython import get_ipython
398 get_ipython().user_ns.update(session)
399 except Exception:
400 pass
403def _scapy_prestart_builtins():
404 # type: () -> Dict[str, Any]
405 """Load Scapy prestart and return all builtins"""
406 return {
407 k: v
408 for k, v in importlib.import_module(".config", "scapy").__dict__.copy().items()
409 if _validate_local(k)
410 }
413def _scapy_builtins():
414 # type: () -> Dict[str, Any]
415 """Load Scapy and return all builtins"""
416 return {
417 k: v
418 for k, v in importlib.import_module(".all", "scapy").__dict__.copy().items()
419 if _validate_local(k)
420 }
423def _scapy_exts():
424 # type: () -> Dict[str, Any]
425 """Load Scapy exts and return their builtins"""
426 from scapy.config import conf
427 res = {}
428 for modname, spec in conf.exts.all_specs.items():
429 if spec.default:
430 mod = sys.modules[modname]
431 res.update({
432 k: v
433 for k, v in mod.__dict__.copy().items()
434 if _validate_local(k)
435 })
436 return res
439def save_session(fname="", session=None, pickleProto=-1):
440 # type: (str, Optional[Dict[str, Any]], int) -> None
441 """Save current Scapy session to the file specified in the fname arg.
443 params:
444 - fname: file to save the scapy session in
445 - session: scapy session to use. If None, the console one will be used
446 - pickleProto: pickle proto version (default: -1 = latest)"""
447 from scapy import utils
448 from scapy.config import conf, ConfClass
449 if not fname:
450 fname = conf.session
451 if not fname:
452 conf.session = fname = utils.get_temp_file(keep=True)
453 log_interactive.info("Saving session into [%s]", fname)
455 if not session:
456 if conf.interactive_shell in ["ipython", "ptipython"]:
457 from IPython import get_ipython
458 session = get_ipython().user_ns
459 else:
460 session = builtins.__dict__["scapy_session"]
462 if not session:
463 log_interactive.error("No session found ?!")
464 return
466 ignore = session.get("_scpybuiltins", [])
467 hard_ignore = ["scapy_session", "In", "Out", "open"]
468 to_be_saved = session.copy()
470 for k in list(to_be_saved):
471 i = to_be_saved[k]
472 if k[0] == "_":
473 del to_be_saved[k]
474 elif hasattr(i, "__module__") and i.__module__.startswith("IPython"):
475 del to_be_saved[k]
476 elif isinstance(i, ConfClass):
477 del to_be_saved[k]
478 elif k in ignore or k in hard_ignore:
479 del to_be_saved[k]
480 elif isinstance(i, (type, types.ModuleType, types.FunctionType)):
481 if k[0] != "_":
482 log_interactive.warning("[%s] (%s) can't be saved.", k, type(i))
483 del to_be_saved[k]
484 else:
485 try:
486 pickle.dumps(i)
487 except Exception:
488 log_interactive.warning("[%s] (%s) can't be saved.", k, type(i))
490 try:
491 os.rename(fname, fname + ".bak")
492 except OSError:
493 pass
495 f = gzip.open(fname, "wb")
496 pickle.dump(to_be_saved, f, pickleProto)
497 f.close()
500def load_session(fname=None):
501 # type: (Optional[Union[str, None]]) -> None
502 """Load current Scapy session from the file specified in the fname arg.
503 This will erase any existing session.
505 params:
506 - fname: file to load the scapy session from"""
507 from scapy.config import conf
508 if fname is None:
509 fname = conf.session
510 try:
511 s = pickle.load(gzip.open(fname, "rb"))
512 except IOError:
513 try:
514 s = pickle.load(open(fname, "rb"))
515 except IOError:
516 # Raise "No such file exception"
517 raise
519 scapy_session = builtins.__dict__["scapy_session"]
520 s.update({k: scapy_session[k] for k in scapy_session["_scpybuiltins"]})
521 scapy_session.clear()
522 scapy_session.update(s)
523 update_ipython_session(scapy_session)
525 log_loading.info("Loaded session [%s]", fname)
528def update_session(fname=None):
529 # type: (Optional[Union[str, None]]) -> None
530 """Update current Scapy session from the file specified in the fname arg.
532 params:
533 - fname: file to load the scapy session from"""
534 from scapy.config import conf
535 if fname is None:
536 fname = conf.session
537 try:
538 s = pickle.load(gzip.open(fname, "rb"))
539 except IOError:
540 s = pickle.load(open(fname, "rb"))
541 scapy_session = builtins.__dict__["scapy_session"]
542 scapy_session.update(s)
543 update_ipython_session(scapy_session)
546@overload
547def init_session(session_name, # type: Optional[Union[str, None]]
548 mydict, # type: Optional[Union[Dict[str, Any], None]]
549 ret, # type: Literal[True]
550 ):
551 # type: (...) -> Dict[str, Any]
552 pass
555@overload
556def init_session(session_name, # type: Optional[Union[str, None]]
557 mydict=None, # type: Optional[Union[Dict[str, Any], None]]
558 ret=False, # type: Literal[False]
559 ):
560 # type: (...) -> None
561 pass
564def init_session(session_name, # type: Optional[Union[str, None]]
565 mydict=None, # type: Optional[Union[Dict[str, Any], None]]
566 ret=False, # type: bool
567 ):
568 # type: (...) -> Union[Dict[str, Any], None]
569 from scapy.config import conf
570 SESSION = {} # type: Optional[Dict[str, Any]]
572 # Load Scapy
573 scapy_builtins = _scapy_builtins()
575 # Load exts
576 scapy_builtins.update(_scapy_exts())
578 if session_name:
579 try:
580 os.stat(session_name)
581 except OSError:
582 log_loading.info("New session [%s]", session_name)
583 else:
584 try:
585 try:
586 SESSION = pickle.load(gzip.open(session_name, "rb"))
587 except IOError:
588 SESSION = pickle.load(open(session_name, "rb"))
589 log_loading.info("Using existing session [%s]", session_name)
590 except ValueError:
591 msg = "Error opening Python3 pickled session on Python2 [%s]"
592 log_loading.error(msg, session_name)
593 except EOFError:
594 log_loading.error("Error opening session [%s]", session_name)
595 except AttributeError:
596 log_loading.error("Error opening session [%s]. "
597 "Attribute missing", session_name)
599 if SESSION:
600 if "conf" in SESSION:
601 conf.configure(SESSION["conf"])
602 conf.session = session_name
603 SESSION["conf"] = conf
604 else:
605 conf.session = session_name
606 else:
607 conf.session = session_name
608 SESSION = {"conf": conf}
609 else:
610 SESSION = {"conf": conf}
612 SESSION.update(scapy_builtins)
613 SESSION["_scpybuiltins"] = scapy_builtins.keys()
614 builtins.__dict__["scapy_session"] = SESSION
616 if mydict is not None:
617 builtins.__dict__["scapy_session"].update(mydict)
618 update_ipython_session(mydict)
619 if ret:
620 return SESSION
621 return None
623################
624# Main #
625################
628def _prepare_quote(quote, author, max_len=78):
629 # type: (str, str, int) -> List[str]
630 """This function processes a quote and returns a string that is ready
631to be used in the fancy banner.
633 """
634 _quote = quote.split(' ')
635 max_len -= 6
636 lines = []
637 cur_line = [] # type: List[str]
639 def _len(line):
640 # type: (List[str]) -> int
641 return sum(len(elt) for elt in line) + len(line) - 1
642 while _quote:
643 if not cur_line or (_len(cur_line) + len(_quote[0]) - 1 <= max_len):
644 cur_line.append(_quote.pop(0))
645 continue
646 lines.append(' | %s' % ' '.join(cur_line))
647 cur_line = []
648 if cur_line:
649 lines.append(' | %s' % ' '.join(cur_line))
650 cur_line = []
651 lines.append(' | %s-- %s' % (" " * (max_len - len(author) - 5), author))
652 return lines
655def get_fancy_banner(mini: Optional[bool] = None) -> str:
656 """
657 Generates the fancy Scapy banner
659 :param mini: if set, force a mini banner or not. Otherwise detect
660 """
661 from scapy.config import conf
662 from scapy.utils import get_terminal_width
663 if mini is None:
664 mini_banner = (get_terminal_width() or 84) <= 75
665 else:
666 mini_banner = mini
668 the_logo = [
669 " ",
670 " aSPY//YASa ",
671 " apyyyyCY//////////YCa ",
672 " sY//////YSpcs scpCY//Pp ",
673 " ayp ayyyyyyySCP//Pp syY//C ",
674 " AYAsAYYYYYYYY///Ps cY//S",
675 " pCCCCY//p cSSps y//Y",
676 " SPPPP///a pP///AC//Y",
677 " A//A cyP////C",
678 " p///Ac sC///a",
679 " P////YCpc A//A",
680 " scccccp///pSP///p p//Y",
681 " sY/////////y caa S//P",
682 " cayCyayP//Ya pY/Ya",
683 " sY/PsY////YCc aC//Yp ",
684 " sc sccaCY//PCypaapyCP//YSs ",
685 " spCPY//////YPSps ",
686 " ccaacs ",
687 " ",
688 ]
690 # Used on mini screens
691 the_logo_mini = [
692 " .SYPACCCSASYY ",
693 "P /SCS/CCS ACS",
694 " /A AC",
695 " A/PS /SPPS",
696 " YP (SC",
697 " SPS/A. SC",
698 " Y/PACC PP",
699 " PY*AYC CAA",
700 " YYCY//SCYP ",
701 ]
703 the_banner = [
704 "",
705 "",
706 " |",
707 " | Welcome to Scapy",
708 " | Version %s" % conf.version,
709 " |",
710 " | https://github.com/secdev/scapy",
711 " |",
712 " | Have fun!",
713 " |",
714 ]
716 if mini_banner:
717 the_logo = the_logo_mini
718 the_banner = [x[2:] for x in the_banner[3:-1]]
719 the_banner = [""] + the_banner + [""]
720 else:
721 quote, author = choice(QUOTES)
722 the_banner.extend(_prepare_quote(quote, author, max_len=39))
723 the_banner.append(" |")
724 return "\n".join(
725 logo + banner for logo, banner in zip_longest(
726 (conf.color_theme.logo(line) for line in the_logo),
727 (conf.color_theme.success(line) for line in the_banner),
728 fillvalue=""
729 )
730 )
733def interact(mydict=None,
734 argv=None,
735 mybanner=None,
736 mybanneronly=False,
737 loglevel=logging.INFO):
738 # type: (Optional[Any], Optional[Any], Optional[Any], bool, int) -> None
739 """
740 Starts Scapy's console.
741 """
742 # We're in interactive mode, let's throw the DeprecationWarnings
743 warnings.simplefilter("always")
745 # Set interactive mode, load the color scheme
746 from scapy.config import conf
747 conf.interactive = True
748 conf.color_theme = DefaultTheme()
749 if loglevel is not None:
750 conf.logLevel = loglevel
752 STARTUP_FILE = DEFAULT_STARTUP_FILE
753 PRESTART_FILE = DEFAULT_PRESTART_FILE
755 session_name = None
757 if argv is None:
758 argv = sys.argv
760 try:
761 opts = getopt.getopt(argv[1:], "hs:Cc:Pp:d:H")
762 for opt, param in opts[0]:
763 if opt == "-h":
764 _usage()
765 elif opt == "-H":
766 conf.fancy_banner = False
767 conf.verb = 1
768 conf.logLevel = logging.WARNING
769 elif opt == "-s":
770 session_name = param
771 elif opt == "-c":
772 STARTUP_FILE = param
773 elif opt == "-C":
774 STARTUP_FILE = None
775 elif opt == "-p":
776 PRESTART_FILE = param
777 elif opt == "-P":
778 PRESTART_FILE = None
779 elif opt == "-d":
780 conf.logLevel = max(1, conf.logLevel - 10)
782 if len(opts[1]) > 0:
783 raise getopt.GetoptError(
784 "Too many parameters : [%s]" % " ".join(opts[1])
785 )
787 except getopt.GetoptError as msg:
788 log_loading.error(msg)
789 sys.exit(1)
791 # Reset sys.argv, otherwise IPython thinks it is for him
792 sys.argv = sys.argv[:1]
794 if PRESTART_FILE:
795 _read_config_file(
796 PRESTART_FILE,
797 interactive=True,
798 _locals=_scapy_prestart_builtins(),
799 default=DEFAULT_PRESTART,
800 )
802 SESSION = init_session(session_name, mydict=mydict, ret=True)
804 if STARTUP_FILE:
805 _read_config_file(
806 STARTUP_FILE,
807 interactive=True,
808 _locals=SESSION
809 )
811 if conf.fancy_banner:
812 banner_text = get_fancy_banner()
813 else:
814 banner_text = "Welcome to Scapy (%s)" % conf.version
816 # Make sure the history file has proper permissions
817 try:
818 if not pathlib.Path(conf.histfile).exists():
819 pathlib.Path(conf.histfile).touch()
820 _check_perms(conf.histfile)
821 except OSError:
822 pass
824 # Configure interactive terminal
826 if conf.interactive_shell not in [
827 "ipython",
828 "python",
829 "ptpython",
830 "ptipython",
831 "bpython",
832 "auto"]:
833 log_loading.warning("Unknown conf.interactive_shell ! Using 'auto'")
834 conf.interactive_shell = "auto"
836 # Auto detect available shells.
837 # Order:
838 # 1. IPython
839 # 2. bpython
840 # 3. ptpython
842 _IMPORTS = {
843 "ipython": ["IPython"],
844 "bpython": ["bpython"],
845 "ptpython": ["ptpython"],
846 "ptipython": ["IPython", "ptpython"],
847 }
849 if conf.interactive_shell == "auto":
850 # Auto detect
851 for imp in ["IPython", "bpython", "ptpython"]:
852 try:
853 importlib.import_module(imp)
854 conf.interactive_shell = imp.lower()
855 break
856 except ImportError:
857 continue
858 else:
859 log_loading.warning(
860 "No alternative Python interpreters found ! "
861 "Using standard Python shell instead."
862 )
863 conf.interactive_shell = "python"
865 if conf.interactive_shell in _IMPORTS:
866 # Check import
867 for imp in _IMPORTS[conf.interactive_shell]:
868 try:
869 importlib.import_module(imp)
870 except ImportError:
871 log_loading.warning("%s requested but not found !" % imp)
872 conf.interactive_shell = "python"
874 # Default shell
875 if conf.interactive_shell == "python":
876 disabled = ["History"]
877 if WINDOWS:
878 disabled.append("Colors")
879 conf.color_theme = BlackAndWhite()
880 else:
881 try:
882 # Bad completer.. but better than nothing
883 import rlcompleter
884 import readline
885 readline.set_completer(
886 rlcompleter.Completer(namespace=SESSION).complete
887 )
888 readline.parse_and_bind('tab: complete')
889 except ImportError:
890 disabled.insert(0, "AutoCompletion")
891 # Display warning when using the default REPL
892 log_loading.info(
893 "Using the default Python shell: %s %s disabled." % (
894 ",".join(disabled),
895 "is" if len(disabled) == 1 else "are"
896 )
897 )
899 # ptpython configure function
900 def ptpython_configure(repl):
901 # type: (Any) -> None
902 # Hide status bar
903 repl.show_status_bar = False
904 # Complete while typing (versus only when pressing tab)
905 repl.complete_while_typing = False
906 # Enable auto-suggestions
907 repl.enable_auto_suggest = True
908 # Disable exit confirmation
909 repl.confirm_exit = False
910 # Show signature
911 repl.show_signature = True
912 # Apply Scapy color theme: TODO
913 # repl.install_ui_colorscheme("scapy",
914 # Style.from_dict(_custom_ui_colorscheme))
915 # repl.use_ui_colorscheme("scapy")
917 # Extend banner text
918 if conf.interactive_shell in ["ipython", "ptipython"]:
919 import IPython
920 if conf.interactive_shell == "ptipython":
921 banner = banner_text + " using IPython %s" % IPython.__version__
922 try:
923 from importlib.metadata import version
924 ptpython_version = " " + version('ptpython')
925 except ImportError:
926 ptpython_version = ""
927 banner += " and ptpython%s" % ptpython_version
928 else:
929 banner = banner_text + " using IPython %s" % IPython.__version__
930 elif conf.interactive_shell == "ptpython":
931 try:
932 from importlib.metadata import version
933 ptpython_version = " " + version('ptpython')
934 except ImportError:
935 ptpython_version = ""
936 banner = banner_text + " using ptpython%s" % ptpython_version
937 elif conf.interactive_shell == "bpython":
938 import bpython
939 banner = banner_text + " using bpython %s" % bpython.__version__
941 if mybanner is not None:
942 if mybanneronly:
943 banner = ""
944 banner += "\n"
945 banner += mybanner
947 # Start IPython or ptipython
948 if conf.interactive_shell in ["ipython", "ptipython"]:
949 banner += "\n"
950 if conf.interactive_shell == "ptipython":
951 from ptpython.ipython import embed
952 else:
953 from IPython import embed
954 try:
955 from traitlets.config.loader import Config
956 except ImportError:
957 log_loading.warning(
958 "traitlets not available. Some Scapy shell features won't be "
959 "available."
960 )
961 try:
962 embed(
963 display_banner=False,
964 user_ns=SESSION,
965 exec_lines=["print(\"\"\"" + banner + "\"\"\")"]
966 )
967 except Exception:
968 code.interact(banner=banner_text, local=SESSION)
969 else:
970 cfg = Config()
971 try:
972 from IPython import get_ipython
973 if not get_ipython():
974 raise ImportError
975 except ImportError:
976 # Set "classic" prompt style when launched from
977 # run_scapy(.bat) files Register and apply scapy
978 # color+prompt style
979 apply_ipython_style(shell=cfg.InteractiveShellEmbed)
980 cfg.InteractiveShellEmbed.confirm_exit = False
981 cfg.InteractiveShellEmbed.separate_in = u''
982 if int(IPython.__version__[0]) >= 6:
983 cfg.InteractiveShellEmbed.term_title = True
984 cfg.InteractiveShellEmbed.term_title_format = ("Scapy %s" %
985 conf.version)
986 # As of IPython 6-7, the jedi completion module is a dumpster
987 # of fire that should be scrapped never to be seen again.
988 # This is why the following defaults to False. Feel free to hurt
989 # yourself (#GH4056) :P
990 cfg.Completer.use_jedi = conf.ipython_use_jedi
991 else:
992 cfg.InteractiveShellEmbed.term_title = False
993 cfg.HistoryAccessor.hist_file = conf.histfile
994 cfg.InteractiveShell.banner1 = banner
995 if conf.verb < 2:
996 cfg.InteractiveShellEmbed.enable_tip = False
997 # configuration can thus be specified here.
998 _kwargs = {}
999 if conf.interactive_shell == "ptipython":
1000 _kwargs["configure"] = ptpython_configure
1001 try:
1002 embed(config=cfg, user_ns=SESSION, **_kwargs)
1003 except (AttributeError, TypeError):
1004 code.interact(banner=banner_text, local=SESSION)
1005 # Start ptpython
1006 elif conf.interactive_shell == "ptpython":
1007 # ptpython has special, non-default handling of __repr__ which breaks Scapy.
1008 # For instance: >>> IP()
1009 log_loading.warning("ptpython support is currently partially broken")
1010 from ptpython.repl import embed
1011 # ptpython has no banner option
1012 banner += "\n"
1013 print(banner)
1014 embed(
1015 locals=SESSION,
1016 history_filename=conf.histfile,
1017 title="Scapy %s" % conf.version,
1018 configure=ptpython_configure
1019 )
1020 # Start bpython
1021 elif conf.interactive_shell == "bpython":
1022 from bpython.curtsies import main as embed
1023 embed(
1024 args=["-q", "-i"],
1025 locals_=SESSION,
1026 banner=banner,
1027 welcome_message=""
1028 )
1029 # Start Python
1030 elif conf.interactive_shell == "python":
1031 code.interact(banner=banner_text, local=SESSION)
1032 else:
1033 raise ValueError("Invalid conf.interactive_shell")
1035 if conf.session:
1036 save_session(conf.session, SESSION)
1039if __name__ == "__main__":
1040 interact()