Skip to content
Snippets Groups Projects
Commit bea47cc7 authored by Guillaume Valadon's avatar Guillaume Valadon Committed by GitHub
Browse files

Merge pull request #809 from p-l-/prompt

Add a fancy prompt
parents 8535404b 4ad79777
No related branches found
No related tags found
No related merge requests found
...@@ -449,6 +449,7 @@ debug_tls:When 1, print some TLS session secrets when they are computed. ...@@ -449,6 +449,7 @@ debug_tls:When 1, print some TLS session secrets when they are computed.
contribs = dict() contribs = dict()
crypto_valid = isCryptographyValid() crypto_valid = isCryptographyValid()
crypto_valid_advanced = isCryptographyAdvanced() crypto_valid_advanced = isCryptographyAdvanced()
fancy_prompt = True
if not Conf.ipv6_enabled: if not Conf.ipv6_enabled:
......
...@@ -9,20 +9,46 @@ Main module for interactive startup. ...@@ -9,20 +9,46 @@ Main module for interactive startup.
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
import os,sys
import atexit
import code
import getopt
import glob import glob
import types
import gzip import gzip
import scapy.modules.six as six
import importlib import importlib
ignored = list(six.moves.builtins.__dict__.keys()) import logging
import os
from random import choice
import re
import sys
import types
from scapy.config import conf
from scapy.error import log_interactive, log_loading, log_scapy, warning
import scapy.modules.six as six
from scapy.themes import DefaultTheme
from scapy import utils
from scapy.error import * IGNORED = list(six.moves.builtins.__dict__)
GLOBKEYS = []
LAYER_ALIASES = { LAYER_ALIASES = {
"tls": "tls.all" "tls": "tls.all"
} }
QUOTES = [
("Craft packets like it is your last day on earth.", "Lao-Tze"),
("Craft packets like I craft my beer.", "Jean De Clerck"),
("Craft packets before they craft you.", "Socrate"),
("Craft me if you can.", "IPv6 layer"),
("To craft a packet, you have to be a packet, and learn how to swim in the "
"wires and in the waves.", "Jean-Claude Van Damme"),
]
def _probe_config_file(cf): def _probe_config_file(cf):
cf_path = os.path.join(os.path.expanduser("~"), cf) cf_path = os.path.join(os.path.expanduser("~"), cf)
try: try:
...@@ -45,12 +71,12 @@ def _validate_local(x): ...@@ -45,12 +71,12 @@ def _validate_local(x):
"""Returns whether or not a variable should be imported. """Returns whether or not a variable should be imported.
Will return False for any default modules (sys), or if Will return False for any default modules (sys), or if
they are detected as private vars (starting with a _)""" they are detected as private vars (starting with a _)"""
global ignored global IGNORED
return x[0] != "_" and not x in ignored return x[0] != "_" and not x in IGNORED
DEFAULT_PRESTART_FILE = _probe_config_file(".scapy_prestart.py") DEFAULT_PRESTART_FILE = _probe_config_file(".scapy_prestart.py")
DEFAULT_STARTUP_FILE = _probe_config_file(".scapy_startup.py") DEFAULT_STARTUP_FILE = _probe_config_file(".scapy_startup.py")
session = None SESSION = None
def _usage(): def _usage():
print("""Usage: scapy.py [-s sessionfile] [-c new_startup_file] [-p new_prestart_file] [-C] [-P] print("""Usage: scapy.py [-s sessionfile] [-c new_startup_file] [-p new_prestart_file] [-C] [-P]
...@@ -59,10 +85,6 @@ def _usage(): ...@@ -59,10 +85,6 @@ def _usage():
sys.exit(0) sys.exit(0)
from scapy.config import conf
from scapy.themes import DefaultTheme
###################### ######################
## Extension system ## ## Extension system ##
###################### ######################
...@@ -160,7 +182,6 @@ def list_contrib(name=None): ...@@ -160,7 +182,6 @@ def list_contrib(name=None):
def save_session(fname=None, session=None, pickleProto=-1): def save_session(fname=None, session=None, pickleProto=-1):
from scapy import utils
if fname is None: if fname is None:
fname = conf.session fname = conf.session
if not fname: if not fname:
...@@ -218,17 +239,17 @@ def update_session(fname=None): ...@@ -218,17 +239,17 @@ def update_session(fname=None):
scapy_session.update(s) scapy_session.update(s)
def init_session(session_name, mydict=None): def init_session(session_name, mydict=None):
global session global SESSION
global globkeys global GLOBKEYS
scapy_builtins = {k: v for k, v in six.iteritems(importlib.import_module(".all", "scapy").__dict__) if _validate_local(k)} scapy_builtins = {k: v for k, v in six.iteritems(importlib.import_module(".all", "scapy").__dict__) if _validate_local(k)}
six.moves.builtins.__dict__.update(scapy_builtins) six.moves.builtins.__dict__.update(scapy_builtins)
globkeys = list(scapy_builtins.keys()) GLOBKEYS.extend(scapy_builtins)
globkeys.append("scapy_session") GLOBKEYS.append("scapy_session")
scapy_builtins=None # XXX replace with "with" statement scapy_builtins=None # XXX replace with "with" statement
if mydict is not None: if mydict is not None:
six.moves.builtins.__dict__.update(mydict) six.moves.builtins.__dict__.update(mydict)
globkeys += list(mydict.keys()) GLOBKEYS.extend(mydict)
if session_name: if session_name:
try: try:
...@@ -238,26 +259,26 @@ def init_session(session_name, mydict=None): ...@@ -238,26 +259,26 @@ def init_session(session_name, mydict=None):
else: else:
try: try:
try: try:
session = six.moves.cPickle.load(gzip.open(session_name,"rb")) SESSION = six.moves.cPickle.load(gzip.open(session_name,"rb"))
except IOError: except IOError:
session = six.moves.cPickle.load(open(session_name,"rb")) SESSION = six.moves.cPickle.load(open(session_name,"rb"))
log_loading.info("Using session [%s]" % session_name) log_loading.info("Using session [%s]" % session_name)
except EOFError: except EOFError:
log_loading.error("Error opening session [%s]" % session_name) log_loading.error("Error opening session [%s]" % session_name)
except AttributeError: except AttributeError:
log_loading.error("Error opening session [%s]. Attribute missing" % session_name) log_loading.error("Error opening session [%s]. Attribute missing" % session_name)
if session: if SESSION:
if "conf" in session: if "conf" in SESSION:
conf.configure(session["conf"]) conf.configure(SESSION["conf"])
session["conf"] = conf SESSION["conf"] = conf
else: else:
conf.session = session_name conf.session = session_name
session={"conf":conf} SESSION = {"conf":conf}
else: else:
session={"conf": conf} SESSION = {"conf": conf}
six.moves.builtins.__dict__["scapy_session"] = session six.moves.builtins.__dict__["scapy_session"] = SESSION
################ ################
##### Main ##### ##### Main #####
...@@ -270,8 +291,30 @@ def scapy_delete_temp_files(): ...@@ -270,8 +291,30 @@ def scapy_delete_temp_files():
except: except:
pass pass
def _prepare_quote(quote, author, max_len=78):
"""This function processes a quote and returns a string that is ready
to be used in the fancy prompt.
"""
quote = quote.split(' ')
max_len -= 6
lines = []
cur_line = []
def _len(line):
return sum(len(elt) for elt in line) + len(line) - 1
while quote:
if not cur_line or (_len(cur_line) + len(quote[0]) - 1 <= max_len):
cur_line.append(quote.pop(0))
continue
lines.append(' | %s' % ' '.join(cur_line))
cur_line = []
if cur_line:
lines.append(' | %s' % ' '.join(cur_line))
cur_line = []
lines.append(' | %s-- %s' % (" " * (max_len - len(author) - 5), author))
return lines
def scapy_write_history_file(readline): def scapy_write_history_file(readline):
from scapy import utils
if conf.histfile: if conf.histfile:
try: try:
readline.write_history_file(conf.histfile) readline.write_history_file(conf.histfile)
...@@ -286,10 +329,8 @@ def scapy_write_history_file(readline): ...@@ -286,10 +329,8 @@ def scapy_write_history_file(readline):
def interact(mydict=None,argv=None,mybanner=None,loglevel=20): def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
global session global SESSION
global globkeys global GLOBKEYS
import code,sys,os,getopt,re
from scapy.config import conf
conf.interactive = True conf.interactive = True
if loglevel is not None: if loglevel is not None:
conf.logLevel=loglevel conf.logLevel=loglevel
...@@ -298,17 +339,104 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20): ...@@ -298,17 +339,104 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
log_scapy.addHandler(console_handler) log_scapy.addHandler(console_handler)
the_banner = "Welcome to Scapy (%s)" conf.color_theme = DefaultTheme()
if mybanner is not None:
the_banner += "\n" STARTUP_FILE = DEFAULT_STARTUP_FILE
the_banner += mybanner PRESTART_FILE = DEFAULT_PRESTART_FILE
session_name = None
if argv is None: if argv is None:
argv = sys.argv argv = sys.argv
import atexit
try: try:
import rlcompleter,readline opts = getopt.getopt(argv[1:], "hs:Cc:Pp:d")
for opt, parm in opts[0]:
if opt == "-h":
_usage()
elif opt == "-s":
session_name = parm
elif opt == "-c":
STARTUP_FILE = parm
elif opt == "-C":
STARTUP_FILE = None
elif opt == "-p":
PRESTART_FILE = parm
elif opt == "-P":
PRESTART_FILE = None
elif opt == "-d":
conf.logLevel = max(1, conf.logLevel-10)
if len(opts[1]) > 0:
raise getopt.GetoptError("Too many parameters : [%s]" % " ".join(opts[1]))
except getopt.GetoptError as msg:
log_loading.error(msg)
sys.exit(1)
if STARTUP_FILE:
_read_config_file(STARTUP_FILE)
if PRESTART_FILE:
_read_config_file(PRESTART_FILE)
init_session(session_name, mydict)
if conf.fancy_prompt:
the_logo = [
" ",
" aSPY//YASa ",
" apyyyyCY//////////YCa ",
" sY//////YSpcs scpCY//Pp ",
" ayp ayyyyyyySCP//Pp syY//C ",
" AYAsAYYYYYYYY///Ps cY//S",
" pCCCCY//p cSSps y//Y",
" SPPPP///a pP///AC//Y",
" A//A cyP////C",
" p///Ac sC///a",
" P////YCpc A//A",
" scccccp///pSP///p p//Y",
" sY/////////y caa S//P",
" cayCyayP//Ya pY/Ya",
" sY/PsY////YCc aC//Yp ",
" sc sccaCY//PCypaapyCP//YSs ",
" spCPY//////YPSps ",
" ccaacs ",
" ",
]
the_banner = [
"",
"",
" |",
" | Welcome to Scapy",
" | Version %s" % conf.version,
" |",
" | https://github.com/secdev/scapy",
" |",
" | Have fun!",
" |",
]
quote, author = choice(QUOTES)
the_banner.extend(_prepare_quote(quote, author, max_len=39))
the_banner.append(" |")
the_banner = "\n".join(
logo + banner for logo, banner in six.moves.zip_longest(
(conf.color_theme.logo(line) for line in the_logo),
(conf.color_theme.success(line) for line in the_banner),
fillvalue=""
)
)
else:
the_banner = "Welcome to Scapy (%s)" % conf.version
if mybanner is not None:
the_banner += "\n"
the_banner += mybanner
try:
import readline, rlcompleter
except ImportError: except ImportError:
log_loading.info("Can't load Python libreadline or completer") log_loading.info("Can't load Python libreadline or completer")
READLINE=0 READLINE=0
...@@ -318,7 +446,7 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20): ...@@ -318,7 +446,7 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
def global_matches(self, text): def global_matches(self, text):
matches = [] matches = []
n = len(text) n = len(text)
for lst in [dir(six.moves.builtins), session]: for lst in [dir(six.moves.builtins), SESSION]:
for word in lst: for word in lst:
if word[:n] == text and word != "__builtins__": if word[:n] == text and word != "__builtins__":
matches.append(word) matches.append(word)
...@@ -334,7 +462,7 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20): ...@@ -334,7 +462,7 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
object = eval(expr) object = eval(expr)
except: except:
try: try:
object = eval(expr, session) object = eval(expr, SESSION)
except (NameError, AttributeError): except (NameError, AttributeError):
return [] return []
from scapy.packet import Packet, Packet_metaclass from scapy.packet import Packet, Packet_metaclass
...@@ -355,47 +483,6 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20): ...@@ -355,47 +483,6 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
readline.set_completer(ScapyCompleter().complete) readline.set_completer(ScapyCompleter().complete)
readline.parse_and_bind("C-o: operate-and-get-next") readline.parse_and_bind("C-o: operate-and-get-next")
readline.parse_and_bind("tab: complete") readline.parse_and_bind("tab: complete")
STARTUP_FILE = DEFAULT_STARTUP_FILE
PRESTART_FILE = DEFAULT_PRESTART_FILE
session_name = None
try:
opts=getopt.getopt(argv[1:], "hs:Cc:Pp:d")
for opt, parm in opts[0]:
if opt == "-h":
_usage()
elif opt == "-s":
session_name = parm
elif opt == "-c":
STARTUP_FILE = parm
elif opt == "-C":
STARTUP_FILE = None
elif opt == "-p":
PRESTART_FILE = parm
elif opt == "-P":
PRESTART_FILE = None
elif opt == "-d":
conf.logLevel = max(1,conf.logLevel-10)
if len(opts[1]) > 0:
raise getopt.GetoptError("Too many parameters : [%s]" % " ".join(opts[1]))
except getopt.GetoptError as msg:
log_loading.error(msg)
sys.exit(1)
conf.color_theme = DefaultTheme()
if STARTUP_FILE:
_read_config_file(STARTUP_FILE)
if PRESTART_FILE:
_read_config_file(PRESTART_FILE)
init_session(session_name, mydict)
if READLINE: if READLINE:
if conf.histfile: if conf.histfile:
...@@ -412,33 +499,32 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20): ...@@ -412,33 +499,32 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
try: try:
import IPython import IPython
IPYTHON=True IPYTHON=True
except ImportError as e: except ImportError:
log_loading.warning("IPython not available. Using standard Python shell instead.") log_loading.warning("IPython not available. Using standard Python "
"shell instead.")
IPYTHON=False IPYTHON=False
if IPYTHON: if IPYTHON:
banner = the_banner % (conf.version) + " using IPython %s" % IPython.__version__ banner = the_banner + " using IPython %s" % IPython.__version__
# Old way to embed IPython kept for backward compatibility # Old way to embed IPython kept for backward compatibility
try: try:
args = [''] # IPython command line args (will be seen as sys.argv) args = [''] # IPython command line args (will be seen as sys.argv)
ipshell = IPython.Shell.IPShellEmbed(args, banner = banner) ipshell = IPython.Shell.IPShellEmbed(args, banner = banner)
ipshell(local_ns=session) ipshell(local_ns=SESSION)
except AttributeError as e: except AttributeError:
pass pass
# In the IPython cookbook, see 'Updating-code-for-use-with-IPython-0.11-and-later' # In the IPython cookbook, see 'Updating-code-for-use-with-IPython-0.11-and-later'
IPython.embed(user_ns=session, banner2=banner) IPython.embed(user_ns=SESSION, banner2=banner)
else: else:
code.interact(banner = the_banner % (conf.version), code.interact(banner=the_banner, local=SESSION, readfunc=conf.readfunc)
local=session, readfunc=conf.readfunc)
if conf.session: if conf.session:
save_session(conf.session, session) save_session(conf.session, SESSION)
for k in globkeys: for k in GLOBKEYS:
try: try:
del(six.moves.builtins.__dict__[k]) del(six.moves.builtins.__dict__[k])
except: except:
......
...@@ -85,6 +85,7 @@ class AnsiColorTheme(ColorTheme): ...@@ -85,6 +85,7 @@ class AnsiColorTheme(ColorTheme):
style_closed = "" style_closed = ""
style_left = "" style_left = ""
style_right = "" style_right = ""
style_logo = ""
class BlackAndWhite(AnsiColorTheme): class BlackAndWhite(AnsiColorTheme):
pass pass
...@@ -112,6 +113,7 @@ class DefaultTheme(AnsiColorTheme): ...@@ -112,6 +113,7 @@ class DefaultTheme(AnsiColorTheme):
style_closed = Color.grey style_closed = Color.grey
style_left = Color.blue+Color.invert style_left = Color.blue+Color.invert
style_right = Color.red+Color.invert style_right = Color.red+Color.invert
style_logo = Color.green+Color.bold
class BrightTheme(AnsiColorTheme): class BrightTheme(AnsiColorTheme):
style_normal = Color.normal style_normal = Color.normal
...@@ -131,6 +133,7 @@ class BrightTheme(AnsiColorTheme): ...@@ -131,6 +133,7 @@ class BrightTheme(AnsiColorTheme):
style_odd = Color.black style_odd = Color.black
style_left = Color.cyan+Color.invert style_left = Color.cyan+Color.invert
style_right = Color.purple+Color.invert style_right = Color.purple+Color.invert
style_logo = Color.green+Color.bold
class RastaTheme(AnsiColorTheme): class RastaTheme(AnsiColorTheme):
...@@ -153,6 +156,8 @@ class RastaTheme(AnsiColorTheme): ...@@ -153,6 +156,8 @@ class RastaTheme(AnsiColorTheme):
style_odd = Color.green style_odd = Color.green
style_left = Color.yellow+Color.invert style_left = Color.yellow+Color.invert
style_right = Color.red+Color.invert style_right = Color.red+Color.invert
style_logo = Color.green+Color.bold
class ColorOnBlackTheme(AnsiColorTheme): class ColorOnBlackTheme(AnsiColorTheme):
"""Color theme for black backgrounds""" """Color theme for black backgrounds"""
...@@ -178,6 +183,7 @@ class ColorOnBlackTheme(AnsiColorTheme): ...@@ -178,6 +183,7 @@ class ColorOnBlackTheme(AnsiColorTheme):
style_closed = Color.black+Color.bold style_closed = Color.black+Color.bold
style_left = Color.cyan+Color.bold style_left = Color.cyan+Color.bold
style_right = Color.red+Color.bold style_right = Color.red+Color.bold
style_logo = Color.green+Color.bold
class FormatTheme(ColorTheme): class FormatTheme(ColorTheme):
...@@ -204,6 +210,7 @@ class LatexTheme(FormatTheme): ...@@ -204,6 +210,7 @@ class LatexTheme(FormatTheme):
style_right = r"\textcolor{red}{%s}" style_right = r"\textcolor{red}{%s}"
# style_even = r"}{\bf " # style_even = r"}{\bf "
# style_odd = "" # style_odd = ""
style_logo = r"\textcolor{green}{\bf %s}"
class LatexTheme2(FormatTheme): class LatexTheme2(FormatTheme):
style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@" style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@"
...@@ -222,6 +229,7 @@ class LatexTheme2(FormatTheme): ...@@ -222,6 +229,7 @@ class LatexTheme2(FormatTheme):
# style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@" # style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@"
style_left = r"@`@textcolor@[@blue@]@@[@%s@]@" style_left = r"@`@textcolor@[@blue@]@@[@%s@]@"
style_right = r"@`@textcolor@[@red@]@@[@%s@]@" style_right = r"@`@textcolor@[@red@]@@[@%s@]@"
style_logo = r"@`@textcolor@[@green@]@@[@@`@bfseries@[@@]@%s@]@"
class HTMLTheme(FormatTheme): class HTMLTheme(FormatTheme):
style_prompt = "<span class=prompt>%s</span>" style_prompt = "<span class=prompt>%s</span>"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment