Labo

Sign in or create your account | Project List | Help

Labo Git Source Tree

Root/arprequest/arprequest.py

1#!/usr/bin/env python
2#coding=utf8
3#
4# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
5# Version 2, December 2004
6#
7# Copyright (C) 2004 Sam Hocevar
8# 14 rue de Plaisance, 75014 Paris, France
9# Everyone is permitted to copy and distribute verbatim or modified
10# copies of this license document, and changing it is allowed as long
11# as the name is changed.
12#
13# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
14# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
15#
16# 0. You just DO WHAT THE FUCK YOU WANT TO.
17#
18
19import socket
20from struct import pack, unpack
21import signal
22
23ARP_GRATUITOUS = 1
24ARP_STANDARD = 2
25
26def val2int(val):
27    '''Retourne une valeur sous forme d'octet en valeur sous forme
28       d'entier.'''
29
30    return int(''.join(['%02d'%ord(c) for c in val]), 16)
31
32class TimeoutError(Exception):
33    '''Exception levée après un timeout.'''
34    pass
35
36def timeout(function, timeout=10):
37    '''Exécute la fonction function (référence) et stoppe son exécution
38       au bout d'un certain temps déterminé par timeout.
39       
40       Retourne None si la fonction à été arretée par le timeout, et
41       la valeur retournée par la fonction si son exécution se
42       termine.'''
43
44    def raise_timeout(num, frame):
45        raise TimeoutError
46    
47    # On mappe la fonction à notre signal
48    signal.signal(signal.SIGALRM, raise_timeout)
49    # Et on définie le temps à attendre avant de lancer le signal
50    signal.alarm(timeout)
51    try:
52        retvalue = function()
53    except TimeoutError: # = Fonction quittée à cause du timeout
54        return None
55    else: # = Fonction quittée avant le timeout
56        # On annule le signal
57        signal.alarm(0)
58        return retvalue
59
60# Classes :
61###########
62
63class ArpRequest:
64    '''Génère une requête ARP et attend la réponse'''
65    
66    def __init__(self, ipaddr, if_name, arp_type=ARP_GRATUITOUS):
67        # Initialisation du socket (socket brut, donc besoin d'ê root)
68        self.arp_type = arp_type
69        self.if_ipaddr = socket.gethostbyname(socket.gethostname())
70        
71        self.socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
72                                                        socket.SOCK_RAW)
73        self.socket.bind((if_name, socket.SOCK_RAW))
74        
75        self.ipaddr = ipaddr
76        
77        
78    def request(self):
79        '''Envois une requête arp et attend la réponse'''
80
81        # Envois de 5 requêtes ARP
82        for _ in range(5):
83            self._send_arp_request()
84        
85        # Puis attente de la réponse
86        if timeout(self._wait_response, 3):
87            return True
88        else:
89            return False
90    
91        
92    def _send_arp_request(self):
93        '''Envois une requête ARP pour la machine'''
94        
95        # Adresse logicielle de l'émetteur :
96        if self.arp_type == ARP_STANDARD:
97            saddr = pack('!4B',
98                           *[int(x) for x in self.if_ipaddr.split('.')])
99        else:
100            saddr = pack('!4B',
101                              *[int(x) for x in self.ipaddr.split('.')])
102            
103        
104        
105        # Forge de la trame :
106        frame = [
107            ### Partie ETHERNET ###
108            # Adresse mac destination (=broadcast) :
109            pack('!6B', *(0xFF,) * 6),
110            # Adresse mac source :
111            self.socket.getsockname()[4],
112            # Type de protocole (=ARP) :
113            pack('!H', 0x0806),
114            
115            ### Partie ARP ###
116            # Type de protocole matériel/logiciel (=Ethernet/IP) :
117            pack('!HHBB', 0x0001, 0x0800, 0x0006, 0x0004),
118            # Type d'opération (=ARP Request) :
119            pack('!H', 0x0001),
120            # Adresse matériel de l'émetteur :
121            self.socket.getsockname()[4],
122            # Adresse logicielle de l'émetteur :
123            saddr,
124            # Adresse matérielle de la cible (=00*6) :
125            pack('!6B', *(0,) * 6),
126            # Adresse logicielle de la cible (=adresse fournie au
127            # constructeur) :
128            pack('!4B', *[int(x) for x in self.ipaddr.split('.')])
129        ]
130        
131        self.socket.send(''.join(frame)) # Envois de la trame sur le
132        # réseau
133        
134    
135    def _wait_response(self):
136        '''Attend la réponse de la machine'''
137        while 0xBeef:
138            # Récupération de la trame :
139            frame = self.socket.recv(1024)
140            
141            # Récupération du protocole sous forme d'entier :
142            proto_type = val2int(unpack('!2s', frame[12:14])[0])
143            if proto_type != 0x0806: # On passe le traitement si ce
144                continue # n'est pas de l'arp
145
146            # Récupération du type d'opération sous forme d'entier :
147            op = val2int(unpack('!2s', frame[20:22])[0])
148            if op != 2: # On passe le traitement pour tout ce qui n'est
149                continue # pas une réponse ARP
150
151            # Récupération des différentes addresses de la trame :
152            arp_headers = frame[18:20]
153            arp_headers_values = unpack('!1s1s', arp_headers)
154            hw_size, pt_size = [val2int(v) for v in arp_headers_values]
155            total_addresses_byte = hw_size * 2 + pt_size * 2
156            arp_addrs = frame[22:22 + total_addresses_byte]
157            src_hw, src_pt, dst_hw, dst_pt = unpack('!%ss%ss%ss%ss'
158                    % (hw_size, pt_size, hw_size, pt_size), arp_addrs)
159            
160            # Comparaison de l'adresse recherchée avec l'adresse trouvée
161            # dans la trame :
162            if src_pt == pack('!4B',
163                             *[int(x) for x in self.ipaddr.split('.')]):
164                return True # Quand on a trouvé, on arrete de chercher !
165                # Et oui, c'est mal de faire un retour dans une boucle,
166                # je sais :)
167

Archive Download this file

Branches:
master