Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/main.py: 17%

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

419 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 code 

13import getopt 

14import glob 

15import importlib 

16import io 

17import logging 

18import os 

19import pathlib 

20import shutil 

21import sys 

22import warnings 

23 

24from itertools import zip_longest 

25from random import choice 

26 

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 

36 

37from typing import ( 

38 Any, 

39 Dict, 

40 List, 

41 Optional, 

42 Union, 

43 overload, 

44) 

45from scapy.compat import ( 

46 Literal, 

47) 

48 

49LAYER_ALIASES = { 

50 "tls": "tls.all", 

51 "msrpce": "msrpce.all", 

52} 

53 

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] 

65 

66 

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 if WINDOWS: 

77 # https://github.com/secdev/scapy/issues/5190 

78 # 0o700 on Windows is very specific and isn't what we expect. 

79 path.mkdir(exist_ok=True) 

80 else: 

81 path.mkdir(mode=0o700, exist_ok=True) 

82 except Exception: 

83 # There is a gazillion ways this can fail. Most notably, a read-only fs or no 

84 # permissions to even check for folder to exist (e.x. privileges were dropped 

85 # before scapy was started). 

86 return None 

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

88 

89 

90def _probe_config_folder(*cf): 

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

92 return _probe_xdg_folder( 

93 "XDG_CONFIG_HOME", 

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

95 *cf 

96 ) 

97 

98 

99def _probe_cache_folder(*cf): 

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

101 return _probe_xdg_folder( 

102 "XDG_CACHE_HOME", 

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

104 *cf 

105 ) 

106 

107 

108def _probe_share_folder(*cf): 

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

110 return _probe_xdg_folder( 

111 "XDG_DATA_HOME", 

112 os.path.join(os.path.expanduser("~"), ".local", "share"), 

113 *cf 

114 ) 

115 

116 

117def _check_perms(file: Union[pathlib.Path, str]) -> None: 

118 """ 

119 Checks that the permissions of a file are properly user-specific, if sudo is used. 

120 """ 

121 if ( 

122 not WINDOWS and 

123 "SUDO_UID" in os.environ and 

124 "SUDO_GID" in os.environ 

125 ): 

126 # Was started with sudo. Still, chown to the user. 

127 try: 

128 os.chown( 

129 file, 

130 int(os.environ["SUDO_UID"]), 

131 int(os.environ["SUDO_GID"]), 

132 ) 

133 except Exception: 

134 pass 

135 

136 

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

138 interactive=True, default=None): 

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

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

141 may contain some pre-configured values. 

142 

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

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

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

146 console. 

147 

148 Parameters: 

149 

150 :param _globals: the globals() vars 

151 :param _locals: the locals() vars 

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

153 using the scapy console or raised. 

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

155 

156 ex, content of a config.py file: 

157 'conf.verb = 42\n' 

158 Manual loading: 

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

160 >>> conf.verb 

161 2 

162 

163 """ 

164 cf_path = pathlib.Path(cf) 

165 if not cf_path.exists(): 

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

167 if default is None: 

168 return 

169 # We have a default ! set it 

170 try: 

171 if not cf_path.parent.exists(): 

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

173 _check_perms(cf_path.parent) 

174 

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

176 fd.write(default) 

177 

178 _check_perms(cf_path) 

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

180 except OSError: 

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

182 exc_info=True) 

183 return 

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

185 try: 

186 with open(cf) as cfgf: 

187 exec( 

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

189 _globals, _locals 

190 ) 

191 except IOError as e: 

192 if interactive: 

193 raise 

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

195 except Exception: 

196 if interactive: 

197 raise 

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

199 cf) 

200 

201 

202def _validate_local(k): 

203 # type: (str) -> bool 

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

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

206 

207 

208# This is ~/.config/scapy 

209SCAPY_CONFIG_FOLDER = _probe_config_folder("scapy") 

210SCAPY_CACHE_FOLDER = _probe_cache_folder("scapy") 

211 

212if SCAPY_CONFIG_FOLDER: 

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

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

215else: 

216 DEFAULT_PRESTART_FILE = None 

217 DEFAULT_STARTUP_FILE = None 

218 

219if "/bash" in os.environ.get("SHELL", ""): 

220 # https://github.com/scop/bash-completion/blob/main/README.md#faq 

221 if "BASH_COMPLETION_USER_DIR" in os.environ: 

222 BASH_COMPLETION_USER_DIR: Optional[pathlib.Path] = pathlib.Path( 

223 os.environ["BASH_COMPLETION_USER_DIR"] 

224 ) 

225 else: 

226 BASH_COMPLETION_USER_DIR = _probe_share_folder("bash-completion") 

227 

228 if BASH_COMPLETION_USER_DIR: 

229 BASH_COMPLETION_FOLDER: Optional[pathlib.Path] = ( 

230 BASH_COMPLETION_USER_DIR / "completions" 

231 ) 

232 else: 

233 BASH_COMPLETION_FOLDER = None 

234else: 

235 BASH_COMPLETION_FOLDER = None 

236 

237 

238# Default scapy prestart.py config file 

239 

240DEFAULT_PRESTART = """ 

241# Scapy CLI 'pre-start' config file 

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

243# for all available options 

244 

245# default interpreter 

246conf.interactive_shell = "auto" 

247 

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

249conf.color_theme = DefaultTheme() 

250 

251# disable INFO: tags related to dependencies missing 

252# log_loading.setLevel(logging.WARNING) 

253 

254# extensions to load by default 

255conf.load_extensions = [ 

256 # "scapy-red", 

257 # "scapy-rpc", 

258] 

259 

260# force-use libpcap 

261# conf.use_pcap = True 

262""".strip() 

263 

264 

265def _usage(): 

266 # type: () -> None 

267 print( 

268 "Usage: scapy.py [-c new_startup_file] " 

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

270 "Args:\n" 

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

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

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

274 ) 

275 sys.exit(0) 

276 

277 

278def _add_bash_autocompletion(fname: str, script: pathlib.Path) -> None: 

279 """ 

280 Util function used most notably in setup.py to add a bash autocompletion script. 

281 """ 

282 if BASH_COMPLETION_FOLDER is None: 

283 return 

284 try: 

285 # If already defined, exit. 

286 dest = BASH_COMPLETION_FOLDER / fname 

287 if dest.exists(): 

288 return 

289 

290 # Check that bash autocompletion folder exists 

291 if not BASH_COMPLETION_FOLDER.exists(): 

292 BASH_COMPLETION_FOLDER.mkdir(parents=True, exist_ok=True) 

293 _check_perms(BASH_COMPLETION_FOLDER) 

294 

295 # Copy file 

296 shutil.copy(script, BASH_COMPLETION_FOLDER) 

297 except OSError: 

298 log_loading.warning("Bash autocompletion script could not be copied.", 

299 exc_info=True) 

300 

301 

302###################### 

303# Extension system # 

304###################### 

305 

306 

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

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

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

310available globally. 

311 

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

313symbols to the global symbol table. 

314 

315 """ 

316 if globals_dict is None: 

317 globals_dict = builtins.__dict__ 

318 try: 

319 mod = importlib.import_module(module) 

320 if '__all__' in mod.__dict__: 

321 # import listed symbols 

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

323 if symb_list is not None: 

324 symb_list.append(name) 

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

326 else: 

327 # only import non-private symbols 

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

329 if _validate_local(name): 

330 if symb_list is not None: 

331 symb_list.append(name) 

332 globals_dict[name] = sym 

333 except Exception: 

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

335 

336 

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

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

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

340 available globally. 

341 

342 """ 

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

344 globals_dict=globals_dict, symb_list=symb_list) 

345 

346 

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

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

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

350 available globally. 

351 

352 """ 

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

354 globals_dict=globals_dict, symb_list=symb_list) 

355 

356 

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

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

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

360 functions available globally. 

361 

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

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

364 

365 """ 

366 try: 

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

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

369 globals_dict=globals_dict, symb_list=symb_list) 

370 except ImportError as e: 

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

372 try: 

373 load_layer(name, 

374 globals_dict=globals_dict, symb_list=symb_list) 

375 except ImportError: 

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

377 

378 

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

380 ret=False, # type: bool 

381 _debug=False # type: bool 

382 ): 

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

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

385 

386 :param name: filter to search the contribs 

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

388 printing it 

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

390 """ 

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

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

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

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

395 # or set the flag: 

396 # # scapy.contrib.description = skip 

397 # to skip the file 

398 if name is None: 

399 name = "*.py" 

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

401 name += ".py" 

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

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

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

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

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

407 else: 

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

409 iterator = glob.iglob(name) 

410 for f in iterator: 

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

412 if mod.startswith("__"): 

413 continue 

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

415 mod = mod[:-3] 

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

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

418 for line in fd: 

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

420 continue 

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

422 if p >= 0: 

423 p += 14 

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

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

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

427 desc[key] = value 

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

429 break 

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

431 results.append(desc) 

432 break 

433 if _debug: 

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

435 pass 

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

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

438 "contrib infos !" % mod) 

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

440 if ret: 

441 return results 

442 else: 

443 for desc in results: 

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

445 return None 

446 

447 

448def _scapy_prestart_builtins(): 

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

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

451 return { 

452 k: v 

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

454 if _validate_local(k) 

455 } 

456 

457 

458def _scapy_builtins(): 

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

460 """Load Scapy and return all builtins""" 

461 return { 

462 k: v 

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

464 if _validate_local(k) 

465 } 

466 

467 

468def _scapy_exts(): 

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

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

471 from scapy.config import conf 

472 res = {} 

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

474 if spec.default: 

475 mod = sys.modules[modname] 

476 res.update({ 

477 k: v 

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

479 if _validate_local(k) 

480 }) 

481 return res 

482 

483 

484@overload 

485def init_session(mydict, # type: Optional[Union[Dict[str, Any], None]] 

486 ret, # type: Literal[True] 

487 ): 

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

489 pass 

490 

491 

492@overload 

493def init_session(mydict=None, # type: Optional[Union[Dict[str, Any], None]] 

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

495 ): 

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

497 pass 

498 

499 

500def init_session(mydict=None, # type: Optional[Union[Dict[str, Any], None]] 

501 ret=False, # type: bool 

502 ): 

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

504 from scapy.config import conf 

505 

506 # Load Scapy 

507 scapy_builtins = _scapy_builtins() 

508 

509 # Load exts 

510 scapy_builtins.update(_scapy_exts()) 

511 

512 SESSION = {"conf": conf} # type: Dict[str, Any] 

513 SESSION.update(scapy_builtins) 

514 

515 if mydict is not None: 

516 SESSION.update(mydict) 

517 if ret: 

518 return SESSION 

519 return None 

520 

521 

522################ 

523# Main # 

524################ 

525 

526 

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

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

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

530to be used in the fancy banner. 

531 

532 """ 

533 _quote = quote.split(' ') 

534 max_len -= 6 

535 lines = [] 

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

537 

538 def _len(line): 

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

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

541 while _quote: 

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

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

544 continue 

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

546 cur_line = [] 

547 if cur_line: 

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

549 cur_line = [] 

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

551 return lines 

552 

553 

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

555 """ 

556 Generates the fancy Scapy banner 

557 

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

559 """ 

560 from scapy.config import conf 

561 from scapy.utils import get_terminal_width 

562 if mini is None: 

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

564 else: 

565 mini_banner = mini 

566 

567 the_logo = [ 

568 " ", 

569 " aSPY//YASa ", 

570 " apyyyyCY//////////YCa ", 

571 " sY//////YSpcs scpCY//Pp ", 

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

573 " AYAsAYYYYYYYY///Ps cY//S", 

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

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

576 " A//A cyP////C", 

577 " p///Ac sC///a", 

578 " P////YCpc A//A", 

579 " scccccp///pSP///p p//Y", 

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

581 " cayCyayP//Ya pY/Ya", 

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

583 " sc sccaCY//PCypaapyCP//YSs ", 

584 " spCPY//////YPSps ", 

585 " ccaacs ", 

586 " ", 

587 ] 

588 

589 # Used on mini screens 

590 the_logo_mini = [ 

591 " .SYPACCCSASYY ", 

592 "P /SCS/CCS ACS", 

593 " /A AC", 

594 " A/PS /SPPS", 

595 " YP (SC", 

596 " SPS/A. SC", 

597 " Y/PACC PP", 

598 " PY*AYC CAA", 

599 " YYCY//SCYP ", 

600 ] 

601 

602 the_banner = [ 

603 "", 

604 "", 

605 " |", 

606 " | Welcome to Scapy", 

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

608 " |", 

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

610 " |", 

611 " | Have fun!", 

612 " |", 

613 ] 

614 

615 if mini_banner: 

616 the_logo = the_logo_mini 

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

618 the_banner = [""] + the_banner + [""] 

619 else: 

620 quote, author = choice(QUOTES) 

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

622 the_banner.append(" |") 

623 return "\n".join( 

624 logo + banner for logo, banner in zip_longest( 

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

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

627 fillvalue="" 

628 ) 

629 ) 

630 

631 

632def interact(mydict=None, 

633 argv=None, 

634 mybanner=None, 

635 mybanneronly=False, 

636 loglevel=logging.INFO): 

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

638 """ 

639 Starts Scapy's console. 

640 """ 

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

642 warnings.simplefilter("always") 

643 

644 # Set interactive mode, load the color scheme 

645 from scapy.config import conf 

646 conf.interactive = True 

647 conf.color_theme = DefaultTheme() 

648 if loglevel is not None: 

649 conf.logLevel = loglevel 

650 

651 STARTUP_FILE = DEFAULT_STARTUP_FILE 

652 PRESTART_FILE = DEFAULT_PRESTART_FILE 

653 

654 if argv is None: 

655 argv = sys.argv 

656 

657 try: 

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

659 for opt, param in opts[0]: 

660 if opt == "-h": 

661 _usage() 

662 elif opt == "-H": 

663 conf.fancy_banner = False 

664 conf.verb = 1 

665 conf.logLevel = logging.WARNING 

666 elif opt == "-c": 

667 STARTUP_FILE = param 

668 elif opt == "-C": 

669 STARTUP_FILE = None 

670 elif opt == "-p": 

671 PRESTART_FILE = param 

672 elif opt == "-P": 

673 PRESTART_FILE = None 

674 elif opt == "-d": 

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

676 

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

678 raise getopt.GetoptError( 

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

680 ) 

681 

682 except getopt.GetoptError as msg: 

683 log_loading.error(msg) 

684 sys.exit(1) 

685 

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

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

688 

689 if PRESTART_FILE: 

690 _read_config_file( 

691 PRESTART_FILE, 

692 interactive=True, 

693 _locals=_scapy_prestart_builtins(), 

694 default=DEFAULT_PRESTART, 

695 ) 

696 

697 SESSION = init_session(mydict=mydict, ret=True) 

698 

699 if STARTUP_FILE: 

700 _read_config_file( 

701 STARTUP_FILE, 

702 interactive=True, 

703 _locals=SESSION 

704 ) 

705 

706 # Load extensions (Python 3.8 Only) 

707 if sys.version_info >= (3, 8): 

708 conf.exts.loadall() 

709 

710 if conf.fancy_banner: 

711 banner_text = get_fancy_banner() 

712 else: 

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

714 

715 # Make sure the history file has proper permissions 

716 try: 

717 if not pathlib.Path(conf.histfile).exists(): 

718 pathlib.Path(conf.histfile).touch() 

719 _check_perms(conf.histfile) 

720 except OSError: 

721 pass 

722 

723 # Configure interactive terminal 

724 

725 if conf.interactive_shell not in [ 

726 "ipython", 

727 "python", 

728 "ptpython", 

729 "ptipython", 

730 "bpython", 

731 "auto"]: 

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

733 conf.interactive_shell = "auto" 

734 

735 # Auto detect available shells. 

736 # Order: 

737 # 1. IPython 

738 # 2. bpython 

739 # 3. ptpython 

740 

741 _IMPORTS = { 

742 "ipython": ["IPython"], 

743 "bpython": ["bpython"], 

744 "ptpython": ["ptpython"], 

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

746 } 

747 

748 if conf.interactive_shell == "auto": 

749 # Auto detect 

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

751 try: 

752 importlib.import_module(imp) 

753 conf.interactive_shell = imp.lower() 

754 break 

755 except ImportError: 

756 continue 

757 else: 

758 log_loading.warning( 

759 "No alternative Python interpreters found ! " 

760 "Using standard Python shell instead." 

761 ) 

762 conf.interactive_shell = "python" 

763 

764 if conf.interactive_shell in _IMPORTS: 

765 # Check import 

766 for imp in _IMPORTS[conf.interactive_shell]: 

767 try: 

768 importlib.import_module(imp) 

769 except ImportError: 

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

771 conf.interactive_shell = "python" 

772 

773 # Default shell 

774 if conf.interactive_shell == "python": 

775 disabled = ["History"] 

776 if WINDOWS: 

777 disabled.append("Colors") 

778 conf.color_theme = BlackAndWhite() 

779 else: 

780 try: 

781 # Bad completer.. but better than nothing 

782 import rlcompleter 

783 import readline 

784 readline.set_completer( 

785 rlcompleter.Completer(namespace=SESSION).complete 

786 ) 

787 readline.parse_and_bind('tab: complete') 

788 except ImportError: 

789 disabled.insert(0, "AutoCompletion") 

790 # Display warning when using the default REPL 

791 log_loading.info( 

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

793 ",".join(disabled), 

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

795 ) 

796 ) 

797 

798 # ptpython configure function 

799 def ptpython_configure(repl): 

800 # type: (Any) -> None 

801 # Hide status bar 

802 repl.show_status_bar = False 

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

804 repl.complete_while_typing = False 

805 # Enable auto-suggestions 

806 repl.enable_auto_suggest = True 

807 # Disable exit confirmation 

808 repl.confirm_exit = False 

809 # Show signature 

810 repl.show_signature = True 

811 # Apply Scapy color theme: TODO 

812 # repl.install_ui_colorscheme("scapy", 

813 # Style.from_dict(_custom_ui_colorscheme)) 

814 # repl.use_ui_colorscheme("scapy") 

815 

816 # Extend banner text 

817 banner = banner_text 

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

819 import IPython 

820 if conf.interactive_shell == "ptipython": 

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

822 try: 

823 from importlib.metadata import version 

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

825 except ImportError: 

826 ptpython_version = "" 

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

828 else: 

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

830 elif conf.interactive_shell == "ptpython": 

831 try: 

832 from importlib.metadata import version 

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

834 except ImportError: 

835 ptpython_version = "" 

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

837 elif conf.interactive_shell == "bpython": 

838 import bpython 

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

840 

841 if mybanner is not None: 

842 if mybanneronly: 

843 banner = "" 

844 banner += "\n" 

845 banner += mybanner 

846 

847 # Start IPython or ptipython 

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

849 banner += "\n" 

850 if conf.interactive_shell == "ptipython": 

851 from ptpython.ipython import embed 

852 else: 

853 from IPython import embed 

854 try: 

855 from traitlets.config.loader import Config 

856 except ImportError: 

857 log_loading.warning( 

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

859 "available." 

860 ) 

861 try: 

862 embed( 

863 display_banner=False, 

864 user_ns=SESSION, 

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

866 ) 

867 except Exception: 

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

869 else: 

870 cfg = Config() 

871 try: 

872 from IPython import get_ipython 

873 if not get_ipython(): 

874 raise ImportError 

875 except ImportError: 

876 # Set "classic" prompt style when launched from 

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

878 # color+prompt style 

879 apply_ipython_style(shell=cfg.InteractiveShellEmbed) 

880 cfg.InteractiveShellEmbed.confirm_exit = False 

881 cfg.InteractiveShellEmbed.separate_in = u'' 

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

883 cfg.InteractiveShellEmbed.term_title = True 

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

885 conf.version) 

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

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

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

889 # yourself (#GH4056) :P 

890 cfg.Completer.use_jedi = conf.ipython_use_jedi 

891 else: 

892 cfg.InteractiveShellEmbed.term_title = False 

893 cfg.HistoryAccessor.hist_file = conf.histfile 

894 cfg.InteractiveShell.banner1 = banner 

895 if conf.verb < 2: 

896 cfg.InteractiveShellEmbed.enable_tip = False 

897 # configuration can thus be specified here. 

898 _kwargs = {} 

899 if conf.interactive_shell == "ptipython": 

900 _kwargs["configure"] = ptpython_configure 

901 try: 

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

903 except (AttributeError, TypeError): 

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

905 # Start ptpython 

906 elif conf.interactive_shell == "ptpython": 

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

908 # For instance: >>> IP() 

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

910 from ptpython.repl import embed 

911 # ptpython has no banner option 

912 banner += "\n" 

913 print(banner) 

914 embed( 

915 locals=SESSION, 

916 history_filename=conf.histfile, 

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

918 configure=ptpython_configure 

919 ) 

920 # Start bpython 

921 elif conf.interactive_shell == "bpython": 

922 from bpython.curtsies import main as embed 

923 embed( 

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

925 locals_=SESSION, 

926 banner=banner, 

927 welcome_message="" 

928 ) 

929 # Start Python 

930 elif conf.interactive_shell == "python": 

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

932 else: 

933 raise ValueError("Invalid conf.interactive_shell") 

934 

935 

936if __name__ == "__main__": 

937 interact()