1+ #define _GNU_SOURCE
2+
3+ #include <Python.h>
4+ #include <stdio.h>
5+ #include <stdlib.h>
6+ #include <string.h>
7+ #include <pcap.h>
8+ #include <arpa/inet.h>
9+ #include <ifaddrs.h>
10+ #include <net/if.h>
11+ #include <net/if_dl.h>
12+ #include <net/ethernet.h>
13+ #include <pthread.h>
14+ #include <unistd.h>
15+
16+ // Define missing ARP constants for macOS
17+ #define ARPHRD_ETHER 1 /* Ethernet hardware format */
18+ #define ARPOP_REQUEST 1 /* ARP request */
19+ #define ARPOP_REPLY 2 /* ARP reply */
20+
21+ #ifdef __APPLE__
22+ struct arphdr {
23+ unsigned short ar_hrd ; /* Format of hardware address */
24+ unsigned short ar_pro ; /* Format of protocol address */
25+ unsigned char ar_hln ; /* Length of hardware address */
26+ unsigned char ar_pln ; /* Length of protocol address */
27+ unsigned short ar_op ; /* ARP opcode (command) */
28+ };
29+
30+ struct ether_arp {
31+ struct arphdr ea_hdr ; /* Fixed-size header */
32+ unsigned char arp_sha [6 ];/* Sender hardware address */
33+ unsigned char arp_spa [4 ];/* Sender protocol address */
34+ unsigned char arp_tha [6 ];/* Target hardware address */
35+ unsigned char arp_tpa [4 ];/* Target protocol address */
36+ };
37+ #else
38+ #include <net/if_arp.h>
39+ #endif
40+
41+ // Structure to hold ARP response data
42+ typedef struct {
43+ char ip_addr [16 ];
44+ unsigned char mac_addr [6 ];
45+ int found ;
46+ } arp_response_t ;
47+
48+ // Structure for packet capture thread
49+ typedef struct {
50+ pcap_t * handle ;
51+ arp_response_t * response ;
52+ struct in_addr target_ip ;
53+ int timeout_ms ;
54+ int finished ;
55+ } capture_thread_args_t ;
56+
57+ /*
58+ * Function to get MAC address for interface
59+ */
60+ static int get_mac_address (const char * iface , unsigned char * mac ) {
61+ struct ifaddrs * ifap , * ifa ;
62+ int found = 0 ;
63+
64+ if (getifaddrs (& ifap ) != 0 ) {
65+ return -1 ;
66+ }
67+
68+ for (ifa = ifap ; ifa != NULL ; ifa = ifa -> ifa_next ) {
69+ if (ifa -> ifa_addr == NULL ) continue ;
70+
71+ // Check if this is the interface we want and it's a link-layer address
72+ if ((ifa -> ifa_addr -> sa_family == AF_LINK ) &&
73+ (strcmp (ifa -> ifa_name , iface ) == 0 )) {
74+ struct sockaddr_dl * sdl = (struct sockaddr_dl * )ifa -> ifa_addr ;
75+
76+ if (sdl -> sdl_alen == 6 ) { // MAC address is 6 bytes
77+ memcpy (mac , LLADDR (sdl ), 6 );
78+ found = 1 ;
79+ break ;
80+ }
81+ }
82+ }
83+
84+ freeifaddrs (ifap );
85+ return found ? 0 : -1 ;
86+ }
87+
88+ // Function to convert MAC address to string
89+ static void mac_to_string (unsigned char * mac , char * str ) {
90+ snprintf (str , 18 , "%02x:%02x:%02x:%02x:%02x:%02x" ,
91+ mac [0 ], mac [1 ], mac [2 ], mac [3 ], mac [4 ], mac [5 ]);
92+ }
93+
94+ // Packet capture callback function
95+ static void packet_handler (u_char * user_data , const struct pcap_pkthdr * pkthdr , const u_char * packet ) {
96+ capture_thread_args_t * args = (capture_thread_args_t * )user_data ;
97+
98+ struct ether_header * eth_header = (struct ether_header * )packet ;
99+ if (ntohs (eth_header -> ether_type ) != ETHERTYPE_ARP )
100+ return ;
101+
102+ struct ether_arp * arp_packet = (struct ether_arp * )(packet + sizeof (struct ether_header ));
103+ if (ntohs (arp_packet -> ea_hdr .ar_op ) != ARPOP_REPLY )
104+ return ;
105+
106+ // Check if this is the response we're looking for
107+ if (memcmp (arp_packet -> arp_spa , & args -> target_ip .s_addr , 4 ) == 0 ) {
108+ memcpy (args -> response -> mac_addr , arp_packet -> arp_sha , 6 );
109+ inet_ntop (AF_INET , arp_packet -> arp_spa , args -> response -> ip_addr , 16 );
110+ args -> response -> found = 1 ;
111+ args -> finished = 1 ;
112+ pcap_breakloop (args -> handle );
113+ }
114+ }
115+
116+ // Packet capture thread function
117+ static void * capture_thread (void * arg ) {
118+ capture_thread_args_t * args = (capture_thread_args_t * )arg ;
119+ pcap_loop (args -> handle , -1 , packet_handler , (u_char * )args );
120+ return NULL ;
121+ }
122+
123+ static PyObject * perform_arp_scan (PyObject * self , PyObject * args ) {
124+ char * iface , * src_ip_str , * dst_ip_str ;
125+ int timeout_ms = 1000 ; // Default timeout 1 second
126+
127+ if (!PyArg_ParseTuple (args , "sss|i" , & iface , & src_ip_str , & dst_ip_str , & timeout_ms )) {
128+ return NULL ;
129+ }
130+
131+ // Get interface MAC address
132+ unsigned char src_mac [6 ];
133+ if (get_mac_address (iface , src_mac ) < 0 ) {
134+ PyErr_SetString (PyExc_RuntimeError , "Failed to get MAC address" );
135+ return NULL ;
136+ }
137+
138+ // Convert IP addresses
139+ struct in_addr src_ip , dst_ip ;
140+ if (inet_aton (src_ip_str , & src_ip ) == 0 || inet_aton (dst_ip_str , & dst_ip ) == 0 ) {
141+ PyErr_SetString (PyExc_ValueError , "Invalid IP address" );
142+ return NULL ;
143+ }
144+
145+ // Open pcap handle
146+ char errbuf [PCAP_ERRBUF_SIZE ];
147+ pcap_t * handle = pcap_open_live (iface , 65536 , 1 , timeout_ms , errbuf );
148+ if (!handle ) {
149+ PyErr_SetString (PyExc_RuntimeError , errbuf );
150+ return NULL ;
151+ }
152+
153+ // Set up packet capture filter for ARP
154+ struct bpf_program fp ;
155+ char filter_exp [64 ];
156+ snprintf (filter_exp , sizeof (filter_exp ), "arp src host %s" , dst_ip_str );
157+ if (pcap_compile (handle , & fp , filter_exp , 0 , PCAP_NETMASK_UNKNOWN ) == -1 ) {
158+ pcap_close (handle );
159+ PyErr_SetString (PyExc_RuntimeError , "Failed to compile filter" );
160+ return NULL ;
161+ }
162+ if (pcap_setfilter (handle , & fp ) == -1 ) {
163+ pcap_freecode (& fp );
164+ pcap_close (handle );
165+ PyErr_SetString (PyExc_RuntimeError , "Failed to set filter" );
166+ return NULL ;
167+ }
168+ pcap_freecode (& fp );
169+
170+ // Prepare ARP request packet
171+ unsigned char packet [42 ];
172+ memset (packet , 0 , sizeof (packet ));
173+
174+ // Fill Ethernet header
175+ struct ether_header * eth_hdr = (struct ether_header * )packet ;
176+ memset (eth_hdr -> ether_dhost , 0xff , 6 );
177+ memcpy (eth_hdr -> ether_shost , src_mac , 6 );
178+ eth_hdr -> ether_type = htons (ETHERTYPE_ARP );
179+
180+ // Fill ARP header
181+ struct ether_arp * arp_hdr = (struct ether_arp * )(packet + sizeof (struct ether_header ));
182+ arp_hdr -> ea_hdr .ar_hrd = htons (ARPHRD_ETHER );
183+ arp_hdr -> ea_hdr .ar_pro = htons (ETHERTYPE_IP );
184+ arp_hdr -> ea_hdr .ar_hln = 6 ;
185+ arp_hdr -> ea_hdr .ar_pln = 4 ;
186+ arp_hdr -> ea_hdr .ar_op = htons (ARPOP_REQUEST );
187+ memcpy (arp_hdr -> arp_sha , src_mac , 6 );
188+ memcpy (arp_hdr -> arp_spa , & src_ip .s_addr , 4 );
189+ memset (arp_hdr -> arp_tha , 0 , 6 );
190+ memcpy (arp_hdr -> arp_tpa , & dst_ip .s_addr , 4 );
191+
192+ // Set up response structure and capture thread
193+ arp_response_t response = {0 };
194+ capture_thread_args_t thread_args = {
195+ .handle = handle ,
196+ .response = & response ,
197+ .target_ip = dst_ip ,
198+ .timeout_ms = timeout_ms ,
199+ .finished = 0
200+ };
201+
202+ // Start capture thread
203+ pthread_t tid ;
204+ if (pthread_create (& tid , NULL , capture_thread , & thread_args ) != 0 ) {
205+ pcap_close (handle );
206+ PyErr_SetString (PyExc_RuntimeError , "Failed to create capture thread" );
207+ return NULL ;
208+ }
209+
210+ // Send ARP request
211+ if (pcap_sendpacket (handle , packet , sizeof (packet )) != 0 ) {
212+ pthread_cancel (tid );
213+ pcap_close (handle );
214+ PyErr_SetString (PyExc_RuntimeError , "Failed to send ARP packet" );
215+ return NULL ;
216+ }
217+
218+ // Wait for response or timeout
219+ while (!thread_args .finished && timeout_ms > 0 ) {
220+ usleep (10000 ); // Sleep for 10ms
221+ timeout_ms -= 10 ;
222+ }
223+
224+ // Clean up
225+ pthread_cancel (tid );
226+ pthread_join (tid , NULL );
227+ pcap_close (handle );
228+
229+ // Return results
230+ if (response .found ) {
231+ char mac_str [18 ];
232+ mac_to_string (response .mac_addr , mac_str );
233+ return Py_BuildValue ("{s:s,s:s}" , "ip" , response .ip_addr , "mac" , mac_str );
234+ }
235+
236+ Py_RETURN_NONE ;
237+ }
238+
239+ // Module method definitions
240+ static PyMethodDef ArpScannerMethods [] = {
241+ {"perform_arp_scan" , perform_arp_scan , METH_VARARGS ,
242+ "Perform an ARP scan with response handling. Args: interface, src_ip, target_ip, [timeout_ms]" },
243+ {NULL , NULL , 0 , NULL }
244+ };
245+
246+ static struct PyModuleDef arpscannermodule = {
247+ PyModuleDef_HEAD_INIT ,
248+ "arpscanner" ,
249+ NULL ,
250+ -1 ,
251+ ArpScannerMethods
252+ };
253+
254+ PyMODINIT_FUNC PyInit_arpscanner (void ) {
255+ return PyModule_Create (& arpscannermodule );
256+ }
0 commit comments