Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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

486 statements  

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> 

5 

6""" 

7Main module for interactive startup. 

8""" 

9 

10 

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 

27 

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 

37 

38from typing import ( 

39 Any, 

40 Dict, 

41 List, 

42 Optional, 

43 Union, 

44 overload, 

45) 

46from scapy.compat import ( 

47 Literal, 

48) 

49 

50LAYER_ALIASES = { 

51 "tls": "tls.all", 

52 "msrpce": "msrpce.all", 

53} 

54 

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 ("What is dead may never die!", "Python 2"), 

65] 

66 

67 

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 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) 

77 return path.joinpath(*cf).resolve() 

78 

79 

80def _probe_config_folder(*cf): 

81 # type: (str) -> Optional[pathlib.Path] 

82 return _probe_xdg_folder( 

83 "XDG_CONFIG_HOME", 

84 os.path.join(os.path.expanduser("~"), ".config"), 

85 *cf 

86 ) 

87 

88 

89def _probe_cache_folder(*cf): 

90 # type: (str) -> Optional[pathlib.Path] 

91 return _probe_xdg_folder( 

92 "XDG_CACHE_HOME", 

93 os.path.join(os.path.expanduser("~"), ".cache"), 

94 *cf 

95 ) 

96 

97 

98def _read_config_file(cf, _globals=globals(), _locals=locals(), 

99 interactive=True, default=None): 

100 # type: (str, Dict[str, Any], Dict[str, Any], bool, Optional[str]) -> None 

101 """Read a config file: execute a python file while loading scapy, that 

102 may contain some pre-configured values. 

103 

104 If _globals or _locals are specified, they will be updated with 

105 the loaded vars. This allows an external program to use the 

106 function. Otherwise, vars are only available from inside the scapy 

107 console. 

108 

109 Parameters: 

110 

111 :param _globals: the globals() vars 

112 :param _locals: the locals() vars 

113 :param interactive: specified whether or not errors should be printed 

114 using the scapy console or raised. 

115 :param default: if provided, set a default value for the config file 

116 

117 ex, content of a config.py file: 

118 'conf.verb = 42\n' 

119 Manual loading: 

120 >>> _read_config_file("./config.py")) 

121 >>> conf.verb 

122 2 

123 

124 """ 

125 cf_path = pathlib.Path(cf) 

126 if not cf_path.exists(): 

127 log_loading.debug("Config file [%s] does not exist.", cf) 

128 if default is None: 

129 return 

130 # We have a default ! set it 

131 try: 

132 cf_path.parent.mkdir(parents=True, exist_ok=True) 

133 with cf_path.open("w") as fd: 

134 fd.write(default) 

135 log_loading.debug("Config file [%s] created with default.", cf) 

136 except OSError: 

137 log_loading.warning("Config file [%s] could not be created.", cf, 

138 exc_info=True) 

139 return 

140 log_loading.debug("Loading config file [%s]", cf) 

141 try: 

142 with open(cf) as cfgf: 

143 exec( 

144 compile(cfgf.read(), cf, 'exec'), 

145 _globals, _locals 

146 ) 

147 except IOError as e: 

148 if interactive: 

149 raise 

150 log_loading.warning("Cannot read config file [%s] [%s]", cf, e) 

151 except Exception: 

152 if interactive: 

153 raise 

154 log_loading.exception("Error during evaluation of config file [%s]", 

155 cf) 

156 

157 

158def _validate_local(k): 

159 # type: (str) -> bool 

160 """Returns whether or not a variable should be imported.""" 

161 return k[0] != "_" and k not in ["range", "map"] 

162 

163 

164# This is ~/.config/scapy 

165SCAPY_CONFIG_FOLDER = _probe_config_folder("scapy") 

166SCAPY_CACHE_FOLDER = _probe_cache_folder("scapy") 

167 

168if SCAPY_CONFIG_FOLDER: 

169 DEFAULT_PRESTART_FILE: Optional[str] = str(SCAPY_CONFIG_FOLDER / "prestart.py") 

170 DEFAULT_STARTUP_FILE: Optional[str] = str(SCAPY_CONFIG_FOLDER / "startup.py") 

171else: 

172 DEFAULT_PRESTART_FILE = None 

173 DEFAULT_STARTUP_FILE = None 

174 

175# Default scapy prestart.py config file 

176 

177DEFAULT_PRESTART = """ 

178# Scapy CLI 'pre-start' config file 

179# see https://scapy.readthedocs.io/en/latest/api/scapy.config.html#scapy.config.Conf 

180# for all available options 

181 

182# default interpreter 

183conf.interactive_shell = "auto" 

184 

185# color theme (DefaultTheme, BrightTheme, ColorOnBlackTheme, BlackAndWhite, ...) 

186conf.color_theme = DefaultTheme() 

187 

188# disable INFO: tags related to dependencies missing 

189# log_loading.setLevel(logging.WARNING) 

190 

191# force-use libpcap 

192# conf.use_pcap = True 

193""".strip() 

194 

195 

196def _usage(): 

197 # type: () -> None 

198 print( 

199 "Usage: scapy.py [-s sessionfile] [-c new_startup_file] " 

200 "[-p new_prestart_file] [-C] [-P] [-H]\n" 

201 "Args:\n" 

202 "\t-H: header-less start\n" 

203 "\t-C: do not read startup file\n" 

204 "\t-P: do not read pre-startup file\n" 

205 ) 

206 sys.exit(0) 

207 

208 

209###################### 

210# Extension system # 

211###################### 

212 

213 

214def _load(module, globals_dict=None, symb_list=None): 

215 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None 

216 """Loads a Python module to make variables, objects and functions 

217available globally. 

218 

219 The idea is to load the module using importlib, then copy the 

220symbols to the global symbol table. 

221 

222 """ 

223 if globals_dict is None: 

224 globals_dict = builtins.__dict__ 

225 try: 

226 mod = importlib.import_module(module) 

227 if '__all__' in mod.__dict__: 

228 # import listed symbols 

229 for name in mod.__dict__['__all__']: 

230 if symb_list is not None: 

231 symb_list.append(name) 

232 globals_dict[name] = mod.__dict__[name] 

233 else: 

234 # only import non-private symbols 

235 for name, sym in mod.__dict__.items(): 

236 if _validate_local(name): 

237 if symb_list is not None: 

238 symb_list.append(name) 

239 globals_dict[name] = sym 

240 except Exception: 

241 log_interactive.error("Loading module %s", module, exc_info=True) 

242 

243 

244def load_module(name, globals_dict=None, symb_list=None): 

245 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None 

246 """Loads a Scapy module to make variables, objects and functions 

247 available globally. 

248 

249 """ 

250 _load("scapy.modules." + name, 

251 globals_dict=globals_dict, symb_list=symb_list) 

252 

253 

254def load_layer(name, globals_dict=None, symb_list=None): 

255 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None 

256 """Loads a Scapy layer module to make variables, objects and functions 

257 available globally. 

258 

259 """ 

260 _load("scapy.layers." + LAYER_ALIASES.get(name, name), 

261 globals_dict=globals_dict, symb_list=symb_list) 

262 

263 

264def load_contrib(name, globals_dict=None, symb_list=None): 

265 # type: (str, Optional[Dict[str, Any]], Optional[List[str]]) -> None 

266 """Loads a Scapy contrib module to make variables, objects and 

267 functions available globally. 

268 

269 If no contrib module can be found with the given name, try to find 

270 a layer module, since a contrib module may become a layer module. 

271 

272 """ 

273 try: 

274 importlib.import_module("scapy.contrib." + name) 

275 _load("scapy.contrib." + name, 

276 globals_dict=globals_dict, symb_list=symb_list) 

277 except ImportError as e: 

278 # if layer not found in contrib, try in layers 

279 try: 

280 load_layer(name, 

281 globals_dict=globals_dict, symb_list=symb_list) 

282 except ImportError: 

283 raise e # Let's raise the original error to avoid confusion 

284 

285 

286def list_contrib(name=None, # type: Optional[str] 

287 ret=False, # type: bool 

288 _debug=False # type: bool 

289 ): 

290 # type: (...) -> Optional[List[Dict[str, str]]] 

291 """Show the list of all existing contribs. 

292 

293 :param name: filter to search the contribs 

294 :param ret: whether the function should return a dict instead of 

295 printing it 

296 :returns: None or a dictionary containing the results if ret=True 

297 """ 

298 # _debug: checks that all contrib modules have correctly defined: 

299 # # scapy.contrib.description = [...] 

300 # # scapy.contrib.status = [...] 

301 # # scapy.contrib.name = [...] (optional) 

302 # or set the flag: 

303 # # scapy.contrib.description = skip 

304 # to skip the file 

305 if name is None: 

306 name = "*.py" 

307 elif "*" not in name and "?" not in name and not name.endswith(".py"): 

308 name += ".py" 

309 results = [] # type: List[Dict[str, str]] 

310 dir_path = os.path.join(os.path.dirname(__file__), "contrib") 

311 if sys.version_info >= (3, 5): 

312 name = os.path.join(dir_path, "**", name) 

313 iterator = glob.iglob(name, recursive=True) 

314 else: 

315 name = os.path.join(dir_path, name) 

316 iterator = glob.iglob(name) 

317 for f in iterator: 

318 mod = f.replace(os.path.sep, ".").partition("contrib.")[2] 

319 if mod.startswith("__"): 

320 continue 

321 if mod.endswith(".py"): 

322 mod = mod[:-3] 

323 desc = {"description": "", "status": "", "name": mod} 

324 with io.open(f, errors="replace") as fd: 

325 for line in fd: 

326 if line[0] != "#": 

327 continue 

328 p = line.find("scapy.contrib.") 

329 if p >= 0: 

330 p += 14 

331 q = line.find("=", p) 

332 key = line[p:q].strip() 

333 value = line[q + 1:].strip() 

334 desc[key] = value 

335 if desc["status"] == "skip": 

336 break 

337 if desc["description"] and desc["status"]: 

338 results.append(desc) 

339 break 

340 if _debug: 

341 if desc["status"] == "skip": 

342 pass 

343 elif not desc["description"] or not desc["status"]: 

344 raise Scapy_Exception("Module %s is missing its " 

345 "contrib infos !" % mod) 

346 results.sort(key=lambda x: x["name"]) 

347 if ret: 

348 return results 

349 else: 

350 for desc in results: 

351 print("%(name)-20s: %(description)-40s status=%(status)s" % desc) 

352 return None 

353 

354 

355############################## 

356# Session saving/restoring # 

357############################## 

358 

359def update_ipython_session(session): 

360 # type: (Dict[str, Any]) -> None 

361 """Updates IPython session with a custom one""" 

362 if "_oh" not in session: 

363 session["_oh"] = session["Out"] = {} 

364 session["In"] = {} 

365 try: 

366 from IPython import get_ipython 

367 get_ipython().user_ns.update(session) 

368 except Exception: 

369 pass 

370 

371 

372def _scapy_prestart_builtins(): 

373 # type: () -> Dict[str, Any] 

374 """Load Scapy prestart and return all builtins""" 

375 return { 

376 k: v 

377 for k, v in importlib.import_module(".config", "scapy").__dict__.copy().items() 

378 if _validate_local(k) 

379 } 

380 

381 

382def _scapy_builtins(): 

383 # type: () -> Dict[str, Any] 

384 """Load Scapy and return all builtins""" 

385 return { 

386 k: v 

387 for k, v in importlib.import_module(".all", "scapy").__dict__.copy().items() 

388 if _validate_local(k) 

389 } 

390 

391 

392def _scapy_exts(): 

393 # type: () -> Dict[str, Any] 

394 """Load Scapy exts and return their builtins""" 

395 from scapy.config import conf 

396 res = {} 

397 for modname, spec in conf.exts.all_specs.items(): 

398 if spec.default: 

399 mod = sys.modules[modname] 

400 res.update({ 

401 k: v 

402 for k, v in mod.__dict__.copy().items() 

403 if _validate_local(k) 

404 }) 

405 return res 

406 

407 

408def save_session(fname="", session=None, pickleProto=-1): 

409 # type: (str, Optional[Dict[str, Any]], int) -> None 

410 """Save current Scapy session to the file specified in the fname arg. 

411 

412 params: 

413 - fname: file to save the scapy session in 

414 - session: scapy session to use. If None, the console one will be used 

415 - pickleProto: pickle proto version (default: -1 = latest)""" 

416 from scapy import utils 

417 from scapy.config import conf, ConfClass 

418 if not fname: 

419 fname = conf.session 

420 if not fname: 

421 conf.session = fname = utils.get_temp_file(keep=True) 

422 log_interactive.info("Saving session into [%s]", fname) 

423 

424 if not session: 

425 if conf.interactive_shell in ["ipython", "ptipython"]: 

426 from IPython import get_ipython 

427 session = get_ipython().user_ns 

428 else: 

429 session = builtins.__dict__["scapy_session"] 

430 

431 if not session: 

432 log_interactive.error("No session found ?!") 

433 return 

434 

435 ignore = session.get("_scpybuiltins", []) 

436 hard_ignore = ["scapy_session", "In", "Out", "open"] 

437 to_be_saved = session.copy() 

438 

439 for k in list(to_be_saved): 

440 i = to_be_saved[k] 

441 if k[0] == "_": 

442 del to_be_saved[k] 

443 elif hasattr(i, "__module__") and i.__module__.startswith("IPython"): 

444 del to_be_saved[k] 

445 elif isinstance(i, ConfClass): 

446 del to_be_saved[k] 

447 elif k in ignore or k in hard_ignore: 

448 del to_be_saved[k] 

449 elif isinstance(i, (type, types.ModuleType, types.FunctionType)): 

450 if k[0] != "_": 

451 log_interactive.warning("[%s] (%s) can't be saved.", k, type(i)) 

452 del to_be_saved[k] 

453 else: 

454 try: 

455 pickle.dumps(i) 

456 except Exception: 

457 log_interactive.warning("[%s] (%s) can't be saved.", k, type(i)) 

458 

459 try: 

460 os.rename(fname, fname + ".bak") 

461 except OSError: 

462 pass 

463 

464 f = gzip.open(fname, "wb") 

465 pickle.dump(to_be_saved, f, pickleProto) 

466 f.close() 

467 

468 

469def load_session(fname=None): 

470 # type: (Optional[Union[str, None]]) -> None 

471 """Load current Scapy session from the file specified in the fname arg. 

472 This will erase any existing session. 

473 

474 params: 

475 - fname: file to load the scapy session from""" 

476 from scapy.config import conf 

477 if fname is None: 

478 fname = conf.session 

479 try: 

480 s = pickle.load(gzip.open(fname, "rb")) 

481 except IOError: 

482 try: 

483 s = pickle.load(open(fname, "rb")) 

484 except IOError: 

485 # Raise "No such file exception" 

486 raise 

487 

488 scapy_session = builtins.__dict__["scapy_session"] 

489 s.update({k: scapy_session[k] for k in scapy_session["_scpybuiltins"]}) 

490 scapy_session.clear() 

491 scapy_session.update(s) 

492 update_ipython_session(scapy_session) 

493 

494 log_loading.info("Loaded session [%s]", fname) 

495 

496 

497def update_session(fname=None): 

498 # type: (Optional[Union[str, None]]) -> None 

499 """Update current Scapy session from the file specified in the fname arg. 

500 

501 params: 

502 - fname: file to load the scapy session from""" 

503 from scapy.config import conf 

504 if fname is None: 

505 fname = conf.session 

506 try: 

507 s = pickle.load(gzip.open(fname, "rb")) 

508 except IOError: 

509 s = pickle.load(open(fname, "rb")) 

510 scapy_session = builtins.__dict__["scapy_session"] 

511 scapy_session.update(s) 

512 update_ipython_session(scapy_session) 

513 

514 

515@overload 

516def init_session(session_name, # type: Optional[Union[str, None]] 

517 mydict, # type: Optional[Union[Dict[str, Any], None]] 

518 ret, # type: Literal[True] 

519 ): 

520 # type: (...) -> Dict[str, Any] 

521 pass 

522 

523 

524@overload 

525def init_session(session_name, # type: Optional[Union[str, None]] 

526 mydict=None, # type: Optional[Union[Dict[str, Any], None]] 

527 ret=False, # type: Literal[False] 

528 ): 

529 # type: (...) -> None 

530 pass 

531 

532 

533def init_session(session_name, # type: Optional[Union[str, None]] 

534 mydict=None, # type: Optional[Union[Dict[str, Any], None]] 

535 ret=False, # type: bool 

536 ): 

537 # type: (...) -> Union[Dict[str, Any], None] 

538 from scapy.config import conf 

539 SESSION = {} # type: Optional[Dict[str, Any]] 

540 

541 # Load Scapy 

542 scapy_builtins = _scapy_builtins() 

543 

544 # Load exts 

545 scapy_builtins.update(_scapy_exts()) 

546 

547 if session_name: 

548 try: 

549 os.stat(session_name) 

550 except OSError: 

551 log_loading.info("New session [%s]", session_name) 

552 else: 

553 try: 

554 try: 

555 SESSION = pickle.load(gzip.open(session_name, "rb")) 

556 except IOError: 

557 SESSION = pickle.load(open(session_name, "rb")) 

558 log_loading.info("Using existing session [%s]", session_name) 

559 except ValueError: 

560 msg = "Error opening Python3 pickled session on Python2 [%s]" 

561 log_loading.error(msg, session_name) 

562 except EOFError: 

563 log_loading.error("Error opening session [%s]", session_name) 

564 except AttributeError: 

565 log_loading.error("Error opening session [%s]. " 

566 "Attribute missing", session_name) 

567 

568 if SESSION: 

569 if "conf" in SESSION: 

570 conf.configure(SESSION["conf"]) 

571 conf.session = session_name 

572 SESSION["conf"] = conf 

573 else: 

574 conf.session = session_name 

575 else: 

576 conf.session = session_name 

577 SESSION = {"conf": conf} 

578 else: 

579 SESSION = {"conf": conf} 

580 

581 SESSION.update(scapy_builtins) 

582 SESSION["_scpybuiltins"] = scapy_builtins.keys() 

583 builtins.__dict__["scapy_session"] = SESSION 

584 

585 if mydict is not None: 

586 builtins.__dict__["scapy_session"].update(mydict) 

587 update_ipython_session(mydict) 

588 if ret: 

589 return SESSION 

590 return None 

591 

592################ 

593# Main # 

594################ 

595 

596 

597def _prepare_quote(quote, author, max_len=78): 

598 # type: (str, str, int) -> List[str] 

599 """This function processes a quote and returns a string that is ready 

600to be used in the fancy banner. 

601 

602 """ 

603 _quote = quote.split(' ') 

604 max_len -= 6 

605 lines = [] 

606 cur_line = [] # type: List[str] 

607 

608 def _len(line): 

609 # type: (List[str]) -> int 

610 return sum(len(elt) for elt in line) + len(line) - 1 

611 while _quote: 

612 if not cur_line or (_len(cur_line) + len(_quote[0]) - 1 <= max_len): 

613 cur_line.append(_quote.pop(0)) 

614 continue 

615 lines.append(' | %s' % ' '.join(cur_line)) 

616 cur_line = [] 

617 if cur_line: 

618 lines.append(' | %s' % ' '.join(cur_line)) 

619 cur_line = [] 

620 lines.append(' | %s-- %s' % (" " * (max_len - len(author) - 5), author)) 

621 return lines 

622 

623 

624def get_fancy_banner(mini: Optional[bool] = None) -> str: 

625 """ 

626 Generates the fancy Scapy banner 

627 

628 :param mini: if set, force a mini banner or not. Otherwise detect 

629 """ 

630 from scapy.config import conf 

631 from scapy.utils import get_terminal_width 

632 if mini is None: 

633 mini_banner = (get_terminal_width() or 84) <= 75 

634 else: 

635 mini_banner = mini 

636 

637 the_logo = [ 

638 " ", 

639 " aSPY//YASa ", 

640 " apyyyyCY//////////YCa ", 

641 " sY//////YSpcs scpCY//Pp ", 

642 " ayp ayyyyyyySCP//Pp syY//C ", 

643 " AYAsAYYYYYYYY///Ps cY//S", 

644 " pCCCCY//p cSSps y//Y", 

645 " SPPPP///a pP///AC//Y", 

646 " A//A cyP////C", 

647 " p///Ac sC///a", 

648 " P////YCpc A//A", 

649 " scccccp///pSP///p p//Y", 

650 " sY/////////y caa S//P", 

651 " cayCyayP//Ya pY/Ya", 

652 " sY/PsY////YCc aC//Yp ", 

653 " sc sccaCY//PCypaapyCP//YSs ", 

654 " spCPY//////YPSps ", 

655 " ccaacs ", 

656 " ", 

657 ] 

658 

659 # Used on mini screens 

660 the_logo_mini = [ 

661 " .SYPACCCSASYY ", 

662 "P /SCS/CCS ACS", 

663 " /A AC", 

664 " A/PS /SPPS", 

665 " YP (SC", 

666 " SPS/A. SC", 

667 " Y/PACC PP", 

668 " PY*AYC CAA", 

669 " YYCY//SCYP ", 

670 ] 

671 

672 the_banner = [ 

673 "", 

674 "", 

675 " |", 

676 " | Welcome to Scapy", 

677 " | Version %s" % conf.version, 

678 " |", 

679 " | https://github.com/secdev/scapy", 

680 " |", 

681 " | Have fun!", 

682 " |", 

683 ] 

684 

685 if mini_banner: 

686 the_logo = the_logo_mini 

687 the_banner = [x[2:] for x in the_banner[3:-1]] 

688 the_banner = [""] + the_banner + [""] 

689 else: 

690 quote, author = choice(QUOTES) 

691 the_banner.extend(_prepare_quote(quote, author, max_len=39)) 

692 the_banner.append(" |") 

693 return "\n".join( 

694 logo + banner for logo, banner in zip_longest( 

695 (conf.color_theme.logo(line) for line in the_logo), 

696 (conf.color_theme.success(line) for line in the_banner), 

697 fillvalue="" 

698 ) 

699 ) 

700 

701 

702def interact(mydict=None, argv=None, mybanner=None, loglevel=logging.INFO): 

703 # type: (Optional[Any], Optional[Any], Optional[Any], int) -> None 

704 """ 

705 Starts Scapy's console. 

706 """ 

707 # We're in interactive mode, let's throw the DeprecationWarnings 

708 warnings.simplefilter("always") 

709 

710 # Set interactive mode, load the color scheme 

711 from scapy.config import conf 

712 conf.interactive = True 

713 conf.color_theme = DefaultTheme() 

714 if loglevel is not None: 

715 conf.logLevel = loglevel 

716 

717 STARTUP_FILE = DEFAULT_STARTUP_FILE 

718 PRESTART_FILE = DEFAULT_PRESTART_FILE 

719 

720 session_name = None 

721 

722 if argv is None: 

723 argv = sys.argv 

724 

725 try: 

726 opts = getopt.getopt(argv[1:], "hs:Cc:Pp:d:H") 

727 for opt, param in opts[0]: 

728 if opt == "-h": 

729 _usage() 

730 elif opt == "-H": 

731 conf.fancy_banner = False 

732 conf.verb = 1 

733 conf.logLevel = logging.WARNING 

734 elif opt == "-s": 

735 session_name = param 

736 elif opt == "-c": 

737 STARTUP_FILE = param 

738 elif opt == "-C": 

739 STARTUP_FILE = None 

740 elif opt == "-p": 

741 PRESTART_FILE = param 

742 elif opt == "-P": 

743 PRESTART_FILE = None 

744 elif opt == "-d": 

745 conf.logLevel = max(1, conf.logLevel - 10) 

746 

747 if len(opts[1]) > 0: 

748 raise getopt.GetoptError( 

749 "Too many parameters : [%s]" % " ".join(opts[1]) 

750 ) 

751 

752 except getopt.GetoptError as msg: 

753 log_loading.error(msg) 

754 sys.exit(1) 

755 

756 # Reset sys.argv, otherwise IPython thinks it is for him 

757 sys.argv = sys.argv[:1] 

758 

759 if PRESTART_FILE: 

760 _read_config_file( 

761 PRESTART_FILE, 

762 interactive=True, 

763 _locals=_scapy_prestart_builtins(), 

764 default=DEFAULT_PRESTART, 

765 ) 

766 

767 SESSION = init_session(session_name, mydict=mydict, ret=True) 

768 

769 if STARTUP_FILE: 

770 _read_config_file( 

771 STARTUP_FILE, 

772 interactive=True, 

773 _locals=SESSION 

774 ) 

775 

776 if conf.fancy_banner: 

777 banner_text = get_fancy_banner() 

778 else: 

779 banner_text = "Welcome to Scapy (%s)" % conf.version 

780 if mybanner is not None: 

781 banner_text += "\n" 

782 banner_text += mybanner 

783 

784 # Configure interactive terminal 

785 

786 if conf.interactive_shell not in [ 

787 "ipython", 

788 "python", 

789 "ptpython", 

790 "ptipython", 

791 "bpython", 

792 "auto"]: 

793 log_loading.warning("Unknown conf.interactive_shell ! Using 'auto'") 

794 conf.interactive_shell = "auto" 

795 

796 # Auto detect available shells. 

797 # Order: 

798 # 1. IPython 

799 # 2. bpython 

800 # 3. ptpython 

801 

802 _IMPORTS = { 

803 "ipython": ["IPython"], 

804 "bpython": ["bpython"], 

805 "ptpython": ["ptpython"], 

806 "ptipython": ["IPython", "ptpython"], 

807 } 

808 

809 if conf.interactive_shell == "auto": 

810 # Auto detect 

811 for imp in ["IPython", "bpython", "ptpython"]: 

812 try: 

813 importlib.import_module(imp) 

814 conf.interactive_shell = imp.lower() 

815 break 

816 except ImportError: 

817 continue 

818 else: 

819 log_loading.warning( 

820 "No alternative Python interpreters found ! " 

821 "Using standard Python shell instead." 

822 ) 

823 conf.interactive_shell = "python" 

824 

825 if conf.interactive_shell in _IMPORTS: 

826 # Check import 

827 for imp in _IMPORTS[conf.interactive_shell]: 

828 try: 

829 importlib.import_module(imp) 

830 except ImportError: 

831 log_loading.warning("%s requested but not found !" % imp) 

832 conf.interactive_shell = "python" 

833 

834 # Default shell 

835 if conf.interactive_shell == "python": 

836 disabled = ["History"] 

837 if WINDOWS: 

838 disabled.append("Colors") 

839 conf.color_theme = BlackAndWhite() 

840 else: 

841 try: 

842 # Bad completer.. but better than nothing 

843 import rlcompleter 

844 import readline 

845 readline.set_completer( 

846 rlcompleter.Completer(namespace=SESSION).complete 

847 ) 

848 readline.parse_and_bind('tab: complete') 

849 except ImportError: 

850 disabled.insert(0, "AutoCompletion") 

851 # Display warning when using the default REPL 

852 log_loading.info( 

853 "Using the default Python shell: %s %s disabled." % ( 

854 ",".join(disabled), 

855 "is" if len(disabled) == 1 else "are" 

856 ) 

857 ) 

858 

859 # ptpython configure function 

860 def ptpython_configure(repl): 

861 # type: (Any) -> None 

862 # Hide status bar 

863 repl.show_status_bar = False 

864 # Complete while typing (versus only when pressing tab) 

865 repl.complete_while_typing = False 

866 # Enable auto-suggestions 

867 repl.enable_auto_suggest = True 

868 # Disable exit confirmation 

869 repl.confirm_exit = False 

870 # Show signature 

871 repl.show_signature = True 

872 # Apply Scapy color theme: TODO 

873 # repl.install_ui_colorscheme("scapy", 

874 # Style.from_dict(_custom_ui_colorscheme)) 

875 # repl.use_ui_colorscheme("scapy") 

876 

877 # Extend banner text 

878 if conf.interactive_shell in ["ipython", "ptipython"]: 

879 import IPython 

880 if conf.interactive_shell == "ptipython": 

881 banner = banner_text + " using IPython %s" % IPython.__version__ 

882 try: 

883 from importlib.metadata import version 

884 ptpython_version = " " + version('ptpython') 

885 except ImportError: 

886 ptpython_version = "" 

887 banner += " and ptpython%s" % ptpython_version 

888 else: 

889 banner = banner_text + " using IPython %s" % IPython.__version__ 

890 elif conf.interactive_shell == "ptpython": 

891 try: 

892 from importlib.metadata import version 

893 ptpython_version = " " + version('ptpython') 

894 except ImportError: 

895 ptpython_version = "" 

896 banner = banner_text + " using ptpython%s" % ptpython_version 

897 elif conf.interactive_shell == "bpython": 

898 import bpython 

899 banner = banner_text + " using bpython %s" % bpython.__version__ 

900 

901 # Start IPython or ptipython 

902 if conf.interactive_shell in ["ipython", "ptipython"]: 

903 banner += "\n" 

904 if conf.interactive_shell == "ptipython": 

905 from ptpython.ipython import embed 

906 else: 

907 from IPython import embed 

908 try: 

909 from traitlets.config.loader import Config 

910 except ImportError: 

911 log_loading.warning( 

912 "traitlets not available. Some Scapy shell features won't be " 

913 "available." 

914 ) 

915 try: 

916 embed( 

917 display_banner=False, 

918 user_ns=SESSION, 

919 exec_lines=["print(\"\"\"" + banner + "\"\"\")"] 

920 ) 

921 except Exception: 

922 code.interact(banner=banner_text, local=SESSION) 

923 else: 

924 cfg = Config() 

925 try: 

926 from IPython import get_ipython 

927 if not get_ipython(): 

928 raise ImportError 

929 except ImportError: 

930 # Set "classic" prompt style when launched from 

931 # run_scapy(.bat) files Register and apply scapy 

932 # color+prompt style 

933 apply_ipython_style(shell=cfg.InteractiveShellEmbed) 

934 cfg.InteractiveShellEmbed.confirm_exit = False 

935 cfg.InteractiveShellEmbed.separate_in = u'' 

936 if int(IPython.__version__[0]) >= 6: 

937 cfg.InteractiveShellEmbed.term_title = True 

938 cfg.InteractiveShellEmbed.term_title_format = ("Scapy %s" % 

939 conf.version) 

940 # As of IPython 6-7, the jedi completion module is a dumpster 

941 # of fire that should be scrapped never to be seen again. 

942 # This is why the following defaults to False. Feel free to hurt 

943 # yourself (#GH4056) :P 

944 cfg.Completer.use_jedi = conf.ipython_use_jedi 

945 else: 

946 cfg.InteractiveShellEmbed.term_title = False 

947 cfg.HistoryAccessor.hist_file = conf.histfile 

948 cfg.InteractiveShell.banner1 = banner 

949 # configuration can thus be specified here. 

950 _kwargs = {} 

951 if conf.interactive_shell == "ptipython": 

952 _kwargs["configure"] = ptpython_configure 

953 try: 

954 embed(config=cfg, user_ns=SESSION, **_kwargs) 

955 except (AttributeError, TypeError): 

956 code.interact(banner=banner_text, local=SESSION) 

957 # Start ptpython 

958 elif conf.interactive_shell == "ptpython": 

959 # ptpython has special, non-default handling of __repr__ which breaks Scapy. 

960 # For instance: >>> IP() 

961 log_loading.warning("ptpython support is currently partially broken") 

962 from ptpython.repl import embed 

963 # ptpython has no banner option 

964 banner += "\n" 

965 print(banner) 

966 embed( 

967 locals=SESSION, 

968 history_filename=conf.histfile, 

969 title="Scapy %s" % conf.version, 

970 configure=ptpython_configure 

971 ) 

972 # Start bpython 

973 elif conf.interactive_shell == "bpython": 

974 from bpython.curtsies import main as embed 

975 embed( 

976 args=["-q", "-i"], 

977 locals_=SESSION, 

978 banner=banner, 

979 welcome_message="" 

980 ) 

981 # Start Python 

982 elif conf.interactive_shell == "python": 

983 code.interact(banner=banner_text, local=SESSION) 

984 else: 

985 raise ValueError("Invalid conf.interactive_shell") 

986 

987 if conf.session: 

988 save_session(conf.session, SESSION) 

989 

990 

991if __name__ == "__main__": 

992 interact()