## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ SuperSocket. """ from __future__ import absolute_import import os import socket import subprocess import struct import time from scapy.config import conf from scapy.consts import LINUX, OPENBSD, BSD, DARWIN, WINDOWS from scapy.data import * from scapy.compat import * from scapy.error import warning, log_runtime import scapy.modules.six as six import scapy.packet from scapy.utils import PcapReader, tcpdump class _SuperSocket_metaclass(type): def __repr__(self): if self.desc is not None: return "<%s: %s>" % (self.__name__,self.desc) else: return "<%s>" % self.__name__ class SuperSocket(six.with_metaclass(_SuperSocket_metaclass)): desc = None closed=0 def __init__(self, family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0): self.ins = socket.socket(family, type, proto) self.outs = self.ins self.promisc=None def send(self, x): sx = raw(x) if hasattr(x, "sent_time"): x.sent_time = time.time() return self.outs.send(sx) def recv(self, x=MTU): return conf.raw_layer(self.ins.recv(x)) def fileno(self): return self.ins.fileno() def close(self): if self.closed: return self.closed = True if hasattr(self, "outs"): if not hasattr(self, "ins") or self.ins != self.outs: if self.outs and self.outs.fileno() != -1: self.outs.close() if hasattr(self, "ins"): if self.ins and self.ins.fileno() != -1: self.ins.close() def sr(self, *args, **kargs): from scapy import sendrecv return sendrecv.sndrcv(self, *args, **kargs) def sr1(self, *args, **kargs): from scapy import sendrecv a,b = sendrecv.sndrcv(self, *args, **kargs) if len(a) > 0: return a[0][1] else: return None def sniff(self, *args, **kargs): from scapy import sendrecv return sendrecv.sniff(opened_socket=self, *args, **kargs) class L3RawSocket(SuperSocket): desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)" def __init__(self, type = ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0): self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) if iface is not None: self.ins.bind((iface, type)) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[2] == socket.PACKET_OUTGOING: return None if sa_ll[3] in conf.l2types: cls = conf.l2types[sa_ll[3]] lvl = 2 elif sa_ll[1] in conf.l3types: cls = conf.l3types[sa_ll[1]] lvl = 3 else: cls = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s", sa_ll[0], sa_ll[1], sa_ll[3], cls.name) lvl = 3 try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) if lvl == 2: pkt = pkt.payload if pkt is not None: from scapy.arch import get_last_packet_timestamp pkt.time = get_last_packet_timestamp(self.ins) return pkt def send(self, x): try: sx = raw(x) x.sent_time = time.time() self.outs.sendto(sx,(x.dst,0)) except socket.error as msg: log_runtime.error(msg) class SimpleSocket(SuperSocket): desc = "wrapper around a classic socket" def __init__(self, sock): self.ins = sock self.outs = sock class StreamSocket(SimpleSocket): desc = "transforms a stream socket into a layer 2" def __init__(self, sock, basecls=None): if basecls is None: basecls = conf.raw_layer SimpleSocket.__init__(self, sock) self.basecls = basecls def recv(self, x=MTU): pkt = self.ins.recv(x, socket.MSG_PEEK) x = len(pkt) if x == 0: raise socket.error((100,"Underlying stream socket tore down")) pkt = self.basecls(pkt) pad = pkt.getlayer(conf.padding_layer) if pad is not None and pad.underlayer is not None: del(pad.underlayer.payload) from scapy.packet import NoPayload while pad is not None and not isinstance(pad, NoPayload): x -= len(pad.load) pad = pad.payload self.ins.recv(x) return pkt class SSLStreamSocket(StreamSocket): desc = "similar usage than StreamSocket but specialized for handling SSL-wrapped sockets" def __init__(self, sock, basecls=None): self._buf = b"" super(SSLStreamSocket, self).__init__(sock, basecls) #65535, the default value of x is the maximum length of a TLS record def recv(self, x=65535): pkt = None if self._buf != b"": try: pkt = self.basecls(self._buf) except: # We assume that the exception is generated by a buffer underflow pass if not pkt: buf = self.ins.recv(x) if len(buf) == 0: raise socket.error((100,"Underlying stream socket tore down")) self._buf += buf x = len(self._buf) pkt = self.basecls(self._buf) pad = pkt.getlayer(conf.padding_layer) if pad is not None and pad.underlayer is not None: del(pad.underlayer.payload) while pad is not None and not isinstance(pad, scapy.packet.NoPayload): x -= len(pad.load) pad = pad.payload self._buf = self._buf[x:] return pkt class L2ListenTcpdump(SuperSocket): desc = "read packets at layer 2 using tcpdump" def __init__(self, iface=None, promisc=None, filter=None, nofilter=False, prog=None, *arg, **karg): self.outs = None args = ['-w', '-', '-s', '65535'] if iface is not None: if WINDOWS: try: args.extend(['-i', iface.pcap_name]) except AttributeError: args.extend(['-i', iface]) else: args.extend(['-i', iface]) elif WINDOWS or DARWIN: args.extend(['-i', conf.iface.pcap_name if WINDOWS else conf.iface]) if not promisc: args.append('-p') if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: args.append(filter) self.tcpdump_proc = tcpdump(None, prog=prog, args=args, getproc=True) self.ins = PcapReader(self.tcpdump_proc.stdout) def recv(self, x=MTU): return self.ins.recv(x) def close(self): SuperSocket.close(self) self.tcpdump_proc.kill() class TunTapInterface(SuperSocket): """A socket to act as the host's peer of a tun / tap interface. """ desc = "Act as the host's peer of a tun / tap interface" def __init__(self, iface=None, mode_tun=None, *arg, **karg): self.iface = conf.iface if iface is None else iface self.mode_tun = ("tun" in iface) if mode_tun is None else mode_tun self.closed = True self.open() def __enter__(self): return self def __del__(self): self.close() def __exit__(self, *_): self.close() def open(self): """Open the TUN or TAP device.""" if not self.closed: return self.outs = self.ins = open( "/dev/net/tun" if LINUX else ("/dev/%s" % self.iface), "r+b", buffering=0 ) if LINUX: from fcntl import ioctl # TUNSETIFF = 0x400454ca # IFF_TUN = 0x0001 # IFF_TAP = 0x0002 # IFF_NO_PI = 0x1000 ioctl(self.ins, 0x400454ca, struct.pack( "16sH", raw(self.iface), 0x0001 if self.mode_tun else 0x1002, )) self.closed = False def __call__(self, *arg, **karg): """Needed when using an instantiated TunTapInterface object for conf.L2listen, conf.L2socket or conf.L3socket. """ return self def recv(self, x=MTU): if self.mode_tun: data = os.read(self.ins.fileno(), x + 4) proto = struct.unpack('!H', data[2:4])[0] return conf.l3types.get(proto, conf.raw_layer)(data[4:]) return conf.l2types.get(1, conf.raw_layer)( os.read(self.ins.fileno(), x) ) def send(self, x): sx = raw(x) if hasattr(x, "sent_time"): x.sent_time = time.time() if self.mode_tun: try: proto = conf.l3types[type(x)] except KeyError: log_runtime.warning( "Cannot find layer 3 protocol value to send %s in " "conf.l3types, using 0", x.name if hasattr(x, "name") else type(x).__name__ ) proto = 0 sx = struct.pack('!HH', 0, proto) + sx try: os.write(self.outs.fileno(), sx) except socket.error: log_runtime.error("%s send", self.__class__.__name__, exc_info=True) if conf.L3socket is None: conf.L3socket = L3RawSocket