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

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

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

7SuperSocket. 

8""" 

9 

10from select import select, error as select_error 

11import ctypes 

12import errno 

13import socket 

14import struct 

15import time 

16 

17from scapy.config import conf 

18from scapy.consts import DARWIN, WINDOWS 

19from scapy.data import ( 

20 MTU, 

21 ETH_P_IP, 

22 ETH_P_IPV6, 

23 SOL_PACKET, 

24 SO_TIMESTAMPNS, 

25) 

26from scapy.compat import raw 

27from scapy.error import warning, log_runtime 

28from scapy.interfaces import network_name 

29from scapy.packet import Packet, NoPayload 

30from scapy.plist import ( 

31 PacketList, 

32 SndRcvList, 

33 _PacketIterable, 

34) 

35from scapy.utils import PcapReader, tcpdump 

36 

37# Typing imports 

38from scapy.interfaces import _GlobInterfaceType 

39from typing import ( 

40 Any, 

41 Dict, 

42 Iterator, 

43 List, 

44 Optional, 

45 Tuple, 

46 Type, 

47 cast, 

48) 

49 

50# Utils 

51 

52 

53class _SuperSocket_metaclass(type): 

54 desc = None # type: Optional[str] 

55 

56 def __repr__(self): 

57 # type: () -> str 

58 if self.desc is not None: 

59 return "<%s: %s>" % (self.__name__, self.desc) 

60 else: 

61 return "<%s>" % self.__name__ 

62 

63 

64# Used to get ancillary data 

65PACKET_AUXDATA = 8 

66ETH_P_8021Q = 0x8100 

67TP_STATUS_VLAN_VALID = 1 << 4 

68TP_STATUS_VLAN_TPID_VALID = 1 << 6 

69 

70 

71class tpacket_auxdata(ctypes.Structure): 

72 _fields_ = [ 

73 ("tp_status", ctypes.c_uint), 

74 ("tp_len", ctypes.c_uint), 

75 ("tp_snaplen", ctypes.c_uint), 

76 ("tp_mac", ctypes.c_ushort), 

77 ("tp_net", ctypes.c_ushort), 

78 ("tp_vlan_tci", ctypes.c_ushort), 

79 ("tp_vlan_tpid", ctypes.c_ushort), 

80 ] # type: List[Tuple[str, Any]] 

81 

82 

83# SuperSocket 

84 

85class SuperSocket(metaclass=_SuperSocket_metaclass): 

86 closed = False # type: bool 

87 nonblocking_socket = False # type: bool 

88 auxdata_available = False # type: bool 

89 

90 def __init__(self, 

91 family=socket.AF_INET, # type: int 

92 type=socket.SOCK_STREAM, # type: int 

93 proto=0, # type: int 

94 iface=None, # type: Optional[_GlobInterfaceType] 

95 **kwargs # type: Any 

96 ): 

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

98 self.ins = socket.socket(family, type, proto) # type: socket.socket 

99 self.outs = self.ins # type: Optional[socket.socket] 

100 self.promisc = conf.sniff_promisc 

101 self.iface = iface or conf.iface 

102 

103 def send(self, x): 

104 # type: (Packet) -> int 

105 """Sends a `Packet` object 

106 

107 :param x: `Packet` to be send 

108 :return: Number of bytes that have been sent 

109 """ 

110 sx = raw(x) 

111 try: 

112 x.sent_time = time.time() 

113 except AttributeError: 

114 pass 

115 

116 if self.outs: 

117 return self.outs.send(sx) 

118 else: 

119 return 0 

120 

121 if WINDOWS: 

122 def _recv_raw(self, sock, x): 

123 # type: (socket.socket, int) -> Tuple[bytes, Any, Optional[float]] 

124 """Internal function to receive a Packet. 

125 

126 :param sock: Socket object from which data are received 

127 :param x: Number of bytes to be received 

128 :return: Received bytes, address information and no timestamp 

129 """ 

130 pkt, sa_ll = sock.recvfrom(x) 

131 return pkt, sa_ll, None 

132 else: 

133 def _recv_raw(self, sock, x): 

134 # type: (socket.socket, int) -> Tuple[bytes, Any, Optional[float]] 

135 """Internal function to receive a Packet, 

136 and process ancillary data. 

137 

138 :param sock: Socket object from which data are received 

139 :param x: Number of bytes to be received 

140 :return: Received bytes, address information and an optional timestamp 

141 """ 

142 timestamp = None 

143 if not self.auxdata_available: 

144 pkt, _, _, sa_ll = sock.recvmsg(x) 

145 return pkt, sa_ll, timestamp 

146 flags_len = socket.CMSG_LEN(4096) 

147 pkt, ancdata, flags, sa_ll = sock.recvmsg(x, flags_len) 

148 if not pkt: 

149 return pkt, sa_ll, timestamp 

150 for cmsg_lvl, cmsg_type, cmsg_data in ancdata: 

151 # Check available ancillary data 

152 if (cmsg_lvl == SOL_PACKET and cmsg_type == PACKET_AUXDATA): 

153 # Parse AUXDATA 

154 try: 

155 auxdata = tpacket_auxdata.from_buffer_copy(cmsg_data) 

156 except ValueError: 

157 # Note: according to Python documentation, recvmsg() 

158 # can return a truncated message. A ValueError 

159 # exception likely indicates that Auxiliary 

160 # Data is not supported by the Linux kernel. 

161 return pkt, sa_ll, timestamp 

162 if auxdata.tp_vlan_tci != 0 or \ 

163 auxdata.tp_status & TP_STATUS_VLAN_VALID: 

164 # Insert VLAN tag 

165 tpid = ETH_P_8021Q 

166 if auxdata.tp_status & TP_STATUS_VLAN_TPID_VALID: 

167 tpid = auxdata.tp_vlan_tpid 

168 tag = struct.pack( 

169 "!HH", 

170 tpid, 

171 auxdata.tp_vlan_tci 

172 ) 

173 pkt = pkt[:12] + tag + pkt[12:] 

174 elif cmsg_lvl == socket.SOL_SOCKET and \ 

175 cmsg_type == SO_TIMESTAMPNS: 

176 length = len(cmsg_data) 

177 if length == 16: # __kernel_timespec 

178 tmp = struct.unpack("ll", cmsg_data) 

179 elif length == 8: # timespec 

180 tmp = struct.unpack("ii", cmsg_data) 

181 else: 

182 log_runtime.warning("Unknown timespec format.. ?!") 

183 continue 

184 timestamp = tmp[0] + tmp[1] * 1e-9 

185 return pkt, sa_ll, timestamp 

186 

187 def recv_raw(self, x=MTU): 

188 # type: (int) -> Tuple[Optional[Type[Packet]], Optional[bytes], Optional[float]] # noqa: E501 

189 """Returns a tuple containing (cls, pkt_data, time) 

190 

191 

192 :param x: Maximum number of bytes to be received, defaults to MTU 

193 :return: A tuple, consisting of a Packet type, the received data, 

194 and a timestamp 

195 """ 

196 return conf.raw_layer, self.ins.recv(x), None 

197 

198 def recv(self, x=MTU, **kwargs): 

199 # type: (int, **Any) -> Optional[Packet] 

200 """Receive a Packet according to the `basecls` of this socket 

201 

202 :param x: Maximum number of bytes to be received, defaults to MTU 

203 :return: The received `Packet` object, or None 

204 """ 

205 cls, val, ts = self.recv_raw(x) 

206 if not val or not cls: 

207 return None 

208 try: 

209 pkt = cls(val, **kwargs) # type: Packet 

210 except KeyboardInterrupt: 

211 raise 

212 except Exception: 

213 if conf.debug_dissector: 

214 from scapy.sendrecv import debug 

215 debug.crashed_on = (cls, val) 

216 raise 

217 pkt = conf.raw_layer(val) 

218 if ts: 

219 pkt.time = ts 

220 return pkt 

221 

222 def fileno(self): 

223 # type: () -> int 

224 return self.ins.fileno() 

225 

226 def close(self): 

227 # type: () -> None 

228 """Gracefully close this socket 

229 """ 

230 if self.closed: 

231 return 

232 self.closed = True 

233 if getattr(self, "outs", None): 

234 if getattr(self, "ins", None) != self.outs: 

235 if self.outs and self.outs.fileno() != -1: 

236 self.outs.close() 

237 if getattr(self, "ins", None): 

238 if self.ins.fileno() != -1: 

239 self.ins.close() 

240 

241 def sr(self, *args, **kargs): 

242 # type: (Any, Any) -> Tuple[SndRcvList, PacketList] 

243 """Send and Receive multiple packets 

244 """ 

245 from scapy import sendrecv 

246 return sendrecv.sndrcv(self, *args, **kargs) 

247 

248 def sr1(self, *args, **kargs): 

249 # type: (Any, Any) -> Optional[Packet] 

250 """Send one packet and receive one answer 

251 """ 

252 from scapy import sendrecv 

253 # if not explicitly specified by the user, 

254 # set threaded to False in sr1 to remove the overhead 

255 # for a Thread creation 

256 kargs.setdefault("threaded", False) 

257 ans = sendrecv.sndrcv(self, *args, **kargs)[0] # type: SndRcvList 

258 if len(ans) > 0: 

259 pkt = ans[0][1] # type: Packet 

260 return pkt 

261 else: 

262 return None 

263 

264 def sniff(self, *args, **kargs): 

265 # type: (Any, Any) -> PacketList 

266 from scapy import sendrecv 

267 return sendrecv.sniff(opened_socket=self, *args, **kargs) 

268 

269 def tshark(self, *args, **kargs): 

270 # type: (Any, Any) -> None 

271 from scapy import sendrecv 

272 sendrecv.tshark(opened_socket=self, *args, **kargs) 

273 

274 # TODO: use 'scapy.ansmachine.AnsweringMachine' when typed 

275 def am(self, 

276 cls, # type: Type[Any] 

277 *args, # type: Any 

278 **kwargs # type: Any 

279 ): 

280 # type: (...) -> Any 

281 """ 

282 Creates an AnsweringMachine associated with this socket. 

283 

284 :param cls: A subclass of AnsweringMachine to instantiate 

285 """ 

286 return cls(*args, opened_socket=self, socket=self, **kwargs) 

287 

288 @staticmethod 

289 def select(sockets, remain=conf.recv_poll_rate): 

290 # type: (List[SuperSocket], Optional[float]) -> List[SuperSocket] 

291 """This function is called during sendrecv() routine to select 

292 the available sockets. 

293 

294 :param sockets: an array of sockets that need to be selected 

295 :returns: an array of sockets that were selected and 

296 the function to be called next to get the packets (i.g. recv) 

297 """ 

298 try: 

299 inp, _, _ = select(sockets, [], [], remain) 

300 except (IOError, select_error) as exc: 

301 # select.error has no .errno attribute 

302 if not exc.args or exc.args[0] != errno.EINTR: 

303 raise 

304 return inp 

305 

306 def __del__(self): 

307 # type: () -> None 

308 """Close the socket""" 

309 self.close() 

310 

311 def __enter__(self): 

312 # type: () -> SuperSocket 

313 return self 

314 

315 def __exit__(self, exc_type, exc_value, traceback): 

316 # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[Any]) -> None # noqa: E501 

317 """Close the socket""" 

318 self.close() 

319 

320 

321if not WINDOWS: 

322 class L3RawSocket(SuperSocket): 

323 desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)" 

324 

325 def __init__(self, 

326 type=ETH_P_IP, # type: int 

327 filter=None, # type: Optional[str] 

328 iface=None, # type: Optional[_GlobInterfaceType] 

329 promisc=None, # type: Optional[bool] 

330 nofilter=0 # type: int 

331 ): 

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

333 self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) # noqa: E501 

334 self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) 

335 self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) # noqa: E501 

336 if iface is not None: 

337 iface = network_name(iface) 

338 self.iface = iface 

339 self.ins.bind((iface, type)) 

340 else: 

341 self.iface = "any" 

342 try: 

343 # Receive Auxiliary Data (VLAN tags) 

344 self.ins.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1) 

345 self.ins.setsockopt( 

346 socket.SOL_SOCKET, 

347 SO_TIMESTAMPNS, 

348 1 

349 ) 

350 self.auxdata_available = True 

351 except OSError: 

352 # Note: Auxiliary Data is only supported since 

353 # Linux 2.6.21 

354 msg = "Your Linux Kernel does not support Auxiliary Data!" 

355 log_runtime.info(msg) 

356 

357 def recv(self, x=MTU, **kwargs): 

358 # type: (int, **Any) -> Optional[Packet] 

359 data, sa_ll, ts = self._recv_raw(self.ins, x) 

360 if sa_ll[2] == socket.PACKET_OUTGOING: 

361 return None 

362 if sa_ll[3] in conf.l2types: 

363 cls = conf.l2types.num2layer[sa_ll[3]] # type: Type[Packet] 

364 lvl = 2 

365 elif sa_ll[1] in conf.l3types: 

366 cls = conf.l3types.num2layer[sa_ll[1]] 

367 lvl = 3 

368 else: 

369 cls = conf.default_l2 

370 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s", sa_ll[0], sa_ll[1], sa_ll[3], cls.name) # noqa: E501 

371 lvl = 3 

372 

373 try: 

374 pkt = cls(data, **kwargs) 

375 except KeyboardInterrupt: 

376 raise 

377 except Exception: 

378 if conf.debug_dissector: 

379 raise 

380 pkt = conf.raw_layer(data) 

381 

382 if lvl == 2: 

383 pkt = pkt.payload 

384 

385 if pkt is not None: 

386 if ts is None: 

387 from scapy.arch.linux import get_last_packet_timestamp 

388 ts = get_last_packet_timestamp(self.ins) 

389 pkt.time = ts 

390 return pkt 

391 

392 def send(self, x): 

393 # type: (Packet) -> int 

394 try: 

395 sx = raw(x) 

396 if self.outs: 

397 x.sent_time = time.time() 

398 return self.outs.sendto( 

399 sx, 

400 (x.dst, 0) 

401 ) 

402 except AttributeError: 

403 raise ValueError( 

404 "Missing 'dst' attribute in the first layer to be " 

405 "sent using a native L3 socket ! (make sure you passed the " 

406 "IP layer)" 

407 ) 

408 except socket.error as msg: 

409 log_runtime.error(msg) 

410 return 0 

411 

412 class L3RawSocket6(L3RawSocket): 

413 def __init__(self, 

414 type: int = ETH_P_IPV6, 

415 filter: Optional[str] = None, 

416 iface: Optional[_GlobInterfaceType] = None, 

417 promisc: Optional[bool] = None, 

418 nofilter: bool = False) -> None: 

419 # NOTE: if fragmentation is needed, it will be done by the kernel (RFC 2292) # noqa: E501 

420 self.outs = socket.socket( 

421 socket.AF_INET6, 

422 socket.SOCK_RAW, 

423 socket.IPPROTO_RAW 

424 ) 

425 self.ins = socket.socket( 

426 socket.AF_PACKET, 

427 socket.SOCK_RAW, 

428 socket.htons(type) 

429 ) 

430 self.iface = cast(_GlobInterfaceType, iface) 

431 

432 

433class SimpleSocket(SuperSocket): 

434 desc = "wrapper around a classic socket" 

435 __selectable_force_select__ = True 

436 

437 def __init__(self, sock, basecls=None): 

438 # type: (socket.socket, Optional[Type[Packet]]) -> None 

439 self.ins = sock 

440 self.outs = sock 

441 if basecls is None: 

442 basecls = conf.raw_layer 

443 self.basecls = basecls 

444 

445 def recv_raw(self, x=MTU): 

446 # type: (int) -> Tuple[Optional[Type[Packet]], Optional[bytes], Optional[float]] 

447 return self.basecls, self.ins.recv(x), None 

448 

449 if WINDOWS: 

450 @staticmethod 

451 def select(sockets, remain=None): 

452 # type: (List[SuperSocket], Optional[float]) -> List[SuperSocket] 

453 from scapy.automaton import select_objects 

454 return select_objects(sockets, remain) 

455 

456 

457class StreamSocket(SimpleSocket): 

458 """ 

459 Wrap a stream socket into a layer 2 SuperSocket 

460 

461 :param sock: the socket to wrap 

462 :param basecls: the base class packet to use to dissect the packet 

463 """ 

464 desc = "transforms a stream socket into a layer 2" 

465 

466 def __init__(self, 

467 sock, # type: socket.socket 

468 basecls=None, # type: Optional[Type[Packet]] 

469 ): 

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

471 from scapy.sessions import streamcls 

472 self.rcvcls = streamcls(basecls or conf.raw_layer) 

473 self.metadata: Dict[str, Any] = {} 

474 self.streamsession: Dict[str, Any] = {} 

475 self._buf = b"" 

476 super(StreamSocket, self).__init__(sock, basecls=basecls) 

477 

478 def recv(self, x=None, **kwargs): 

479 # type: (Optional[int], Any) -> Optional[Packet] 

480 if x is None: 

481 x = MTU 

482 # Block but in PEEK mode 

483 data = self.ins.recv(x, socket.MSG_PEEK) 

484 if data == b"": 

485 raise EOFError 

486 x = len(data) 

487 pkt = self.rcvcls(self._buf + data, self.metadata, self.streamsession) 

488 if pkt is None: # Incomplete packet. 

489 self._buf += self.ins.recv(x) 

490 return self.recv(x) 

491 self.metadata.clear() 

492 # Strip any madding 

493 pad = pkt.getlayer(conf.padding_layer) 

494 if pad is not None and pad.underlayer is not None: 

495 del pad.underlayer.payload 

496 while pad is not None and not isinstance(pad, NoPayload): 

497 x -= len(pad.load) 

498 pad = pad.payload 

499 # Only receive the packet length 

500 self.ins.recv(x) 

501 self._buf = b"" 

502 return pkt 

503 

504 

505class SSLStreamSocket(StreamSocket): 

506 desc = "similar usage than StreamSocket but specialized for handling SSL-wrapped sockets" # noqa: E501 

507 

508 # Basically StreamSocket but we can't PEEK 

509 

510 def __init__(self, sock, basecls=None): 

511 # type: (socket.socket, Optional[Type[Packet]]) -> None 

512 from scapy.sessions import TCPSession 

513 self.sess = TCPSession(app=True) 

514 super(SSLStreamSocket, self).__init__(sock, basecls) 

515 

516 # 65535, the default value of x is the maximum length of a TLS record 

517 def recv(self, x=None, **kwargs): 

518 # type: (Optional[int], **Any) -> Optional[Packet] 

519 if x is None: 

520 x = MTU 

521 # Block 

522 try: 

523 data = self.ins.recv(x) 

524 except OSError: 

525 raise EOFError 

526 try: 

527 pkt = self.sess.process(data, cls=self.basecls) # type: ignore 

528 except struct.error: 

529 # Buffer underflow 

530 pkt = None 

531 if data == b"" and not pkt: 

532 raise EOFError 

533 if not pkt: 

534 return self.recv(x) 

535 return pkt 

536 

537 @staticmethod 

538 def select(sockets, remain=None): 

539 # type: (List[SuperSocket], Optional[float]) -> List[SuperSocket] 

540 queued = [ 

541 x 

542 for x in sockets 

543 if isinstance(x, SSLStreamSocket) and x.sess.data 

544 ] 

545 if queued: 

546 return queued # type: ignore 

547 return super(SSLStreamSocket, SSLStreamSocket).select(sockets, remain=remain) 

548 

549 

550class L2ListenTcpdump(SuperSocket): 

551 desc = "read packets at layer 2 using tcpdump" 

552 

553 def __init__(self, 

554 iface=None, # type: Optional[_GlobInterfaceType] 

555 promisc=None, # type: Optional[bool] 

556 filter=None, # type: Optional[str] 

557 nofilter=False, # type: bool 

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

559 quiet=False, # type: bool 

560 *arg, # type: Any 

561 **karg # type: Any 

562 ): 

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

564 self.outs = None 

565 args = ['-w', '-', '-s', '65535'] 

566 self.iface = "any" 

567 if iface is None and (WINDOWS or DARWIN): 

568 self.iface = iface = conf.iface 

569 if promisc is None: 

570 promisc = conf.sniff_promisc 

571 if iface is not None: 

572 args.extend(['-i', network_name(iface)]) 

573 if not promisc: 

574 args.append('-p') 

575 if not nofilter: 

576 if conf.except_filter: 

577 if filter: 

578 filter = "(%s) and not (%s)" % (filter, conf.except_filter) 

579 else: 

580 filter = "not (%s)" % conf.except_filter 

581 if filter is not None: 

582 args.append(filter) 

583 self.tcpdump_proc = tcpdump( 

584 None, prog=prog, args=args, getproc=True, quiet=quiet) 

585 self.reader = PcapReader(self.tcpdump_proc.stdout) 

586 self.ins = self.reader # type: ignore 

587 

588 def recv(self, x=MTU, **kwargs): 

589 # type: (int, **Any) -> Optional[Packet] 

590 return self.reader.recv(x, **kwargs) 

591 

592 def close(self): 

593 # type: () -> None 

594 SuperSocket.close(self) 

595 self.tcpdump_proc.kill() 

596 

597 @staticmethod 

598 def select(sockets, remain=None): 

599 # type: (List[SuperSocket], Optional[float]) -> List[SuperSocket] 

600 if (WINDOWS or DARWIN): 

601 return sockets 

602 return SuperSocket.select(sockets, remain=remain) 

603 

604 

605# More abstract objects 

606 

607class IterSocket(SuperSocket): 

608 desc = "wrapper around an iterable" 

609 nonblocking_socket = True 

610 

611 def __init__(self, obj): 

612 # type: (_PacketIterable) -> None 

613 if not obj: 

614 self.iter = iter([]) # type: Iterator[Packet] 

615 elif isinstance(obj, IterSocket): 

616 self.iter = obj.iter 

617 elif isinstance(obj, SndRcvList): 

618 def _iter(obj=cast(SndRcvList, obj)): 

619 # type: (SndRcvList) -> Iterator[Packet] 

620 for s, r in obj: 

621 if s.sent_time: 

622 s.time = s.sent_time 

623 yield s 

624 yield r 

625 self.iter = _iter() 

626 elif isinstance(obj, (list, PacketList)): 

627 if isinstance(obj[0], bytes): 

628 self.iter = iter(obj) 

629 else: 

630 self.iter = (y for x in obj for y in x) 

631 else: 

632 self.iter = obj.__iter__() 

633 

634 @staticmethod 

635 def select(sockets, remain=None): 

636 # type: (List[SuperSocket], Any) -> List[SuperSocket] 

637 return sockets 

638 

639 def recv(self, x=None, **kwargs): 

640 # type: (Optional[int], Any) -> Optional[Packet] 

641 try: 

642 pkt = next(self.iter) 

643 return pkt.__class__(bytes(pkt), **kwargs) 

644 except StopIteration: 

645 raise EOFError 

646 

647 def close(self): 

648 # type: () -> None 

649 pass