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 ans = sendrecv.sndrcv(self, *args, **kargs)[0] # type: SndRcvList
254 if len(ans) > 0:
255 pkt = ans[0][1] # type: Packet
256 return pkt
257 else:
258 return None
259
260 def sniff(self, *args, **kargs):
261 # type: (Any, Any) -> PacketList
262 from scapy import sendrecv
263 return sendrecv.sniff(opened_socket=self, *args, **kargs)
264
265 def tshark(self, *args, **kargs):
266 # type: (Any, Any) -> None
267 from scapy import sendrecv
268 sendrecv.tshark(opened_socket=self, *args, **kargs)
269
270 # TODO: use 'scapy.ansmachine.AnsweringMachine' when typed
271 def am(self,
272 cls, # type: Type[Any]
273 *args, # type: Any
274 **kwargs # type: Any
275 ):
276 # type: (...) -> Any
277 """
278 Creates an AnsweringMachine associated with this socket.
279
280 :param cls: A subclass of AnsweringMachine to instantiate
281 """
282 return cls(*args, opened_socket=self, socket=self, **kwargs)
283
284 @staticmethod
285 def select(sockets, remain=conf.recv_poll_rate):
286 # type: (List[SuperSocket], Optional[float]) -> List[SuperSocket]
287 """This function is called during sendrecv() routine to select
288 the available sockets.
289
290 :param sockets: an array of sockets that need to be selected
291 :returns: an array of sockets that were selected and
292 the function to be called next to get the packets (i.g. recv)
293 """
294 try:
295 inp, _, _ = select(sockets, [], [], remain)
296 except (IOError, select_error) as exc:
297 # select.error has no .errno attribute
298 if not exc.args or exc.args[0] != errno.EINTR:
299 raise
300 return inp
301
302 def __del__(self):
303 # type: () -> None
304 """Close the socket"""
305 self.close()
306
307 def __enter__(self):
308 # type: () -> SuperSocket
309 return self
310
311 def __exit__(self, exc_type, exc_value, traceback):
312 # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[Any]) -> None # noqa: E501
313 """Close the socket"""
314 self.close()
315
316
317if not WINDOWS:
318 class L3RawSocket(SuperSocket):
319 desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)"
320
321 def __init__(self,
322 type=ETH_P_IP, # type: int
323 filter=None, # type: Optional[str]
324 iface=None, # type: Optional[_GlobInterfaceType]
325 promisc=None, # type: Optional[bool]
326 nofilter=0 # type: int
327 ):
328 # type: (...) -> None
329 self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) # noqa: E501
330 self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
331 self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) # noqa: E501
332 if iface is not None:
333 iface = network_name(iface)
334 self.iface = iface
335 self.ins.bind((iface, type))
336 else:
337 self.iface = "any"
338 try:
339 # Receive Auxiliary Data (VLAN tags)
340 self.ins.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1)
341 self.ins.setsockopt(
342 socket.SOL_SOCKET,
343 SO_TIMESTAMPNS,
344 1
345 )
346 self.auxdata_available = True
347 except OSError:
348 # Note: Auxiliary Data is only supported since
349 # Linux 2.6.21
350 msg = "Your Linux Kernel does not support Auxiliary Data!"
351 log_runtime.info(msg)
352
353 def recv(self, x=MTU, **kwargs):
354 # type: (int, **Any) -> Optional[Packet]
355 data, sa_ll, ts = self._recv_raw(self.ins, x)
356 if sa_ll[2] == socket.PACKET_OUTGOING:
357 return None
358 if sa_ll[3] in conf.l2types:
359 cls = conf.l2types.num2layer[sa_ll[3]] # type: Type[Packet]
360 lvl = 2
361 elif sa_ll[1] in conf.l3types:
362 cls = conf.l3types.num2layer[sa_ll[1]]
363 lvl = 3
364 else:
365 cls = conf.default_l2
366 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
367 lvl = 3
368
369 try:
370 pkt = cls(data, **kwargs)
371 except KeyboardInterrupt:
372 raise
373 except Exception:
374 if conf.debug_dissector:
375 raise
376 pkt = conf.raw_layer(data)
377
378 if lvl == 2:
379 pkt = pkt.payload
380
381 if pkt is not None:
382 if ts is None:
383 from scapy.arch.linux import get_last_packet_timestamp
384 ts = get_last_packet_timestamp(self.ins)
385 pkt.time = ts
386 return pkt
387
388 def send(self, x):
389 # type: (Packet) -> int
390 try:
391 sx = raw(x)
392 if self.outs:
393 x.sent_time = time.time()
394 return self.outs.sendto(
395 sx,
396 (x.dst, 0)
397 )
398 except AttributeError:
399 raise ValueError(
400 "Missing 'dst' attribute in the first layer to be "
401 "sent using a native L3 socket ! (make sure you passed the "
402 "IP layer)"
403 )
404 except socket.error as msg:
405 log_runtime.error(msg)
406 return 0
407
408 class L3RawSocket6(L3RawSocket):
409 def __init__(self,
410 type: int = ETH_P_IPV6,
411 filter: Optional[str] = None,
412 iface: Optional[_GlobInterfaceType] = None,
413 promisc: Optional[bool] = None,
414 nofilter: bool = False) -> None:
415 # NOTE: if fragmentation is needed, it will be done by the kernel (RFC 2292) # noqa: E501
416 self.outs = socket.socket(
417 socket.AF_INET6,
418 socket.SOCK_RAW,
419 socket.IPPROTO_RAW
420 )
421 self.ins = socket.socket(
422 socket.AF_PACKET,
423 socket.SOCK_RAW,
424 socket.htons(type)
425 )
426 self.iface = cast(_GlobInterfaceType, iface)
427
428
429class SimpleSocket(SuperSocket):
430 desc = "wrapper around a classic socket"
431 __selectable_force_select__ = True
432
433 def __init__(self, sock, basecls=None):
434 # type: (socket.socket, Optional[Type[Packet]]) -> None
435 self.ins = sock
436 self.outs = sock
437 if basecls is None:
438 basecls = conf.raw_layer
439 self.basecls = basecls
440
441 def recv_raw(self, x=MTU):
442 # type: (int) -> Tuple[Optional[Type[Packet]], Optional[bytes], Optional[float]]
443 return self.basecls, self.ins.recv(x), None
444
445 if WINDOWS:
446 @staticmethod
447 def select(sockets, remain=None):
448 # type: (List[SuperSocket], Optional[float]) -> List[SuperSocket]
449 from scapy.automaton import select_objects
450 return select_objects(sockets, remain)
451
452
453class StreamSocket(SimpleSocket):
454 """
455 Wrap a stream socket into a layer 2 SuperSocket
456
457 :param sock: the socket to wrap
458 :param basecls: the base class packet to use to dissect the packet
459 """
460 desc = "transforms a stream socket into a layer 2"
461
462 def __init__(self,
463 sock, # type: socket.socket
464 basecls=None, # type: Optional[Type[Packet]]
465 ):
466 # type: (...) -> None
467 from scapy.sessions import streamcls
468 self.rcvcls = streamcls(basecls or conf.raw_layer)
469 self.metadata: Dict[str, Any] = {}
470 self.streamsession: Dict[str, Any] = {}
471 self._buf = b""
472 super(StreamSocket, self).__init__(sock, basecls=basecls)
473
474 def recv(self, x=None, **kwargs):
475 # type: (Optional[int], Any) -> Optional[Packet]
476 if x is None:
477 x = MTU
478 # Block but in PEEK mode
479 data = self.ins.recv(x, socket.MSG_PEEK)
480 if data == b"":
481 raise EOFError
482 x = len(data)
483 pkt = self.rcvcls(self._buf + data, self.metadata, self.streamsession)
484 if pkt is None: # Incomplete packet.
485 self._buf += self.ins.recv(x)
486 return self.recv(x)
487 self.metadata.clear()
488 # Strip any madding
489 pad = pkt.getlayer(conf.padding_layer)
490 if pad is not None and pad.underlayer is not None:
491 del pad.underlayer.payload
492 while pad is not None and not isinstance(pad, NoPayload):
493 x -= len(pad.load)
494 pad = pad.payload
495 # Only receive the packet length
496 self.ins.recv(x)
497 self._buf = b""
498 return pkt
499
500
501class SSLStreamSocket(StreamSocket):
502 desc = "similar usage than StreamSocket but specialized for handling SSL-wrapped sockets" # noqa: E501
503
504 # Basically StreamSocket but we can't PEEK
505
506 def __init__(self, sock, basecls=None):
507 # type: (socket.socket, Optional[Type[Packet]]) -> None
508 from scapy.sessions import TCPSession
509 self.sess = TCPSession(app=True)
510 super(SSLStreamSocket, self).__init__(sock, basecls)
511
512 # 65535, the default value of x is the maximum length of a TLS record
513 def recv(self, x=None, **kwargs):
514 # type: (Optional[int], **Any) -> Optional[Packet]
515 if x is None:
516 x = MTU
517 # Block
518 data = self.ins.recv(x)
519 try:
520 pkt = self.sess.process(data, cls=self.basecls) # type: ignore
521 except struct.error:
522 # Buffer underflow
523 pkt = None
524 if data == b"" and not pkt:
525 raise EOFError
526 if not pkt:
527 return self.recv(x)
528 return pkt
529
530
531class L2ListenTcpdump(SuperSocket):
532 desc = "read packets at layer 2 using tcpdump"
533
534 def __init__(self,
535 iface=None, # type: Optional[_GlobInterfaceType]
536 promisc=None, # type: Optional[bool]
537 filter=None, # type: Optional[str]
538 nofilter=False, # type: bool
539 prog=None, # type: Optional[str]
540 quiet=False, # type: bool
541 *arg, # type: Any
542 **karg # type: Any
543 ):
544 # type: (...) -> None
545 self.outs = None
546 args = ['-w', '-', '-s', '65535']
547 self.iface = "any"
548 if iface is None and (WINDOWS or DARWIN):
549 self.iface = iface = conf.iface
550 if promisc is None:
551 promisc = conf.sniff_promisc
552 if iface is not None:
553 args.extend(['-i', network_name(iface)])
554 if not promisc:
555 args.append('-p')
556 if not nofilter:
557 if conf.except_filter:
558 if filter:
559 filter = "(%s) and not (%s)" % (filter, conf.except_filter)
560 else:
561 filter = "not (%s)" % conf.except_filter
562 if filter is not None:
563 args.append(filter)
564 self.tcpdump_proc = tcpdump(
565 None, prog=prog, args=args, getproc=True, quiet=quiet)
566 self.reader = PcapReader(self.tcpdump_proc.stdout)
567 self.ins = self.reader # type: ignore
568
569 def recv(self, x=MTU, **kwargs):
570 # type: (int, **Any) -> Optional[Packet]
571 return self.reader.recv(x, **kwargs)
572
573 def close(self):
574 # type: () -> None
575 SuperSocket.close(self)
576 self.tcpdump_proc.kill()
577
578 @staticmethod
579 def select(sockets, remain=None):
580 # type: (List[SuperSocket], Optional[float]) -> List[SuperSocket]
581 if (WINDOWS or DARWIN):
582 return sockets
583 return SuperSocket.select(sockets, remain=remain)
584
585
586# More abstract objects
587
588class IterSocket(SuperSocket):
589 desc = "wrapper around an iterable"
590 nonblocking_socket = True
591
592 def __init__(self, obj):
593 # type: (_PacketIterable) -> None
594 if not obj:
595 self.iter = iter([]) # type: Iterator[Packet]
596 elif isinstance(obj, IterSocket):
597 self.iter = obj.iter
598 elif isinstance(obj, SndRcvList):
599 def _iter(obj=cast(SndRcvList, obj)):
600 # type: (SndRcvList) -> Iterator[Packet]
601 for s, r in obj:
602 if s.sent_time:
603 s.time = s.sent_time
604 yield s
605 yield r
606 self.iter = _iter()
607 elif isinstance(obj, (list, PacketList)):
608 if isinstance(obj[0], bytes):
609 self.iter = iter(obj)
610 else:
611 self.iter = (y for x in obj for y in x)
612 else:
613 self.iter = obj.__iter__()
614
615 @staticmethod
616 def select(sockets, remain=None):
617 # type: (List[SuperSocket], Any) -> List[SuperSocket]
618 return sockets
619
620 def recv(self, x=None, **kwargs):
621 # type: (Optional[int], Any) -> Optional[Packet]
622 try:
623 pkt = next(self.iter)
624 return pkt.__class__(bytes(pkt), **kwargs)
625 except StopIteration:
626 raise EOFError
627
628 def close(self):
629 # type: () -> None
630 pass