root/UMPA/branch/link-layer-integration/umit/umpa/_sockets.py @ 5592

Revision 5592, 7.0 kB (checked in by kosma, 3 years ago)

obliterating re module at getxsick's request :*

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (C) 2008-2010 Adriano Monteiro Marques.
5#
6# Authors: Bartosz SKOWRON <getxsick at gmail dot com>
7#          Kosma Moczek <kosma at kosma dot pl>
8#
9# This library is free software; you can redistribute it and/or modify
10# it under the terms of the GNU Lesser General Public License as published
11# by the Free Software Foundation; either version 2.1 of the License, or
12# (at your option) any later version.
13#
14# This library is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17# License for more details.
18#
19# You should have received a copy of the GNU Lesser General Public License
20# along with this library; if not, write to the Free Software Foundation,
21# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23"""
24Raw sockets support.
25
26Contains Socket classes which can be used to send raw packets in
27a platform-independent manner. The standard Python socket module can be used
28instead if advanced functionalities are needed.
29"""
30
31import socket
32import sys
33from fcntl import ioctl
34
35from umit.umpa.utils.exceptions import UMPAException, UMPANotPermittedException
36
37# constants from various header files not available under Python
38ETH_P_ALL = 3                     # from linux/if_ether.h
39BIOCSETIF = 2149597804            # from net/bpf.h
40
41# Detect socket programming model. This greatly simplifies socket code.
42if sys.platform == 'linux2':
43    _l2model = 'AF_PACKET'
44    _l3model = 'AF_INET'
45    _l3quirk = None
46elif sys.platform.find('freebsd') != -1 or \
47     sys.platform.find('netbsd') != -1 or \
48     sys.platform.find('darwin') != -1:
49    _l2model = 'bpf'
50    _l3model = 'AF_INET'
51    _l3quirk = 'ntohs'
52elif sys.platform.find('openbsd') != -1:
53    _l2model = 'bpf'
54    _l3model = 'AF_INET',
55    _l3quirk = None
56elif sys.platform.find('win') != -1:
57    _l2model = 'NDIS'
58    _l3model = 'AF_INET'
59    _l3quirk = 'windows'
60else:
61    _l2model = None
62    _l3model = None
63    _l3quirk = None
64
65def send(*packets, **kwargs):
66    """
67    Send arbitrary packets.
68
69    The function creates sockets of proper level as needed. The 'iface'
70    named argument must be supplied for L2 (link-layer) sockets.
71
72    @type packets: C{Packet}
73    @param packets: list of umit.umpa.Packet objects to send.
74
75    @returns: List of return values (byte counts) from the send() function.
76    """
77
78    sent_bytes = []
79    for packet in packets:
80        # create appropriate socket based on packet's lowermost layer
81        if packet.protos[0].layer == 2:
82            sock = SocketL2(iface=kwargs.get('iface'))
83        else:
84            sock = SocketL3()
85        sent_bytes.extend(sock.send(packet))
86    return sent_bytes
87
88class SocketL2(object):
89    """
90    Level 2 (link-layer) socket class.
91
92    Supported platforms: Linux (AF_PACKET), BSD (bpf).
93    """
94
95    def __init__(self, iface=None):
96        """
97        Create a new SocketL2.
98
99        @type iface: C{str}
100        @param iface: Interface to use for sending the packets.
101        """
102        if iface is None:
103            # TODO: port interface detection from the link-layer branch
104            raise NotImplementedError("You need to specify iface")
105
106        if _l2model == 'AF_PACKET':
107            try:
108                self._sock = socket.socket(socket.AF_PACKET,
109                        socket.SOCK_RAW,
110                        ETH_P_ALL)
111            except socket.error, msg:
112                raise UMPANotPermittedException(msg)
113
114            self._sock.bind((iface, ETH_P_ALL))
115            self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**20)
116        elif _l2model == 'bpf':
117            try:
118                self._sock = open("/dev/bpf", 'w')
119            except IOError, msg:
120                raise UMPAException('cannot open /dev/bpf: ' + msg)
121
122            ioctl(self._sock, BIOCSETIF, iface)
123        else:
124            raise NotImplementedError("L2 sockets unsupported on your platform")
125
126    def send(self, *packets):
127        """
128        Send packets through the socket.
129
130        @type packets: C{Packet}
131        @param packets: List of umit.umpa.Packet objects to send.
132
133        @returns: List of return values (byte counts) from the send() function.
134        """
135
136        sent_bytes = []
137        for packet in packets:
138            if _l2model == 'AF_PACKET':
139                sent_bytes.append(self._sock.send(packet.get_raw()))
140            elif _l2model == 'bpf':
141                sent_bytes.append(self._sock.write(packet.get_raw()))
142            else:
143                raise NotImplementedError("L2 send unsupported on your platform")
144        return sent_bytes
145
146# TODO: legacy L3 code below, not integrated yet
147
148class Socket(object):
149    """
150    This class handles with sockets.
151
152    To send built packets your need to create a socket.
153    You can use socket module from Python Standard Library directly
154    but it's recommended to use this class instead.
155
156    That is because there are some other features,
157    and for some security issues.
158    """
159
160    def __init__(self):
161        """
162        Create a new Socket().
163        """
164
165        # to create socket object we need root priviligies.
166        # if non-root EUID, then exception is raised
167        # use umit.umpa.utils.security.super_priviliges() to avoid exception
168        # when a new Socket object is created
169        try:
170            self._sock = socket.socket(socket.AF_INET, socket.SOCK_RAW,
171                                                            socket.IPPROTO_RAW)
172        except socket.error, msg:
173            raise UMPANotPermittedException(msg)
174
175        # to build own headers of IP
176        self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
177
178    def send(self, *packets):
179        """
180        Send packets in to the network.
181
182        @type packets: C{Packet}
183        @param packets: packets which were built by umit.umpa.Packet objects.
184        """
185
186        sent_bits = []
187        for packet in packets:
188            # XXX try to use similar mechanism as is for SocketL2
189            # to pick up correct interface, using bind() and send()
190            # then there is no need to pick out a destination address
191            dst_addr = self._get_address(packet)
192            # if dst_addr is a tuple, convert it to a string; works only for IPv4
193            if type(dst_addr) is tuple:
194                dst_addr = ".".join(str(y) for y in dst_addr)
195            sent_bits.append(self._sock.sendto(packet.get_raw(),
196                                                            (dst_addr, 0)))
197        return sent_bits
198
199    def _get_address(self, packet):
200        """
201        Pick out the destination address from 3rd layer.
202
203        @return: destination address from 3rd layer of OSI model.
204        """
205        for proto in packet.protos:
206            if proto.layer == 3:    # XXX: if we included more than one protocol
207                break               #   of layer 3 we got IP from the first one
208
209        if not proto:
210            raise UMPAException("There is not prototocol from 3rd layer.")
211
212        return proto.dst
Note: See TracBrowser for help on using the browser.