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