masscan-mark-ii/src/stack-arpv4.c
2024-02-26 10:35:54 -06:00

363 lines
11 KiB
C
Executable File

/*
handle ARP
Usage #1:
At startup, we make a synchronous request for the local router.
We'll wait several seconds for a response, but abort the program
if we don't receive a response.
Usage #2:
While running, we'll need to respond to ARPs. That's because we
may be bypassing the stack of the local machine with a "spoofed"
IP address. Every so often, the local router may drop it's route
entry and re-request our address.
*/
#include "rawsock.h"
#include "rawsock-adapter.h"
#include "stack-src.h"
#include "stack-arpv4.h"
#include "stack-queue.h"
#include "util-safefunc.h"
#include "util-logger.h"
#include "pixie-timer.h"
#include "proto-preprocess.h"
#include "util-checksum.h"
#define VERIFY_REMAINING(n) if (offset+(n) > max) return;
/**
* A structure representing the information parsed from an incoming
* ARP packet. Note: unlike normal programming style, this isn't
* overlayed on the incoming ARP header, but instead each field
* is parsed one-by-one and converted into this internal structure.
*/
struct ARP_IncomingRequest
{
unsigned is_valid;
unsigned opcode;
unsigned hardware_type;
unsigned protocol_type;
unsigned hardware_length;
unsigned protocol_length;
unsigned ip_src;
unsigned ip_dst;
const unsigned char *mac_src;
const unsigned char *mac_dst;
};
/****************************************************************************
****************************************************************************/
static void
proto_arp_parse(struct ARP_IncomingRequest *arp,
const unsigned char px[], unsigned offset, unsigned max)
{
/*
* parse the header
*/
VERIFY_REMAINING(8);
arp->is_valid = 0; /* not valid yet */
arp->hardware_type = px[offset]<<8 | px[offset+1];
arp->protocol_type = px[offset+2]<<8 | px[offset+3];
arp->hardware_length = px[offset+4];
arp->protocol_length = px[offset+5];
arp->opcode = px[offset+6]<<8 | px[offset+7];
offset += 8;
/* We only support IPv4 and Ethernet addresses */
if (arp->protocol_length != 4 && arp->hardware_length != 6)
return;
if (arp->protocol_type != 0x0800)
return;
if (arp->hardware_type != 1 && arp->hardware_type != 6)
return;
/*
* parse the addresses
*/
VERIFY_REMAINING(2 * arp->hardware_length + 2 * arp->protocol_length);
arp->mac_src = px+offset;
offset += arp->hardware_length;
arp->ip_src = px[offset+0]<<24 | px[offset+1]<<16 | px[offset+2]<<8 | px[offset+3];
offset += arp->protocol_length;
arp->mac_dst = px+offset;
offset += arp->hardware_length;
arp->ip_dst = px[offset+0]<<24 | px[offset+1]<<16 | px[offset+2]<<8 | px[offset+3];
//offset += arp->protocol_length;
arp->is_valid = 1;
}
/****************************************************************************
* Resolve the IP address into a MAC address. Do this synchronously, meaning,
* we'll stop and wait for the response. This is done at program startup,
* but not during then normal asynchronous operation during the scan.
****************************************************************************/
int
stack_arp_resolve(struct Adapter *adapter,
ipv4address_t my_ipv4, macaddress_t my_mac_address,
ipv4address_t your_ipv4, macaddress_t *your_mac_address)
{
unsigned char xarp_packet[64];
unsigned char *arp_packet = &xarp_packet[0];
unsigned i;
time_t start;
unsigned is_arp_notice_given = 0;
struct ARP_IncomingRequest response;
int is_delay_reported = 0;
/*
* [KLUDGE]
* If this is a VPN connection
*/
if (stack_if_datalink(adapter) == 12) {
memcpy(your_mac_address->addr, "\0\0\0\0\0\2", 6);
return 0; /* success */
}
memset(&response, 0, sizeof(response));
/* zero out bytes in packet to avoid leaking stuff in the padding
* (ARP is 42 byte packet, Ethernet is 60 byte minimum) */
memset(arp_packet, 0, sizeof(xarp_packet));
/*
* Create the request packet
*/
memcpy(arp_packet + 0, "\xFF\xFF\xFF\xFF\xFF\xFF", 6);
memcpy(arp_packet + 6, my_mac_address.addr, 6);
if (adapter->is_vlan) {
memcpy(arp_packet + 12, "\x81\x00", 2);
arp_packet[14] = (unsigned char)(adapter->vlan_id>>8);
arp_packet[15] = (unsigned char)(adapter->vlan_id&0xFF);
arp_packet += 4;
}
memcpy(arp_packet + 12, "\x08\x06", 2);
memcpy(arp_packet + 14,
"\x00\x01" /* hardware = Ethernet */
"\x08\x00" /* protocol = IPv4 */
"\x06\x04" /* MAC length = 6, IPv4 length = 4 */
"\x00\x01" /* opcode = request */
, 8);
memcpy(arp_packet + 22, my_mac_address.addr, 6);
arp_packet[28] = (unsigned char)(my_ipv4 >> 24);
arp_packet[29] = (unsigned char)(my_ipv4 >> 16);
arp_packet[30] = (unsigned char)(my_ipv4 >> 8);
arp_packet[31] = (unsigned char)(my_ipv4 >> 0);
memcpy(arp_packet + 32, "\x00\x00\x00\x00\x00\x00", 6);
arp_packet[38] = (unsigned char)(your_ipv4 >> 24);
arp_packet[39] = (unsigned char)(your_ipv4 >> 16);
arp_packet[40] = (unsigned char)(your_ipv4 >> 8);
arp_packet[41] = (unsigned char)(your_ipv4 >> 0);
/* Kludge: handle VLNA header if it exists. This is probably
* the wrong way to handle this. */
if (adapter->is_vlan)
arp_packet -= 4;
/*
* Now loop for a few seconds looking for the response
*/
rawsock_send_packet(adapter, arp_packet, 60, 1);
start = time(0);
i = 0;
for (;;) {
unsigned length;
unsigned secs;
unsigned usecs;
const unsigned char *px;
int err;
if (time(0) != start) {
start = time(0);
rawsock_send_packet(adapter, arp_packet, 60, 1);
if (i++ >= 10)
break; /* timeout */
/* It's taking too long, so notify the user */
if (!is_delay_reported) {
ipaddress_formatted_t fmt = ipv4address_fmt(your_ipv4);
LOG(0, "[+] resolving router %s with ARP (may take some time)...\n", fmt.string);
is_delay_reported = 1;
}
}
/* If we aren't getting a response back to our ARP, then print a
* status message */
if (time(0) > start+1 && !is_arp_notice_given) {
ipaddress_formatted_t fmt = ipv4address_fmt(your_ipv4);
LOG(0, "[+] arping local router %s\n", fmt.string);
is_arp_notice_given = 1;
}
err = rawsock_recv_packet(
adapter,
&length,
&secs,
&usecs,
&px);
if (err != 0)
continue;
if (adapter->is_vlan && px[17] != 6)
continue;
if (!adapter->is_vlan && px[13] != 6)
continue;
/*
* Parse the response as an ARP packet
*/
if (adapter->is_vlan)
proto_arp_parse(&response, px, 18, length);
else
proto_arp_parse(&response, px, 14, length);
/* Is this an ARP packet? */
if (!response.is_valid) {
LOG(2, "[-] arp: etype=0x%04x, not ARP\n", px[12]*256 + px[13]);
continue;
}
/* Is this an ARP "reply"? */
if (response.opcode != 2) {
LOG(2, "[-] arp: opcode=%u, not reply(2)\n", response.opcode);
continue;
}
/* Is this response directed at us? */
if (response.ip_dst != my_ipv4) {
LOG(2, "[-] arp: dst=%08x, not my ip 0x%08x\n", response.ip_dst, my_ipv4);
continue;
}
if (memcmp(response.mac_dst, my_mac_address.addr, 6) != 0)
continue;
/* Is this the droid we are looking for? */
if (response.ip_src != your_ipv4) {
ipaddress_formatted_t fmt1 = ipv4address_fmt(response.ip_src);
ipaddress_formatted_t fmt2 = ipv4address_fmt(your_ipv4);
LOG(2, "[-] arp: target=%s, not desired %s\n", fmt1.string, fmt2.string);
continue;
}
/*
* GOT IT!
* we've got a valid response, so save the results and
* return.
*/
memcpy(your_mac_address->addr, response.mac_src, 6);
{
ipaddress_formatted_t fmt1 = ipv4address_fmt(response.ip_src);
ipaddress_formatted_t fmt2 = macaddress_fmt(*your_mac_address);
LOG(1, "[+] arp: %s == %s\n", fmt1.string, fmt2.string);
}
return 0;
}
return 1;
}
/****************************************************************************
* Handle an incoming ARP request.
****************************************************************************/
int
stack_arp_incoming_request( struct stack_t *stack,
ipv4address_t my_ip, macaddress_t my_mac,
const unsigned char *px, unsigned length)
{
struct PacketBuffer *response = 0;
struct ARP_IncomingRequest request;
memset(&request, 0, sizeof(request));
/* Get a buffer for sending the response packet. This thread doesn't
* send the packet itself. Instead, it formats a packet, then hands
* that packet off to a transmit thread for later transmission. */
response = stack_get_packetbuffer(stack);
if (response == NULL)
return -1;
/* ARP packets are too short, so increase the packet size to
* the Ethernet minimum */
response->length = 60;
/* Fill the padded area with zeroes to avoid leaking data */
memset(response->px, 0, response->length);
/*
* Parse the response as an ARP packet
*/
proto_arp_parse(&request, px, 14, length);
/* Is this an ARP packet? */
if (!request.is_valid) {
LOG(2, "arp: etype=0x%04x, not ARP\n", px[12]*256 + px[13]);
return -1;
}
/* Is this an ARP "request"? */
if (request.opcode != 1) {
LOG(2, "arp: opcode=%u, not request(1)\n", request.opcode);
return -1;
}
/* Is this response directed at us? */
if (request.ip_dst != my_ip) {
LOG(2, "arp: dst=%08x, not my ip 0x%08x\n", request.ip_dst, my_ip);
return -1;
}
/*
* Create the response packet
*/
memcpy(response->px + 0, request.mac_src, 6);
memcpy(response->px + 6, my_mac.addr, 6);
memcpy(response->px + 12, "\x08\x06", 2);
memcpy(response->px + 14,
"\x00\x01" /* hardware = Ethernet */
"\x08\x00" /* protocol = IPv4 */
"\x06\x04" /* MAC length = 6, IPv4 length = 4 */
"\x00\x02" /* opcode = reply(2) */
, 8);
memcpy(response->px + 22, my_mac.addr, 6);
response->px[28] = (unsigned char)(my_ip >> 24);
response->px[29] = (unsigned char)(my_ip >> 16);
response->px[30] = (unsigned char)(my_ip >> 8);
response->px[31] = (unsigned char)(my_ip >> 0);
memcpy(response->px + 32, request.mac_src, 6);
response->px[38] = (unsigned char)(request.ip_src >> 24);
response->px[39] = (unsigned char)(request.ip_src >> 16);
response->px[40] = (unsigned char)(request.ip_src >> 8);
response->px[41] = (unsigned char)(request.ip_src >> 0);
/*
* Now queue the packet up for transmission
*/
stack_transmit_packetbuffer(stack, response);
return 0;
}