• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7SuperSocket.
8"""
9
10from __future__ import absolute_import
11import os
12import socket
13import subprocess
14import struct
15import time
16
17from scapy.config import conf
18from scapy.consts import LINUX, OPENBSD, BSD, DARWIN, WINDOWS
19from scapy.data import *
20from scapy.compat import *
21from scapy.error import warning, log_runtime
22import scapy.modules.six as six
23import scapy.packet
24from scapy.utils import PcapReader, tcpdump
25
26class _SuperSocket_metaclass(type):
27    def __repr__(self):
28        if self.desc is not None:
29            return "<%s: %s>" % (self.__name__,self.desc)
30        else:
31            return "<%s>" % self.__name__
32
33
34class SuperSocket(six.with_metaclass(_SuperSocket_metaclass)):
35    desc = None
36    closed=0
37    def __init__(self, family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0):
38        self.ins = socket.socket(family, type, proto)
39        self.outs = self.ins
40        self.promisc=None
41    def send(self, x):
42        sx = raw(x)
43        if hasattr(x, "sent_time"):
44            x.sent_time = time.time()
45        return self.outs.send(sx)
46    def recv(self, x=MTU):
47        return conf.raw_layer(self.ins.recv(x))
48    def fileno(self):
49        return self.ins.fileno()
50    def close(self):
51        if self.closed:
52            return
53        self.closed = True
54        if hasattr(self, "outs"):
55            if not hasattr(self, "ins") or self.ins != self.outs:
56                if self.outs and self.outs.fileno() != -1:
57                    self.outs.close()
58        if hasattr(self, "ins"):
59            if self.ins and self.ins.fileno() != -1:
60                self.ins.close()
61    def sr(self, *args, **kargs):
62        from scapy import sendrecv
63        return sendrecv.sndrcv(self, *args, **kargs)
64    def sr1(self, *args, **kargs):
65        from scapy import sendrecv
66        a,b = sendrecv.sndrcv(self, *args, **kargs)
67        if len(a) > 0:
68            return a[0][1]
69        else:
70            return None
71    def sniff(self, *args, **kargs):
72        from scapy import sendrecv
73        return sendrecv.sniff(opened_socket=self, *args, **kargs)
74
75class L3RawSocket(SuperSocket):
76    desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)"
77    def __init__(self, type = ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0):
78        self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
79        self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
80        self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
81        if iface is not None:
82            self.ins.bind((iface, type))
83    def recv(self, x=MTU):
84        pkt, sa_ll = self.ins.recvfrom(x)
85        if sa_ll[2] == socket.PACKET_OUTGOING:
86            return None
87        if sa_ll[3] in conf.l2types:
88            cls = conf.l2types[sa_ll[3]]
89            lvl = 2
90        elif sa_ll[1] in conf.l3types:
91            cls = conf.l3types[sa_ll[1]]
92            lvl = 3
93        else:
94            cls = conf.default_l2
95            warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s", sa_ll[0], sa_ll[1], sa_ll[3], cls.name)
96            lvl = 3
97
98        try:
99            pkt = cls(pkt)
100        except KeyboardInterrupt:
101            raise
102        except:
103            if conf.debug_dissector:
104                raise
105            pkt = conf.raw_layer(pkt)
106        if lvl == 2:
107            pkt = pkt.payload
108
109        if pkt is not None:
110            from scapy.arch import get_last_packet_timestamp
111            pkt.time = get_last_packet_timestamp(self.ins)
112        return pkt
113    def send(self, x):
114        try:
115            sx = raw(x)
116            x.sent_time = time.time()
117            self.outs.sendto(sx,(x.dst,0))
118        except socket.error as msg:
119            log_runtime.error(msg)
120
121class SimpleSocket(SuperSocket):
122    desc = "wrapper around a classic socket"
123    def __init__(self, sock):
124        self.ins = sock
125        self.outs = sock
126
127
128class StreamSocket(SimpleSocket):
129    desc = "transforms a stream socket into a layer 2"
130    def __init__(self, sock, basecls=None):
131        if basecls is None:
132            basecls = conf.raw_layer
133        SimpleSocket.__init__(self, sock)
134        self.basecls = basecls
135
136    def recv(self, x=MTU):
137        pkt = self.ins.recv(x, socket.MSG_PEEK)
138        x = len(pkt)
139        if x == 0:
140            raise socket.error((100,"Underlying stream socket tore down"))
141        pkt = self.basecls(pkt)
142        pad = pkt.getlayer(conf.padding_layer)
143        if pad is not None and pad.underlayer is not None:
144            del(pad.underlayer.payload)
145        from scapy.packet import NoPayload
146        while pad is not None and not isinstance(pad, NoPayload):
147            x -= len(pad.load)
148            pad = pad.payload
149        self.ins.recv(x)
150        return pkt
151
152class SSLStreamSocket(StreamSocket):
153    desc = "similar usage than StreamSocket but specialized for handling SSL-wrapped sockets"
154
155    def __init__(self, sock, basecls=None):
156        self._buf = b""
157        super(SSLStreamSocket, self).__init__(sock, basecls)
158
159    #65535, the default value of x is the maximum length of a TLS record
160    def recv(self, x=65535):
161        pkt = None
162        if self._buf != b"":
163            try:
164                pkt = self.basecls(self._buf)
165            except:
166                # We assume that the exception is generated by a buffer underflow
167                pass
168
169        if not pkt:
170            buf = self.ins.recv(x)
171            if len(buf) == 0:
172                raise socket.error((100,"Underlying stream socket tore down"))
173            self._buf += buf
174
175        x = len(self._buf)
176        pkt = self.basecls(self._buf)
177        pad = pkt.getlayer(conf.padding_layer)
178
179        if pad is not None and pad.underlayer is not None:
180            del(pad.underlayer.payload)
181        while pad is not None and not isinstance(pad, scapy.packet.NoPayload):
182            x -= len(pad.load)
183            pad = pad.payload
184        self._buf = self._buf[x:]
185        return pkt
186
187
188class L2ListenTcpdump(SuperSocket):
189    desc = "read packets at layer 2 using tcpdump"
190
191    def __init__(self, iface=None, promisc=None, filter=None, nofilter=False,
192                 prog=None, *arg, **karg):
193        self.outs = None
194        args = ['-w', '-', '-s', '65535']
195        if iface is not None:
196            if WINDOWS:
197                try:
198                    args.extend(['-i', iface.pcap_name])
199                except AttributeError:
200                    args.extend(['-i', iface])
201            else:
202                args.extend(['-i', iface])
203        elif WINDOWS or DARWIN:
204            args.extend(['-i', conf.iface.pcap_name if WINDOWS else conf.iface])
205        if not promisc:
206            args.append('-p')
207        if not nofilter:
208            if conf.except_filter:
209                if filter:
210                    filter = "(%s) and not (%s)" % (filter, conf.except_filter)
211                else:
212                    filter = "not (%s)" % conf.except_filter
213        if filter is not None:
214            args.append(filter)
215        self.tcpdump_proc = tcpdump(None, prog=prog, args=args, getproc=True)
216        self.ins = PcapReader(self.tcpdump_proc.stdout)
217    def recv(self, x=MTU):
218        return self.ins.recv(x)
219    def close(self):
220        SuperSocket.close(self)
221        self.tcpdump_proc.kill()
222
223
224class TunTapInterface(SuperSocket):
225    """A socket to act as the host's peer of a tun / tap interface.
226
227    """
228    desc = "Act as the host's peer of a tun / tap interface"
229
230    def __init__(self, iface=None, mode_tun=None, *arg, **karg):
231        self.iface = conf.iface if iface is None else iface
232        self.mode_tun = ("tun" in iface) if mode_tun is None else mode_tun
233        self.closed = True
234        self.open()
235
236    def __enter__(self):
237        return self
238
239    def __del__(self):
240        self.close()
241
242    def __exit__(self, *_):
243        self.close()
244
245    def open(self):
246        """Open the TUN or TAP device."""
247        if not self.closed:
248            return
249        self.outs = self.ins = open(
250            "/dev/net/tun" if LINUX else ("/dev/%s" % self.iface), "r+b",
251            buffering=0
252        )
253        if LINUX:
254            from fcntl import ioctl
255            # TUNSETIFF = 0x400454ca
256            # IFF_TUN = 0x0001
257            # IFF_TAP = 0x0002
258            # IFF_NO_PI = 0x1000
259            ioctl(self.ins, 0x400454ca, struct.pack(
260                "16sH", raw(self.iface), 0x0001 if self.mode_tun else 0x1002,
261            ))
262        self.closed = False
263
264    def __call__(self, *arg, **karg):
265        """Needed when using an instantiated TunTapInterface object for
266conf.L2listen, conf.L2socket or conf.L3socket.
267
268        """
269        return self
270
271    def recv(self, x=MTU):
272        if self.mode_tun:
273            data = os.read(self.ins.fileno(), x + 4)
274            proto = struct.unpack('!H', data[2:4])[0]
275            return conf.l3types.get(proto, conf.raw_layer)(data[4:])
276        return conf.l2types.get(1, conf.raw_layer)(
277            os.read(self.ins.fileno(), x)
278        )
279
280    def send(self, x):
281        sx = raw(x)
282        if hasattr(x, "sent_time"):
283            x.sent_time = time.time()
284        if self.mode_tun:
285            try:
286                proto = conf.l3types[type(x)]
287            except KeyError:
288                log_runtime.warning(
289                    "Cannot find layer 3 protocol value to send %s in "
290                    "conf.l3types, using 0",
291                    x.name if hasattr(x, "name") else type(x).__name__
292                )
293                proto = 0
294            sx = struct.pack('!HH', 0, proto) + sx
295        try:
296            os.write(self.outs.fileno(), sx)
297        except socket.error:
298            log_runtime.error("%s send", self.__class__.__name__, exc_info=True)
299
300
301if conf.L3socket is None:
302    conf.L3socket = L3RawSocket
303