Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/main.py: 19%
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 code
13import getopt
14import glob
15import importlib
16import io
17import logging
18import os
19import pathlib
20import shutil
21import sys
22import warnings
24from itertools import zip_longest
25from random import choice
27# Never add any global import, in main.py, that would trigger a
28# warning message before the console handlers gets added in interact()
29from scapy.error import (
30 log_interactive,
31 log_loading,
32 Scapy_Exception,
33)
34from scapy.themes import DefaultTheme, BlackAndWhite, apply_ipython_style
35from scapy.consts import WINDOWS
37from typing import (
38 Any,
39 Dict,
40 List,
41 Optional,
42 Union,
43 overload,
44)
45from scapy.compat import (
46 Literal,
47)
49LAYER_ALIASES = {
50 "tls": "tls.all",
51 "msrpce": "msrpce.all",
52}
54QUOTES = [
55 ("Craft packets like it is your last day on earth.", "Lao-Tze"),
56 ("Craft packets like I craft my beer.", "Jean De Clerck"),
57 ("Craft packets before they craft you.", "Socrate"),
58 ("Craft me if you can.", "IPv6 layer"),
59 ("To craft a packet, you have to be a packet, and learn how to swim in "
60 "the wires and in the waves.", "Jean-Claude Van Damme"),
61 ("We are in France, we say Skappee. OK? Merci.", "Sebastien Chabal"),
62 ("Wanna support scapy? Star us on GitHub!", "Satoshi Nakamoto"),
63 ("I'll be back.", "Python 2"),
64]
67def _probe_xdg_folder(var, default, *cf):
68 # type: (str, str, *str) -> Optional[pathlib.Path]
69 path = pathlib.Path(os.environ.get(var, default))
70 try:
71 if not path.exists():
72 # ~ folder doesn't exist. Create according to spec
73 # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
74 # "If, when attempting to write a file, the destination directory is
75 # non-existent an attempt should be made to create it with permission 0700."
76 path.mkdir(mode=0o700, exist_ok=True)
77 except Exception:
78 # There is a gazillion ways this can fail. Most notably, a read-only fs or no
79 # permissions to even check for folder to exist (e.x. privileges were dropped
80 # before scapy was started).
81 return None
82 return path.joinpath(*cf).resolve()
85def _probe_config_folder(*cf):
86 # type: (str) -> Optional[pathlib.Path]
87 return _probe_xdg_folder(
88 "XDG_CONFIG_HOME",
89 os.path.join(os.path.expanduser("~"), ".config"),
90 *cf
91 )
94def _probe_cache_folder(*cf):
95 # type: (str) -> Optional[pathlib.Path]
96 return _probe_xdg_folder(
97 "XDG_CACHE_HOME",
98 os.path.join(os.path.expanduser("~"), ".cache"),
99 *cf
100 )
103def _probe_share_folder(*cf):
104 # type: (str) -> Optional[pathlib.Path]
105 return _probe_xdg_folder(
106 "XDG_DATA_HOME",
107 os.path.join(os.path.expanduser("~"), ".local", "share"),
108 *cf
109 )
112def _check_perms(file: Union[pathlib.Path, str]) -> None:
113 """
114 Checks that the permissions of a file are properly user-specific, if sudo is used.
115 """
116 if (
117 not WINDOWS and
118 "SUDO_UID" in os.environ and
119 "SUDO_GID" in os.environ
120 ):
121 # Was started with sudo. Still, chown to the user.
122 try:
123 os.chown(
124 file,
125 int(os.environ["SUDO_UID"]),
126 int(os.environ["SUDO_GID"]),
127 )
128 except Exception:
129 pass
132def _read_config_file(cf, _globals=globals(), _locals=locals(),
133 interactive=True, default=None):
134 # type: (str, Dict[str, Any], Dict[str, Any], bool, Optional[str]) -> None
135 """Read a config file: execute a python file while loading scapy, that
136 may contain some pre-configured values.
138 If _globals or _locals are specified, they will be updated with
139 the loaded vars. This allows an external program to use the
140 function. Otherwise, vars are only available from inside the scapy
141 console.
143 Parameters:
145 :param _globals: the globals() vars
146 :param _locals: the locals() vars
147 :param interactive: specified whether or not errors should be printed
148 using the scapy console or raised.
149 :param default: if provided, set a default value for the config file
151 ex, content of a config.py file:
152 'conf.verb = 42\n'
153 Manual loading:
154 >>> _read_config_file("./config.py"))
155 >>> conf.verb
156 2
158 """
159 cf_path = pathlib.Path(cf)
160 if not cf_path.exists():
161 log_loading.debug("Config file [%s] does not exist.", cf)
162 if default is None:
163 return
164 # We have a default ! set it
165 try:
166 if not cf_path.parent.exists():
167 cf_path.parent.mkdir(parents=True, exist_ok=True)
168 _check_perms(cf_path.parent)
170 with cf_path.open("w") as fd:
171 fd.write(default)
173 _check_perms(cf_path)
174 log_loading.debug("Config file [%s] created with default.", cf)
175 except OSError:
176 log_loading.warning("Config file [%s] could not be created.", cf,
177 exc_info=True)
178 return
179 log_loading.debug("Loading config file [%s]", cf)
180 try:
181 with open(cf) as cfgf:
182 exec(
183 compile(cfgf.read(), cf, 'exec'),
184 _globals, _locals
185 )
186 except IOError as e:
187 if interactive:
188 raise
189 log_loading.warning("Cannot read config file [%s] [%s]", cf, e)
190 except Exception:
191 if interactive:
192 raise
193 log_loading.exception("Error during evaluation of config file [%s]",
194 cf)
197def _validate_local(k):
198 # type: (str) -> bool
199 """Returns whether or not a variable should be imported."""
200 return k[0] != "_" and k not in ["range", "map"]
203# This is ~/.config/scapy
204SCAPY_CONFIG_FOLDER = _probe_config_folder("scapy")
205SCAPY_CACHE_FOLDER = _probe_cache_folder("scapy")
207if SCAPY_CONFIG_FOLDER:
208 DEFAULT_PRESTART_FILE: Optional[str] = str(SCAPY_CONFIG_FOLDER / "prestart.py")
209 DEFAULT_STARTUP_FILE: Optional[str] = str(SCAPY_CONFIG_FOLDER / "startup.py")
210else:
211 DEFAULT_PRESTART_FILE = None
212 DEFAULT_STARTUP_FILE = None
214# https://github.com/scop/bash-completion/blob/main/README.md#faq
215if "BASH_COMPLETION_USER_DIR" in os.environ:
216 BASH_COMPLETION_USER_DIR: Optional[pathlib.Path] = pathlib.Path(
217 os.environ["BASH_COMPLETION_USER_DIR"]
218 )
219else:
220 BASH_COMPLETION_USER_DIR = _probe_share_folder("bash-completion")
222if BASH_COMPLETION_USER_DIR:
223 BASH_COMPLETION_FOLDER: Optional[pathlib.Path] = (
224 BASH_COMPLETION_USER_DIR / "completions"
225 )
226else:
227 BASH_COMPLETION_FOLDER = None
230# Default scapy prestart.py config file
232DEFAULT_PRESTART = """
233# Scapy CLI 'pre-start' config file
234# see https://scapy.readthedocs.io/en/latest/api/scapy.config.html#scapy.config.Conf
235# for all available options
237# default interpreter
238conf.interactive_shell = "auto"
240# color theme (DefaultTheme, BrightTheme, ColorOnBlackTheme, BlackAndWhite, ...)
241conf.color_theme = DefaultTheme()
243# disable INFO: tags related to dependencies missing
244# log_loading.setLevel(logging.WARNING)
246# extensions to load by default
247conf.load_extensions = [
248 # "scapy-red",
249 # "scapy-rpc",
250]
252# force-use libpcap
253# conf.use_pcap = True
254""".strip()
257def _usage():
258 # type: () -> None
259 print(
260 "Usage: scapy.py [-c new_startup_file] "
261 "[-p new_prestart_file] [-C] [-P] [-H]\n"
262 "Args:\n"
263 "\t-H: header-less start\n"
264 "\t-C: do not read startup file\n"
265 "\t-P: do not read pre-startup file\n"
266 )
267 sys.exit(0)
270def _add_bash_autocompletion(fname: str, script: pathlib.Path) -> None:
271 """
272 Util function used most notably in setup.py to add a bash autocompletion script.
273 """
274 try:
275 if BASH_COMPLETION_FOLDER is None:
276 raise OSError()
278 # If already defined, exit.
279 dest = BASH_COMPLETION_FOLDER / fname
280 if dest.exists():
281 return
283 # Check that bash autocompletion folder exists
284 if not BASH_COMPLETION_FOLDER.exists():
285 BASH_COMPLETION_FOLDER.mkdir(parents=True, exist_ok=True)
286 _check_perms(BASH_COMPLETION_FOLDER)
288 # Copy file
289 shutil.copy(script, BASH_COMPLETION_FOLDER)
290 except OSError:
291 log_loading.warning("Bash autocompletion script could not be copied.",
292 exc_info=True)
295######################
296# Extension system #
297######################
300def _load(module, globals_dict=None, symb_list=None):
301 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None
302 """Loads a Python module to make variables, objects and functions
303available globally.
305 The idea is to load the module using importlib, then copy the
306symbols to the global symbol table.
308 """
309 if globals_dict is None:
310 globals_dict = builtins.__dict__
311 try:
312 mod = importlib.import_module(module)
313 if '__all__' in mod.__dict__:
314 # import listed symbols
315 for name in mod.__dict__['__all__']:
316 if symb_list is not None:
317 symb_list.append(name)
318 globals_dict[name] = mod.__dict__[name]
319 else:
320 # only import non-private symbols
321 for name, sym in mod.__dict__.items():
322 if _validate_local(name):
323 if symb_list is not None:
324 symb_list.append(name)
325 globals_dict[name] = sym
326 except Exception:
327 log_interactive.error("Loading module %s", module, exc_info=True)
330def load_module(name, globals_dict=None, symb_list=None):
331 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None
332 """Loads a Scapy module to make variables, objects and functions
333 available globally.
335 """
336 _load("scapy.modules." + name,
337 globals_dict=globals_dict, symb_list=symb_list)
340def load_layer(name, globals_dict=None, symb_list=None):
341 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None
342 """Loads a Scapy layer module to make variables, objects and functions
343 available globally.
345 """
346 _load("scapy.layers." + LAYER_ALIASES.get(name, name),
347 globals_dict=globals_dict, symb_list=symb_list)
350def load_contrib(name, globals_dict=None, symb_list=None):
351 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None
352 """Loads a Scapy contrib module to make variables, objects and
353 functions available globally.
355 If no contrib module can be found with the given name, try to find
356 a layer module, since a contrib module may become a layer module.
358 """
359 try:
360 importlib.import_module("scapy.contrib." + name)
361 _load("scapy.contrib." + name,
362 globals_dict=globals_dict, symb_list=symb_list)
363 except ImportError as e:
364 # if layer not found in contrib, try in layers
365 try:
366 load_layer(name,
367 globals_dict=globals_dict, symb_list=symb_list)
368 except ImportError:
369 raise e # Let's raise the original error to avoid confusion
372def list_contrib(name=None, # type: Optional[str]
373 ret=False, # type: bool
374 _debug=False # type: bool
375 ):
376 # type: (...) -> Optional[List[Dict[str, str]]]
377 """Show the list of all existing contribs.
379 :param name: filter to search the contribs
380 :param ret: whether the function should return a dict instead of
381 printing it
382 :returns: None or a dictionary containing the results if ret=True
383 """
384 # _debug: checks that all contrib modules have correctly defined:
385 # # scapy.contrib.description = [...]
386 # # scapy.contrib.status = [...]
387 # # scapy.contrib.name = [...] (optional)
388 # or set the flag:
389 # # scapy.contrib.description = skip
390 # to skip the file
391 if name is None:
392 name = "*.py"
393 elif "*" not in name and "?" not in name and not name.endswith(".py"):
394 name += ".py"
395 results = [] # type: List[Dict[str, str]]
396 dir_path = os.path.join(os.path.dirname(__file__), "contrib")
397 if sys.version_info >= (3, 5):
398 name = os.path.join(dir_path, "**", name)
399 iterator = glob.iglob(name, recursive=True)
400 else:
401 name = os.path.join(dir_path, name)
402 iterator = glob.iglob(name)
403 for f in iterator:
404 mod = f.replace(os.path.sep, ".").partition("contrib.")[2]
405 if mod.startswith("__"):
406 continue
407 if mod.endswith(".py"):
408 mod = mod[:-3]
409 desc = {"description": "", "status": "", "name": mod}
410 with io.open(f, errors="replace") as fd:
411 for line in fd:
412 if line[0] != "#":
413 continue
414 p = line.find("scapy.contrib.")
415 if p >= 0:
416 p += 14
417 q = line.find("=", p)
418 key = line[p:q].strip()
419 value = line[q + 1:].strip()
420 desc[key] = value
421 if desc["status"] == "skip":
422 break
423 if desc["description"] and desc["status"]:
424 results.append(desc)
425 break
426 if _debug:
427 if desc["status"] == "skip":
428 pass
429 elif not desc["description"] or not desc["status"]:
430 raise Scapy_Exception("Module %s is missing its "
431 "contrib infos !" % mod)
432 results.sort(key=lambda x: x["name"])
433 if ret:
434 return results
435 else:
436 for desc in results:
437 print("%(name)-20s: %(description)-40s status=%(status)s" % desc)
438 return None
441##############################
442# Session saving/restoring #
443##############################
445def update_ipython_session(session):
446 # type: (Dict[str, Any]) -> None
447 """Updates IPython session with a custom one"""
448 if "_oh" not in session:
449 session["_oh"] = session["Out"] = {}
450 session["In"] = {}
451 try:
452 from IPython import get_ipython
453 get_ipython().user_ns.update(session)
454 except Exception:
455 pass
458def _scapy_prestart_builtins():
459 # type: () -> Dict[str, Any]
460 """Load Scapy prestart and return all builtins"""
461 return {
462 k: v
463 for k, v in importlib.import_module(".config", "scapy").__dict__.copy().items()
464 if _validate_local(k)
465 }
468def _scapy_builtins():
469 # type: () -> Dict[str, Any]
470 """Load Scapy and return all builtins"""
471 return {
472 k: v
473 for k, v in importlib.import_module(".all", "scapy").__dict__.copy().items()
474 if _validate_local(k)
475 }
478def _scapy_exts():
479 # type: () -> Dict[str, Any]
480 """Load Scapy exts and return their builtins"""
481 from scapy.config import conf
482 res = {}
483 for modname, spec in conf.exts.all_specs.items():
484 if spec.default:
485 mod = sys.modules[modname]
486 res.update({
487 k: v
488 for k, v in mod.__dict__.copy().items()
489 if _validate_local(k)
490 })
491 return res
494@overload
495def init_session(mydict, # type: Optional[Union[Dict[str, Any], None]]
496 ret, # type: Literal[True]
497 ):
498 # type: (...) -> Dict[str, Any]
499 pass
502@overload
503def init_session(mydict=None, # type: Optional[Union[Dict[str, Any], None]]
504 ret=False, # type: Literal[False]
505 ):
506 # type: (...) -> None
507 pass
510def init_session(mydict=None, # type: Optional[Union[Dict[str, Any], None]]
511 ret=False, # type: bool
512 ):
513 # type: (...) -> Union[Dict[str, Any], None]
514 from scapy.config import conf
516 # Load Scapy
517 scapy_builtins = _scapy_builtins()
519 # Load exts
520 scapy_builtins.update(_scapy_exts())
522 SESSION = {"conf": conf} # type: Dict[str, Any]
524 SESSION.update(scapy_builtins)
525 SESSION["_scpybuiltins"] = scapy_builtins.keys()
526 builtins.__dict__["scapy_session"] = SESSION
528 if mydict is not None:
529 builtins.__dict__["scapy_session"].update(mydict)
530 update_ipython_session(mydict)
531 if ret:
532 return SESSION
533 return None
536################
537# Main #
538################
541def _prepare_quote(quote, author, max_len=78):
542 # type: (str, str, int) -> List[str]
543 """This function processes a quote and returns a string that is ready
544to be used in the fancy banner.
546 """
547 _quote = quote.split(' ')
548 max_len -= 6
549 lines = []
550 cur_line = [] # type: List[str]
552 def _len(line):
553 # type: (List[str]) -> int
554 return sum(len(elt) for elt in line) + len(line) - 1
555 while _quote:
556 if not cur_line or (_len(cur_line) + len(_quote[0]) - 1 <= max_len):
557 cur_line.append(_quote.pop(0))
558 continue
559 lines.append(' | %s' % ' '.join(cur_line))
560 cur_line = []
561 if cur_line:
562 lines.append(' | %s' % ' '.join(cur_line))
563 cur_line = []
564 lines.append(' | %s-- %s' % (" " * (max_len - len(author) - 5), author))
565 return lines
568def get_fancy_banner(mini: Optional[bool] = None) -> str:
569 """
570 Generates the fancy Scapy banner
572 :param mini: if set, force a mini banner or not. Otherwise detect
573 """
574 from scapy.config import conf
575 from scapy.utils import get_terminal_width
576 if mini is None:
577 mini_banner = (get_terminal_width() or 84) <= 75
578 else:
579 mini_banner = mini
581 the_logo = [
582 " ",
583 " aSPY//YASa ",
584 " apyyyyCY//////////YCa ",
585 " sY//////YSpcs scpCY//Pp ",
586 " ayp ayyyyyyySCP//Pp syY//C ",
587 " AYAsAYYYYYYYY///Ps cY//S",
588 " pCCCCY//p cSSps y//Y",
589 " SPPPP///a pP///AC//Y",
590 " A//A cyP////C",
591 " p///Ac sC///a",
592 " P////YCpc A//A",
593 " scccccp///pSP///p p//Y",
594 " sY/////////y caa S//P",
595 " cayCyayP//Ya pY/Ya",
596 " sY/PsY////YCc aC//Yp ",
597 " sc sccaCY//PCypaapyCP//YSs ",
598 " spCPY//////YPSps ",
599 " ccaacs ",
600 " ",
601 ]
603 # Used on mini screens
604 the_logo_mini = [
605 " .SYPACCCSASYY ",
606 "P /SCS/CCS ACS",
607 " /A AC",
608 " A/PS /SPPS",
609 " YP (SC",
610 " SPS/A. SC",
611 " Y/PACC PP",
612 " PY*AYC CAA",
613 " YYCY//SCYP ",
614 ]
616 the_banner = [
617 "",
618 "",
619 " |",
620 " | Welcome to Scapy",
621 " | Version %s" % conf.version,
622 " |",
623 " | https://github.com/secdev/scapy",
624 " |",
625 " | Have fun!",
626 " |",
627 ]
629 if mini_banner:
630 the_logo = the_logo_mini
631 the_banner = [x[2:] for x in the_banner[3:-1]]
632 the_banner = [""] + the_banner + [""]
633 else:
634 quote, author = choice(QUOTES)
635 the_banner.extend(_prepare_quote(quote, author, max_len=39))
636 the_banner.append(" |")
637 return "\n".join(
638 logo + banner for logo, banner in zip_longest(
639 (conf.color_theme.logo(line) for line in the_logo),
640 (conf.color_theme.success(line) for line in the_banner),
641 fillvalue=""
642 )
643 )
646def interact(mydict=None,
647 argv=None,
648 mybanner=None,
649 mybanneronly=False,
650 loglevel=logging.INFO):
651 # type: (Optional[Any], Optional[Any], Optional[Any], bool, int) -> None
652 """
653 Starts Scapy's console.
654 """
655 # We're in interactive mode, let's throw the DeprecationWarnings
656 warnings.simplefilter("always")
658 # Set interactive mode, load the color scheme
659 from scapy.config import conf
660 conf.interactive = True
661 conf.color_theme = DefaultTheme()
662 if loglevel is not None:
663 conf.logLevel = loglevel
665 STARTUP_FILE = DEFAULT_STARTUP_FILE
666 PRESTART_FILE = DEFAULT_PRESTART_FILE
668 if argv is None:
669 argv = sys.argv
671 try:
672 opts = getopt.getopt(argv[1:], "hs:Cc:Pp:d:H")
673 for opt, param in opts[0]:
674 if opt == "-h":
675 _usage()
676 elif opt == "-H":
677 conf.fancy_banner = False
678 conf.verb = 1
679 conf.logLevel = logging.WARNING
680 elif opt == "-c":
681 STARTUP_FILE = param
682 elif opt == "-C":
683 STARTUP_FILE = None
684 elif opt == "-p":
685 PRESTART_FILE = param
686 elif opt == "-P":
687 PRESTART_FILE = None
688 elif opt == "-d":
689 conf.logLevel = max(1, conf.logLevel - 10)
691 if len(opts[1]) > 0:
692 raise getopt.GetoptError(
693 "Too many parameters : [%s]" % " ".join(opts[1])
694 )
696 except getopt.GetoptError as msg:
697 log_loading.error(msg)
698 sys.exit(1)
700 # Reset sys.argv, otherwise IPython thinks it is for him
701 sys.argv = sys.argv[:1]
703 if PRESTART_FILE:
704 _read_config_file(
705 PRESTART_FILE,
706 interactive=True,
707 _locals=_scapy_prestart_builtins(),
708 default=DEFAULT_PRESTART,
709 )
711 SESSION = init_session(mydict=mydict, ret=True)
713 if STARTUP_FILE:
714 _read_config_file(
715 STARTUP_FILE,
716 interactive=True,
717 _locals=SESSION
718 )
720 # Load extensions (Python 3.8 Only)
721 if sys.version_info >= (3, 8):
722 conf.exts.loadall()
724 if conf.fancy_banner:
725 banner_text = get_fancy_banner()
726 else:
727 banner_text = "Welcome to Scapy (%s)" % conf.version
729 # Make sure the history file has proper permissions
730 try:
731 if not pathlib.Path(conf.histfile).exists():
732 pathlib.Path(conf.histfile).touch()
733 _check_perms(conf.histfile)
734 except OSError:
735 pass
737 # Configure interactive terminal
739 if conf.interactive_shell not in [
740 "ipython",
741 "python",
742 "ptpython",
743 "ptipython",
744 "bpython",
745 "auto"]:
746 log_loading.warning("Unknown conf.interactive_shell ! Using 'auto'")
747 conf.interactive_shell = "auto"
749 # Auto detect available shells.
750 # Order:
751 # 1. IPython
752 # 2. bpython
753 # 3. ptpython
755 _IMPORTS = {
756 "ipython": ["IPython"],
757 "bpython": ["bpython"],
758 "ptpython": ["ptpython"],
759 "ptipython": ["IPython", "ptpython"],
760 }
762 if conf.interactive_shell == "auto":
763 # Auto detect
764 for imp in ["IPython", "bpython", "ptpython"]:
765 try:
766 importlib.import_module(imp)
767 conf.interactive_shell = imp.lower()
768 break
769 except ImportError:
770 continue
771 else:
772 log_loading.warning(
773 "No alternative Python interpreters found ! "
774 "Using standard Python shell instead."
775 )
776 conf.interactive_shell = "python"
778 if conf.interactive_shell in _IMPORTS:
779 # Check import
780 for imp in _IMPORTS[conf.interactive_shell]:
781 try:
782 importlib.import_module(imp)
783 except ImportError:
784 log_loading.warning("%s requested but not found !" % imp)
785 conf.interactive_shell = "python"
787 # Default shell
788 if conf.interactive_shell == "python":
789 disabled = ["History"]
790 if WINDOWS:
791 disabled.append("Colors")
792 conf.color_theme = BlackAndWhite()
793 else:
794 try:
795 # Bad completer.. but better than nothing
796 import rlcompleter
797 import readline
798 readline.set_completer(
799 rlcompleter.Completer(namespace=SESSION).complete
800 )
801 readline.parse_and_bind('tab: complete')
802 except ImportError:
803 disabled.insert(0, "AutoCompletion")
804 # Display warning when using the default REPL
805 log_loading.info(
806 "Using the default Python shell: %s %s disabled." % (
807 ",".join(disabled),
808 "is" if len(disabled) == 1 else "are"
809 )
810 )
812 # ptpython configure function
813 def ptpython_configure(repl):
814 # type: (Any) -> None
815 # Hide status bar
816 repl.show_status_bar = False
817 # Complete while typing (versus only when pressing tab)
818 repl.complete_while_typing = False
819 # Enable auto-suggestions
820 repl.enable_auto_suggest = True
821 # Disable exit confirmation
822 repl.confirm_exit = False
823 # Show signature
824 repl.show_signature = True
825 # Apply Scapy color theme: TODO
826 # repl.install_ui_colorscheme("scapy",
827 # Style.from_dict(_custom_ui_colorscheme))
828 # repl.use_ui_colorscheme("scapy")
830 # Extend banner text
831 if conf.interactive_shell in ["ipython", "ptipython"]:
832 import IPython
833 if conf.interactive_shell == "ptipython":
834 banner = banner_text + " using IPython %s" % IPython.__version__
835 try:
836 from importlib.metadata import version
837 ptpython_version = " " + version('ptpython')
838 except ImportError:
839 ptpython_version = ""
840 banner += " and ptpython%s" % ptpython_version
841 else:
842 banner = banner_text + " using IPython %s" % IPython.__version__
843 elif conf.interactive_shell == "ptpython":
844 try:
845 from importlib.metadata import version
846 ptpython_version = " " + version('ptpython')
847 except ImportError:
848 ptpython_version = ""
849 banner = banner_text + " using ptpython%s" % ptpython_version
850 elif conf.interactive_shell == "bpython":
851 import bpython
852 banner = banner_text + " using bpython %s" % bpython.__version__
854 if mybanner is not None:
855 if mybanneronly:
856 banner = ""
857 banner += "\n"
858 banner += mybanner
860 # Start IPython or ptipython
861 if conf.interactive_shell in ["ipython", "ptipython"]:
862 banner += "\n"
863 if conf.interactive_shell == "ptipython":
864 from ptpython.ipython import embed
865 else:
866 from IPython import embed
867 try:
868 from traitlets.config.loader import Config
869 except ImportError:
870 log_loading.warning(
871 "traitlets not available. Some Scapy shell features won't be "
872 "available."
873 )
874 try:
875 embed(
876 display_banner=False,
877 user_ns=SESSION,
878 exec_lines=["print(\"\"\"" + banner + "\"\"\")"]
879 )
880 except Exception:
881 code.interact(banner=banner_text, local=SESSION)
882 else:
883 cfg = Config()
884 try:
885 from IPython import get_ipython
886 if not get_ipython():
887 raise ImportError
888 except ImportError:
889 # Set "classic" prompt style when launched from
890 # run_scapy(.bat) files Register and apply scapy
891 # color+prompt style
892 apply_ipython_style(shell=cfg.InteractiveShellEmbed)
893 cfg.InteractiveShellEmbed.confirm_exit = False
894 cfg.InteractiveShellEmbed.separate_in = u''
895 if int(IPython.__version__[0]) >= 6:
896 cfg.InteractiveShellEmbed.term_title = True
897 cfg.InteractiveShellEmbed.term_title_format = ("Scapy %s" %
898 conf.version)
899 # As of IPython 6-7, the jedi completion module is a dumpster
900 # of fire that should be scrapped never to be seen again.
901 # This is why the following defaults to False. Feel free to hurt
902 # yourself (#GH4056) :P
903 cfg.Completer.use_jedi = conf.ipython_use_jedi
904 else:
905 cfg.InteractiveShellEmbed.term_title = False
906 cfg.HistoryAccessor.hist_file = conf.histfile
907 cfg.InteractiveShell.banner1 = banner
908 if conf.verb < 2:
909 cfg.InteractiveShellEmbed.enable_tip = False
910 # configuration can thus be specified here.
911 _kwargs = {}
912 if conf.interactive_shell == "ptipython":
913 _kwargs["configure"] = ptpython_configure
914 try:
915 embed(config=cfg, user_ns=SESSION, **_kwargs)
916 except (AttributeError, TypeError):
917 code.interact(banner=banner_text, local=SESSION)
918 # Start ptpython
919 elif conf.interactive_shell == "ptpython":
920 # ptpython has special, non-default handling of __repr__ which breaks Scapy.
921 # For instance: >>> IP()
922 log_loading.warning("ptpython support is currently partially broken")
923 from ptpython.repl import embed
924 # ptpython has no banner option
925 banner += "\n"
926 print(banner)
927 embed(
928 locals=SESSION,
929 history_filename=conf.histfile,
930 title="Scapy %s" % conf.version,
931 configure=ptpython_configure
932 )
933 # Start bpython
934 elif conf.interactive_shell == "bpython":
935 from bpython.curtsies import main as embed
936 embed(
937 args=["-q", "-i"],
938 locals_=SESSION,
939 banner=banner,
940 welcome_message=""
941 )
942 # Start Python
943 elif conf.interactive_shell == "python":
944 code.interact(banner=banner_text, local=SESSION)
945 else:
946 raise ValueError("Invalid conf.interactive_shell")
949if __name__ == "__main__":
950 interact()