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

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

2111 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""" 

7General utility functions. 

8""" 

9 

10 

11from decimal import Decimal 

12from io import StringIO 

13from itertools import zip_longest 

14from uuid import UUID 

15 

16import argparse 

17import array 

18import collections 

19import decimal 

20import difflib 

21import enum 

22import gzip 

23import inspect 

24import locale 

25import math 

26import os 

27import random 

28import re 

29import shutil 

30import socket 

31import struct 

32import subprocess 

33import sys 

34import tempfile 

35import threading 

36import time 

37import traceback 

38import warnings 

39import zlib 

40 

41from scapy.config import conf 

42from scapy.consts import DARWIN, OPENBSD, WINDOWS 

43from scapy.data import MTU, DLT_EN10MB, DLT_RAW 

44from scapy.compat import ( 

45 plain_str, 

46 chb, 

47 hex_bytes, 

48 bytes_encode, 

49) 

50from scapy.error import ( 

51 log_interactive, 

52 log_runtime, 

53 Scapy_Exception, 

54 warning, 

55) 

56from scapy.pton_ntop import inet_pton 

57 

58# Typing imports 

59from typing import ( 

60 Any, 

61 AnyStr, 

62 Callable, 

63 cast, 

64 Dict, 

65 IO, 

66 Iterator, 

67 List, 

68 Optional, 

69 overload, 

70 Tuple, 

71 TYPE_CHECKING, 

72 Type, 

73 Union, 

74) 

75from scapy.compat import ( 

76 DecoratorCallable, 

77 Literal, 

78) 

79 

80if TYPE_CHECKING: 

81 from scapy.packet import Packet 

82 from scapy.plist import _PacketIterable, PacketList 

83 from scapy.supersocket import SuperSocket 

84 import prompt_toolkit 

85 

86_ByteStream = Union[IO[bytes], gzip.GzipFile] 

87 

88########### 

89# Tools # 

90########### 

91 

92 

93def issubtype(x, # type: Any 

94 t, # type: Union[type, str] 

95 ): 

96 # type: (...) -> bool 

97 """issubtype(C, B) -> bool 

98 

99 Return whether C is a class and if it is a subclass of class B. 

100 When using a tuple as the second argument issubtype(X, (A, B, ...)), 

101 is a shortcut for issubtype(X, A) or issubtype(X, B) or ... (etc.). 

102 """ 

103 if isinstance(t, str): 

104 return t in (z.__name__ for z in x.__bases__) 

105 if isinstance(x, type) and issubclass(x, t): 

106 return True 

107 return False 

108 

109 

110_Decimal = Union[Decimal, int] 

111 

112 

113class EDecimal(Decimal): 

114 """Extended Decimal 

115 

116 This implements arithmetic and comparison with float for 

117 backward compatibility 

118 """ 

119 

120 def __add__(self, other, context=None): 

121 # type: (_Decimal, Any) -> EDecimal 

122 return EDecimal(Decimal.__add__(self, Decimal(other))) 

123 

124 def __radd__(self, other): 

125 # type: (_Decimal) -> EDecimal 

126 return EDecimal(Decimal.__add__(self, Decimal(other))) 

127 

128 def __sub__(self, other): 

129 # type: (_Decimal) -> EDecimal 

130 return EDecimal(Decimal.__sub__(self, Decimal(other))) 

131 

132 def __rsub__(self, other): 

133 # type: (_Decimal) -> EDecimal 

134 return EDecimal(Decimal.__rsub__(self, Decimal(other))) 

135 

136 def __mul__(self, other): 

137 # type: (_Decimal) -> EDecimal 

138 return EDecimal(Decimal.__mul__(self, Decimal(other))) 

139 

140 def __rmul__(self, other): 

141 # type: (_Decimal) -> EDecimal 

142 return EDecimal(Decimal.__mul__(self, Decimal(other))) 

143 

144 def __truediv__(self, other): 

145 # type: (_Decimal) -> EDecimal 

146 return EDecimal(Decimal.__truediv__(self, Decimal(other))) 

147 

148 def __floordiv__(self, other): 

149 # type: (_Decimal) -> EDecimal 

150 return EDecimal(Decimal.__floordiv__(self, Decimal(other))) 

151 

152 def __divmod__(self, other): 

153 # type: (_Decimal) -> Tuple[EDecimal, EDecimal] 

154 r = Decimal.__divmod__(self, Decimal(other)) 

155 return EDecimal(r[0]), EDecimal(r[1]) 

156 

157 def __mod__(self, other): 

158 # type: (_Decimal) -> EDecimal 

159 return EDecimal(Decimal.__mod__(self, Decimal(other))) 

160 

161 def __rmod__(self, other): 

162 # type: (_Decimal) -> EDecimal 

163 return EDecimal(Decimal.__rmod__(self, Decimal(other))) 

164 

165 def __pow__(self, other, modulo=None): 

166 # type: (_Decimal, Optional[_Decimal]) -> EDecimal 

167 return EDecimal(Decimal.__pow__(self, Decimal(other), modulo)) 

168 

169 def __eq__(self, other): 

170 # type: (Any) -> bool 

171 if isinstance(other, Decimal): 

172 return super(EDecimal, self).__eq__(other) 

173 else: 

174 return bool(float(self) == other) 

175 

176 def normalize(self, precision): # type: ignore 

177 # type: (int) -> EDecimal 

178 with decimal.localcontext() as ctx: 

179 ctx.prec = precision 

180 return EDecimal(super(EDecimal, self).normalize(ctx)) 

181 

182 

183@overload 

184def get_temp_file(keep, autoext, fd): 

185 # type: (bool, str, Literal[True]) -> IO[bytes] 

186 pass 

187 

188 

189@overload 

190def get_temp_file(keep=False, autoext="", fd=False): 

191 # type: (bool, str, Literal[False]) -> str 

192 pass 

193 

194 

195def get_temp_file(keep=False, autoext="", fd=False): 

196 # type: (bool, str, bool) -> Union[IO[bytes], str] 

197 """Creates a temporary file. 

198 

199 :param keep: If False, automatically delete the file when Scapy exits. 

200 :param autoext: Suffix to add to the generated file name. 

201 :param fd: If True, this returns a file-like object with the temporary 

202 file opened. If False (default), this returns a file path. 

203 """ 

204 f = tempfile.NamedTemporaryFile(prefix="scapy", suffix=autoext, 

205 delete=False) 

206 if not keep: 

207 conf.temp_files.append(f.name) 

208 

209 if fd: 

210 return f 

211 else: 

212 # Close the file so something else can take it. 

213 f.close() 

214 return f.name 

215 

216 

217def get_temp_dir(keep=False): 

218 # type: (bool) -> str 

219 """Creates a temporary file, and returns its name. 

220 

221 :param keep: If False (default), the directory will be recursively 

222 deleted when Scapy exits. 

223 :return: A full path to a temporary directory. 

224 """ 

225 

226 dname = tempfile.mkdtemp(prefix="scapy") 

227 

228 if not keep: 

229 conf.temp_files.append(dname) 

230 

231 return dname 

232 

233 

234def _create_fifo() -> Tuple[str, Any]: 

235 """Creates a temporary fifo. 

236 

237 You must then use open_fifo() on the server_fd once 

238 the client is connected to use it. 

239 

240 :returns: (client_file, server_fd) 

241 """ 

242 if WINDOWS: 

243 from scapy.arch.windows.structures import _get_win_fifo 

244 return _get_win_fifo() 

245 else: 

246 f = get_temp_file() 

247 os.unlink(f) 

248 os.mkfifo(f, 0o600) 

249 return f, f 

250 

251 

252def _open_fifo(fd: Any, mode: str = "rb") -> IO[bytes]: 

253 """Open the server_fd (see create_fifo) 

254 """ 

255 if WINDOWS: 

256 from scapy.arch.windows.structures import _win_fifo_open 

257 return _win_fifo_open(fd) 

258 else: 

259 return open(fd, mode) 

260 

261 

262def sane(x, color=False): 

263 # type: (bytes, bool) -> str 

264 r = "" 

265 for i in x: 

266 j = i 

267 if (j < 32) or (j >= 127): 

268 if color: 

269 r += conf.color_theme.not_printable(".") 

270 else: 

271 r += "." 

272 else: 

273 r += chr(j) 

274 return r 

275 

276 

277@conf.commands.register 

278def restart(): 

279 # type: () -> None 

280 """Restarts scapy""" 

281 if not conf.interactive or not os.path.isfile(sys.argv[0]): 

282 raise OSError("Scapy was not started from console") 

283 if WINDOWS: 

284 res_code = 1 

285 try: 

286 res_code = subprocess.call([sys.executable] + sys.argv) 

287 finally: 

288 os._exit(res_code) 

289 os.execv(sys.executable, [sys.executable] + sys.argv) 

290 

291 

292def lhex(x): 

293 # type: (Any) -> str 

294 from scapy.volatile import VolatileValue 

295 if isinstance(x, VolatileValue): 

296 return repr(x) 

297 if isinstance(x, int): 

298 return hex(x) 

299 if isinstance(x, tuple): 

300 return "(%s)" % ", ".join(lhex(v) for v in x) 

301 if isinstance(x, list): 

302 return "[%s]" % ", ".join(lhex(v) for v in x) 

303 return str(x) 

304 

305 

306@conf.commands.register 

307def hexdump(p, dump=False): 

308 # type: (Union[Packet, AnyStr], bool) -> Optional[str] 

309 """Build a tcpdump like hexadecimal view 

310 

311 :param p: a Packet 

312 :param dump: define if the result must be printed or returned in a variable 

313 :return: a String only when dump=True 

314 """ 

315 s = "" 

316 x = bytes_encode(p) 

317 x_len = len(x) 

318 i = 0 

319 while i < x_len: 

320 s += "%04x " % i 

321 for j in range(16): 

322 if i + j < x_len: 

323 s += "%02X " % x[i + j] 

324 else: 

325 s += " " 

326 s += " %s\n" % sane(x[i:i + 16], color=True) 

327 i += 16 

328 # remove trailing \n 

329 s = s[:-1] if s.endswith("\n") else s 

330 if dump: 

331 return s 

332 else: 

333 print(s) 

334 return None 

335 

336 

337@conf.commands.register 

338def linehexdump(p, onlyasc=0, onlyhex=0, dump=False): 

339 # type: (Union[Packet, AnyStr], int, int, bool) -> Optional[str] 

340 """Build an equivalent view of hexdump() on a single line 

341 

342 Note that setting both onlyasc and onlyhex to 1 results in a empty output 

343 

344 :param p: a Packet 

345 :param onlyasc: 1 to display only the ascii view 

346 :param onlyhex: 1 to display only the hexadecimal view 

347 :param dump: print the view if False 

348 :return: a String only when dump=True 

349 """ 

350 s = "" 

351 s = hexstr(p, onlyasc=onlyasc, onlyhex=onlyhex, color=not dump) 

352 if dump: 

353 return s 

354 else: 

355 print(s) 

356 return None 

357 

358 

359@conf.commands.register 

360def chexdump(p, dump=False): 

361 # type: (Union[Packet, AnyStr], bool) -> Optional[str] 

362 """Build a per byte hexadecimal representation 

363 

364 Example: 

365 >>> chexdump(IP()) 

366 0x45, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x7c, 0xe7, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01 # noqa: E501 

367 

368 :param p: a Packet 

369 :param dump: print the view if False 

370 :return: a String only if dump=True 

371 """ 

372 x = bytes_encode(p) 

373 s = ", ".join("%#04x" % x for x in x) 

374 if dump: 

375 return s 

376 else: 

377 print(s) 

378 return None 

379 

380 

381@conf.commands.register 

382def hexstr(p, onlyasc=0, onlyhex=0, color=False): 

383 # type: (Union[Packet, AnyStr], int, int, bool) -> str 

384 """Build a fancy tcpdump like hex from bytes.""" 

385 x = bytes_encode(p) 

386 s = [] 

387 if not onlyasc: 

388 s.append(" ".join("%02X" % b for b in x)) 

389 if not onlyhex: 

390 s.append(sane(x, color=color)) 

391 return " ".join(s) 

392 

393 

394def repr_hex(s): 

395 # type: (bytes) -> str 

396 """ Convert provided bitstring to a simple string of hex digits """ 

397 return "".join("%02x" % x for x in s) 

398 

399 

400@conf.commands.register 

401def hexdiff( 

402 a: Union['Packet', AnyStr], 

403 b: Union['Packet', AnyStr], 

404 algo: Optional[str] = None, 

405 autojunk: bool = False, 

406) -> None: 

407 """ 

408 Show differences between 2 binary strings, Packets... 

409 

410 Available algorithms: 

411 - wagnerfischer: Use the Wagner and Fischer algorithm to compute the 

412 Levenstein distance between the strings then backtrack. 

413 - difflib: Use the difflib.SequenceMatcher implementation. This based on a 

414 modified version of the Ratcliff and Obershelp algorithm. 

415 This is much faster, but far less accurate. 

416 https://docs.python.org/3.8/library/difflib.html#difflib.SequenceMatcher 

417 

418 :param a: 

419 :param b: The binary strings, packets... to compare 

420 :param algo: Force the algo to be 'wagnerfischer' or 'difflib'. 

421 By default, this is chosen depending on the complexity, optimistically 

422 preferring wagnerfischer unless really necessary. 

423 :param autojunk: (difflib only) See difflib documentation. 

424 """ 

425 xb = bytes_encode(a) 

426 yb = bytes_encode(b) 

427 

428 if algo is None: 

429 # Choose the best algorithm 

430 complexity = len(xb) * len(yb) 

431 if complexity < 1e7: 

432 # Comparing two (non-jumbos) Ethernet packets is ~2e6 which is manageable. 

433 # Anything much larger than this shouldn't be attempted by default. 

434 algo = "wagnerfischer" 

435 if complexity > 1e6: 

436 log_interactive.info( 

437 "Complexity is a bit high. hexdiff will take a few seconds." 

438 ) 

439 else: 

440 algo = "difflib" 

441 

442 backtrackx = [] 

443 backtracky = [] 

444 

445 if algo == "wagnerfischer": 

446 xb = xb[::-1] 

447 yb = yb[::-1] 

448 

449 # costs for the 3 operations 

450 INSERT = 1 

451 DELETE = 1 

452 SUBST = 1 

453 

454 # Typically, d[i,j] will hold the distance between 

455 # the first i characters of xb and the first j characters of yb. 

456 # We change the Wagner Fischer to also store pointers to all 

457 # the intermediate steps taken while calculating the Levenstein distance. 

458 d = {(-1, -1): (0, (-1, -1))} 

459 for j in range(len(yb)): 

460 d[-1, j] = (j + 1) * INSERT, (-1, j - 1) 

461 for i in range(len(xb)): 

462 d[i, -1] = (i + 1) * INSERT + 1, (i - 1, -1) 

463 

464 # Compute the Levenstein distance between the two strings, but 

465 # store all the steps to be able to backtrack at the end. 

466 for j in range(len(yb)): 

467 for i in range(len(xb)): 

468 d[i, j] = min( 

469 (d[i - 1, j - 1][0] + SUBST * (xb[i] != yb[j]), (i - 1, j - 1)), 

470 (d[i - 1, j][0] + DELETE, (i - 1, j)), 

471 (d[i, j - 1][0] + INSERT, (i, j - 1)), 

472 ) 

473 

474 # Iterate through the steps backwards to create the diff 

475 i = len(xb) - 1 

476 j = len(yb) - 1 

477 while not (i == j == -1): 

478 i2, j2 = d[i, j][1] 

479 backtrackx.append(xb[i2 + 1:i + 1]) 

480 backtracky.append(yb[j2 + 1:j + 1]) 

481 i, j = i2, j2 

482 elif algo == "difflib": 

483 sm = difflib.SequenceMatcher(a=xb, b=yb, autojunk=autojunk) 

484 xarr = [xb[i:i + 1] for i in range(len(xb))] 

485 yarr = [yb[i:i + 1] for i in range(len(yb))] 

486 # Iterate through opcodes to build the backtrack 

487 for opcode in sm.get_opcodes(): 

488 typ, x0, x1, y0, y1 = opcode 

489 if typ == 'delete': 

490 backtrackx += xarr[x0:x1] 

491 backtracky += [b''] * (x1 - x0) 

492 elif typ == 'insert': 

493 backtrackx += [b''] * (y1 - y0) 

494 backtracky += yarr[y0:y1] 

495 elif typ in ['equal', 'replace']: 

496 backtrackx += xarr[x0:x1] 

497 backtracky += yarr[y0:y1] 

498 # Some lines may have been considered as junk. Check the sizes 

499 if autojunk: 

500 lbx = len(backtrackx) 

501 lby = len(backtracky) 

502 backtrackx += [b''] * (max(lbx, lby) - lbx) 

503 backtracky += [b''] * (max(lbx, lby) - lby) 

504 else: 

505 raise ValueError("Unknown algorithm '%s'" % algo) 

506 

507 # Print the diff 

508 

509 x = y = i = 0 

510 colorize: Dict[int, Callable[[str], str]] = { 

511 0: lambda x: x, 

512 -1: conf.color_theme.left, 

513 1: conf.color_theme.right 

514 } 

515 

516 dox = 1 

517 doy = 0 

518 btx_len = len(backtrackx) 

519 while i < btx_len: 

520 linex = backtrackx[i:i + 16] 

521 liney = backtracky[i:i + 16] 

522 xx = sum(len(k) for k in linex) 

523 yy = sum(len(k) for k in liney) 

524 if dox and not xx: 

525 dox = 0 

526 doy = 1 

527 if dox and linex == liney: 

528 doy = 1 

529 

530 if dox: 

531 xd = y 

532 j = 0 

533 while j < len(linex) and not linex[j]: 

534 j += 1 

535 xd -= 1 

536 print(colorize[doy - dox]("%04x" % xd), end=' ') 

537 x += xx 

538 line = linex 

539 else: 

540 print(" ", end=' ') 

541 if doy: 

542 yd = y 

543 j = 0 

544 while j < len(liney) and not liney[j]: 

545 j += 1 

546 yd -= 1 

547 print(colorize[doy - dox]("%04x" % yd), end=' ') 

548 y += yy 

549 line = liney 

550 else: 

551 print(" ", end=' ') 

552 

553 print(" ", end=' ') 

554 

555 cl = "" 

556 for j in range(16): 

557 if i + j < min(len(backtrackx), len(backtracky)): 

558 if line[j]: 

559 col = colorize[(linex[j] != liney[j]) * (doy - dox)] 

560 print(col("%02X" % line[j][0]), end=' ') 

561 if linex[j] == liney[j]: 

562 cl += sane(line[j], color=True) 

563 else: 

564 cl += col(sane(line[j])) 

565 else: 

566 print(" ", end=' ') 

567 cl += " " 

568 else: 

569 print(" ", end=' ') 

570 if j == 7: 

571 print("", end=' ') 

572 

573 print(" ", cl) 

574 

575 if doy or not yy: 

576 doy = 0 

577 dox = 1 

578 i += 16 

579 else: 

580 if yy: 

581 dox = 0 

582 doy = 1 

583 else: 

584 i += 16 

585 

586 

587if struct.pack("H", 1) == b"\x00\x01": # big endian 

588 checksum_endian_transform = lambda chk: chk # type: Callable[[int], int] 

589else: 

590 checksum_endian_transform = lambda chk: ((chk >> 8) & 0xff) | chk << 8 

591 

592 

593def checksum(pkt): 

594 # type: (bytes) -> int 

595 if len(pkt) % 2 == 1: 

596 pkt += b"\0" 

597 s = sum(array.array("H", pkt)) 

598 s = (s >> 16) + (s & 0xffff) 

599 s += s >> 16 

600 s = ~s 

601 return checksum_endian_transform(s) & 0xffff 

602 

603 

604def _fletcher16(charbuf): 

605 # type: (bytes) -> Tuple[int, int] 

606 # This is based on the GPLed C implementation in Zebra <http://www.zebra.org/> # noqa: E501 

607 c0 = c1 = 0 

608 for char in charbuf: 

609 c0 += char 

610 c1 += c0 

611 

612 c0 %= 255 

613 c1 %= 255 

614 return (c0, c1) 

615 

616 

617@conf.commands.register 

618def fletcher16_checksum(binbuf): 

619 # type: (bytes) -> int 

620 """Calculates Fletcher-16 checksum of the given buffer. 

621 

622 Note: 

623 If the buffer contains the two checkbytes derived from the Fletcher-16 checksum # noqa: E501 

624 the result of this function has to be 0. Otherwise the buffer has been corrupted. # noqa: E501 

625 """ 

626 (c0, c1) = _fletcher16(binbuf) 

627 return (c1 << 8) | c0 

628 

629 

630@conf.commands.register 

631def fletcher16_checkbytes(binbuf, offset): 

632 # type: (bytes, int) -> bytes 

633 """Calculates the Fletcher-16 checkbytes returned as 2 byte binary-string. 

634 

635 Including the bytes into the buffer (at the position marked by offset) the # noqa: E501 

636 global Fletcher-16 checksum of the buffer will be 0. Thus it is easy to verify # noqa: E501 

637 the integrity of the buffer on the receiver side. 

638 

639 For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B. # noqa: E501 

640 """ 

641 

642 # This is based on the GPLed C implementation in Zebra <http://www.zebra.org/> # noqa: E501 

643 if len(binbuf) < offset: 

644 raise Exception("Packet too short for checkbytes %d" % len(binbuf)) 

645 

646 binbuf = binbuf[:offset] + b"\x00\x00" + binbuf[offset + 2:] 

647 (c0, c1) = _fletcher16(binbuf) 

648 

649 x = ((len(binbuf) - offset - 1) * c0 - c1) % 255 

650 

651 if (x <= 0): 

652 x += 255 

653 

654 y = 510 - c0 - x 

655 

656 if (y > 255): 

657 y -= 255 

658 return chb(x) + chb(y) 

659 

660 

661def mac2str(mac): 

662 # type: (str) -> bytes 

663 return b"".join(chb(int(x, 16)) for x in plain_str(mac).split(':')) 

664 

665 

666def valid_mac(mac): 

667 # type: (str) -> bool 

668 try: 

669 return len(mac2str(mac)) == 6 

670 except ValueError: 

671 pass 

672 return False 

673 

674 

675def str2mac(s): 

676 # type: (bytes) -> str 

677 if isinstance(s, str): 

678 return ("%02x:" * len(s))[:-1] % tuple(map(ord, s)) 

679 return ("%02x:" * len(s))[:-1] % tuple(s) 

680 

681 

682def randstring(length): 

683 # type: (int) -> bytes 

684 """ 

685 Returns a random string of length (length >= 0) 

686 """ 

687 return b"".join(struct.pack('B', random.randint(0, 255)) 

688 for _ in range(length)) 

689 

690 

691def zerofree_randstring(length): 

692 # type: (int) -> bytes 

693 """ 

694 Returns a random string of length (length >= 0) without zero in it. 

695 """ 

696 return b"".join(struct.pack('B', random.randint(1, 255)) 

697 for _ in range(length)) 

698 

699 

700def stror(s1, s2): 

701 # type: (bytes, bytes) -> bytes 

702 """ 

703 Returns the binary OR of the 2 provided strings s1 and s2. s1 and s2 

704 must be of same length. 

705 """ 

706 return b"".join(map(lambda x, y: struct.pack("!B", x | y), s1, s2)) 

707 

708 

709def strxor(s1, s2): 

710 # type: (bytes, bytes) -> bytes 

711 """ 

712 Returns the binary XOR of the 2 provided strings s1 and s2. s1 and s2 

713 must be of same length. 

714 """ 

715 return b"".join(map(lambda x, y: struct.pack("!B", x ^ y), s1, s2)) 

716 

717 

718def strand(s1, s2): 

719 # type: (bytes, bytes) -> bytes 

720 """ 

721 Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2 

722 must be of same length. 

723 """ 

724 return b"".join(map(lambda x, y: struct.pack("!B", x & y), s1, s2)) 

725 

726 

727def strrot(s1, count, right=True): 

728 # type: (bytes, int, bool) -> bytes 

729 """ 

730 Rotate the binary by 'count' bytes 

731 """ 

732 off = count % len(s1) 

733 if right: 

734 return s1[-off:] + s1[:-off] 

735 else: 

736 return s1[off:] + s1[:off] 

737 

738 

739# Workaround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470 # noqa: E501 

740try: 

741 socket.inet_aton("255.255.255.255") 

742except socket.error: 

743 def inet_aton(ip_string): 

744 # type: (str) -> bytes 

745 if ip_string == "255.255.255.255": 

746 return b"\xff" * 4 

747 else: 

748 return socket.inet_aton(ip_string) 

749else: 

750 inet_aton = socket.inet_aton # type: ignore 

751 

752inet_ntoa = socket.inet_ntoa 

753 

754 

755def atol(x): 

756 # type: (str) -> int 

757 try: 

758 ip = inet_aton(x) 

759 except socket.error: 

760 raise ValueError("Bad IP format: %s" % x) 

761 return cast(int, struct.unpack("!I", ip)[0]) 

762 

763 

764def valid_ip(addr): 

765 # type: (str) -> bool 

766 try: 

767 addr = plain_str(addr) 

768 except UnicodeDecodeError: 

769 return False 

770 try: 

771 atol(addr) 

772 except (OSError, ValueError, socket.error): 

773 return False 

774 return True 

775 

776 

777def valid_net(addr): 

778 # type: (str) -> bool 

779 try: 

780 addr = plain_str(addr) 

781 except UnicodeDecodeError: 

782 return False 

783 if '/' in addr: 

784 ip, mask = addr.split('/', 1) 

785 return valid_ip(ip) and mask.isdigit() and 0 <= int(mask) <= 32 

786 return valid_ip(addr) 

787 

788 

789def valid_ip6(addr): 

790 # type: (str) -> bool 

791 try: 

792 addr = plain_str(addr) 

793 except UnicodeDecodeError: 

794 return False 

795 try: 

796 inet_pton(socket.AF_INET6, addr) 

797 except socket.error: 

798 return False 

799 return True 

800 

801 

802def valid_net6(addr): 

803 # type: (str) -> bool 

804 try: 

805 addr = plain_str(addr) 

806 except UnicodeDecodeError: 

807 return False 

808 if '/' in addr: 

809 ip, mask = addr.split('/', 1) 

810 return valid_ip6(ip) and mask.isdigit() and 0 <= int(mask) <= 128 

811 return valid_ip6(addr) 

812 

813 

814def ltoa(x): 

815 # type: (int) -> str 

816 return inet_ntoa(struct.pack("!I", x & 0xffffffff)) 

817 

818 

819def itom(x): 

820 # type: (int) -> int 

821 return (0xffffffff00000000 >> x) & 0xffffffff 

822 

823 

824def in4_cidr2mask(m): 

825 # type: (int) -> bytes 

826 """ 

827 Return the mask (bitstring) associated with provided length 

828 value. For instance if function is called on 20, return value is 

829 b'\xff\xff\xf0\x00'. 

830 """ 

831 if m > 32 or m < 0: 

832 raise Scapy_Exception("value provided to in4_cidr2mask outside [0, 32] domain (%d)" % m) # noqa: E501 

833 

834 return strxor( 

835 b"\xff" * 4, 

836 struct.pack(">I", 2**(32 - m) - 1) 

837 ) 

838 

839 

840def in4_isincluded(addr, prefix, mask): 

841 # type: (str, str, int) -> bool 

842 """ 

843 Returns True when 'addr' belongs to prefix/mask. False otherwise. 

844 """ 

845 temp = inet_pton(socket.AF_INET, addr) 

846 pref = in4_cidr2mask(mask) 

847 zero = inet_pton(socket.AF_INET, prefix) 

848 return zero == strand(temp, pref) 

849 

850 

851def in4_ismaddr(str): 

852 # type: (str) -> bool 

853 """ 

854 Returns True if provided address in printable format belongs to 

855 allocated Multicast address space (224.0.0.0/4). 

856 """ 

857 return in4_isincluded(str, "224.0.0.0", 4) 

858 

859 

860def in4_ismlladdr(str): 

861 # type: (str) -> bool 

862 """ 

863 Returns True if address belongs to link-local multicast address 

864 space (224.0.0.0/24) 

865 """ 

866 return in4_isincluded(str, "224.0.0.0", 24) 

867 

868 

869def in4_ismgladdr(str): 

870 # type: (str) -> bool 

871 """ 

872 Returns True if address belongs to global multicast address 

873 space (224.0.1.0-238.255.255.255). 

874 """ 

875 return ( 

876 in4_isincluded(str, "224.0.0.0", 4) and 

877 not in4_isincluded(str, "224.0.0.0", 24) and 

878 not in4_isincluded(str, "239.0.0.0", 8) 

879 ) 

880 

881 

882def in4_ismlsaddr(str): 

883 # type: (str) -> bool 

884 """ 

885 Returns True if address belongs to limited scope multicast address 

886 space (239.0.0.0/8). 

887 """ 

888 return in4_isincluded(str, "239.0.0.0", 8) 

889 

890 

891def in4_isaddrllallnodes(str): 

892 # type: (str) -> bool 

893 """ 

894 Returns True if address is the link-local all-nodes multicast 

895 address (224.0.0.1). 

896 """ 

897 return (inet_pton(socket.AF_INET, "224.0.0.1") == 

898 inet_pton(socket.AF_INET, str)) 

899 

900 

901def in4_getnsmac(a): 

902 # type: (bytes) -> str 

903 """ 

904 Return the multicast mac address associated with provided 

905 IPv4 address. Passed address must be in network format. 

906 """ 

907 

908 return "01:00:5e:%.2x:%.2x:%.2x" % (a[1] & 0x7f, a[2], a[3]) 

909 

910 

911def decode_locale_str(x): 

912 # type: (bytes) -> str 

913 """ 

914 Decode bytes into a string using the system locale. 

915 Useful on Windows where it can be unusual (e.g. cp1252) 

916 """ 

917 return x.decode(encoding=locale.getlocale()[1] or "utf-8", errors="replace") 

918 

919 

920class ContextManagerSubprocess(object): 

921 """ 

922 Context manager that eases checking for unknown command, without 

923 crashing. 

924 

925 Example: 

926 >>> with ContextManagerSubprocess("tcpdump"): 

927 >>> subprocess.Popen(["tcpdump", "--version"]) 

928 ERROR: Could not execute tcpdump, is it installed? 

929 

930 """ 

931 

932 def __init__(self, prog, suppress=True): 

933 # type: (str, bool) -> None 

934 self.prog = prog 

935 self.suppress = suppress 

936 

937 def __enter__(self): 

938 # type: () -> None 

939 pass 

940 

941 def __exit__(self, 

942 exc_type, # type: Optional[type] 

943 exc_value, # type: Optional[Exception] 

944 traceback, # type: Optional[Any] 

945 ): 

946 # type: (...) -> Optional[bool] 

947 if exc_value is None or exc_type is None: 

948 return None 

949 # Errored 

950 if isinstance(exc_value, EnvironmentError): 

951 msg = "Could not execute %s, is it installed?" % self.prog 

952 else: 

953 msg = "%s: execution failed (%s)" % ( 

954 self.prog, 

955 exc_type.__class__.__name__ 

956 ) 

957 if not self.suppress: 

958 raise exc_type(msg) 

959 log_runtime.error(msg, exc_info=True) 

960 return True # Suppress the exception 

961 

962 

963class ContextManagerCaptureOutput(object): 

964 """ 

965 Context manager that intercept the console's output. 

966 

967 Example: 

968 >>> with ContextManagerCaptureOutput() as cmco: 

969 ... print("hey") 

970 ... assert cmco.get_output() == "hey" 

971 """ 

972 

973 def __init__(self): 

974 # type: () -> None 

975 self.result_export_object = "" 

976 

977 def __enter__(self): 

978 # type: () -> ContextManagerCaptureOutput 

979 from unittest import mock 

980 

981 def write(s, decorator=self): 

982 # type: (str, ContextManagerCaptureOutput) -> None 

983 decorator.result_export_object += s 

984 mock_stdout = mock.Mock() 

985 mock_stdout.write = write 

986 self.bck_stdout = sys.stdout 

987 sys.stdout = mock_stdout 

988 return self 

989 

990 def __exit__(self, *exc): 

991 # type: (*Any) -> Literal[False] 

992 sys.stdout = self.bck_stdout 

993 return False 

994 

995 def get_output(self, eval_bytes=False): 

996 # type: (bool) -> str 

997 if self.result_export_object.startswith("b'") and eval_bytes: 

998 return plain_str(eval(self.result_export_object)) 

999 return self.result_export_object 

1000 

1001 

1002def do_graph( 

1003 graph, # type: str 

1004 prog=None, # type: Optional[str] 

1005 format=None, # type: Optional[str] 

1006 target=None, # type: Optional[Union[IO[bytes], str]] 

1007 type=None, # type: Optional[str] 

1008 string=None, # type: Optional[bool] 

1009 options=None # type: Optional[List[str]] 

1010): 

1011 # type: (...) -> Optional[str] 

1012 """Processes graph description using an external software. 

1013 This method is used to convert a graphviz format to an image. 

1014 

1015 :param graph: GraphViz graph description 

1016 :param prog: which graphviz program to use 

1017 :param format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" 

1018 option 

1019 :param string: if not None, simply return the graph string 

1020 :param target: filename or redirect. Defaults pipe to Imagemagick's 

1021 display program 

1022 :param options: options to be passed to prog 

1023 """ 

1024 

1025 if format is None: 

1026 format = "svg" 

1027 if string: 

1028 return graph 

1029 if type is not None: 

1030 warnings.warn( 

1031 "type is deprecated, and was renamed format", 

1032 DeprecationWarning 

1033 ) 

1034 format = type 

1035 if prog is None: 

1036 prog = conf.prog.dot 

1037 start_viewer = False 

1038 if target is None: 

1039 if WINDOWS: 

1040 target = get_temp_file(autoext="." + format) 

1041 start_viewer = True 

1042 else: 

1043 with ContextManagerSubprocess(conf.prog.display): 

1044 target = subprocess.Popen([conf.prog.display], 

1045 stdin=subprocess.PIPE).stdin 

1046 if format is not None: 

1047 format = "-T%s" % format 

1048 if isinstance(target, str): 

1049 if target.startswith('|'): 

1050 target = subprocess.Popen(target[1:].lstrip(), shell=True, 

1051 stdin=subprocess.PIPE).stdin 

1052 elif target.startswith('>'): 

1053 target = open(target[1:].lstrip(), "wb") 

1054 else: 

1055 target = open(os.path.abspath(target), "wb") 

1056 target = cast(IO[bytes], target) 

1057 proc = subprocess.Popen( 

1058 "\"%s\" %s %s" % (prog, options or "", format or ""), 

1059 shell=True, stdin=subprocess.PIPE, stdout=target, 

1060 stderr=subprocess.PIPE 

1061 ) 

1062 _, stderr = proc.communicate(bytes_encode(graph)) 

1063 if proc.returncode != 0: 

1064 raise OSError( 

1065 "GraphViz call failed (is it installed?):\n" + 

1066 plain_str(stderr) 

1067 ) 

1068 try: 

1069 target.close() 

1070 except Exception: 

1071 pass 

1072 if start_viewer: 

1073 # Workaround for file not found error: We wait until tempfile is written. # noqa: E501 

1074 waiting_start = time.time() 

1075 while not os.path.exists(target.name): 

1076 time.sleep(0.1) 

1077 if time.time() - waiting_start > 3: 

1078 warning("Temporary file '%s' could not be written. Graphic will not be displayed.", tempfile) # noqa: E501 

1079 break 

1080 else: 

1081 if WINDOWS and conf.prog.display == conf.prog._default: 

1082 os.startfile(target.name) 

1083 else: 

1084 with ContextManagerSubprocess(conf.prog.display): 

1085 subprocess.Popen([conf.prog.display, target.name]) 

1086 return None 

1087 

1088 

1089_TEX_TR = { 

1090 "{": "{\\tt\\char123}", 

1091 "}": "{\\tt\\char125}", 

1092 "\\": "{\\tt\\char92}", 

1093 "^": "\\^{}", 

1094 "$": "\\$", 

1095 "#": "\\#", 

1096 "_": "\\_", 

1097 "&": "\\&", 

1098 "%": "\\%", 

1099 "|": "{\\tt\\char124}", 

1100 "~": "{\\tt\\char126}", 

1101 "<": "{\\tt\\char60}", 

1102 ">": "{\\tt\\char62}", 

1103} 

1104 

1105 

1106def tex_escape(x): 

1107 # type: (str) -> str 

1108 s = "" 

1109 for c in x: 

1110 s += _TEX_TR.get(c, c) 

1111 return s 

1112 

1113 

1114def graphviz_escape(x): 

1115 # type: (Any) -> str 

1116 return str(x).replace("\\", "\\\\").replace('"', '\\"') 

1117 

1118 

1119def colgen(*lstcol, # type: Any 

1120 **kargs # type: Any 

1121 ): 

1122 # type: (...) -> Iterator[Any] 

1123 """Returns a generator that mixes provided quantities forever 

1124 trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default""" # noqa: E501 

1125 if len(lstcol) < 2: 

1126 lstcol *= 2 

1127 trans = kargs.get("trans", lambda x, y, z: (x, y, z)) 

1128 while True: 

1129 for i in range(len(lstcol)): 

1130 for j in range(len(lstcol)): 

1131 for k in range(len(lstcol)): 

1132 if i != j or j != k or k != i: 

1133 yield trans(lstcol[(i + j) % len(lstcol)], lstcol[(j + k) % len(lstcol)], lstcol[(k + i) % len(lstcol)]) # noqa: E501 

1134 

1135 

1136def incremental_label(label="tag%05i", start=0): 

1137 # type: (str, int) -> Iterator[str] 

1138 while True: 

1139 yield label % start 

1140 start += 1 

1141 

1142 

1143def binrepr(val): 

1144 # type: (int) -> str 

1145 return bin(val)[2:] 

1146 

1147 

1148def long_converter(s): 

1149 # type: (str) -> int 

1150 return int(s.replace('\n', '').replace(' ', ''), 16) 

1151 

1152######################### 

1153# Enum management # 

1154######################### 

1155 

1156 

1157class EnumElement: 

1158 def __init__(self, key, value): 

1159 # type: (str, int) -> None 

1160 self._key = key 

1161 self._value = value 

1162 

1163 def __repr__(self): 

1164 # type: () -> str 

1165 return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value) # noqa: E501 

1166 

1167 def __getattr__(self, attr): 

1168 # type: (str) -> Any 

1169 return getattr(self._value, attr) 

1170 

1171 def __str__(self): 

1172 # type: () -> str 

1173 return self._key 

1174 

1175 def __bytes__(self): 

1176 # type: () -> bytes 

1177 return bytes_encode(self.__str__()) 

1178 

1179 def __hash__(self): 

1180 # type: () -> int 

1181 return self._value 

1182 

1183 def __int__(self): 

1184 # type: () -> int 

1185 return int(self._value) 

1186 

1187 def __eq__(self, other): 

1188 # type: (Any) -> bool 

1189 return self._value == int(other) 

1190 

1191 def __neq__(self, other): 

1192 # type: (Any) -> bool 

1193 return not self.__eq__(other) 

1194 

1195 

1196class Enum_metaclass(type): 

1197 element_class = EnumElement 

1198 

1199 def __new__(cls, name, bases, dct): 

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

1201 rdict = {} 

1202 for k, v in dct.items(): 

1203 if isinstance(v, int): 

1204 v = cls.element_class(k, v) 

1205 dct[k] = v 

1206 rdict[v] = k 

1207 dct["__rdict__"] = rdict 

1208 return super(Enum_metaclass, cls).__new__(cls, name, bases, dct) 

1209 

1210 def __getitem__(self, attr): 

1211 # type: (int) -> Any 

1212 return self.__rdict__[attr] # type: ignore 

1213 

1214 def __contains__(self, val): 

1215 # type: (int) -> bool 

1216 return val in self.__rdict__ # type: ignore 

1217 

1218 def get(self, attr, val=None): 

1219 # type: (str, Optional[Any]) -> Any 

1220 return self.__rdict__.get(attr, val) # type: ignore 

1221 

1222 def __repr__(self): 

1223 # type: () -> str 

1224 return "<%s>" % self.__dict__.get("name", self.__name__) 

1225 

1226 

1227################## 

1228# Corrupt data # 

1229################## 

1230 

1231@conf.commands.register 

1232def corrupt_bytes(data, p=0.01, n=None): 

1233 # type: (str, float, Optional[int]) -> bytes 

1234 """ 

1235 Corrupt a given percentage (at least one byte) or number of bytes 

1236 from a string 

1237 """ 

1238 s = array.array("B", bytes_encode(data)) 

1239 s_len = len(s) 

1240 if n is None: 

1241 n = max(1, int(s_len * p)) 

1242 for i in random.sample(range(s_len), n): 

1243 s[i] = (s[i] + random.randint(1, 255)) % 256 

1244 return s.tobytes() 

1245 

1246 

1247@conf.commands.register 

1248def corrupt_bits(data, p=0.01, n=None): 

1249 # type: (str, float, Optional[int]) -> bytes 

1250 """ 

1251 Flip a given percentage (at least one bit) or number of bits 

1252 from a string 

1253 """ 

1254 s = array.array("B", bytes_encode(data)) 

1255 s_len = len(s) * 8 

1256 if n is None: 

1257 n = max(1, int(s_len * p)) 

1258 for i in random.sample(range(s_len), n): 

1259 s[i // 8] ^= 1 << (i % 8) 

1260 return s.tobytes() 

1261 

1262 

1263############################# 

1264# pcap capture file stuff # 

1265############################# 

1266 

1267@conf.commands.register 

1268def wrpcap(filename, # type: Union[IO[bytes], str] 

1269 pkt, # type: _PacketIterable 

1270 *args, # type: Any 

1271 **kargs # type: Any 

1272 ): 

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

1274 """Write a list of packets to a pcap file 

1275 

1276 :param filename: the name of the file to write packets to, or an open, 

1277 writable file-like object. The file descriptor will be 

1278 closed at the end of the call, so do not use an object you 

1279 do not want to close (e.g., running wrpcap(sys.stdout, []) 

1280 in interactive mode will crash Scapy). 

1281 :param gz: set to 1 to save a gzipped capture 

1282 :param linktype: force linktype value 

1283 :param endianness: "<" or ">", force endianness 

1284 :param sync: do not bufferize writes to the capture file 

1285 """ 

1286 with PcapWriter(filename, *args, **kargs) as fdesc: 

1287 fdesc.write(pkt) 

1288 

1289 

1290@conf.commands.register 

1291def wrpcapng(filename, # type: str 

1292 pkt, # type: _PacketIterable 

1293 ): 

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

1295 """Write a list of packets to a pcapng file 

1296 

1297 :param filename: the name of the file to write packets to, or an open, 

1298 writable file-like object. The file descriptor will be 

1299 closed at the end of the call, so do not use an object you 

1300 do not want to close (e.g., running wrpcapng(sys.stdout, []) 

1301 in interactive mode will crash Scapy). 

1302 :param pkt: packets to write 

1303 """ 

1304 with PcapNgWriter(filename) as fdesc: 

1305 fdesc.write(pkt) 

1306 

1307 

1308@conf.commands.register 

1309def rdpcap(filename, count=-1): 

1310 # type: (Union[IO[bytes], str], int) -> PacketList 

1311 """Read a pcap or pcapng file and return a packet list 

1312 

1313 :param count: read only <count> packets 

1314 """ 

1315 # Rant: Our complicated use of metaclasses and especially the 

1316 # __call__ function is, of course, not supported by MyPy. 

1317 # One day we should simplify this mess and use a much simpler 

1318 # layout that will actually be supported and properly dissected. 

1319 with PcapReader(filename) as fdesc: # type: ignore 

1320 return fdesc.read_all(count=count) 

1321 

1322 

1323# NOTE: Type hinting 

1324# Mypy doesn't understand the following metaclass, and thinks each 

1325# constructor (PcapReader...) needs 3 arguments each. To avoid this, 

1326# we add a fake (=None) to the last 2 arguments then force the value 

1327# to not be None in the signature and pack the whole thing in an ignore. 

1328# This allows to not have # type: ignore every time we call those 

1329# constructors. 

1330 

1331class PcapReader_metaclass(type): 

1332 """Metaclass for (Raw)Pcap(Ng)Readers""" 

1333 

1334 def __new__(cls, name, bases, dct): 

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

1336 """The `alternative` class attribute is declared in the PcapNg 

1337 variant, and set here to the Pcap variant. 

1338 

1339 """ 

1340 newcls = super(PcapReader_metaclass, cls).__new__( 

1341 cls, name, bases, dct 

1342 ) 

1343 if 'alternative' in dct: 

1344 dct['alternative'].alternative = newcls 

1345 return newcls 

1346 

1347 def __call__(cls, filename): 

1348 # type: (Union[IO[bytes], str]) -> Any 

1349 """Creates a cls instance, use the `alternative` if that 

1350 fails. 

1351 

1352 """ 

1353 i = cls.__new__( 

1354 cls, 

1355 cls.__name__, 

1356 cls.__bases__, 

1357 cls.__dict__ # type: ignore 

1358 ) 

1359 filename, fdesc, magic = cls.open(filename) 

1360 if not magic: 

1361 raise Scapy_Exception( 

1362 "No data could be read!" 

1363 ) 

1364 try: 

1365 i.__init__(filename, fdesc, magic) 

1366 return i 

1367 except (Scapy_Exception, EOFError): 

1368 pass 

1369 

1370 if "alternative" in cls.__dict__: 

1371 cls = cls.__dict__["alternative"] 

1372 i = cls.__new__( 

1373 cls, 

1374 cls.__name__, 

1375 cls.__bases__, 

1376 cls.__dict__ # type: ignore 

1377 ) 

1378 try: 

1379 i.__init__(filename, fdesc, magic) 

1380 return i 

1381 except (Scapy_Exception, EOFError): 

1382 pass 

1383 

1384 raise Scapy_Exception("Not a supported capture file") 

1385 

1386 @staticmethod 

1387 def open(fname # type: Union[IO[bytes], str] 

1388 ): 

1389 # type: (...) -> Tuple[str, _ByteStream, bytes] 

1390 """Open (if necessary) filename, and read the magic.""" 

1391 if isinstance(fname, str): 

1392 filename = fname 

1393 fdesc = open(filename, "rb") # type: _ByteStream 

1394 magic = fdesc.peek(2)[:2] # type: ignore[union-attr] # BufferedReader 

1395 if magic != b"\x1f\x8b": 

1396 magic = fdesc.read(4) 

1397 else: 

1398 fdesc = fname 

1399 filename = getattr(fdesc, "name", "No name") 

1400 magic = fdesc.read(2) 

1401 if magic == b"\x1f\x8b": 

1402 # GZIP header detected. 

1403 if not isinstance(fname, str): 

1404 fdesc.seek(0) 

1405 raw_fdesc = fdesc 

1406 fdesc = gzip.GzipFile(fileobj=raw_fdesc) 

1407 try: 

1408 magic = fdesc.read(4) 

1409 except (EOFError, OSError, zlib.error) as err: 

1410 fdesc.close() 

1411 if isinstance(fname, str): 

1412 raw_fdesc.close() 

1413 raise Scapy_Exception( 

1414 "Not a supported capture file (invalid gzip file)" 

1415 ) from err 

1416 else: 

1417 magic += fdesc.read(4 - len(magic)) 

1418 return filename, fdesc, magic 

1419 

1420 

1421class RawPcapReader(metaclass=PcapReader_metaclass): 

1422 """A stateful pcap reader. Each packet is returned as a string""" 

1423 

1424 # TODO: use Generics to properly type the various readers. 

1425 # As of right now, RawPcapReader is typed as if it returned packets 

1426 # because all of its child do. Fix that 

1427 

1428 nonblocking_socket = True 

1429 PacketMetadata = collections.namedtuple("PacketMetadata", 

1430 ["sec", "usec", "wirelen", "caplen"]) # noqa: E501 

1431 # A helper subprocess (e.g. the tcpdump prefilter that sniff() spawns for 

1432 # an offline capture with a filter) whose lifetime is bound to this reader. 

1433 # It is reaped in close() so it does not linger as a zombie (#4512). 

1434 subproc = None # type: Optional[subprocess.Popen[bytes]] 

1435 

1436 def __init__(self, filename, fdesc=None, magic=None): # type: ignore 

1437 # type: (str, _ByteStream, bytes) -> None 

1438 self.filename = filename 

1439 self.f = fdesc 

1440 if magic == b"\xa1\xb2\xc3\xd4": # big endian 

1441 self.endian = ">" 

1442 self.nano = False 

1443 elif magic == b"\xd4\xc3\xb2\xa1": # little endian 

1444 self.endian = "<" 

1445 self.nano = False 

1446 elif magic == b"\xa1\xb2\x3c\x4d": # big endian, nanosecond-precision 

1447 self.endian = ">" 

1448 self.nano = True 

1449 elif magic == b"\x4d\x3c\xb2\xa1": # little endian, nanosecond-precision # noqa: E501 

1450 self.endian = "<" 

1451 self.nano = True 

1452 else: 

1453 raise Scapy_Exception( 

1454 "Not a pcap capture file (bad magic: %r)" % magic 

1455 ) 

1456 try: 

1457 hdr = self.f.read(20) 

1458 except (OSError, OverflowError, zlib.error) as e: 

1459 warning(f"Pcap: {e}") 

1460 raise Scapy_Exception("Invalid pcap file (corrupted stream)") 

1461 if len(hdr) < 20: 

1462 raise Scapy_Exception("Invalid pcap file (too short)") 

1463 vermaj, vermin, tz, sig, snaplen, linktype = struct.unpack( 

1464 self.endian + "HHIIII", hdr 

1465 ) 

1466 self.linktype = linktype 

1467 self.snaplen = snaplen 

1468 

1469 def __enter__(self): 

1470 # type: () -> RawPcapReader 

1471 return self 

1472 

1473 def __iter__(self): 

1474 # type: () -> RawPcapReader 

1475 return self 

1476 

1477 def __next__(self): 

1478 # type: () -> Tuple[bytes, RawPcapReader.PacketMetadata] 

1479 """ 

1480 implement the iterator protocol on a set of packets in a pcap file 

1481 """ 

1482 try: 

1483 return self._read_packet() 

1484 except EOFError: 

1485 raise StopIteration 

1486 

1487 def _read_packet(self, size=MTU): 

1488 # type: (int) -> Tuple[bytes, RawPcapReader.PacketMetadata] 

1489 """return a single packet read from the file as a tuple containing 

1490 (pkt_data, pkt_metadata) 

1491 

1492 raise EOFError when no more packets are available 

1493 """ 

1494 try: 

1495 hdr = self.f.read(16) 

1496 if len(hdr) < 16: 

1497 raise EOFError 

1498 sec, usec, caplen, wirelen = struct.unpack(self.endian + "IIII", hdr) 

1499 # A malicious caplen can be up to 4 GiB: bound each read, 

1500 # truncate the packet, then skip the rest of the record so the 

1501 # next pcap header can still be parsed. 

1502 read_size = min(caplen, MTU * 4) 

1503 read_data = self.f.read(read_size) 

1504 data = read_data[:size] 

1505 remaining = caplen - len(read_data) 

1506 if remaining > 0: 

1507 warning("Pcap: packet has been truncated") 

1508 while remaining > 0: 

1509 skipped = self.f.read(min(remaining, MTU * 4)) 

1510 if not skipped: 

1511 break 

1512 remaining -= len(skipped) 

1513 except (OSError, OverflowError, zlib.error) as e: 

1514 warning(f"Pcap: {e}") 

1515 raise EOFError 

1516 

1517 return (data, 

1518 RawPcapReader.PacketMetadata(sec=sec, usec=usec, 

1519 wirelen=wirelen, caplen=caplen)) 

1520 

1521 def read_packet(self, size=MTU): 

1522 # type: (int) -> Packet 

1523 raise Exception( 

1524 "Cannot call read_packet() in RawPcapReader. Use " 

1525 "_read_packet()" 

1526 ) 

1527 

1528 def dispatch(self, 

1529 callback # type: Callable[[Tuple[bytes, RawPcapReader.PacketMetadata]], Any] # noqa: E501 

1530 ): 

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

1532 """call the specified callback routine for each packet read 

1533 

1534 This is just a convenience function for the main loop 

1535 that allows for easy launching of packet processing in a 

1536 thread. 

1537 """ 

1538 for p in self: 

1539 callback(p) 

1540 

1541 def _read_all(self, count=-1): 

1542 # type: (int) -> List[Packet] 

1543 """return a list of all packets in the pcap file 

1544 """ 

1545 res = [] # type: List[Packet] 

1546 while count != 0: 

1547 count -= 1 

1548 try: 

1549 p = self.read_packet() # type: Packet 

1550 except EOFError: 

1551 break 

1552 res.append(p) 

1553 return res 

1554 

1555 def recv(self, size=MTU): 

1556 # type: (int) -> bytes 

1557 """ Emulate a socket 

1558 """ 

1559 return self._read_packet(size=size)[0] 

1560 

1561 def fileno(self): 

1562 # type: () -> int 

1563 return -1 if WINDOWS else self.f.fileno() 

1564 

1565 def close(self): 

1566 # type: () -> None 

1567 if isinstance(self.f, gzip.GzipFile): 

1568 self.f.fileobj.close() # type: ignore 

1569 self.f.close() 

1570 if self.subproc is not None: 

1571 # Reap the prefilter subprocess. The read pipe is already closed 

1572 # above, so a still-running tcpdump gets a SIGTERM and we then 

1573 # wait() to avoid a zombie; an already-finished one is just reaped. 

1574 self.subproc.terminate() 

1575 self.subproc.wait() 

1576 self.subproc = None 

1577 

1578 def __exit__(self, exc_type, exc_value, tracback): 

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

1580 self.close() 

1581 

1582 # emulate SuperSocket 

1583 @staticmethod 

1584 def select(sockets, # type: List[SuperSocket] 

1585 remain=None, # type: Optional[float] 

1586 ): 

1587 # type: (...) -> List[SuperSocket] 

1588 return sockets 

1589 

1590 

1591class PcapReader(RawPcapReader): 

1592 def __init__(self, filename, fdesc=None, magic=None): # type: ignore 

1593 # type: (str, IO[bytes], bytes) -> None 

1594 RawPcapReader.__init__(self, filename, fdesc, magic) 

1595 try: 

1596 self.LLcls = conf.l2types.num2layer[ 

1597 self.linktype 

1598 ] # type: Type[Packet] 

1599 except KeyError: 

1600 warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype, self.linktype)) # noqa: E501 

1601 if conf.raw_layer is None: 

1602 # conf.raw_layer is set on import 

1603 import scapy.packet # noqa: F401 

1604 self.LLcls = conf.raw_layer 

1605 

1606 def __enter__(self): 

1607 # type: () -> PcapReader 

1608 return self 

1609 

1610 def read_packet(self, size=MTU, **kwargs): 

1611 # type: (int, **Any) -> Packet 

1612 rp = super(PcapReader, self)._read_packet(size=size) 

1613 if rp is None: 

1614 raise EOFError 

1615 s, pkt_info = rp 

1616 

1617 try: 

1618 p = self.LLcls(s, **kwargs) # type: Packet 

1619 except KeyboardInterrupt: 

1620 raise 

1621 except Exception: 

1622 if conf.debug_dissector: 

1623 from scapy.sendrecv import debug 

1624 debug.crashed_on = (self.LLcls, s) 

1625 raise 

1626 if conf.raw_layer is None: 

1627 # conf.raw_layer is set on import 

1628 import scapy.packet # noqa: F401 

1629 p = conf.raw_layer(s) 

1630 power = Decimal(10) ** Decimal(-9 if self.nano else -6) 

1631 p.time = EDecimal(pkt_info.sec + power * pkt_info.usec) 

1632 p.wirelen = pkt_info.wirelen 

1633 return p 

1634 

1635 def recv(self, size=MTU, **kwargs): # type: ignore 

1636 # type: (int, **Any) -> Packet 

1637 return self.read_packet(size=size, **kwargs) 

1638 

1639 def __iter__(self): 

1640 # type: () -> PcapReader 

1641 return self 

1642 

1643 def __next__(self): # type: ignore 

1644 # type: () -> Packet 

1645 try: 

1646 return self.read_packet() 

1647 except EOFError: 

1648 raise StopIteration 

1649 

1650 def read_all(self, count=-1): 

1651 # type: (int) -> PacketList 

1652 res = self._read_all(count) 

1653 from scapy import plist 

1654 return plist.PacketList(res, name=os.path.basename(self.filename)) 

1655 

1656 

1657class RawPcapNgReader(RawPcapReader): 

1658 """A stateful pcapng reader. Each packet is returned as 

1659 bytes. 

1660 

1661 """ 

1662 

1663 alternative = RawPcapReader # type: Type[Any] 

1664 

1665 PacketMetadata = collections.namedtuple("PacketMetadataNg", # type: ignore 

1666 ["linktype", "tsresol", 

1667 "tshigh", "tslow", "wirelen", 

1668 "comments", "ifname", "direction", 

1669 "process_information"]) 

1670 

1671 def __init__(self, filename, fdesc=None, magic=None): # type: ignore 

1672 # type: (str, IO[bytes], bytes) -> None 

1673 self.filename = filename 

1674 self.f = fdesc 

1675 # A list of (linktype, snaplen, tsresol); will be populated by IDBs. 

1676 self.interfaces = [] # type: List[Tuple[int, int, Dict[str, Any]]] 

1677 self.default_options = { 

1678 "tsresol": 1000000 

1679 } 

1680 self.blocktypes: Dict[ 

1681 int, 

1682 Callable[ 

1683 [bytes, int], 

1684 Optional[Tuple[bytes, RawPcapNgReader.PacketMetadata]] 

1685 ]] = { 

1686 1: self._read_block_idb, 

1687 2: self._read_block_pkt, 

1688 3: self._read_block_spb, 

1689 6: self._read_block_epb, 

1690 10: self._read_block_dsb, 

1691 0x80000001: self._read_block_pib, 

1692 } 

1693 self.endian = "!" # Will be overwritten by first SHB 

1694 self.process_information = [] # type: List[Dict[str, Any]] 

1695 self._tls_state = None # type: Optional[Tuple[Dict[str, bytes], bool]] 

1696 

1697 if magic != b"\x0a\x0d\x0d\x0a": # PcapNg: 

1698 raise Scapy_Exception( 

1699 "Not a pcapng capture file (bad magic: %r)" % magic 

1700 ) 

1701 

1702 try: 

1703 self._read_block_shb() 

1704 except (EOFError, OSError, OverflowError, zlib.error) as e: 

1705 warning(f"PcapNg: {e}") 

1706 raise Scapy_Exception( 

1707 "The first SHB of the pcapng file is malformed !" 

1708 ) 

1709 

1710 def _read_block(self, size=MTU): 

1711 # type: (int) -> Optional[Tuple[bytes, RawPcapNgReader.PacketMetadata]] # noqa: E501 

1712 try: 

1713 blocktype = struct.unpack(self.endian + "I", self.f.read(4))[0] 

1714 except struct.error: 

1715 raise EOFError 

1716 if blocktype == 0x0A0D0D0A: 

1717 # This function updates the endianness based on the block content. 

1718 self._read_block_shb() 

1719 return None 

1720 try: 

1721 blocklen = struct.unpack(self.endian + "I", self.f.read(4))[0] 

1722 except struct.error: 

1723 warning("PcapNg: Error reading blocklen before block body") 

1724 raise EOFError 

1725 if blocklen < 12: 

1726 warning("PcapNg: Invalid block length !") 

1727 raise EOFError 

1728 

1729 _block_body_length = blocklen - 12 

1730 block = self.f.read(_block_body_length) 

1731 if len(block) != _block_body_length: 

1732 warning("PcapNg: Invalid Block body length (too short)") 

1733 raise EOFError 

1734 self._read_block_tail(blocklen) 

1735 if blocktype in self.blocktypes: 

1736 return self.blocktypes[blocktype](block, size) 

1737 return None 

1738 

1739 def _read_block_tail(self, blocklen): 

1740 # type: (int) -> None 

1741 if blocklen % 4: 

1742 pad = self.f.read(-blocklen % 4) 

1743 warning("PcapNg: bad blocklen %d (MUST be a multiple of 4. " 

1744 "Ignored padding %r" % (blocklen, pad)) 

1745 try: 

1746 if blocklen != struct.unpack(self.endian + 'I', 

1747 self.f.read(4))[0]: 

1748 raise EOFError("PcapNg: Invalid pcapng block (bad blocklen)") 

1749 except struct.error: 

1750 warning("PcapNg: Could not read blocklen after block body") 

1751 raise EOFError 

1752 

1753 def _read_block_shb(self): 

1754 # type: () -> None 

1755 """Section Header Block""" 

1756 _blocklen = self.f.read(4) 

1757 endian = self.f.read(4) 

1758 if endian == b"\x1a\x2b\x3c\x4d": 

1759 self.endian = ">" 

1760 elif endian == b"\x4d\x3c\x2b\x1a": 

1761 self.endian = "<" 

1762 else: 

1763 warning("PcapNg: Bad magic in Section Header Block" 

1764 " (not a pcapng file?)") 

1765 raise EOFError 

1766 

1767 try: 

1768 blocklen = struct.unpack(self.endian + "I", _blocklen)[0] 

1769 except struct.error: 

1770 warning("PcapNg: Could not read blocklen") 

1771 raise EOFError 

1772 if blocklen < 28: 

1773 warning(f"PcapNg: Invalid Section Header Block length ({blocklen})!") # noqa: E501 

1774 raise EOFError 

1775 

1776 # Major version must be 1 

1777 _major = self.f.read(2) 

1778 try: 

1779 major = struct.unpack(self.endian + "H", _major)[0] 

1780 except struct.error: 

1781 warning("PcapNg: Could not read major value") 

1782 raise EOFError 

1783 if major != 1: 

1784 warning(f"PcapNg: SHB Major version {major} unsupported !") 

1785 raise EOFError 

1786 

1787 # Skip minor version & section length 

1788 skipped = self.f.read(10) 

1789 if len(skipped) != 10: 

1790 warning("PcapNg: Could not read minor value & section length") 

1791 raise EOFError 

1792 

1793 _options_len = blocklen - 28 

1794 options = self.f.read(_options_len) 

1795 if len(options) != _options_len: 

1796 raise Scapy_Exception("PcapNg: Invalid Section Header Block " 

1797 " options (too short)") 

1798 self._read_block_tail(blocklen) 

1799 self._read_options(options) 

1800 

1801 def _read_packet(self, size=MTU): # type: ignore 

1802 # type: (int) -> Tuple[bytes, RawPcapNgReader.PacketMetadata] 

1803 """Read blocks until it reaches either EOF or a packet, and 

1804 returns None or (packet, (linktype, sec, usec, wirelen)), 

1805 where packet is a string. 

1806 

1807 """ 

1808 while True: 

1809 try: 

1810 res = self._read_block(size=size) 

1811 except (OSError, OverflowError, zlib.error) as e: 

1812 warning(f"PcapNg: {e}") 

1813 raise EOFError 

1814 if res is not None: 

1815 return res 

1816 

1817 def _read_options(self, options): 

1818 # type: (bytes) -> Dict[int, Union[bytes, List[bytes]]] 

1819 opts = dict() # type: Dict[int, Union[bytes, List[bytes]]] 

1820 offset = 0 

1821 while len(options) - offset >= 4: 

1822 try: 

1823 code, length = struct.unpack_from( 

1824 self.endian + "HH", options, offset 

1825 ) 

1826 except struct.error: 

1827 warning("PcapNg: options header is too small " 

1828 "%d !" % (len(options) - offset)) 

1829 raise EOFError 

1830 value_offset = offset + 4 

1831 if code != 0 and value_offset + length <= len(options): 

1832 # https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#name-options-format 

1833 if code in [1, 2988, 2989, 19372, 19373]: 

1834 if code not in opts: 

1835 opts[code] = [] 

1836 opts[code].append( # type: ignore 

1837 options[value_offset:value_offset + length] 

1838 ) 

1839 else: 

1840 opts[code] = options[value_offset:value_offset + length] 

1841 if code == 0: 

1842 if length != 0: 

1843 warning("PcapNg: invalid option " 

1844 "length %d for end-of-option" % length) 

1845 break 

1846 offset = value_offset + length + (-length % 4) 

1847 return opts 

1848 

1849 def _read_block_idb(self, block, _): 

1850 # type: (bytes, int) -> None 

1851 """Interface Description Block""" 

1852 # 2 bytes LinkType + 2 bytes Reserved 

1853 # 4 bytes Snaplen 

1854 options_raw = self._read_options(block[8:]) 

1855 options = self.default_options.copy() # type: Dict[str, Any] 

1856 for c, v in options_raw.items(): 

1857 if isinstance(v, list): 

1858 # Spec allows multiple occurrences (see 

1859 # https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#section-4.2-8.6) 

1860 # but does not define which to use. We take the first for 

1861 # backward compatibility. 

1862 v = v[0] 

1863 if c == 9: 

1864 length = len(v) 

1865 if length == 1: 

1866 tsresol = v[0] 

1867 options["tsresol"] = (2 if tsresol & 128 else 10) ** ( 

1868 tsresol & 127 

1869 ) 

1870 else: 

1871 warning("PcapNg: invalid options " 

1872 "length %d for IDB tsresol" % length) 

1873 elif c == 2: 

1874 options["name"] = v 

1875 elif c == 1: 

1876 options["comment"] = v 

1877 try: 

1878 interface: Tuple[int, int, Dict[str, Any]] = struct.unpack( 

1879 self.endian + "HxxI", 

1880 block[:8] 

1881 ) + (options,) 

1882 except struct.error: 

1883 warning("PcapNg: IDB is too small %d/8 !" % len(block)) 

1884 raise EOFError 

1885 self.interfaces.append(interface) 

1886 

1887 def _check_interface_id(self, intid): 

1888 # type: (int) -> bool 

1889 """Whether this block names an interface the file has described.""" 

1890 tmp_len = len(self.interfaces) 

1891 if intid >= tmp_len: 

1892 warning("PcapNg: invalid interface id %d/%d" % (intid, tmp_len)) 

1893 return False 

1894 return True 

1895 

1896 def _read_block_epb(self, block, size): 

1897 # type: (bytes, int) -> Optional[Tuple[bytes, RawPcapNgReader.PacketMetadata]] # noqa: E501 

1898 """Enhanced Packet Block""" 

1899 try: 

1900 intid, tshigh, tslow, caplen, wirelen = struct.unpack( 

1901 self.endian + "5I", 

1902 block[:20], 

1903 ) 

1904 except struct.error: 

1905 warning("PcapNg: EPB is too small %d/20 !" % len(block)) 

1906 raise EOFError 

1907 

1908 # Compute the options offset taking padding into account 

1909 if caplen % 4: 

1910 opt_offset = 20 + caplen + (-caplen) % 4 

1911 else: 

1912 opt_offset = 20 + caplen 

1913 

1914 # Parse options 

1915 options = self._read_options(block[opt_offset:]) 

1916 

1917 process_information = {} 

1918 for code, value in options.items(): 

1919 # PCAPNG_EPB_PIB_INDEX, PCAPNG_EPB_E_PIB_INDEX 

1920 if code in [0x8001, 0x8003]: 

1921 try: 

1922 proc_index = struct.unpack( 

1923 self.endian + "I", value)[0] # type: ignore 

1924 except struct.error: 

1925 warning("PcapNg: EPB invalid proc index " 

1926 "(expected 4 bytes, got %d) !" % len(value)) 

1927 raise EOFError 

1928 if proc_index < len(self.process_information): 

1929 key = "proc" if code == 0x8001 else "eproc" 

1930 process_information[key] = self.process_information[proc_index] 

1931 else: 

1932 warning("PcapNg: EPB invalid process information index " 

1933 "(%d/%d) !" % (proc_index, len(self.process_information))) 

1934 

1935 comments = options.get(1, None) 

1936 epb_flags_raw = options.get(2, None) 

1937 if epb_flags_raw and isinstance(epb_flags_raw, bytes): 

1938 try: 

1939 epb_flags, = struct.unpack(self.endian + "I", epb_flags_raw) 

1940 except struct.error: 

1941 warning("PcapNg: EPB invalid flags size" 

1942 "(expected 4 bytes, got %d) !" % len(epb_flags_raw)) 

1943 raise EOFError 

1944 direction = epb_flags & 3 

1945 

1946 else: 

1947 direction = None 

1948 

1949 if not self._check_interface_id(intid): 

1950 return None 

1951 ifname = self.interfaces[intid][2].get('name', None) 

1952 

1953 return (block[20:20 + caplen][:size], 

1954 RawPcapNgReader.PacketMetadata(linktype=self.interfaces[intid][0], # noqa: E501 

1955 tsresol=self.interfaces[intid][2]['tsresol'], # noqa: E501 

1956 tshigh=tshigh, 

1957 tslow=tslow, 

1958 wirelen=wirelen, 

1959 ifname=ifname, 

1960 direction=direction, 

1961 process_information=process_information, 

1962 comments=comments)) 

1963 

1964 def _read_block_spb(self, block, size): 

1965 # type: (bytes, int) -> Optional[Tuple[bytes, RawPcapNgReader.PacketMetadata]] # noqa: E501 

1966 """Simple Packet Block""" 

1967 # "it MUST be assumed that all the Simple Packet Blocks have 

1968 # been captured on the interface previously specified in the 

1969 # first Interface Description Block." 

1970 intid = 0 

1971 if not self._check_interface_id(intid): 

1972 return None 

1973 

1974 try: 

1975 wirelen, = struct.unpack(self.endian + "I", block[:4]) 

1976 except struct.error: 

1977 warning("PcapNg: SPB is too small %d/4 !" % len(block)) 

1978 raise EOFError 

1979 

1980 caplen = min(wirelen, self.interfaces[intid][1]) 

1981 return (block[4:4 + caplen][:size], 

1982 RawPcapNgReader.PacketMetadata(linktype=self.interfaces[intid][0], # noqa: E501 

1983 tsresol=self.interfaces[intid][2]['tsresol'], # noqa: E501 

1984 tshigh=None, 

1985 tslow=None, 

1986 wirelen=wirelen, 

1987 ifname=None, 

1988 direction=None, 

1989 process_information={}, 

1990 comments=None)) 

1991 

1992 def _read_block_pkt(self, block, size): 

1993 # type: (bytes, int) -> Optional[Tuple[bytes, RawPcapNgReader.PacketMetadata]] # noqa: E501 

1994 """(Obsolete) Packet Block""" 

1995 try: 

1996 intid, drops, tshigh, tslow, caplen, wirelen = struct.unpack( 

1997 self.endian + "HH4I", 

1998 block[:20], 

1999 ) 

2000 except struct.error: 

2001 warning("PcapNg: PKT is too small %d/20 !" % len(block)) 

2002 raise EOFError 

2003 

2004 if not self._check_interface_id(intid): 

2005 return None 

2006 return (block[20:20 + caplen][:size], 

2007 RawPcapNgReader.PacketMetadata(linktype=self.interfaces[intid][0], # noqa: E501 

2008 tsresol=self.interfaces[intid][2]['tsresol'], # noqa: E501 

2009 tshigh=tshigh, 

2010 tslow=tslow, 

2011 wirelen=wirelen, 

2012 ifname=None, 

2013 direction=None, 

2014 process_information={}, 

2015 comments=None)) 

2016 

2017 def _read_block_dsb(self, block, size): 

2018 # type: (bytes, int) -> None 

2019 """Decryption Secrets Block""" 

2020 

2021 # Parse the secrets type and length fields 

2022 try: 

2023 secrets_type, secrets_length = struct.unpack( 

2024 self.endian + "II", 

2025 block[:8], 

2026 ) 

2027 block = block[8:] 

2028 except struct.error: 

2029 warning("PcapNg: DSB is too small %d!", len(block)) 

2030 raise EOFError 

2031 

2032 # Compute the secrets length including the padding 

2033 padded_secrets_length = secrets_length + (-secrets_length) % 4 

2034 if len(block) < padded_secrets_length: 

2035 warning("PcapNg: invalid DSB secrets length!") 

2036 raise EOFError 

2037 

2038 # Extract secrets data and options 

2039 secrets_data = block[:padded_secrets_length][:secrets_length] 

2040 if block[padded_secrets_length:]: 

2041 warning("PcapNg: DSB options are not supported!") 

2042 

2043 # TLS Key Log 

2044 if secrets_type == 0x544c534b: 

2045 if getattr(conf, "tls_sessions", False) is False: 

2046 warning("PcapNg: TLS Key Log available, but " 

2047 "the TLS layer is not loaded! Scapy won't be able " 

2048 "to decrypt the packets.") 

2049 else: 

2050 from scapy.layers.tls.session import parse_nss_keys 

2051 

2052 try: 

2053 keys = parse_nss_keys(secrets_data.decode()) 

2054 except UnicodeDecodeError as ex: 

2055 warning("Cannot read NSS Key Log: %s", str(ex)) 

2056 keys = {} 

2057 if not keys: 

2058 warning("PcapNg: invalid TLS Key Log in DSB!") 

2059 else: 

2060 # Note: these attributes are only available when the TLS 

2061 # layer is loaded. 

2062 if self._tls_state is None: 

2063 self._tls_state = ( 

2064 conf.tls_nss_keys, 

2065 conf.tls_session_enable, 

2066 ) 

2067 conf.tls_nss_keys = keys 

2068 conf.tls_session_enable = True 

2069 else: 

2070 warning("PcapNg: Unknown DSB secrets type (0x%x)!", secrets_type) 

2071 

2072 def close(self): 

2073 # type: () -> None 

2074 if self._tls_state is not None: 

2075 conf.tls_nss_keys, conf.tls_session_enable = self._tls_state 

2076 self._tls_state = None 

2077 RawPcapReader.close(self) 

2078 

2079 def _read_block_pib(self, block, _): 

2080 # type: (bytes, int) -> None 

2081 """Apple Process Information Block""" 

2082 

2083 # Get the Process ID 

2084 try: 

2085 dpeb_pid = struct.unpack(self.endian + "I", block[:4])[0] 

2086 process_information = {"id": dpeb_pid} 

2087 block = block[4:] 

2088 except struct.error: 

2089 warning("PcapNg: DPEB is too small (%d). Cannot get PID!", 

2090 len(block)) 

2091 raise EOFError 

2092 

2093 # Get Options 

2094 options = self._read_options(block) 

2095 for code, value in options.items(): 

2096 if code == 2: 

2097 process_information["name"] = value.decode( # type: ignore 

2098 "ascii", "backslashreplace") 

2099 elif code == 4: 

2100 if len(value) == 16: 

2101 process_information["uuid"] = str(UUID(bytes=value)) # type: ignore 

2102 else: 

2103 warning("PcapNg: DPEB UUID length is invalid (%d)!", 

2104 len(value)) 

2105 

2106 # Store process information 

2107 self.process_information.append(process_information) 

2108 

2109 

2110class PcapNgReader(RawPcapNgReader, PcapReader): 

2111 

2112 alternative = PcapReader 

2113 

2114 def __init__(self, filename, fdesc=None, magic=None): # type: ignore 

2115 # type: (str, IO[bytes], bytes) -> None 

2116 RawPcapNgReader.__init__(self, filename, fdesc, magic) 

2117 

2118 def __enter__(self): 

2119 # type: () -> PcapNgReader 

2120 return self 

2121 

2122 def read_packet(self, size=MTU, **kwargs): 

2123 # type: (int, **Any) -> Packet 

2124 rp = super(PcapNgReader, self)._read_packet(size=size) 

2125 if rp is None: 

2126 raise EOFError 

2127 s, (linktype, tsresol, tshigh, tslow, wirelen, comments, ifname, direction, process_information) = rp # noqa: E501 

2128 try: 

2129 cls = conf.l2types.num2layer[linktype] # type: Type[Packet] 

2130 p = cls(s, **kwargs) # type: Packet 

2131 except KeyboardInterrupt: 

2132 raise 

2133 except Exception: 

2134 if conf.debug_dissector: 

2135 raise 

2136 if conf.raw_layer is None: 

2137 # conf.raw_layer is set on import 

2138 import scapy.packet # noqa: F401 

2139 p = conf.raw_layer(s) 

2140 if tshigh is not None: 

2141 p.time = EDecimal((tshigh << 32) + tslow) / tsresol 

2142 p.wirelen = wirelen 

2143 p.comments = comments 

2144 p.direction = direction 

2145 p.process_information = process_information.copy() 

2146 if ifname is not None: 

2147 p.sniffed_on = ifname.decode('utf-8', 'backslashreplace') 

2148 return p 

2149 

2150 def recv(self, size: int = MTU, **kwargs: Any) -> 'Packet': # type: ignore 

2151 return self.read_packet(size=size, **kwargs) 

2152 

2153 

2154class GenericPcapWriter(object): 

2155 nano = False 

2156 linktype: int 

2157 

2158 def _write_header(self, pkt): 

2159 # type: (Optional[Union[Packet, bytes]]) -> None 

2160 raise NotImplementedError 

2161 

2162 def _write_packet(self, 

2163 packet, # type: Union[bytes, Packet] 

2164 linktype, # type: int 

2165 sec=None, # type: Optional[float] 

2166 usec=None, # type: Optional[int] 

2167 caplen=None, # type: Optional[int] 

2168 wirelen=None, # type: Optional[int] 

2169 ifname=None, # type: Optional[bytes] 

2170 direction=None, # type: Optional[int] 

2171 comments=None, # type: Optional[List[bytes]] 

2172 ): 

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

2174 raise NotImplementedError 

2175 

2176 def _get_time(self, 

2177 packet, # type: Union[bytes, Packet] 

2178 sec, # type: Optional[float] 

2179 usec # type: Optional[int] 

2180 ): 

2181 # type: (...) -> Tuple[float, int] 

2182 if hasattr(packet, "time"): 

2183 if sec is None: 

2184 packet_time = packet.time 

2185 tmp = int(packet_time) 

2186 usec = int(round((packet_time - tmp) * 

2187 (1000000000 if self.nano else 1000000))) 

2188 sec = float(packet_time) 

2189 if sec is not None and usec is None: 

2190 usec = 0 

2191 return sec, usec # type: ignore 

2192 

2193 def write_header(self, pkt): 

2194 # type: (Optional[Union[Packet, bytes]]) -> None 

2195 if not hasattr(self, 'linktype'): 

2196 try: 

2197 if pkt is None or isinstance(pkt, bytes): 

2198 # Can't guess LL 

2199 raise KeyError 

2200 self.linktype = conf.l2types.layer2num[ 

2201 pkt.__class__ 

2202 ] 

2203 except KeyError: 

2204 msg = "%s: unknown LL type for %s. Using type 1 (Ethernet)" 

2205 warning(msg, self.__class__.__name__, pkt.__class__.__name__) 

2206 self.linktype = DLT_EN10MB 

2207 self._write_header(pkt) 

2208 

2209 def write_packet(self, 

2210 packet, # type: Union[bytes, Packet] 

2211 sec=None, # type: Optional[float] 

2212 usec=None, # type: Optional[int] 

2213 caplen=None, # type: Optional[int] 

2214 wirelen=None, # type: Optional[int] 

2215 ): 

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

2217 """ 

2218 Writes a single packet to the pcap file. 

2219 

2220 :param packet: Packet, or bytes for a single packet 

2221 :type packet: scapy.packet.Packet or bytes 

2222 :param sec: time the packet was captured, in seconds since epoch. If 

2223 not supplied, defaults to now. 

2224 :type sec: float 

2225 :param usec: If ``nano=True``, then number of nanoseconds after the 

2226 second that the packet was captured. If ``nano=False``, 

2227 then the number of microseconds after the second the 

2228 packet was captured. If ``sec`` is not specified, 

2229 this value is ignored. 

2230 :type usec: int or long 

2231 :param caplen: The length of the packet in the capture file. If not 

2232 specified, uses ``len(raw(packet))``. 

2233 :type caplen: int 

2234 :param wirelen: The length of the packet on the wire. If not 

2235 specified, tries ``packet.wirelen``, otherwise uses 

2236 ``caplen``. 

2237 :type wirelen: int 

2238 :return: None 

2239 :rtype: None 

2240 """ 

2241 f_sec, usec = self._get_time(packet, sec, usec) 

2242 

2243 rawpkt = bytes_encode(packet) 

2244 caplen = len(rawpkt) if caplen is None else caplen 

2245 

2246 if wirelen is None: 

2247 if hasattr(packet, "wirelen"): 

2248 wirelen = packet.wirelen 

2249 if wirelen is None: 

2250 wirelen = caplen 

2251 

2252 comments = getattr(packet, "comments", None) 

2253 ifname = getattr(packet, "sniffed_on", None) 

2254 direction = getattr(packet, "direction", None) 

2255 if not isinstance(packet, bytes): 

2256 # The class may not be bound to any linktype (e.g. conf.raw_layer), 

2257 # in which case fall back to the one write_header() settled on. 

2258 linktype: int = conf.l2types.layer2num.get( 

2259 packet.__class__, 

2260 self.linktype, 

2261 ) 

2262 else: 

2263 linktype = self.linktype 

2264 if ifname is not None: 

2265 ifname = str(ifname).encode('utf-8') 

2266 self._write_packet( 

2267 rawpkt, 

2268 sec=f_sec, usec=usec, 

2269 caplen=caplen, wirelen=wirelen, 

2270 ifname=ifname, 

2271 direction=direction, 

2272 linktype=linktype, 

2273 comments=comments, 

2274 ) 

2275 

2276 

2277class GenericRawPcapWriter(GenericPcapWriter): 

2278 header_present = False 

2279 nano = False 

2280 sync = False 

2281 f = None # type: Union[IO[bytes], gzip.GzipFile] 

2282 

2283 def fileno(self): 

2284 # type: () -> int 

2285 return -1 if WINDOWS else self.f.fileno() 

2286 

2287 def flush(self): 

2288 # type: () -> Optional[Any] 

2289 return self.f.flush() 

2290 

2291 def close(self): 

2292 # type: () -> Optional[Any] 

2293 if not self.header_present: 

2294 self.write_header(None) 

2295 return self.f.close() 

2296 

2297 def __enter__(self): 

2298 # type: () -> GenericRawPcapWriter 

2299 return self 

2300 

2301 def __exit__(self, exc_type, exc_value, tracback): 

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

2303 self.flush() 

2304 self.close() 

2305 

2306 def write(self, pkt): 

2307 # type: (Union[_PacketIterable, bytes]) -> None 

2308 """ 

2309 Writes a Packet, a SndRcvList object, or bytes to a pcap file. 

2310 

2311 :param pkt: Packet(s) to write (one record for each Packet), or raw 

2312 bytes to write (as one record). 

2313 :type pkt: iterable[scapy.packet.Packet], scapy.packet.Packet or bytes 

2314 """ 

2315 if isinstance(pkt, bytes): 

2316 if not self.header_present: 

2317 self.write_header(pkt) 

2318 self.write_packet(pkt) 

2319 else: 

2320 # Import here to avoid circular dependency 

2321 from scapy.supersocket import IterSocket 

2322 for p in IterSocket(pkt).iter: 

2323 if not self.header_present: 

2324 self.write_header(p) 

2325 

2326 if not isinstance(p, bytes) and \ 

2327 self.linktype != conf.l2types.get(type(p), None): 

2328 warning("Inconsistent linktypes detected!" 

2329 " The resulting file might contain" 

2330 " invalid packets." 

2331 ) 

2332 

2333 self.write_packet(p) 

2334 

2335 

2336class RawPcapWriter(GenericRawPcapWriter): 

2337 """A stream PCAP writer with more control than wrpcap()""" 

2338 

2339 def __init__(self, 

2340 filename, # type: Union[IO[bytes], str] 

2341 linktype=None, # type: Optional[int] 

2342 gz=False, # type: bool 

2343 endianness="", # type: str 

2344 append=False, # type: bool 

2345 sync=False, # type: bool 

2346 nano=False, # type: bool 

2347 snaplen=MTU, # type: int 

2348 bufsz=4096, # type: int 

2349 ): 

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

2351 """ 

2352 :param filename: the name of the file to write packets to, or an open, 

2353 writable file-like object. 

2354 :param linktype: force linktype to a given value. If None, linktype is 

2355 taken from the first writer packet 

2356 :param gz: compress the capture on the fly 

2357 :param endianness: force an endianness (little:"<", big:">"). 

2358 Default is native 

2359 :param append: append packets to the capture file instead of 

2360 truncating it 

2361 :param sync: do not bufferize writes to the capture file 

2362 :param nano: use nanosecond-precision (requires libpcap >= 1.5.0) 

2363 

2364 """ 

2365 

2366 if linktype: 

2367 self.linktype = linktype 

2368 self.snaplen = snaplen 

2369 self.append = append 

2370 self.gz = gz 

2371 self.endian = endianness 

2372 self.sync = sync 

2373 self.nano = nano 

2374 if sync: 

2375 bufsz = 0 

2376 

2377 if isinstance(filename, str): 

2378 self.filename = filename 

2379 if gz: 

2380 self.f = cast(_ByteStream, gzip.open( 

2381 filename, append and "ab" or "wb", 9 

2382 )) 

2383 else: 

2384 self.f = open(filename, append and "ab" or "wb", bufsz) 

2385 else: 

2386 self.f = filename 

2387 self.filename = getattr(filename, "name", "No name") 

2388 

2389 def _write_header(self, pkt): 

2390 # type: (Optional[Union[Packet, bytes]]) -> None 

2391 self.header_present = True 

2392 

2393 if self.append: 

2394 # Even if prone to race conditions, this seems to be 

2395 # safest way to tell whether the header is already present 

2396 # because we have to handle compressed streams that 

2397 # are not as flexible as basic files 

2398 if self.gz: 

2399 g = gzip.open(self.filename, "rb") # type: _ByteStream 

2400 else: 

2401 g = open(self.filename, "rb") 

2402 try: 

2403 if g.read(16): 

2404 return 

2405 finally: 

2406 g.close() 

2407 

2408 if not hasattr(self, 'linktype'): 

2409 raise ValueError( 

2410 "linktype could not be guessed. " 

2411 "Please pass a linktype while creating the writer" 

2412 ) 

2413 

2414 self.f.write(struct.pack(self.endian + "IHHIIII", 0xa1b23c4d if self.nano else 0xa1b2c3d4, # noqa: E501 

2415 2, 4, 0, 0, self.snaplen, self.linktype)) 

2416 self.f.flush() 

2417 

2418 def _write_packet(self, 

2419 packet, # type: Union[bytes, Packet] 

2420 linktype, # type: int 

2421 sec=None, # type: Optional[float] 

2422 usec=None, # type: Optional[int] 

2423 caplen=None, # type: Optional[int] 

2424 wirelen=None, # type: Optional[int] 

2425 ifname=None, # type: Optional[bytes] 

2426 direction=None, # type: Optional[int] 

2427 comments=None, # type: Optional[List[bytes]] 

2428 ): 

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

2430 """ 

2431 Writes a single packet to the pcap file. 

2432 

2433 :param packet: bytes for a single packet 

2434 :type packet: bytes 

2435 :param linktype: linktype value associated with the packet 

2436 :type linktype: int 

2437 :param sec: time the packet was captured, in seconds since epoch. If 

2438 not supplied, defaults to now. 

2439 :type sec: float 

2440 :param usec: not used with pcapng 

2441 packet was captured 

2442 :type usec: int or long 

2443 :param caplen: The length of the packet in the capture file. If not 

2444 specified, uses ``len(packet)``. 

2445 :type caplen: int 

2446 :param wirelen: The length of the packet on the wire. If not 

2447 specified, uses ``caplen``. 

2448 :type wirelen: int 

2449 :return: None 

2450 :rtype: None 

2451 """ 

2452 if caplen is None: 

2453 caplen = len(packet) 

2454 if wirelen is None: 

2455 wirelen = caplen 

2456 if sec is None or usec is None: 

2457 t = time.time() 

2458 it = int(t) 

2459 if sec is None: 

2460 sec = it 

2461 usec = int(round((t - it) * 

2462 (1000000000 if self.nano else 1000000))) 

2463 elif usec is None: 

2464 usec = 0 

2465 

2466 self.f.write(struct.pack(self.endian + "IIII", 

2467 int(sec), usec, caplen, wirelen)) 

2468 self.f.write(bytes(packet)) 

2469 if self.sync: 

2470 self.f.flush() 

2471 

2472 

2473class RawPcapNgWriter(GenericRawPcapWriter): 

2474 """A stream pcapng writer with more control than wrpcapng()""" 

2475 

2476 def __init__(self, 

2477 filename, # type: str 

2478 ): 

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

2480 

2481 self.header_present = False 

2482 self.tsresol = 1000000 

2483 # A dict to keep if_name to IDB id mapping. 

2484 # unknown if_name(None) id=0 

2485 self.interfaces2id: Dict[Optional[bytes], int] = {None: 0} 

2486 

2487 # tcpdump only support little-endian in PCAPng files 

2488 self.endian = "<" 

2489 self.endian_magic = b"\x4d\x3c\x2b\x1a" 

2490 

2491 self.filename = filename 

2492 self.f = open(filename, "wb", 4096) 

2493 

2494 def _get_time(self, 

2495 packet, # type: Union[bytes, Packet] 

2496 sec, # type: Optional[float] 

2497 usec # type: Optional[int] 

2498 ): 

2499 # type: (...) -> Tuple[float, int] 

2500 if hasattr(packet, "time"): 

2501 if sec is None: 

2502 sec = float(packet.time) 

2503 

2504 if usec is None: 

2505 usec = 0 

2506 

2507 return sec, usec # type: ignore 

2508 

2509 def _add_padding(self, raw_data): 

2510 # type: (bytes) -> bytes 

2511 raw_data += ((-len(raw_data)) % 4) * b"\x00" 

2512 return raw_data 

2513 

2514 def build_block(self, block_type, block_body, options=None): 

2515 # type: (bytes, bytes, Optional[bytes]) -> bytes 

2516 

2517 # Pad Block Body to 32 bits 

2518 block_body = self._add_padding(block_body) 

2519 

2520 if options: 

2521 block_body += options 

2522 

2523 # An empty block is 12 bytes long 

2524 block_total_length = 12 + len(block_body) 

2525 

2526 # Block Type 

2527 block = block_type 

2528 # Block Total Length$ 

2529 block += struct.pack(self.endian + "I", block_total_length) 

2530 # Block Body 

2531 block += block_body 

2532 # Block Total Length$ 

2533 block += struct.pack(self.endian + "I", block_total_length) 

2534 

2535 return block 

2536 

2537 def _write_header(self, pkt): 

2538 # type: (Optional[Union[Packet, bytes]]) -> None 

2539 if not self.header_present: 

2540 self.header_present = True 

2541 self._write_block_shb() 

2542 self._write_block_idb(linktype=self.linktype) 

2543 

2544 def _write_block_shb(self): 

2545 # type: () -> None 

2546 

2547 # Block Type 

2548 block_type = b"\x0A\x0D\x0D\x0A" 

2549 # Byte-Order Magic 

2550 block_shb = self.endian_magic 

2551 # Major Version 

2552 block_shb += struct.pack(self.endian + "H", 1) 

2553 # Minor Version 

2554 block_shb += struct.pack(self.endian + "H", 0) 

2555 # Section Length 

2556 block_shb += struct.pack(self.endian + "q", -1) 

2557 

2558 self.f.write(self.build_block(block_type, block_shb)) 

2559 

2560 def _write_block_idb(self, 

2561 linktype, # type: int 

2562 ifname=None # type: Optional[bytes] 

2563 ): 

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

2565 

2566 # Block Type 

2567 block_type = struct.pack(self.endian + "I", 1) 

2568 # LinkType 

2569 block_idb = struct.pack(self.endian + "H", linktype) 

2570 # Reserved 

2571 block_idb += struct.pack(self.endian + "H", 0) 

2572 # SnapLen 

2573 block_idb += struct.pack(self.endian + "I", 262144) 

2574 

2575 # if_name option 

2576 opts = None 

2577 if ifname is not None: 

2578 opts = struct.pack(self.endian + "HH", 2, len(ifname)) 

2579 # Pad Option Value to 32 bits 

2580 opts += self._add_padding(ifname) 

2581 opts += struct.pack(self.endian + "HH", 0, 0) 

2582 

2583 self.f.write(self.build_block(block_type, block_idb, options=opts)) 

2584 

2585 def _write_block_spb(self, raw_pkt): 

2586 # type: (bytes) -> None 

2587 

2588 # Block Type 

2589 block_type = struct.pack(self.endian + "I", 3) 

2590 # Original Packet Length 

2591 block_spb = struct.pack(self.endian + "I", len(raw_pkt)) 

2592 # Packet Data 

2593 block_spb += raw_pkt 

2594 

2595 self.f.write(self.build_block(block_type, block_spb)) 

2596 

2597 def _write_block_epb(self, 

2598 raw_pkt, # type: bytes 

2599 ifid, # type: int 

2600 timestamp=None, # type: Optional[Union[EDecimal, float]] # noqa: E501 

2601 caplen=None, # type: Optional[int] 

2602 orglen=None, # type: Optional[int] 

2603 comments=None, # type: Optional[List[bytes]] 

2604 flags=None, # type: Optional[int] 

2605 ): 

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

2607 

2608 if timestamp: 

2609 tmp_ts = int(timestamp * self.tsresol) 

2610 ts_high = tmp_ts >> 32 

2611 ts_low = tmp_ts & 0xFFFFFFFF 

2612 else: 

2613 ts_high = ts_low = 0 

2614 

2615 if not caplen: 

2616 caplen = len(raw_pkt) 

2617 

2618 if not orglen: 

2619 orglen = len(raw_pkt) 

2620 

2621 # Block Type 

2622 block_type = struct.pack(self.endian + "I", 6) 

2623 # Interface ID 

2624 block_epb = struct.pack(self.endian + "I", ifid) 

2625 # Timestamp (High) 

2626 block_epb += struct.pack(self.endian + "I", ts_high) 

2627 # Timestamp (Low) 

2628 block_epb += struct.pack(self.endian + "I", ts_low) 

2629 # Captured Packet Length 

2630 block_epb += struct.pack(self.endian + "I", caplen) 

2631 # Original Packet Length 

2632 block_epb += struct.pack(self.endian + "I", orglen) 

2633 # Packet Data 

2634 block_epb += raw_pkt 

2635 

2636 # Options 

2637 opts = b'' 

2638 if comments and len(comments): 

2639 for c in comments: 

2640 comment = bytes_encode(c) 

2641 opts += struct.pack(self.endian + "HH", 1, len(comment)) 

2642 # Pad Option Value to 32 bits 

2643 opts += self._add_padding(comment) 

2644 if type(flags) == int: 

2645 opts += struct.pack(self.endian + "HH", 2, 4) 

2646 opts += struct.pack(self.endian + "I", flags) 

2647 if opts: 

2648 opts += struct.pack(self.endian + "HH", 0, 0) 

2649 

2650 self.f.write(self.build_block(block_type, block_epb, 

2651 options=opts)) 

2652 

2653 def _write_packet(self, # type: ignore 

2654 packet, # type: bytes 

2655 linktype, # type: int 

2656 sec=None, # type: Optional[float] 

2657 usec=None, # type: Optional[int] 

2658 caplen=None, # type: Optional[int] 

2659 wirelen=None, # type: Optional[int] 

2660 ifname=None, # type: Optional[bytes] 

2661 direction=None, # type: Optional[int] 

2662 comments=None, # type: Optional[List[bytes]] 

2663 ): 

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

2665 """ 

2666 Writes a single packet to the pcap file. 

2667 

2668 :param packet: bytes for a single packet 

2669 :type packet: bytes 

2670 :param linktype: linktype value associated with the packet 

2671 :type linktype: int 

2672 :param sec: time the packet was captured, in seconds since epoch. If 

2673 not supplied, defaults to now. 

2674 :type sec: float 

2675 :param caplen: The length of the packet in the capture file. If not 

2676 specified, uses ``len(packet)``. 

2677 :type caplen: int 

2678 :param wirelen: The length of the packet on the wire. If not 

2679 specified, uses ``caplen``. 

2680 :type wirelen: int 

2681 :param comment: UTF-8 string containing human-readable comment text 

2682 that is associated to the current block. Line separators 

2683 SHOULD be a carriage-return + linefeed ('\r\n') or 

2684 just linefeed ('\n'); either form may appear and 

2685 be considered a line separator. The string is not 

2686 zero-terminated. 

2687 :type bytes 

2688 :param ifname: UTF-8 string containing the 

2689 name of the device used to capture data. 

2690 The string is not zero-terminated. 

2691 :type bytes 

2692 :param direction: 0 = information not available, 

2693 1 = inbound, 

2694 2 = outbound 

2695 :type int 

2696 :return: None 

2697 :rtype: None 

2698 """ 

2699 if caplen is None: 

2700 caplen = len(packet) 

2701 if wirelen is None: 

2702 wirelen = caplen 

2703 

2704 ifid = self.interfaces2id.get(ifname, None) 

2705 if ifid is None: 

2706 ifid = max(self.interfaces2id.values()) + 1 

2707 self.interfaces2id[ifname] = ifid 

2708 self._write_block_idb(linktype=linktype, ifname=ifname) 

2709 

2710 # EPB flags (32 bits). 

2711 # currently only direction is implemented (least 2 significant bits) 

2712 if type(direction) == int: 

2713 flags = direction & 0x3 

2714 else: 

2715 flags = None 

2716 

2717 self._write_block_epb(packet, timestamp=sec, caplen=caplen, 

2718 orglen=wirelen, comments=comments, ifid=ifid, flags=flags) 

2719 if self.sync: 

2720 self.f.flush() 

2721 

2722 

2723class PcapWriter(RawPcapWriter): 

2724 """A stream PCAP writer with more control than wrpcap()""" 

2725 pass 

2726 

2727 

2728class PcapNgWriter(RawPcapNgWriter): 

2729 """A stream pcapng writer with more control than wrpcapng()""" 

2730 

2731 def _get_time(self, 

2732 packet, # type: Union[bytes, Packet] 

2733 sec, # type: Optional[float] 

2734 usec # type: Optional[int] 

2735 ): 

2736 # type: (...) -> Tuple[float, int] 

2737 if hasattr(packet, "time"): 

2738 if sec is None: 

2739 sec = float(packet.time) 

2740 

2741 if usec is None: 

2742 usec = 0 

2743 

2744 return sec, usec # type: ignore 

2745 

2746 

2747@conf.commands.register 

2748def rderf(filename, count=-1): 

2749 # type: (Union[IO[bytes], str], int) -> PacketList 

2750 """Read a ERF file and return a packet list 

2751 

2752 :param count: read only <count> packets 

2753 """ 

2754 with ERFEthernetReader(filename) as fdesc: 

2755 return fdesc.read_all(count=count) 

2756 

2757 

2758class ERFEthernetReader_metaclass(PcapReader_metaclass): 

2759 def __call__(cls, filename): 

2760 # type: (Union[IO[bytes], str]) -> Any 

2761 i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) # type: ignore 

2762 filename, fdesc = cls.open(filename) 

2763 try: 

2764 i.__init__(filename, fdesc) 

2765 return i 

2766 except (Scapy_Exception, EOFError): 

2767 pass 

2768 

2769 if "alternative" in cls.__dict__: 

2770 cls = cls.__dict__["alternative"] 

2771 i = cls.__new__( 

2772 cls, 

2773 cls.__name__, 

2774 cls.__bases__, 

2775 cls.__dict__ # type: ignore 

2776 ) 

2777 try: 

2778 i.__init__(filename, fdesc) 

2779 return i 

2780 except (Scapy_Exception, EOFError): 

2781 pass 

2782 

2783 raise Scapy_Exception("Not a supported capture file") 

2784 

2785 @staticmethod 

2786 def open(fname # type: ignore 

2787 ): 

2788 # type: (...) -> Tuple[str, _ByteStream] 

2789 """Open (if necessary) filename""" 

2790 if isinstance(fname, str): 

2791 filename = fname 

2792 try: 

2793 with gzip.open(filename, "rb") as tmp: 

2794 tmp.read(1) 

2795 fdesc = gzip.open(filename, "rb") # type: _ByteStream 

2796 except IOError: 

2797 fdesc = open(filename, "rb") 

2798 

2799 else: 

2800 fdesc = fname 

2801 filename = getattr(fdesc, "name", "No name") 

2802 return filename, fdesc 

2803 

2804 

2805class ERFEthernetReader(PcapReader, 

2806 metaclass=ERFEthernetReader_metaclass): 

2807 

2808 def __init__(self, filename, fdesc=None): # type: ignore 

2809 # type: (Union[IO[bytes], str], IO[bytes]) -> None 

2810 self.filename = filename # type: ignore 

2811 self.f = fdesc 

2812 self.power = Decimal(10) ** Decimal(-9) 

2813 

2814 # time is in 64-bits Endace's format which can be see here: 

2815 # https://www.endace.com/erf-extensible-record-format-types.pdf 

2816 def _convert_erf_timestamp(self, t): 

2817 # type: (int) -> EDecimal 

2818 sec = t >> 32 

2819 frac_sec = t & 0xffffffff 

2820 frac_sec *= 10**9 

2821 frac_sec += (frac_sec & 0x80000000) << 1 

2822 frac_sec >>= 32 

2823 return EDecimal(sec + self.power * frac_sec) 

2824 

2825 # The details of ERF Packet format can be see here: 

2826 # https://www.endace.com/erf-extensible-record-format-types.pdf 

2827 def read_packet(self, size=MTU, **kwargs): 

2828 # type: (int, **Any) -> Packet 

2829 

2830 # General ERF Header have exactly 16 bytes 

2831 hdr = self.f.read(16) 

2832 if len(hdr) < 16: 

2833 raise EOFError 

2834 

2835 # The timestamp is in little-endian byte-order. 

2836 time = struct.unpack('<Q', hdr[:8])[0] 

2837 # The rest is in big-endian byte-order. 

2838 # Ignoring flags and lctr (loss counter) since they are ERF specific 

2839 # header fields which Packet object does not support. 

2840 type, _, rlen, _, wlen = struct.unpack('>BBHHH', hdr[8:]) 

2841 # Check if the type != 0x02, type Ethernet 

2842 if type & 0x02 == 0: 

2843 raise Scapy_Exception("Invalid ERF Type (Not TYPE_ETH)") 

2844 

2845 # If there are extended headers, ignore it because Packet object does 

2846 # not support it. Extended headers size is 8 bytes before the payload. 

2847 if type & 0x80: 

2848 _ = self.f.read(8) 

2849 s = self.f.read(max(0, rlen - 24)) 

2850 else: 

2851 s = self.f.read(max(0, rlen - 16)) 

2852 

2853 # Ethernet has 2 bytes of padding containing `offset` and `pad`. Both 

2854 # of the fields are disregarded by Endace. 

2855 pb = s[2:size] 

2856 from scapy.layers.l2 import Ether 

2857 try: 

2858 p = Ether(pb, **kwargs) # type: Packet 

2859 except KeyboardInterrupt: 

2860 raise 

2861 except Exception: 

2862 if conf.debug_dissector: 

2863 from scapy.sendrecv import debug 

2864 debug.crashed_on = (Ether, s) 

2865 raise 

2866 if conf.raw_layer is None: 

2867 # conf.raw_layer is set on import 

2868 import scapy.packet # noqa: F401 

2869 p = conf.raw_layer(s) 

2870 

2871 p.time = self._convert_erf_timestamp(time) 

2872 p.wirelen = wlen 

2873 

2874 return p 

2875 

2876 

2877@conf.commands.register 

2878def wrerf(filename, # type: Union[IO[bytes], str] 

2879 pkt, # type: _PacketIterable 

2880 *args, # type: Any 

2881 **kargs # type: Any 

2882 ): 

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

2884 """Write a list of packets to a ERF file 

2885 

2886 :param filename: the name of the file to write packets to, or an open, 

2887 writable file-like object. The file descriptor will be 

2888 closed at the end of the call, so do not use an object you 

2889 do not want to close (e.g., running wrerf(sys.stdout, []) 

2890 in interactive mode will crash Scapy). 

2891 :param gz: set to 1 to save a gzipped capture 

2892 :param append: append packets to the capture file instead of 

2893 truncating it 

2894 :param sync: do not bufferize writes to the capture file 

2895 """ 

2896 with ERFEthernetWriter(filename, *args, **kargs) as fdesc: 

2897 fdesc.write(pkt) 

2898 

2899 

2900class ERFEthernetWriter(PcapWriter): 

2901 """A stream ERF Ethernet writer with more control than wrerf()""" 

2902 

2903 def __init__(self, 

2904 filename, # type: Union[IO[bytes], str] 

2905 gz=False, # type: bool 

2906 append=False, # type: bool 

2907 sync=False, # type: bool 

2908 ): 

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

2910 """ 

2911 :param filename: the name of the file to write packets to, or an open, 

2912 writable file-like object. 

2913 :param gz: compress the capture on the fly 

2914 :param append: append packets to the capture file instead of 

2915 truncating it 

2916 :param sync: do not bufferize writes to the capture file 

2917 """ 

2918 super(ERFEthernetWriter, self).__init__(filename, 

2919 gz=gz, 

2920 append=append, 

2921 sync=sync) 

2922 

2923 def write(self, pkt): # type: ignore 

2924 # type: (_PacketIterable) -> None 

2925 """ 

2926 Writes a Packet, a SndRcvList object, or bytes to a ERF file. 

2927 

2928 :param pkt: Packet(s) to write (one record for each Packet) 

2929 :type pkt: iterable[scapy.packet.Packet], scapy.packet.Packet 

2930 """ 

2931 # Import here to avoid circular dependency 

2932 from scapy.supersocket import IterSocket 

2933 for p in IterSocket(pkt).iter: 

2934 self.write_packet(p) 

2935 

2936 def write_packet(self, pkt): # type: ignore 

2937 # type: (Packet) -> None 

2938 

2939 if hasattr(pkt, "time"): 

2940 sec = int(pkt.time) 

2941 usec = int((int(round((pkt.time - sec) * 10**9)) << 32) / 10**9) 

2942 t = (sec << 32) + usec 

2943 else: 

2944 t = int(time.time()) << 32 

2945 

2946 # There are 16 bytes of headers + 2 bytes of padding before the packets 

2947 # payload. 

2948 rlen = len(pkt) + 18 

2949 

2950 if hasattr(pkt, "wirelen"): 

2951 wirelen = pkt.wirelen 

2952 if wirelen is None: 

2953 wirelen = rlen 

2954 

2955 self.f.write(struct.pack("<Q", t)) 

2956 self.f.write(struct.pack(">BBHHHH", 2, 0, rlen, 0, wirelen, 0)) 

2957 self.f.write(bytes(pkt)) 

2958 self.f.flush() 

2959 

2960 def close(self): 

2961 # type: () -> Optional[Any] 

2962 return self.f.close() 

2963 

2964 

2965@conf.commands.register 

2966def import_hexcap(input_string=None): 

2967 # type: (Optional[str]) -> bytes 

2968 """Imports a tcpdump like hexadecimal view 

2969 

2970 e.g: exported via hexdump() or tcpdump or wireshark's "export as hex" 

2971 

2972 :param input_string: String containing the hexdump input to parse. If None, 

2973 read from standard input. 

2974 """ 

2975 re_extract_hexcap = re.compile(r"^((0x)?[0-9a-fA-F]{2,}[ :\t]{,3}|) *(([0-9a-fA-F]{2} {,2}){,16})") # noqa: E501 

2976 p = "" 

2977 try: 

2978 if input_string: 

2979 input_function = StringIO(input_string).readline 

2980 else: 

2981 input_function = input 

2982 while True: 

2983 line = input_function().strip() 

2984 if not line: 

2985 break 

2986 try: 

2987 p += re_extract_hexcap.match(line).groups()[2] # type: ignore 

2988 except Exception: 

2989 warning("Parsing error during hexcap") 

2990 continue 

2991 except EOFError: 

2992 pass 

2993 

2994 p = p.replace(" ", "") 

2995 return hex_bytes(p) 

2996 

2997 

2998@conf.commands.register 

2999def wireshark(pktlist, wait=False, **kwargs): 

3000 # type: (List[Packet], bool, **Any) -> Optional[Any] 

3001 """ 

3002 Runs Wireshark on a list of packets. 

3003 

3004 See :func:`tcpdump` for more parameter description. 

3005 

3006 Note: this defaults to wait=False, to run Wireshark in the background. 

3007 """ 

3008 return tcpdump(pktlist, prog=conf.prog.wireshark, wait=wait, **kwargs) 

3009 

3010 

3011@conf.commands.register 

3012def tdecode( 

3013 pktlist, # type: Union[IO[bytes], None, str, _PacketIterable] 

3014 args=None, # type: Optional[List[str]] 

3015 **kwargs # type: Any 

3016): 

3017 # type: (...) -> Any 

3018 """ 

3019 Run tshark on a list of packets. 

3020 

3021 :param args: If not specified, defaults to ``tshark -V``. 

3022 

3023 See :func:`tcpdump` for more parameters. 

3024 """ 

3025 if args is None: 

3026 args = ["-V"] 

3027 return tcpdump(pktlist, prog=conf.prog.tshark, args=args, **kwargs) 

3028 

3029 

3030def _guess_linktype_name(value): 

3031 # type: (int) -> str 

3032 """Guess the DLT name from its value.""" 

3033 from scapy.libs.winpcapy import pcap_datalink_val_to_name 

3034 return cast(bytes, pcap_datalink_val_to_name(value)).decode() 

3035 

3036 

3037def _guess_linktype_value(name): 

3038 # type: (str) -> int 

3039 """Guess the value of a DLT name.""" 

3040 from scapy.libs.winpcapy import pcap_datalink_name_to_val 

3041 val = cast(int, pcap_datalink_name_to_val(name.encode())) 

3042 if val == -1: 

3043 warning("Unknown linktype: %s. Using EN10MB", name) 

3044 return DLT_EN10MB 

3045 return val 

3046 

3047 

3048@conf.commands.register 

3049def tcpdump( 

3050 pktlist=None, # type: Union[IO[bytes], None, str, _PacketIterable] 

3051 dump=False, # type: bool 

3052 getfd=False, # type: bool 

3053 args=None, # type: Optional[List[str]] 

3054 flt=None, # type: Optional[str] 

3055 prog=None, # type: Optional[Any] 

3056 getproc=False, # type: bool 

3057 quiet=False, # type: bool 

3058 use_tempfile=None, # type: Optional[Any] 

3059 read_stdin_opts=None, # type: Optional[Any] 

3060 linktype=None, # type: Optional[Any] 

3061 wait=True, # type: bool 

3062 _suppress=False # type: bool 

3063): 

3064 # type: (...) -> Any 

3065 """Run tcpdump or tshark on a list of packets. 

3066 

3067 When using ``tcpdump`` on OSX (``prog == conf.prog.tcpdump``), this uses a 

3068 temporary file to store the packets. This works around a bug in Apple's 

3069 version of ``tcpdump``: http://apple.stackexchange.com/questions/152682/ 

3070 

3071 Otherwise, the packets are passed in stdin. 

3072 

3073 This function can be explicitly enabled or disabled with the 

3074 ``use_tempfile`` parameter. 

3075 

3076 When using ``wireshark``, it will be called with ``-ki -`` to start 

3077 immediately capturing packets from stdin. 

3078 

3079 Otherwise, the command will be run with ``-r -`` (which is correct for 

3080 ``tcpdump`` and ``tshark``). 

3081 

3082 This can be overridden with ``read_stdin_opts``. This has no effect when 

3083 ``use_tempfile=True``, or otherwise reading packets from a regular file. 

3084 

3085 :param pktlist: a Packet instance, a PacketList instance or a list of 

3086 Packet instances. Can also be a filename (as a string), an open 

3087 file-like object that must be a file format readable by 

3088 tshark (Pcap, PcapNg, etc.) or None (to sniff) 

3089 :param flt: a filter to use with tcpdump 

3090 :param dump: when set to True, returns a string instead of displaying it. 

3091 :param getfd: when set to True, returns a file-like object to read data 

3092 from tcpdump or tshark from. 

3093 :param getproc: when set to True, the subprocess.Popen object is returned 

3094 :param args: arguments (as a list) to pass to tshark (example for tshark: 

3095 args=["-T", "json"]). 

3096 :param prog: program to use (defaults to tcpdump, will work with tshark) 

3097 :param quiet: when set to True, the process stderr is discarded 

3098 :param use_tempfile: When set to True, always use a temporary file to store 

3099 packets. 

3100 When set to False, pipe packets through stdin. 

3101 When set to None (default), only use a temporary file with 

3102 ``tcpdump`` on OSX. 

3103 :param read_stdin_opts: When set, a list of arguments needed to capture 

3104 from stdin. Otherwise, attempts to guess. 

3105 :param linktype: A custom DLT value or name, to overwrite the default 

3106 values. 

3107 :param wait: If True (default), waits for the process to terminate before 

3108 returning to Scapy. If False, the process will be detached to the 

3109 background. If dump, getproc or getfd is True, these have the same 

3110 effect as ``wait=False``. 

3111 

3112 Examples:: 

3113 

3114 >>> tcpdump([IP()/TCP(), IP()/UDP()]) 

3115 reading from file -, link-type RAW (Raw IP) 

3116 16:46:00.474515 IP 127.0.0.1.20 > 127.0.0.1.80: Flags [S], seq 0, win 8192, length 0 # noqa: E501 

3117 16:46:00.475019 IP 127.0.0.1.53 > 127.0.0.1.53: [|domain] 

3118 

3119 >>> tcpdump([IP()/TCP(), IP()/UDP()], prog=conf.prog.tshark) 

3120 1 0.000000 127.0.0.1 -> 127.0.0.1 TCP 40 20->80 [SYN] Seq=0 Win=8192 Len=0 # noqa: E501 

3121 2 0.000459 127.0.0.1 -> 127.0.0.1 UDP 28 53->53 Len=0 

3122 

3123 To get a JSON representation of a tshark-parsed PacketList(), one can:: 

3124 

3125 >>> import json, pprint 

3126 >>> json_data = json.load(tcpdump(IP(src="217.25.178.5", 

3127 ... dst="45.33.32.156"), 

3128 ... prog=conf.prog.tshark, 

3129 ... args=["-T", "json"], 

3130 ... getfd=True)) 

3131 >>> pprint.pprint(json_data) 

3132 [{u'_index': u'packets-2016-12-23', 

3133 u'_score': None, 

3134 u'_source': {u'layers': {u'frame': {u'frame.cap_len': u'20', 

3135 u'frame.encap_type': u'7', 

3136 [...] 

3137 }, 

3138 u'ip': {u'ip.addr': u'45.33.32.156', 

3139 u'ip.checksum': u'0x0000a20d', 

3140 [...] 

3141 u'ip.ttl': u'64', 

3142 u'ip.version': u'4'}, 

3143 u'raw': u'Raw packet data'}}, 

3144 u'_type': u'pcap_file'}] 

3145 >>> json_data[0]['_source']['layers']['ip']['ip.ttl'] 

3146 u'64' 

3147 """ 

3148 getfd = getfd or getproc 

3149 if prog is None: 

3150 if not conf.prog.tcpdump: 

3151 raise Scapy_Exception( 

3152 "tcpdump is not available" 

3153 ) 

3154 prog = [conf.prog.tcpdump] 

3155 elif isinstance(prog, str): 

3156 prog = [prog] 

3157 else: 

3158 raise ValueError("prog must be a string") 

3159 

3160 if linktype is not None: 

3161 if isinstance(linktype, int): 

3162 # Guess name from value 

3163 try: 

3164 linktype_name = _guess_linktype_name(linktype) 

3165 except StopIteration: 

3166 linktype = -1 

3167 else: 

3168 # Guess value from name 

3169 if linktype.startswith("DLT_"): 

3170 linktype = linktype[4:] 

3171 linktype_name = linktype 

3172 try: 

3173 linktype = _guess_linktype_value(linktype) 

3174 except KeyError: 

3175 linktype = -1 

3176 if linktype == -1: 

3177 raise ValueError( 

3178 "Unknown linktype. Try passing its datalink name instead" 

3179 ) 

3180 prog += ["-y", linktype_name] 

3181 

3182 # Build Popen arguments 

3183 if args is None: 

3184 args = [] 

3185 else: 

3186 # Make a copy of args 

3187 args = list(args) 

3188 

3189 if flt is not None: 

3190 # Check the validity of the filter 

3191 if linktype is None and isinstance(pktlist, str): 

3192 # linktype is unknown but required. Read it from file 

3193 with PcapReader(pktlist) as rd: 

3194 if isinstance(rd, PcapNgReader): 

3195 # Get the linktype from the first packet 

3196 try: 

3197 _, metadata = rd._read_packet() 

3198 linktype = metadata.linktype 

3199 if OPENBSD and linktype == 228: 

3200 linktype = DLT_RAW 

3201 except EOFError: 

3202 raise ValueError( 

3203 "Cannot get linktype from a PcapNg packet." 

3204 ) 

3205 else: 

3206 linktype = rd.linktype 

3207 from scapy.arch.common import compile_filter 

3208 compile_filter(flt, linktype=linktype) 

3209 args.append(flt) 

3210 

3211 stdout = subprocess.PIPE if dump or getfd else None 

3212 stderr = open(os.devnull) if quiet else None 

3213 proc = None 

3214 

3215 if use_tempfile is None: 

3216 # Apple's tcpdump cannot read from stdin, see: 

3217 # http://apple.stackexchange.com/questions/152682/ 

3218 use_tempfile = DARWIN and prog[0] == conf.prog.tcpdump 

3219 

3220 if read_stdin_opts is None: 

3221 if prog[0] == conf.prog.wireshark: 

3222 # Start capturing immediately (-k) from stdin (-i -) 

3223 read_stdin_opts = ["-ki", "-"] 

3224 elif prog[0] == conf.prog.tcpdump and not OPENBSD: 

3225 # Capture in packet-buffered mode (-U) from stdin (-r -) 

3226 read_stdin_opts = ["-U", "-r", "-"] 

3227 else: 

3228 read_stdin_opts = ["-r", "-"] 

3229 else: 

3230 # Make a copy of read_stdin_opts 

3231 read_stdin_opts = list(read_stdin_opts) 

3232 

3233 if pktlist is None: 

3234 # sniff 

3235 with ContextManagerSubprocess(prog[0], suppress=_suppress): 

3236 proc = subprocess.Popen( 

3237 prog + args, 

3238 stdout=stdout, 

3239 stderr=stderr, 

3240 ) 

3241 elif isinstance(pktlist, str): 

3242 # file 

3243 with ContextManagerSubprocess(prog[0], suppress=_suppress): 

3244 proc = subprocess.Popen( 

3245 prog + ["-r", pktlist] + args, 

3246 stdout=stdout, 

3247 stderr=stderr, 

3248 ) 

3249 elif use_tempfile: 

3250 tmpfile = get_temp_file( # type: ignore 

3251 autoext=".pcap", 

3252 fd=True 

3253 ) # type: IO[bytes] 

3254 try: 

3255 tmpfile.writelines( 

3256 iter(lambda: pktlist.read(1048576), b"") # type: ignore 

3257 ) 

3258 except AttributeError: 

3259 pktlist = cast("_PacketIterable", pktlist) 

3260 wrpcap(tmpfile, pktlist, linktype=linktype) 

3261 else: 

3262 tmpfile.close() 

3263 with ContextManagerSubprocess(prog[0], suppress=_suppress): 

3264 proc = subprocess.Popen( 

3265 prog + ["-r", tmpfile.name] + args, 

3266 stdout=stdout, 

3267 stderr=stderr, 

3268 ) 

3269 else: 

3270 try: 

3271 pktlist.fileno() # type: ignore 

3272 # pass the packet stream 

3273 with ContextManagerSubprocess(prog[0], suppress=_suppress): 

3274 proc = subprocess.Popen( 

3275 prog + read_stdin_opts + args, 

3276 stdin=pktlist, # type: ignore 

3277 stdout=stdout, 

3278 stderr=stderr, 

3279 ) 

3280 except (AttributeError, ValueError): 

3281 # write the packet stream to stdin 

3282 with ContextManagerSubprocess(prog[0], suppress=_suppress): 

3283 proc = subprocess.Popen( 

3284 prog + read_stdin_opts + args, 

3285 stdin=subprocess.PIPE, 

3286 stdout=stdout, 

3287 stderr=stderr, 

3288 ) 

3289 if proc is None: 

3290 # An error has occurred 

3291 return 

3292 try: 

3293 proc.stdin.writelines( # type: ignore 

3294 iter(lambda: pktlist.read(1048576), b"") # type: ignore 

3295 ) 

3296 except AttributeError: 

3297 wrpcap(proc.stdin, pktlist, linktype=linktype) # type: ignore 

3298 except UnboundLocalError: 

3299 # The error was handled by ContextManagerSubprocess 

3300 pass 

3301 else: 

3302 proc.stdin.close() # type: ignore 

3303 if proc is None: 

3304 # An error has occurred 

3305 return 

3306 if dump: 

3307 data = b"".join( 

3308 iter(lambda: proc.stdout.read(1048576), b"") # type: ignore 

3309 ) 

3310 proc.terminate() 

3311 return data 

3312 if getproc: 

3313 return proc 

3314 if getfd: 

3315 return proc.stdout 

3316 if wait: 

3317 proc.wait() 

3318 

3319 

3320@conf.commands.register 

3321def hexedit(pktlist): 

3322 # type: (_PacketIterable) -> PacketList 

3323 """Run hexedit on a list of packets, then return the edited packets.""" 

3324 f = get_temp_file() 

3325 wrpcap(f, pktlist) 

3326 with ContextManagerSubprocess(conf.prog.hexedit): 

3327 subprocess.call([conf.prog.hexedit, f]) 

3328 rpktlist = rdpcap(f) 

3329 os.unlink(f) 

3330 return rpktlist 

3331 

3332 

3333def get_terminal_width(): 

3334 # type: () -> Optional[int] 

3335 """Get terminal width (number of characters) if in a window. 

3336 

3337 Notice: this will try several methods in order to 

3338 support as many terminals and OS as possible. 

3339 """ 

3340 sizex = shutil.get_terminal_size(fallback=(0, 0))[0] 

3341 if sizex != 0: 

3342 return sizex 

3343 # Backups 

3344 if WINDOWS: 

3345 from ctypes import windll, create_string_buffer 

3346 # http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/ 

3347 h = windll.kernel32.GetStdHandle(-12) 

3348 csbi = create_string_buffer(22) 

3349 res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) 

3350 if res: 

3351 (bufx, bufy, curx, cury, wattr, 

3352 left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) # noqa: E501 

3353 sizex = right - left + 1 

3354 # sizey = bottom - top + 1 

3355 return sizex 

3356 return sizex 

3357 # We have various methods 

3358 # COLUMNS is set on some terminals 

3359 try: 

3360 sizex = int(os.environ['COLUMNS']) 

3361 except Exception: 

3362 pass 

3363 if sizex: 

3364 return sizex 

3365 # We can query TIOCGWINSZ 

3366 try: 

3367 import fcntl 

3368 import termios 

3369 s = struct.pack('HHHH', 0, 0, 0, 0) 

3370 x = fcntl.ioctl(1, termios.TIOCGWINSZ, s) 

3371 sizex = struct.unpack('HHHH', x)[1] 

3372 except (IOError, ModuleNotFoundError): 

3373 # If everything failed, return default terminal size 

3374 sizex = 79 

3375 return sizex 

3376 

3377 

3378def pretty_list(rtlst, # type: List[Tuple[Union[str, List[str]], ...]] 

3379 header, # type: List[Tuple[str, ...]] 

3380 sortBy=0, # type: Optional[int] 

3381 borders=False, # type: bool 

3382 ): 

3383 # type: (...) -> str 

3384 """ 

3385 Pretty list to fit the terminal, and add header. 

3386 

3387 :param rtlst: a list of tuples. each tuple contains a value which can 

3388 be either a string or a list of string. 

3389 :param sortBy: the column id (starting with 0) which will be used for 

3390 ordering 

3391 :param borders: whether to put borders on the table or not 

3392 """ 

3393 if borders: 

3394 _space = "|" 

3395 else: 

3396 _space = " " 

3397 cols = len(header[0]) 

3398 # Windows has a fat terminal border 

3399 _spacelen = len(_space) * (cols - 1) + int(WINDOWS) 

3400 _croped = False 

3401 if sortBy is not None: 

3402 # Sort correctly 

3403 rtlst.sort(key=lambda x: x[sortBy]) 

3404 # Resolve multi-values 

3405 for i, line in enumerate(rtlst): 

3406 ids = [] # type: List[int] 

3407 values = [] # type: List[Union[str, List[str]]] 

3408 for j, val in enumerate(line): 

3409 if isinstance(val, list): 

3410 ids.append(j) 

3411 values.append(val or " ") 

3412 if values: 

3413 del rtlst[i] 

3414 k = 0 

3415 for ex_vals in zip_longest(*values, fillvalue=" "): 

3416 if k: 

3417 extra_line = [" "] * cols 

3418 else: 

3419 extra_line = list(line) # type: ignore 

3420 for j, h in enumerate(ids): 

3421 extra_line[h] = ex_vals[j] 

3422 rtlst.insert(i + k, tuple(extra_line)) 

3423 k += 1 

3424 rtslst = cast(List[Tuple[str, ...]], rtlst) 

3425 # Append tag 

3426 rtslst = header + rtslst 

3427 # Detect column's width 

3428 colwidth = [max(len(y) for y in x) for x in zip(*rtslst)] 

3429 # Make text fit in box (if required) 

3430 width = get_terminal_width() 

3431 if conf.auto_crop_tables and width: 

3432 width = width - _spacelen 

3433 while sum(colwidth) > width: 

3434 _croped = True 

3435 # Needs to be cropped 

3436 # Get the longest row 

3437 i = colwidth.index(max(colwidth)) 

3438 # Get all elements of this row 

3439 row = [len(x[i]) for x in rtslst] 

3440 # Get biggest element of this row: biggest of the array 

3441 j = row.index(max(row)) 

3442 # Re-build column tuple with the edited element 

3443 t = list(rtslst[j]) 

3444 t[i] = t[i][:-2] + "_" 

3445 rtslst[j] = tuple(t) 

3446 # Update max size 

3447 row[j] = len(t[i]) 

3448 colwidth[i] = max(row) 

3449 if _croped: 

3450 log_runtime.info("Table cropped to fit the terminal (conf.auto_crop_tables==True)") # noqa: E501 

3451 # Generate padding scheme 

3452 fmt = _space.join(["%%-%ds" % x for x in colwidth]) 

3453 # Append separation line if needed 

3454 if borders: 

3455 rtslst.insert(1, tuple("-" * x for x in colwidth)) 

3456 # Compile 

3457 return "\n".join(fmt % x for x in rtslst) 

3458 

3459 

3460def human_size(x, fmt=".1f"): 

3461 # type: (int, str) -> str 

3462 """ 

3463 Convert a size in octets to a human string representation 

3464 """ 

3465 units = ['K', 'M', 'G', 'T', 'P', 'E'] 

3466 if not x: 

3467 return "0B" 

3468 i = int(math.log(x, 2**10)) 

3469 if i and i < len(units): 

3470 return format(x / 2**(10 * i), fmt) + units[i - 1] 

3471 return str(x) + "B" 

3472 

3473 

3474def __make_table( 

3475 yfmtfunc, # type: Callable[[int], str] 

3476 fmtfunc, # type: Callable[[int], str] 

3477 endline, # type: str 

3478 data, # type: List[Tuple[Packet, Packet]] 

3479 fxyz, # type: Callable[[Packet, Packet], Tuple[Any, Any, Any]] 

3480 sortx=None, # type: Optional[Callable[[str], Tuple[Any, ...]]] 

3481 sorty=None, # type: Optional[Callable[[str], Tuple[Any, ...]]] 

3482 seplinefunc=None, # type: Optional[Callable[[int, List[int]], str]] 

3483 dump=False, # type: bool 

3484 stringconv=str, # type: Callable[[str], str] 

3485): 

3486 # type: (...) -> Optional[str] 

3487 """Core function of the make_table suite, which generates the table""" 

3488 vx = {} # type: Dict[str, int] 

3489 vy = {} # type: Dict[str, Optional[int]] 

3490 vz = {} # type: Dict[Tuple[str, str], str] 

3491 vxf = {} # type: Dict[str, str] 

3492 

3493 tmp_len = 0 

3494 for e in data: 

3495 xx, yy, zz = [str(s) for s in fxyz(*e)] 

3496 tmp_len = max(len(yy), tmp_len) 

3497 vx[xx] = max(vx.get(xx, 0), len(xx), len(zz)) 

3498 vy[yy] = None 

3499 vz[(xx, yy)] = zz 

3500 

3501 vxk = list(vx) 

3502 vyk = list(vy) 

3503 if sortx: 

3504 vxk.sort(key=sortx) 

3505 else: 

3506 try: 

3507 vxk.sort(key=int) 

3508 except Exception: 

3509 try: 

3510 vxk.sort(key=atol) 

3511 except Exception: 

3512 vxk.sort() 

3513 if sorty: 

3514 vyk.sort(key=sorty) 

3515 else: 

3516 try: 

3517 vyk.sort(key=int) 

3518 except Exception: 

3519 try: 

3520 vyk.sort(key=atol) 

3521 except Exception: 

3522 vyk.sort() 

3523 

3524 s = "" 

3525 if seplinefunc: 

3526 sepline = seplinefunc(tmp_len, [vx[x] for x in vxk]) 

3527 s += sepline + "\n" 

3528 

3529 fmt = yfmtfunc(tmp_len) 

3530 s += fmt % "" 

3531 s += ' ' 

3532 for x in vxk: 

3533 vxf[x] = fmtfunc(vx[x]) 

3534 s += vxf[x] % stringconv(x) 

3535 s += ' ' 

3536 s += endline + "\n" 

3537 if seplinefunc: 

3538 s += sepline + "\n" 

3539 for y in vyk: 

3540 s += fmt % stringconv(y) 

3541 s += ' ' 

3542 for x in vxk: 

3543 s += vxf[x] % stringconv(vz.get((x, y), "-")) 

3544 s += ' ' 

3545 s += endline + "\n" 

3546 if seplinefunc: 

3547 s += sepline + "\n" 

3548 

3549 if dump: 

3550 return s 

3551 else: 

3552 print(s, end="") 

3553 return None 

3554 

3555 

3556def make_table(*args, **kargs): 

3557 # type: (*Any, **Any) -> Optional[Any] 

3558 return __make_table( 

3559 lambda l: "%%-%is" % l, 

3560 lambda l: "%%-%is" % l, 

3561 "", 

3562 *args, 

3563 **kargs 

3564 ) 

3565 

3566 

3567def make_lined_table(*args, **kargs): 

3568 # type: (*Any, **Any) -> Optional[str] 

3569 return __make_table( # type: ignore 

3570 lambda l: "%%-%is |" % l, 

3571 lambda l: "%%-%is |" % l, 

3572 "", 

3573 *args, 

3574 seplinefunc=lambda a, x: "+".join( 

3575 '-' * (y + 2) for y in [a - 1] + x + [-2] 

3576 ), 

3577 **kargs 

3578 ) 

3579 

3580 

3581def make_tex_table(*args, **kargs): 

3582 # type: (*Any, **Any) -> Optional[str] 

3583 return __make_table( # type: ignore 

3584 lambda l: "%s", 

3585 lambda l: "& %s", 

3586 "\\\\", 

3587 *args, 

3588 seplinefunc=lambda a, x: "\\hline", 

3589 stringconv=tex_escape, 

3590 **kargs 

3591 ) 

3592 

3593#################### 

3594# WHOIS CLIENT # 

3595#################### 

3596 

3597 

3598def whois(ip_address): 

3599 # type: (str) -> bytes 

3600 """Whois client for Python""" 

3601 whois_ip = str(ip_address) 

3602 try: 

3603 query = socket.gethostbyname(whois_ip) 

3604 except Exception: 

3605 query = whois_ip 

3606 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

3607 s.connect(("whois.ripe.net", 43)) 

3608 s.send(query.encode("utf8") + b"\r\n") 

3609 answer = b"" 

3610 while True: 

3611 d = s.recv(4096) 

3612 answer += d 

3613 if not d: 

3614 break 

3615 s.close() 

3616 ignore_tag = b"remarks:" 

3617 # ignore all lines starting with the ignore_tag 

3618 lines = [line for line in answer.split(b"\n") if not line or (line and not line.startswith(ignore_tag))] # noqa: E501 

3619 # remove empty lines at the bottom 

3620 for i in range(1, len(lines)): 

3621 if not lines[-i].strip(): 

3622 del lines[-i] 

3623 else: 

3624 break 

3625 return b"\n".join(lines[3:]) 

3626 

3627#################### 

3628# CLI utils # 

3629#################### 

3630 

3631 

3632class _CLIUtilMetaclass(type): 

3633 class TYPE(enum.Enum): 

3634 COMMAND = 0 

3635 OUTPUT = 1 

3636 COMPLETE = 2 

3637 

3638 def __new__(cls, # type: Type[_CLIUtilMetaclass] 

3639 name, # type: str 

3640 bases, # type: Tuple[type, ...] 

3641 dct # type: Dict[str, Any] 

3642 ): 

3643 # type: (...) -> Type[CLIUtil] 

3644 dct["commands"] = { 

3645 x.__name__: x 

3646 for x in dct.values() 

3647 if getattr(x, "cliutil_type", None) == _CLIUtilMetaclass.TYPE.COMMAND 

3648 } 

3649 dct["commands_output"] = { 

3650 x.cliutil_ref.__name__: x 

3651 for x in dct.values() 

3652 if getattr(x, "cliutil_type", None) == _CLIUtilMetaclass.TYPE.OUTPUT 

3653 } 

3654 dct["commands_complete"] = { 

3655 x.cliutil_ref.__name__: x 

3656 for x in dct.values() 

3657 if getattr(x, "cliutil_type", None) == _CLIUtilMetaclass.TYPE.COMPLETE 

3658 } 

3659 newcls = cast(Type['CLIUtil'], type.__new__(cls, name, bases, dct)) 

3660 return newcls 

3661 

3662 

3663class CLIUtil(metaclass=_CLIUtilMetaclass): 

3664 """ 

3665 Provides a Util class to easily create simple CLI tools in Scapy, 

3666 that can still be used as an API. 

3667 

3668 Doc: 

3669 - override the ps1() function 

3670 - register commands with the @CLIUtil.addcomment decorator 

3671 - call the loop() function when ready 

3672 """ 

3673 

3674 def _depcheck(self) -> None: 

3675 """ 

3676 Check that all dependencies are installed 

3677 """ 

3678 try: 

3679 import prompt_toolkit # noqa: F401 

3680 except ImportError: 

3681 # okay we lie but prompt_toolkit is a dependency... 

3682 raise ImportError("You need to have IPython installed to use the CLI") 

3683 

3684 # Okay let's do nice code 

3685 commands: Dict[str, Callable[..., Any]] = {} 

3686 # print output of command 

3687 commands_output: Dict[str, Callable[..., str]] = {} 

3688 # provides completion to command 

3689 commands_complete: Dict[str, Callable[..., List[str]]] = {} 

3690 

3691 def __init__(self, cli: bool = True, debug: bool = False) -> None: 

3692 """ 

3693 DEV: overwrite 

3694 """ 

3695 if cli: 

3696 self._depcheck() 

3697 self.loop(debug=debug) 

3698 

3699 @staticmethod 

3700 def _inspectkwargs(func: DecoratorCallable) -> None: 

3701 """ 

3702 Internal function to parse arguments from the kwargs of the functions 

3703 """ 

3704 func._flagnames = [ # type: ignore 

3705 x.name for x in 

3706 inspect.signature(func).parameters.values() 

3707 if x.kind == inspect.Parameter.KEYWORD_ONLY 

3708 ] 

3709 func._flags = [ # type: ignore 

3710 ("-%s" % x) if len(x) == 1 else ("--%s" % x) 

3711 for x in func._flagnames # type: ignore 

3712 ] 

3713 

3714 @staticmethod 

3715 def _parsekwargs( 

3716 func: DecoratorCallable, 

3717 args: List[str] 

3718 ) -> Tuple[List[str], Dict[str, Literal[True]]]: 

3719 """ 

3720 Internal function to parse CLI arguments of a function. 

3721 """ 

3722 kwargs: Dict[str, Literal[True]] = {} 

3723 if func._flags: # type: ignore 

3724 i = 0 

3725 for arg in args: 

3726 if arg in func._flags: # type: ignore 

3727 i += 1 

3728 kwargs[func._flagnames[func._flags.index(arg)]] = True # type: ignore # noqa: E501 

3729 continue 

3730 break 

3731 args = args[i:] 

3732 return args, kwargs 

3733 

3734 @classmethod 

3735 def _parseallargs( 

3736 cls, 

3737 func: DecoratorCallable, 

3738 cmd: str, args: List[str] 

3739 ) -> Tuple[List[str], Dict[str, Literal[True]], Dict[str, Literal[True]]]: 

3740 """ 

3741 Internal function to parse CLI arguments of both the function 

3742 and its output function. 

3743 """ 

3744 args, kwargs = cls._parsekwargs(func, args) 

3745 outkwargs: Dict[str, Literal[True]] = {} 

3746 if cmd in cls.commands_output: 

3747 args, outkwargs = cls._parsekwargs(cls.commands_output[cmd], args) 

3748 return args, kwargs, outkwargs 

3749 

3750 @classmethod 

3751 def addcommand( 

3752 cls, 

3753 mono: bool = False, 

3754 globsupport: bool = False, 

3755 ) -> Callable[[DecoratorCallable], DecoratorCallable]: 

3756 """ 

3757 Decorator to register a command 

3758 

3759 :param mono: if True, the command takes a single argument even 

3760 if there are spaces. 

3761 """ 

3762 def func(cmd: DecoratorCallable) -> DecoratorCallable: 

3763 cmd.cliutil_type = _CLIUtilMetaclass.TYPE.COMMAND # type: ignore 

3764 cmd._mono = mono # type: ignore 

3765 cmd._globsupport = globsupport # type: ignore 

3766 cls._inspectkwargs(cmd) 

3767 if cmd._globsupport and not cmd._mono: # type: ignore 

3768 raise ValueError("Cannot use globsupport without mono.") 

3769 return cmd 

3770 return func 

3771 

3772 @classmethod 

3773 def addoutput(cls, cmd: DecoratorCallable) -> Callable[[DecoratorCallable], DecoratorCallable]: # noqa: E501 

3774 """ 

3775 Decorator to register a command output processor 

3776 """ 

3777 def func(processor: DecoratorCallable) -> DecoratorCallable: 

3778 processor.cliutil_type = _CLIUtilMetaclass.TYPE.OUTPUT # type: ignore 

3779 processor.cliutil_ref = cmd # type: ignore 

3780 cls._inspectkwargs(processor) 

3781 return processor 

3782 return func 

3783 

3784 @classmethod 

3785 def addcomplete( 

3786 cls, 

3787 cmd: DecoratorCallable, 

3788 ) -> Callable[[DecoratorCallable], DecoratorCallable]: 

3789 """ 

3790 Decorator to register a command completor 

3791 """ 

3792 def func(processor: DecoratorCallable) -> DecoratorCallable: 

3793 processor.cliutil_type = _CLIUtilMetaclass.TYPE.COMPLETE # type: ignore 

3794 processor.cliutil_ref = cmd # type: ignore 

3795 processor._mono = cmd._mono # type: ignore 

3796 return processor 

3797 return func 

3798 

3799 def ps1(self) -> str: 

3800 """ 

3801 Return the PS1 of the shell 

3802 """ 

3803 return "> " 

3804 

3805 def close(self) -> None: 

3806 """ 

3807 Function called on exiting 

3808 """ 

3809 print("Exited") 

3810 

3811 def help(self, cmd: Optional[str] = None) -> None: 

3812 """ 

3813 Return the help related to this CLI util 

3814 """ 

3815 def _args(func: Any) -> str: 

3816 flags = func._flags.copy() 

3817 if func.__name__ in self.commands_output: 

3818 flags += self.commands_output[func.__name__]._flags # type: ignore 

3819 return " %s%s" % ( 

3820 ( 

3821 "%s " % " ".join("[%s]" % x for x in flags) 

3822 if flags else "" 

3823 ), 

3824 " ".join( 

3825 "<%s%s>" % ( 

3826 x.name, 

3827 "?" if 

3828 (x.default is None or x.default != inspect.Parameter.empty) 

3829 else "" 

3830 ) 

3831 for x in list(inspect.signature(func).parameters.values())[1:] 

3832 if x.name not in func._flagnames and x.name[0] != "_" 

3833 ) 

3834 ) 

3835 

3836 if cmd: 

3837 if cmd not in self.commands: 

3838 print("Unknown command '%s'" % cmd) 

3839 return 

3840 # help for one command 

3841 func = self.commands[cmd] 

3842 print("%s%s: %s" % ( 

3843 cmd, 

3844 _args(func), 

3845 func.__doc__ and func.__doc__.strip() 

3846 )) 

3847 else: 

3848 header = "│ %s - Help │" % self.__class__.__name__ 

3849 print("┌" + "─" * (len(header) - 2) + "┐") 

3850 print(header) 

3851 print("└" + "─" * (len(header) - 2) + "┘") 

3852 print( 

3853 pretty_list( 

3854 [ 

3855 ( 

3856 cmd, 

3857 _args(func), 

3858 func.__doc__ and func.__doc__.strip().split("\n")[0] or "" 

3859 ) 

3860 for cmd, func in self.commands.items() 

3861 ], 

3862 [("Command", "Arguments", "Description")] 

3863 ) 

3864 ) 

3865 

3866 def _split_cmd(self, cmd: str) -> Tuple[List[str], List[int]]: 

3867 """ 

3868 Split the command in multiple arguments 

3869 """ 

3870 quoted = None 

3871 queue = [""] 

3872 offsets = [0] 

3873 for i, c in enumerate(cmd): 

3874 if c == "'" or c == '"': 

3875 # This is a quote. 

3876 if quoted is not None and quoted == c: 

3877 # We are closing the last quote 

3878 quoted = None 

3879 elif quoted: 

3880 queue[-1] += c 

3881 else: 

3882 quoted = c 

3883 elif c == " ": 

3884 # This is a space. 

3885 if quoted is not None: 

3886 # We're in a quote, append it 

3887 queue[-1] += c 

3888 elif queue[-1]: 

3889 # Not in a quote, this splits the argument. 

3890 queue += [""] 

3891 offsets.append(i) 

3892 else: 

3893 # Padding space, advance offset 

3894 offsets[-1] += 1 

3895 else: 

3896 # This is a char 

3897 queue[-1] += c 

3898 return queue, offsets 

3899 

3900 def _completer(self) -> 'prompt_toolkit.completion.Completer': 

3901 """ 

3902 Returns a prompt_toolkit custom completer 

3903 """ 

3904 from prompt_toolkit.completion import Completer, Completion 

3905 

3906 class CLICompleter(Completer): 

3907 def get_completions(cmpl, document, complete_event): # type: ignore 

3908 if not complete_event.completion_requested: 

3909 # Only activate when the user does <TAB> 

3910 return 

3911 parts, offsets = self._split_cmd(document.text) 

3912 cmd = parts[0].lower() 

3913 if cmd not in self.commands: 

3914 # We are trying to complete the command 

3915 for possible_cmd in (x for x in self.commands if x.startswith(cmd)): 

3916 yield Completion(possible_cmd, start_position=-len(cmd)) 

3917 else: 

3918 # We are trying to complete the command content 

3919 if len(parts) == 1: 

3920 return 

3921 args, _, _ = self._parseallargs(self.commands[cmd], cmd, parts[1:]) 

3922 if cmd in self.commands_complete: 

3923 completer = self.commands_complete[cmd] 

3924 # If the completion is 'mono', it's a single argument with 

3925 # spaces. Else we pass the list of arguments to complete, 

3926 # and we only complete the last argument. 

3927 if completer._mono: # type: ignore 

3928 arg = " ".join(args) 

3929 completions = completer(self, arg) 

3930 startpos = offsets[1] 

3931 else: 

3932 completions = completer(self, args) 

3933 startpos = offsets[-1] 

3934 

3935 # For each possible completion 

3936 for possible_arg in completions: 

3937 # If there's a space in the completion, and we're 

3938 # not in mono mode, add quotes. 

3939 if " " in possible_arg and not completer._mono: # type: ignore # noqa: E501 

3940 possible_arg = '"%s"' % possible_arg 

3941 

3942 yield Completion( 

3943 possible_arg, 

3944 start_position=startpos - len(document.text) + 1 

3945 ) 

3946 return 

3947 return CLICompleter() 

3948 

3949 def loop(self, debug: int = 0) -> None: 

3950 """ 

3951 Main command handling loop 

3952 """ 

3953 from prompt_toolkit import PromptSession 

3954 session = PromptSession(completer=self._completer()) 

3955 

3956 while True: 

3957 try: 

3958 cmd = session.prompt(self.ps1()).strip() 

3959 except KeyboardInterrupt: 

3960 continue 

3961 except EOFError: 

3962 self.close() 

3963 break 

3964 parts, _ = self._split_cmd(cmd) 

3965 args = parts[1:] 

3966 cmd = parts[0].strip().lower() 

3967 if not cmd: 

3968 continue 

3969 if cmd in ["help", "h", "?"]: 

3970 self.help(" ".join(args)) 

3971 continue 

3972 if cmd in "exit": 

3973 break 

3974 if cmd not in self.commands: 

3975 print("Unknown command. Type help or ?") 

3976 else: 

3977 # check the number of arguments 

3978 func = self.commands[cmd] 

3979 args, kwargs, outkwargs = self._parseallargs(func, cmd, args) 

3980 if func._mono: # type: ignore 

3981 args = [" ".join(args)] 

3982 # if globsupport is set, we might need to do several calls 

3983 if func._globsupport and "*" in args[0]: # type: ignore 

3984 if args[0].count("*") > 1: 

3985 print("More than 1 glob star (*) is currently unsupported.") 

3986 continue 

3987 before, after = args[0].split("*", 1) 

3988 reg = re.compile(re.escape(before) + r".*" + after) 

3989 calls = [ 

3990 [x] for x in 

3991 self.commands_complete[cmd](self, before) 

3992 if reg.match(x) 

3993 ] 

3994 else: 

3995 calls = [args] 

3996 else: 

3997 calls = [args] 

3998 # now iterate if required, call the function and print its output 

3999 res = None 

4000 for args in calls: 

4001 try: 

4002 res = func(self, *args, **kwargs) 

4003 except KeyboardInterrupt: 

4004 print("Aborted.") 

4005 except TypeError as ex: 

4006 print("Bad number of arguments !") 

4007 if debug: 

4008 traceback.print_exception(ex) 

4009 self.help(cmd=cmd) 

4010 continue 

4011 except Exception as ex: 

4012 print("Command failed with error: %s" % ex) 

4013 if debug: 

4014 traceback.print_exception(ex) 

4015 try: 

4016 if res and cmd in self.commands_output: 

4017 self.commands_output[cmd](self, res, **outkwargs) 

4018 except KeyboardInterrupt: 

4019 print("Aborted.") 

4020 except Exception as ex: 

4021 print("Output processor failed with error: %s" % ex) 

4022 

4023 

4024def AutoArgparse( 

4025 func: DecoratorCallable, 

4026 _parseonly: bool = False, 

4027) -> Optional[Tuple[List[str], List[str]]]: 

4028 """ 

4029 Generate an Argparse call from a function, then call this function. 

4030 

4031 Notes: 

4032 

4033 - for the arguments to have a description, the sphinx docstring format 

4034 must be used. See 

4035 https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html 

4036 - the arguments must be typed in Python (we ignore Sphinx-specific types) 

4037 untyped arguments are ignored. 

4038 - only types that would be supported by argparse are supported. The others 

4039 are omitted. 

4040 """ 

4041 argsdoc = {} 

4042 if func.__doc__: 

4043 # Sphinx doc format parser 

4044 m = re.match( 

4045 r"((?:.|\n)*?)(\n\s*:(?:param|type|raises|return|rtype)(?:.|\n)*)", 

4046 func.__doc__.strip(), 

4047 ) 

4048 if not m: 

4049 desc = func.__doc__.strip() 

4050 else: 

4051 desc = m.group(1) 

4052 sphinxargs = re.findall( 

4053 r"\s*:(param|type|raises|return|rtype)\s*([^:]*):(.*)", 

4054 m.group(2), 

4055 ) 

4056 for argtype, argparam, argdesc in sphinxargs: 

4057 argparam = argparam.strip() 

4058 argdesc = argdesc.strip() 

4059 if argtype == "param": 

4060 if not argparam: 

4061 raise ValueError(":param: without a name !") 

4062 argsdoc[argparam] = argdesc 

4063 else: 

4064 desc = "" 

4065 

4066 # Process the parameters 

4067 positional = [] 

4068 noargument = [] 

4069 hexarguments = [] 

4070 parameters = {} 

4071 for param in inspect.signature(func).parameters.values(): 

4072 if not param.annotation: 

4073 continue 

4074 noarg = False 

4075 parname = param.name.replace("_", "-") 

4076 paramkwargs: Dict[str, Any] = {} 

4077 if param.annotation is bool: 

4078 if param.default is True: 

4079 parname = "no-" + parname 

4080 paramkwargs["action"] = "store_false" 

4081 else: 

4082 paramkwargs["action"] = "store_true" 

4083 noarg = True 

4084 elif param.annotation is bytes: 

4085 paramkwargs["type"] = str 

4086 hexarguments.append(parname) 

4087 elif param.annotation in [str, int, float]: 

4088 paramkwargs["type"] = param.annotation 

4089 elif ( 

4090 isinstance(param.annotation, type) and 

4091 issubclass(param.annotation, enum.Enum) 

4092 ): 

4093 paramkwargs["type"] = param.annotation 

4094 paramkwargs["choices"] = list(param.annotation) 

4095 else: 

4096 continue 

4097 if param.default != inspect.Parameter.empty: 

4098 if param.kind == inspect.Parameter.POSITIONAL_ONLY: 

4099 positional.append(parname) 

4100 paramkwargs["nargs"] = '?' 

4101 else: 

4102 parname = "--" + parname 

4103 paramkwargs["default"] = param.default 

4104 elif param.kind == inspect.Parameter.KEYWORD_ONLY: 

4105 # Required but Keyword only 

4106 parname = "--" + parname 

4107 paramkwargs["required"] = True 

4108 else: 

4109 positional.append(parname) 

4110 if param.kind == inspect.Parameter.VAR_POSITIONAL: 

4111 paramkwargs["action"] = "append" 

4112 if param.name in argsdoc: 

4113 paramkwargs["help"] = argsdoc[param.name] 

4114 if param.annotation is bytes: 

4115 paramkwargs["help"] = "(hex) " + paramkwargs["help"] 

4116 elif param.annotation is bool: 

4117 paramkwargs["help"] = "(flag) " + paramkwargs["help"] 

4118 else: 

4119 paramkwargs["help"] = ( 

4120 "(%s) " % param.annotation.__name__ + paramkwargs["help"] 

4121 ) 

4122 # Add to the parameter list 

4123 parameters[parname] = paramkwargs 

4124 if noarg: 

4125 noargument.append(parname) 

4126 

4127 if _parseonly: 

4128 # An internal mode used to generate bash autocompletion, do it then exit. 

4129 return ( 

4130 [x for x in parameters if x not in positional] + ["--help"], 

4131 [x for x in noargument if x not in positional] + ["--help"], 

4132 ) 

4133 

4134 # Now build the argparse.ArgumentParser 

4135 parser = argparse.ArgumentParser( 

4136 prog=func.__name__, 

4137 description=desc, 

4138 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 

4139 ) 

4140 

4141 # Add parameters to parser 

4142 for parname, paramkwargs in parameters.items(): 

4143 parser.add_argument(parname, **paramkwargs) 

4144 

4145 # Now parse the sys.argv parameters 

4146 params = vars(parser.parse_args()) 

4147 

4148 # Convert hex parameters if provided 

4149 for p in hexarguments: 

4150 if params[p] is not None: 

4151 try: 

4152 params[p] = bytes.fromhex(params[p]) 

4153 except ValueError: 

4154 print( 

4155 conf.color_theme.fail( 

4156 "ERROR: the value of parameter %s " 

4157 "'%s' is not valid hexadecimal !" % (p, params[p]) 

4158 ) 

4159 ) 

4160 return None 

4161 

4162 # Act as in interactive mode 

4163 conf.logLevel = 20 

4164 from scapy.themes import DefaultTheme 

4165 conf.color_theme = DefaultTheme() 

4166 # And call the function 

4167 try: 

4168 func( 

4169 *[params.pop(x) for x in positional], 

4170 **{ 

4171 (k[3:] if k.startswith("no_") else k): v 

4172 for k, v in params.items() 

4173 } 

4174 ) 

4175 except AssertionError as ex: 

4176 print(conf.color_theme.fail("ERROR: " + str(ex))) 

4177 parser.print_help() 

4178 return None 

4179 

4180 

4181####################### 

4182# PERIODIC SENDER # 

4183####################### 

4184 

4185 

4186class PeriodicSenderThread(threading.Thread): 

4187 def __init__(self, sock, pkt, interval=0.5, ignore_exceptions=True): 

4188 # type: (Any, _PacketIterable, float, bool) -> None 

4189 """ Thread to send packets periodically 

4190 

4191 Args: 

4192 sock: socket where packet is sent periodically 

4193 pkt: packet or list of packets to send 

4194 interval: interval between two packets 

4195 """ 

4196 if not isinstance(pkt, list): 

4197 self._pkts = [cast("Packet", pkt)] # type: _PacketIterable 

4198 else: 

4199 self._pkts = pkt 

4200 self._socket = sock 

4201 self._stopped = threading.Event() 

4202 self._enabled = threading.Event() 

4203 self._enabled.set() 

4204 self._interval = interval 

4205 self._ignore_exceptions = ignore_exceptions 

4206 threading.Thread.__init__(self) 

4207 

4208 def enable(self): 

4209 # type: () -> None 

4210 self._enabled.set() 

4211 

4212 def disable(self): 

4213 # type: () -> None 

4214 self._enabled.clear() 

4215 

4216 def run(self): 

4217 # type: () -> None 

4218 while not self._stopped.is_set() and not self._socket.closed: 

4219 for p in self._pkts: 

4220 try: 

4221 if self._enabled.is_set(): 

4222 self._socket.send(p) 

4223 except (OSError, TimeoutError) as e: 

4224 if self._ignore_exceptions: 

4225 return 

4226 else: 

4227 raise e 

4228 self._stopped.wait(timeout=self._interval) 

4229 if self._stopped.is_set() or self._socket.closed: 

4230 break 

4231 

4232 def stop(self): 

4233 # type: () -> None 

4234 self._stopped.set() 

4235 self.join(self._interval * 2) 

4236 

4237 

4238class SingleConversationSocket(object): 

4239 def __init__(self, o): 

4240 # type: (Any) -> None 

4241 self._inner = o 

4242 self._tx_mutex = threading.RLock() 

4243 

4244 @property 

4245 def __dict__(self): # type: ignore 

4246 return self._inner.__dict__ 

4247 

4248 def __getattr__(self, name): 

4249 # type: (str) -> Any 

4250 return getattr(self._inner, name) 

4251 

4252 def sr1(self, *args, **kargs): 

4253 # type: (*Any, **Any) -> Any 

4254 with self._tx_mutex: 

4255 return self._inner.sr1(*args, **kargs) 

4256 

4257 def sr(self, *args, **kargs): 

4258 # type: (*Any, **Any) -> Any 

4259 with self._tx_mutex: 

4260 return self._inner.sr(*args, **kargs) 

4261 

4262 def send(self, x): 

4263 # type: (Packet) -> Any 

4264 with self._tx_mutex: 

4265 try: 

4266 return self._inner.send(x) 

4267 except (ConnectionError, OSError) as e: 

4268 self._inner.close() 

4269 raise e