mirror of
https://github.com/nmap/nmap.git
synced 2026-02-04 20:46:33 +00:00
1202 lines
29 KiB
C
1202 lines
29 KiB
C
/*
|
|
|
|
pcapsend.c: raw IP sends using winpcap
|
|
Copyright (C) 2000 Andy Lutomirski
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License, version 2.1, as published by the Free Software
|
|
Foundation, with the exception that if this copy of the library
|
|
is distributed under the Lesser GNU Public License (as opposed
|
|
to the ordinary GPL), you may ignore section 6b, and that all
|
|
copies distributed without exercising section 3 must retain this
|
|
paragraph in its entirety.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
Implements raw sends using winpcap.
|
|
Not as easy as it sounds...
|
|
|
|
Note: this was inspired by ryan@eeye.com's attempt to
|
|
do the same thing. It no longer bears much (any?)
|
|
resemblance to the original. This version uses no
|
|
registry calls or undoc'd features, and works on 98,
|
|
NT4, W2K, and probably Me. (Ryan's didn't work on
|
|
98, or, as far as I can tell, Win2K).
|
|
|
|
Routing is done with GetBestRoute, or a homebrew version
|
|
for NT4. ARP is in a separate thread to avoid issues
|
|
when scanning multiple hosts. There is a slight ARP
|
|
latency issue (250ms max).
|
|
|
|
Performance when scanning multiple local hosts will go
|
|
down the drain if o.maxparallelism > FAILCACHELEN and
|
|
there are lots of continuous down hosts. Oh, well.
|
|
|
|
Lastly, a question for Fyodor: is WSAEHOSTUNREACH a good
|
|
error return for a failed ARP query? (It should convince
|
|
nmap not to try the host again, while not confusing nmap.)
|
|
|
|
Update 12/08/04: Dana Epp (dana_at_vulscan.com) sdded SendARP stuff
|
|
for XP firewall (on by default w/SP2)
|
|
|
|
*/
|
|
|
|
#include "..\tcpip.h"
|
|
#include "..\..\NmapOps.h"
|
|
#include "winip.h"
|
|
|
|
#define MAXARPTRIES 3
|
|
#define ARPINTERVAL 200 // should be _less_ than a multiple of POLLINTERVAL
|
|
#define POLLINTERVAL 250
|
|
#define FAILCACHELEN 25 // make it high
|
|
#define ARPCACHELEN 25
|
|
|
|
void pcapsend_init();
|
|
|
|
#ifdef _DEBUG
|
|
//#define THREAD_DEBUG 1
|
|
#endif
|
|
|
|
#define REALSEND_WATCH
|
|
|
|
static void pcapsend_cleanup(void);
|
|
|
|
static int realsend(LPADAPTER pAdap,
|
|
const char *packet, int len,
|
|
BYTE *to, BYTE *from, int addrlen,
|
|
DWORD linktype, DWORD protocol);
|
|
|
|
#define ETH_IP 0x0800
|
|
#define ETH_ARP 0x0806
|
|
|
|
|
|
#if defined(THREAD_DEBUG) && THREAD_DEBUG > 1
|
|
#define foo0 printf
|
|
#define foo1 printf
|
|
#define foo2 printf
|
|
#else
|
|
#define foo0(x) ((void)0)
|
|
#define foo1(x,y) ((void)0)
|
|
#define foo2(x,y,z) ((void)0)
|
|
#endif
|
|
|
|
static int pcapsend_inited = 0;
|
|
|
|
static LPADAPTER if2adapter(int ifi, BYTE* phys, int *physlen, DWORD *type);
|
|
static void cleanup_if_cache();
|
|
|
|
// -1 on failure
|
|
static int ip2route(const struct in_addr *dest, DWORD *nexthop, DWORD *ifi);
|
|
|
|
static void releaseadapter();
|
|
|
|
static void send_arp(DWORD ifi, DWORD ip);
|
|
static void send_raw_arp(DWORD ifi, DWORD ip);
|
|
static int lookupip(DWORD ip, DWORD ifi);
|
|
|
|
// ARP cache
|
|
static void AddToARPCache(DWORD ip, int ifi, BYTE *phys, int physlen);
|
|
static int SearchARP(DWORD ip, int ifi, BYTE *phys, int *physlen);
|
|
|
|
|
|
static CRITICAL_SECTION csAdapter, csQueue, csFailCache, csArpCache, csArpTable;
|
|
static HANDLE hEvWakeup, hThread, hSemQueue;
|
|
static int killthread = 0;
|
|
|
|
// For rawsock fallback
|
|
extern SOCKET global_raw_socket;
|
|
extern int rawsock_avail;
|
|
extern int iphlp_avail;
|
|
|
|
extern NmapOps o;
|
|
|
|
#define SENDQUEUE_LEN 10 // max outstanding ARP's
|
|
|
|
struct ethernet_hdr
|
|
{
|
|
#ifndef ETHER_ADDR_LEN
|
|
#define ETHER_ADDR_LEN 6
|
|
#endif
|
|
u_char ether_dhost[ETHER_ADDR_LEN]; /* destination ethernet address */
|
|
u_char ether_shost[ETHER_ADDR_LEN]; /* source ethernet address */
|
|
u_short ether_type; /* packet type ID */
|
|
};
|
|
|
|
struct arp_hdr
|
|
{
|
|
u_short ar_hrd; /* format of hardware address */
|
|
#define ARPHRD_ETHER 1 /* ethernet hardware format */
|
|
u_short ar_pro; /* format of protocol address */
|
|
u_char ar_hln; /* length of hardware address */
|
|
u_char ar_pln; /* length of protocol addres */
|
|
u_short ar_op; /* operation type */
|
|
#define ARPOP_REQUEST 1 /* req to resolve address */
|
|
#define ARPOP_REPLY 2 /* resp to previous request */
|
|
#define ARPOP_REVREQUEST 3 /* req protocol address given hardware */
|
|
#define ARPOP_REVREPLY 4 /* resp giving protocol address */
|
|
#define ARPOP_INVREQUEST 8 /* req to identify peer */
|
|
#define ARPOP_INVREPLY 9 /* resp identifying peer */
|
|
|
|
/*
|
|
* These should implementation defined but I've hardcoded eth/IP.
|
|
*/
|
|
u_char ar_sha[6]; /* sender hardware address */
|
|
u_char ar_spa[4]; /* sender protocol address */
|
|
u_char ar_tha[6]; /* target hardware address */
|
|
u_char ar_tpa[4]; /* target protocol address */
|
|
};
|
|
|
|
static int build_ethernet(u_char *dst, u_char *src, u_short type, const u_char *payload, int payload_s, u_char *buf)
|
|
{
|
|
struct ethernet_hdr eth_hdr;
|
|
|
|
if (!buf)
|
|
{
|
|
return (-1);
|
|
}
|
|
|
|
memcpy(eth_hdr.ether_dhost, dst, ETHER_ADDR_LEN); /* destination address */
|
|
memcpy(eth_hdr.ether_shost, src, ETHER_ADDR_LEN); /* source address */
|
|
eth_hdr.ether_type = htons(type); /* packet type */
|
|
|
|
if (payload && payload_s)
|
|
{
|
|
/*
|
|
* Unchecked runtime error for buf + ETH_H payload to be greater than
|
|
* the allocated heap memory.
|
|
*/
|
|
memcpy(buf + 14, payload, payload_s);
|
|
}
|
|
memcpy(buf, ð_hdr, sizeof(eth_hdr));
|
|
return (1);
|
|
}
|
|
|
|
// assumes Ethernet
|
|
static int realsend(LPADAPTER pAdap,
|
|
const char *packet, int len,
|
|
BYTE *to, BYTE *from, int addrlen,
|
|
DWORD linktype, DWORD protocol)
|
|
{
|
|
u_char packetbuf[2048];
|
|
LPPACKET lpPacket;
|
|
|
|
#ifdef REALSEND_WATCH
|
|
if(o.debugging > 2)
|
|
{
|
|
int i;
|
|
printf("realsend: %d bytes ", len);
|
|
for(i = 0; i < addrlen; i++)
|
|
printf("%02X", from[i]);
|
|
printf(" => ");
|
|
for(i = 0; i < addrlen; i++)
|
|
printf("%02X", to[i]);
|
|
printf(" (link %d, proto %d)\n", linktype, protocol);
|
|
}
|
|
#endif
|
|
|
|
memset(packetbuf,0,2048);
|
|
|
|
if(addrlen != 6)
|
|
fatal("realsend: non-ethernet address\n");
|
|
|
|
build_ethernet(to, from, protocol, (unsigned char *) packet, len, packetbuf);
|
|
|
|
if((lpPacket = PacketAllocatePacket())==NULL)
|
|
{
|
|
printf("\nError:failed to allocate the LPPACKET structure.");
|
|
return (-1);
|
|
}
|
|
|
|
PacketInitPacket(lpPacket, packetbuf, len+14);
|
|
if(!PacketSendPacket(pAdap, lpPacket, TRUE))
|
|
{
|
|
if(o.debugging)
|
|
printf("realsend: synchronous send failed (%d)\n", GetLastError());
|
|
}
|
|
PacketFreePacket(lpPacket);
|
|
|
|
return (len);
|
|
}
|
|
|
|
// The queue
|
|
#define Q_PACKET_SIZE(x) (x + sizeof(int) + sizeof(struct _Q_PACKET))
|
|
typedef struct _Q_PACKET {
|
|
int len;
|
|
struct _Q_PACKET *next;
|
|
BYTE data[1];
|
|
} Q_PACKET;
|
|
|
|
typedef struct _Q_ROUTE {
|
|
DWORD ip;
|
|
int numpackets;
|
|
int tries, timelasttry, timefirsttry;
|
|
Q_PACKET *head, *tail;
|
|
int ifi; // -1 for free
|
|
} Q_ROUTE;
|
|
|
|
typedef struct _Q_FREE {
|
|
struct _Q_FREE *next;
|
|
} Q_FREE;
|
|
|
|
typedef struct _Q_FAIL {
|
|
DWORD ip;
|
|
int ifi;
|
|
} Q_FAIL;
|
|
|
|
Q_FAIL failcache[FAILCACHELEN];
|
|
int failfirst = 0; // 0 <= failfirst < FAILCACHELEN
|
|
|
|
typedef struct _Q_ARP {
|
|
DWORD ip;
|
|
int ifi;
|
|
BYTE phys[MAXLEN_PHYSADDR];
|
|
int physlen;
|
|
} Q_ARP;
|
|
|
|
Q_ARP arpcache[ARPCACHELEN];
|
|
int arpfirst = 0; // 0 <= arpfirst < ARPCACHELEN
|
|
|
|
PMIB_IPNETTABLE pArpTable;
|
|
unsigned long arpalloclen;
|
|
int arprefresh = 1;
|
|
|
|
// statistics
|
|
static int totaltimetofail = 0;
|
|
static int numfails = 0;
|
|
static int maxfailtime = 0;
|
|
static long queuelen = 0;
|
|
|
|
// The actual structure
|
|
static Q_ROUTE sendqueue[SENDQUEUE_LEN];
|
|
static Q_FREE *nextfree = 0; // protected by the hSemQueue
|
|
|
|
// The send thread
|
|
static unsigned int WINAPI SendThreadProc(LPVOID unused0)
|
|
{
|
|
// this thread manages send ops
|
|
|
|
/* the cycle:
|
|
|
|
1. acquire the queue
|
|
2. loop the routes
|
|
2a. is it resolved? then send it
|
|
2b. have 200 ms elapsed since last try? then send an ARP
|
|
2c. have we timed out (3 tries, 600ms)? then kill it
|
|
3. release the queue
|
|
4. wait for 250ms or hEvWakeup
|
|
*/
|
|
|
|
#ifdef _MSC_VER
|
|
__try {
|
|
#endif
|
|
|
|
while(!killthread)
|
|
{
|
|
int nRes;
|
|
int i;
|
|
DWORD time;
|
|
|
|
arprefresh = (queuelen ? 1 : 0);
|
|
|
|
time = GetTickCount(); // handle the wrap correctly
|
|
|
|
// Step 1: acquire the queue
|
|
foo0("sendthread: try acquire csQueue\n");
|
|
EnterCriticalSection(&csQueue);
|
|
foo0("sendthread: acquired csQueue\n");
|
|
|
|
// Step 2: loop the routes
|
|
for(i = 0; i < SENDQUEUE_LEN; i++)
|
|
{
|
|
BYTE phys[MAXLEN_PHYSADDR];
|
|
int physlen = MAXLEN_PHYSADDR;
|
|
if(sendqueue[i].ifi == -1) continue; // free
|
|
|
|
// Step 2a: is it resolved?
|
|
if(0 == SearchARP(sendqueue[i].ip, sendqueue[i].ifi,
|
|
phys, &physlen))
|
|
{
|
|
// we got it!
|
|
Q_FREE *f;
|
|
Q_PACKET *p = sendqueue[i].head;
|
|
BYTE myphys[MAXLEN_PHYSADDR];
|
|
int myphyslen = MAXLEN_PHYSADDR;
|
|
unsigned long mytype;
|
|
LPADAPTER pAdap;
|
|
|
|
#ifdef THREAD_DEBUG
|
|
printf("sendthread: resolved %s\n", inet_ntoa(*(struct in_addr*)&sendqueue[i].ip));
|
|
#endif
|
|
|
|
pAdap = if2adapter(sendqueue[i].ifi,
|
|
myphys, &myphyslen, &mytype);
|
|
if(!pAdap) fatal("if2adapter failed?!?\n");
|
|
while(p)
|
|
{
|
|
Q_PACKET *next = p->next;
|
|
realsend(pAdap, (char *) p->data, p->len, phys,
|
|
myphys, myphyslen, mytype, ETH_IP);
|
|
free(p);
|
|
p = next;
|
|
}
|
|
f = (Q_FREE*)&sendqueue[i];
|
|
sendqueue[i].ifi = -1;
|
|
f->next = nextfree;
|
|
nextfree = f;
|
|
releaseadapter();
|
|
|
|
// notify that we have a free
|
|
InterlockedDecrement(&queuelen);
|
|
ReleaseSemaphore(hSemQueue, 1, 0);
|
|
}
|
|
|
|
// Step 2b: should we try again?
|
|
else if(sendqueue[i].tries < MAXARPTRIES
|
|
&& sendqueue[i].timelasttry <= time - ARPINTERVAL)
|
|
{
|
|
// try again
|
|
send_arp(sendqueue[i].ifi, sendqueue[i].ip);
|
|
sendqueue[i].tries++;
|
|
sendqueue[i].timelasttry = time;
|
|
}
|
|
|
|
// Step 2c: should we kill it?
|
|
else if((sendqueue[i].tries >= MAXARPTRIES
|
|
&& sendqueue[i].timelasttry <= time - ARPINTERVAL))
|
|
{
|
|
// kill it
|
|
Q_FREE *f;
|
|
Q_PACKET *p = sendqueue[i].head;
|
|
while(p)
|
|
{
|
|
Q_PACKET *next = p->next;
|
|
free(p);
|
|
p = next;
|
|
}
|
|
|
|
// cache the failure
|
|
foo0("sendthread: try acquire csFailCache\n");
|
|
EnterCriticalSection(&csFailCache);
|
|
foo0("sendthread: acquired csFailCache\n");
|
|
failcache[failfirst].ifi = sendqueue[i].ifi;
|
|
failcache[failfirst].ip = sendqueue[i].ip;
|
|
failfirst = (failfirst + 1) % FAILCACHELEN;
|
|
LeaveCriticalSection(&csFailCache);
|
|
|
|
// gather stats
|
|
numfails++;
|
|
totaltimetofail += (time - sendqueue[i].timefirsttry);
|
|
if((time - sendqueue[i].timefirsttry) > maxfailtime)
|
|
maxfailtime = (time - sendqueue[i].timefirsttry);
|
|
|
|
#ifdef THREAD_DEBUG
|
|
printf("sendthread: %s failed (avg = %lu ms; max = %lu ms)\n",
|
|
inet_ntoa(*(struct in_addr*)&sendqueue[i].ip),
|
|
totaltimetofail / numfails, maxfailtime);
|
|
#endif
|
|
|
|
// free it
|
|
f = (Q_FREE*)&sendqueue[i];
|
|
sendqueue[i].ifi = -1;
|
|
f->next = nextfree;
|
|
nextfree = f;
|
|
|
|
// and notify that we have a free
|
|
InterlockedDecrement(&queuelen);
|
|
ReleaseSemaphore(hSemQueue, 1, 0);
|
|
}
|
|
// else do nothing
|
|
}
|
|
|
|
// Step 3: release the queue
|
|
LeaveCriticalSection(&csQueue);
|
|
|
|
// Step 4: wait
|
|
// yah yah I know... but i'm too lazy to fix this
|
|
WaitForSingleObject(hEvWakeup, POLLINTERVAL);
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
} __except(printf("\n\n***** ERROR IN SEND THREAD *****\n\n"),
|
|
EXCEPTION_CONTINUE_SEARCH) {}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
// helpers
|
|
static void AddPacketToQueue(const void *data, int len, DWORD ip, int ifi)
|
|
{
|
|
int i;
|
|
Q_ROUTE *r;
|
|
Q_PACKET *p;
|
|
|
|
begin:
|
|
// Is it already there?
|
|
EnterCriticalSection(&csQueue);
|
|
for(i = 0; i < SENDQUEUE_LEN; i++)
|
|
{
|
|
if(sendqueue[i].ifi == ifi && sendqueue[i].ip == ip)
|
|
{
|
|
// We're good to go!
|
|
|
|
if(sendqueue[i].numpackets >= 5)
|
|
{
|
|
// Alas, we need to wait so we don't kill the system
|
|
LeaveCriticalSection(&csQueue);
|
|
Sleep(500); // give it a chance
|
|
goto begin;
|
|
}
|
|
|
|
p = (Q_PACKET*)malloc(Q_PACKET_SIZE(len));
|
|
memcpy(p->data, data, len);
|
|
p->len = len;
|
|
p->next = 0;
|
|
|
|
#ifdef _DEBUG
|
|
{
|
|
int foo = 0;
|
|
if(sendqueue[i].tail) foo++;
|
|
if(sendqueue[i].head) foo++;
|
|
if(sendqueue[i].numpackets) foo++;
|
|
if(foo != 0 && foo != 3)
|
|
fatal("corrupt packet cache\n");
|
|
}
|
|
#endif
|
|
|
|
if(sendqueue[i].tail)
|
|
sendqueue[i].tail->next = p;
|
|
else sendqueue[i].head = p;
|
|
sendqueue[i].tail = p;
|
|
sendqueue[i].numpackets++;
|
|
|
|
LeaveCriticalSection(&csQueue);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// it's not already there -- leave the CS
|
|
LeaveCriticalSection(&csQueue);
|
|
|
|
// get a spot in line
|
|
while(WAIT_TIMEOUT == WaitForSingleObject(hSemQueue, 10000))
|
|
printf("addpackettoqueue: this is taking WAY too long (%lu/%lu)...\n",
|
|
queuelen, SENDQUEUE_LEN);
|
|
|
|
// and write it
|
|
EnterCriticalSection(&csQueue);
|
|
|
|
// we should grab a new route obj
|
|
if(!nextfree) fatal("where'd my block go?\n");
|
|
r = (Q_ROUTE*)nextfree;
|
|
nextfree = nextfree->next;
|
|
r->ifi = ifi;
|
|
r->ip = ip;
|
|
r->numpackets = 1;
|
|
r->tries = 1;
|
|
r->timefirsttry = r->timelasttry = GetTickCount();
|
|
send_arp(ifi, ip); // post the first try now
|
|
p = (Q_PACKET*)malloc(Q_PACKET_SIZE(len));
|
|
if(!p) fatal("out of memory\n");
|
|
r->head = p;
|
|
r->tail = p;
|
|
memcpy(p->data, data, len);
|
|
p->len = len;
|
|
p->next = 0;
|
|
InterlockedIncrement(&queuelen);
|
|
#ifdef THREAD_DEBUG
|
|
printf("addpacket: %s (len = %lu)\n",
|
|
inet_ntoa(*(struct in_addr*)&ip), queuelen);
|
|
#endif
|
|
LeaveCriticalSection(&csQueue);
|
|
|
|
return;
|
|
}
|
|
|
|
// this needs to change for non-Ethernet
|
|
static void send_arp(DWORD ifi, DWORD ip)
|
|
{
|
|
/* Used to send raw ARP packet on the wire, and then read the result
|
|
out of the system cache. Kinda ugly, but anyway ... Windows
|
|
Firewall (default-on with XP SP2) started ignoring the responses
|
|
because it didn't send them (reasonable), thus breaking the
|
|
technique. Now the SendARP iphlpapi function is used. A
|
|
downside is that I believe it blocks, so it may cause
|
|
local-network ping scans to take much longer on Windows. I
|
|
suppose we should send raw and then read results from pcap. But
|
|
for now, we have this */
|
|
HRESULT ret;
|
|
ULONG uMACAddr[2];
|
|
ULONG uSize = 6;
|
|
PBYTE pBuffer;
|
|
struct in_addr myip;
|
|
|
|
/* For windows95 machines that does not load iphlpapi.dll, send raw
|
|
ARP packet */
|
|
if( !iphlp_avail )
|
|
{
|
|
send_raw_arp(ifi,ip);
|
|
return;
|
|
}
|
|
|
|
ret = SendARP( ip, 0, uMACAddr, &uSize );
|
|
|
|
if( NO_ERROR == ret )
|
|
{
|
|
pBuffer = (PBYTE)uMACAddr;
|
|
AddToARPCache( ip, ifi, pBuffer, (int)uSize );
|
|
}
|
|
}
|
|
|
|
// this to send raw arp packet
|
|
static void send_raw_arp(DWORD ifi, DWORD ip)
|
|
{
|
|
struct arp_hdr arp_h;
|
|
LPADAPTER pAdap;
|
|
BYTE mymac[6];
|
|
int len;
|
|
unsigned long mytype;
|
|
struct in_addr myip;
|
|
BYTE bcastmac[6]; // more Ethernet code !
|
|
memset(bcastmac, 0xFF, 6);
|
|
|
|
if(0 != ifi2ipaddr(ifi, &myip))
|
|
fatal("sendarp: failed to find my ip ?!?\n");
|
|
|
|
// get the MAC et al
|
|
len = 6;
|
|
pAdap = if2adapter(ifi, mymac, &len, &mytype);
|
|
if(!pAdap)
|
|
{
|
|
// do nothing for localhost scan
|
|
if(myip.s_addr == 0x0100007f) return;
|
|
else fatal("send_arp: can't send on this interface\n");
|
|
}
|
|
|
|
arp_h.ar_hrd=0x0100;
|
|
|
|
arp_h.ar_pro=0x0008; /* format of protocol address */
|
|
arp_h.ar_hln=6; /* length of hardware address */
|
|
arp_h.ar_pln=4; /* length of protocol addres */
|
|
arp_h.ar_op=0x0100 ;
|
|
memcpy(arp_h.ar_sha,mymac,6);
|
|
memcpy(arp_h.ar_spa,&myip.s_addr,4);
|
|
memset(arp_h.ar_tha,0,6);
|
|
memcpy(arp_h.ar_tpa,&ip,4);
|
|
|
|
realsend(pAdap, (char*)&arp_h, sizeof(arp_h),
|
|
bcastmac, mymac, len, mytype, ETH_ARP);
|
|
|
|
releaseadapter();
|
|
}
|
|
|
|
// resolves an ip addr into a nexthop and index
|
|
static int ip2route(const struct in_addr *dest, DWORD *nexthop, DWORD *ifi)
|
|
{
|
|
static DWORD last_ip = 0;
|
|
static int last_nexthop;
|
|
static int last_ifi;
|
|
|
|
MIB_IPFORWARDROW route;
|
|
|
|
// check the cache
|
|
if(last_ip == dest->s_addr)
|
|
{
|
|
if(nexthop) *nexthop = last_nexthop;
|
|
if(ifi) *ifi = last_ifi;
|
|
return 0;
|
|
}
|
|
|
|
if(0 != get_best_route(dest->s_addr, &route))
|
|
return -1; // failure
|
|
|
|
last_ip = 0; // if we abort, mark as bad
|
|
|
|
// Compute the next hop
|
|
switch(route.dwForwardType)
|
|
{
|
|
case 3:
|
|
// local route; the dest is the next hop
|
|
last_nexthop = dest->s_addr;
|
|
break;
|
|
|
|
case 4:
|
|
// remote route; use the specified gateway
|
|
last_nexthop = route.dwForwardNextHop;
|
|
break;
|
|
|
|
case 2:
|
|
fatal("corrupt route table\n");
|
|
break;
|
|
|
|
default:
|
|
fatal("unknown route format %d\n", route.dwForwardType);
|
|
break;
|
|
}
|
|
|
|
// save the index
|
|
last_ifi = winif2ifi(route.dwForwardIfIndex);
|
|
|
|
// mark it valid
|
|
last_ip = dest->s_addr;
|
|
|
|
// Try to return the answer
|
|
if(nexthop) *nexthop = last_nexthop;
|
|
if(ifi) *ifi = last_ifi;
|
|
|
|
if(o.debugging > 1)
|
|
{
|
|
printf("%s is routed through ", inet_ntoa(*(struct in_addr*)&last_ip));
|
|
printf("%s\n", inet_ntoa(*(struct in_addr*)&last_nexthop));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// The adapter cache
|
|
typedef struct _IFC_ROW {
|
|
int ifi;
|
|
LPADAPTER pAdapter;
|
|
BYTE phys[MAXLEN_PHYSADDR];
|
|
int physlen;
|
|
DWORD type;
|
|
} IFC_ROW;
|
|
|
|
static IFC_ROW last_if = {-1};
|
|
|
|
/*
|
|
static int numif = 0;
|
|
static IFC_ROW *ifcache = 0;
|
|
*/
|
|
static void cleanup_if_cache()
|
|
{
|
|
if(last_if.pAdapter)
|
|
{
|
|
PacketCloseAdapter(last_if.pAdapter);
|
|
last_if.pAdapter = 0;
|
|
}
|
|
|
|
last_if.ifi = -1;
|
|
}
|
|
|
|
static LPADAPTER if2adapter(int ifi, BYTE* phys, int *physlen, DWORD *type)
|
|
{
|
|
const WINIP_IF *ifentry;
|
|
char *name;
|
|
|
|
EnterCriticalSection(&csAdapter);
|
|
|
|
if(last_if.ifi == ifi)
|
|
{
|
|
if(last_if.physlen == -1) return 0;
|
|
if(last_if.pAdapter)
|
|
{
|
|
if(physlen && (*physlen < last_if.physlen)) return 0;
|
|
if(phys) memcpy(phys, last_if.phys, last_if.physlen);
|
|
*physlen = last_if.physlen;
|
|
if(type) *type = last_if.type;
|
|
|
|
// we do not release the CS
|
|
return last_if.pAdapter;
|
|
}
|
|
}
|
|
|
|
ifentry = ifi2ifentry(ifi);
|
|
if(!ifentry || !ifentry->pcapname)
|
|
{
|
|
LeaveCriticalSection(&csAdapter);
|
|
return 0; // Can't do this one...
|
|
}
|
|
|
|
cleanup_if_cache();
|
|
|
|
last_if.pAdapter = PacketOpenAdapter(ifentry->pcapname);
|
|
|
|
// This is required on Win9x (defaults to 0)
|
|
// It's probably a good idea on WinNT/2K
|
|
PacketSetNumWrites(last_if.pAdapter, 1);
|
|
|
|
if(!last_if.pAdapter)
|
|
{
|
|
last_if.physlen = -1;
|
|
LeaveCriticalSection(&csAdapter);
|
|
return 0;
|
|
}
|
|
|
|
last_if.physlen = ifentry->physlen;
|
|
memcpy(last_if.phys, ifentry->physaddr, ifentry->physlen);
|
|
last_if.type = ifentry->type;
|
|
last_if.ifi = ifi;
|
|
|
|
// Try to return the answer
|
|
if(physlen && (*physlen < last_if.physlen))
|
|
{
|
|
LeaveCriticalSection(&csAdapter);
|
|
return 0;
|
|
}
|
|
if(phys) memcpy(phys, last_if.phys, last_if.physlen);
|
|
*physlen = last_if.physlen;
|
|
if(type) *type = last_if.type;
|
|
|
|
// We do not leave the CS
|
|
return last_if.pAdapter;
|
|
}
|
|
|
|
static void releaseadapter()
|
|
{
|
|
LeaveCriticalSection(&csAdapter);
|
|
}
|
|
|
|
static int fallback_raw_send(const char *packet, int len,
|
|
struct sockaddr *to, int tolen)
|
|
{
|
|
if(!rawsock_avail) {
|
|
fatal("fallback_raw_send: no raw sockets\n"
|
|
"This means that you tried to send to an unsupported interface.\n");
|
|
}
|
|
|
|
return sendto(global_raw_socket, packet, len, 0, to, tolen);
|
|
}
|
|
|
|
// The almighty pcapsendraw
|
|
// This is the whole point of this file :)
|
|
int pcapsendraw(const char *packet, int len,
|
|
struct sockaddr *to, int tolen)
|
|
{
|
|
struct sockaddr_in *sin = (struct sockaddr_in *) to;
|
|
int cb = 0;
|
|
int nRes, i;
|
|
DWORD nextip;
|
|
unsigned long ifi;
|
|
LPADAPTER pAdap;
|
|
BYTE myphys[MAXLEN_PHYSADDR], tphys[MAXLEN_PHYSADDR];
|
|
int physlen = MAXLEN_PHYSADDR;
|
|
DWORD type;
|
|
const WINIP_IF *target_ifentry;
|
|
|
|
if(!pcapsend_inited)
|
|
return fallback_raw_send(packet, len, to, tolen);
|
|
|
|
if(-1 == ip2route(&sin->sin_addr, &nextip, &ifi))
|
|
{
|
|
WSASetLastError(WSAENETUNREACH);
|
|
return -1; // no route to host
|
|
}
|
|
|
|
target_ifentry = ifi2ifentry(ifi);
|
|
if(!target_ifentry || !target_ifentry->pcapname)
|
|
return fallback_raw_send(packet, len, to, tolen);
|
|
|
|
// check the failcache
|
|
EnterCriticalSection(&csFailCache);
|
|
for(i = 0; i < FAILCACHELEN; i++)
|
|
{
|
|
if(failcache[i].ip == nextip && failcache[i].ifi == ifi)
|
|
{
|
|
// it failed
|
|
WSASetLastError(WSAEHOSTUNREACH);
|
|
#ifdef THREAD_DEBUG
|
|
printf("sendto: autofailed %s\n", inet_ntoa(*(struct in_addr*)&failcache[i].ip));
|
|
#endif
|
|
LeaveCriticalSection(&csFailCache);
|
|
return -1;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&csFailCache);
|
|
|
|
// Read the data
|
|
nRes = SearchARP(nextip, ifi, tphys, &physlen);
|
|
if(-1 == nRes)
|
|
{
|
|
// defer the send
|
|
AddPacketToQueue(packet, len, nextip, ifi);
|
|
return len; // it's in the queue, so it worked...
|
|
}
|
|
|
|
// otherwise, we have an address
|
|
pAdap = if2adapter(ifi, myphys, &physlen, &type);
|
|
if(!pAdap)
|
|
fatal("can't send to this interface\n");
|
|
realsend(pAdap, packet, len, tphys, myphys, physlen, type, ETH_IP);
|
|
releaseadapter();
|
|
return len;
|
|
}
|
|
|
|
void pcapsend_init()
|
|
{
|
|
int i, nRes;
|
|
unsigned int id;
|
|
|
|
if(pcapsend_inited) return;
|
|
pcapsend_inited = 1;
|
|
|
|
if(o.debugging > 1)
|
|
printf("Initializing winpcap send support...");
|
|
|
|
for(i = 0; i < SENDQUEUE_LEN; i++)
|
|
{
|
|
Q_FREE *f;
|
|
sendqueue[i].ifi = -1;
|
|
f = (Q_FREE*)&sendqueue[i];
|
|
f->next = nextfree;
|
|
nextfree = f;
|
|
}
|
|
|
|
InitializeCriticalSection(&csQueue);
|
|
InitializeCriticalSection(&csAdapter);
|
|
InitializeCriticalSection(&csFailCache);
|
|
InitializeCriticalSection(&csArpCache);
|
|
InitializeCriticalSection(&csArpTable);
|
|
|
|
hEvWakeup = CreateEvent(0, 0, 0, 0);
|
|
hSemQueue = CreateSemaphore(0, SENDQUEUE_LEN, SENDQUEUE_LEN, 0);
|
|
|
|
// allocate the ARP cache
|
|
arpalloclen = 0;
|
|
pArpTable = (PMIB_IPNETTABLE)&i;
|
|
nRes = GetIpNetTableSafe(pArpTable, &arpalloclen, FALSE);
|
|
if(arpalloclen == 0)
|
|
{
|
|
if(o.debugging && nRes != ERROR_NO_DATA)
|
|
printf("ARP table length failure (%lu) during init -- try kludge1 :(\n", nRes);
|
|
arpalloclen = 100 * sizeof(MIB_IPNETROW) + 8;
|
|
}
|
|
|
|
// Read the data
|
|
arpalloclen += 3 * sizeof(MIB_IPNETROW);
|
|
pArpTable = (PMIB_IPNETTABLE)malloc(arpalloclen);
|
|
if(!pArpTable)
|
|
{
|
|
pcapsend_inited = 0;
|
|
fatal("out of memory\n");
|
|
}
|
|
nRes = GetIpNetTableSafe(pArpTable, &arpalloclen, TRUE);
|
|
|
|
if(nRes != NO_ERROR)
|
|
{
|
|
if(o.debugging && nRes != ERROR_NO_DATA)
|
|
printf("ARP failure (%lu) during init -- trying kludge2\n", nRes);
|
|
pArpTable->dwNumEntries = 0;
|
|
}
|
|
|
|
// Start the send thread
|
|
hThread = (HANDLE)_beginthreadex(0, 0, SendThreadProc, 0, 0, &id);
|
|
if(!hThread)
|
|
{
|
|
pcapsend_inited = 0;
|
|
fatal("failed to start thread\n");
|
|
}
|
|
|
|
atexit(pcapsend_cleanup);
|
|
|
|
pcapsend_inited = 1;
|
|
|
|
if(o.debugging > 1)
|
|
printf(" Done\n");
|
|
}
|
|
|
|
// the name cache
|
|
typedef struct _IFNAME_ROW {
|
|
int ifi;
|
|
char name[128];
|
|
DWORD ip; // This is one of the interface's IPs
|
|
DWORD type;
|
|
} IFNAME_ROW;
|
|
|
|
static IFNAME_ROW *names = 0;
|
|
static int num_names;
|
|
|
|
static void pcapsend_cleanup(void)
|
|
{
|
|
if(!pcapsend_inited)
|
|
return;
|
|
|
|
assert(!killthread);
|
|
|
|
killthread = 1;
|
|
SetEvent(hEvWakeup);
|
|
if(WAIT_TIMEOUT == WaitForSingleObject(hThread, 10000))
|
|
{
|
|
error("timed out waiting for thread exit; terminating...\n");
|
|
TerminateThread(hThread, 0);
|
|
}
|
|
|
|
CloseHandle(hEvWakeup);
|
|
CloseHandle(hThread);
|
|
CloseHandle(hSemQueue);
|
|
DeleteCriticalSection(&csQueue);
|
|
DeleteCriticalSection(&csAdapter);
|
|
DeleteCriticalSection(&csFailCache);
|
|
DeleteCriticalSection(&csArpCache);
|
|
DeleteCriticalSection(&csArpTable);
|
|
cleanup_if_cache();
|
|
|
|
if(names) free(names);
|
|
if(pArpTable) free(pArpTable);
|
|
}
|
|
|
|
// safe implementation for getbestroute
|
|
typedef DWORD (__stdcall *PGBR)(DWORD, DWORD, PMIB_IPFORWARDROW);
|
|
int get_best_route(DWORD dest, PMIB_IPFORWARDROW r)
|
|
{
|
|
static PGBR GBR = 0;
|
|
static int inited = 0;
|
|
|
|
int winif = -1, ifi = -1;
|
|
|
|
if(!inited)
|
|
{
|
|
HINSTANCE hInst = GetModuleHandle("iphlpapi.dll");
|
|
inited = 1;
|
|
if(hInst && !wo.nt4route)
|
|
GBR = (PGBR)GetProcAddress(hInst, "GetBestRoute");
|
|
if(o.debugging > 1 && !GBR)
|
|
printf("get_best_route: using NT4-compatible method\n");
|
|
}
|
|
|
|
// Find the index
|
|
if(*o.device)
|
|
{
|
|
ifi = name2ifi(o.device);
|
|
winif = ifi2winif(ifi);
|
|
if(winif == -1)
|
|
fatal("get_best_route: nonexistant interface \"%s\"\n", o.device);
|
|
}
|
|
|
|
// Can we simply redirect?
|
|
tryagain:
|
|
if(GBR)
|
|
{
|
|
DWORD source = 0;
|
|
DWORD nRes;
|
|
|
|
if(ifi != -1)
|
|
{
|
|
if(-1 == ifi2ipaddr(ifi, (struct in_addr*)&source))
|
|
source = 0; // wtf?
|
|
}
|
|
|
|
nRes = GBR(dest, source, r);
|
|
if(nRes == ERROR_CALL_NOT_IMPLEMENTED)
|
|
{
|
|
GBR = 0;
|
|
goto tryagain;
|
|
}
|
|
|
|
if(nRes != 0) return nRes;
|
|
|
|
// verify we have a good match
|
|
if(winif != -1 && r->dwForwardIfIndex != winif)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// We need to do this for real
|
|
else
|
|
{
|
|
PMIB_IPFORWARDTABLE pTable = 0;
|
|
unsigned long cb = 0;
|
|
int bestmatch = -1;
|
|
int bestmask, bestmetric;
|
|
int nRes, i;
|
|
|
|
nRes = GetIpForwardTableSafe(pTable, &cb, FALSE);
|
|
if(cb == 0) return (nRes ? nRes : -1);
|
|
|
|
cb += sizeof(MIB_IPFORWARDROW);
|
|
pTable = (PMIB_IPFORWARDTABLE)_alloca(cb);
|
|
nRes = GetIpForwardTableSafe(pTable, &cb, FALSE);
|
|
if(nRes != NO_ERROR) return nRes;
|
|
|
|
if(pTable->dwNumEntries < 1) return -1;
|
|
|
|
for(i = 0; i < pTable->dwNumEntries; i++)
|
|
{
|
|
// is it a match?
|
|
if(pTable->table[i].dwForwardDest != (dest & pTable->table[i].dwForwardMask))
|
|
continue;
|
|
|
|
if(winif != -1 && pTable->table[i].dwForwardIfIndex != winif) continue;
|
|
|
|
/* if(bestmatch == -1 || (pTable->table[i].dwForwardMask > bestmask)
|
|
|| (pTable->table[i].dwForwardMask == bestmask
|
|
&& pTable->table[i].dwForwardMetric1 < bestmetric))*/
|
|
if(bestmatch == -1 || (pTable->table[i].dwForwardMetric1 > bestmetric)
|
|
|| (pTable->table[i].dwForwardMetric1 == bestmetric
|
|
&& pTable->table[i].dwForwardMask > bestmask))
|
|
|
|
{
|
|
bestmatch = i;
|
|
bestmask = pTable->table[i].dwForwardMask;
|
|
bestmetric = pTable->table[i].dwForwardMetric1;
|
|
}
|
|
}
|
|
|
|
if(bestmatch == -1) return -1;
|
|
|
|
memcpy(r, &pTable->table[bestmatch], sizeof(MIB_IPFORWARDROW));
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// ARP cache
|
|
static void AddToARPCache(DWORD ip, int ifi, BYTE *phys, int physlen)
|
|
{
|
|
if(physlen > MAXLEN_PHYSADDR)
|
|
fatal("physical address too long!\n");
|
|
|
|
foo0("addtoarpcache: try acquire csArpCache\n");
|
|
EnterCriticalSection(&csArpCache);
|
|
foo0("addtoarpcache: acquired csArpCache\n");
|
|
arpcache[arpfirst].ifi = ifi;
|
|
arpcache[arpfirst].ip = ip;
|
|
memcpy(arpcache[arpfirst].phys, phys, physlen);
|
|
arpcache[arpfirst].physlen = physlen;
|
|
arpfirst = (arpfirst + 1) % ARPCACHELEN;
|
|
LeaveCriticalSection(&csArpCache);
|
|
}
|
|
|
|
static int lookupip(DWORD ip, DWORD ifi)
|
|
{
|
|
DWORD time = GetTickCount();
|
|
int nRes;
|
|
int low, high;
|
|
int pass = 0;
|
|
DWORD winif = ifi2winif(ifi);
|
|
|
|
EnterCriticalSection(&csArpTable);
|
|
|
|
goto pass0;
|
|
|
|
pass1:
|
|
pass = 1;
|
|
|
|
if(arprefresh)
|
|
{
|
|
// refresh
|
|
unsigned long len = arpalloclen;
|
|
|
|
#ifdef THREAD_DEBUG
|
|
printf("lookupip: refreshing ARP table\n");
|
|
#endif
|
|
|
|
arprefresh = 0;
|
|
|
|
readarp:
|
|
nRes = GetIpNetTableSafe(pArpTable, &len, TRUE);
|
|
|
|
if(nRes == ERROR_MORE_DATA)
|
|
len += 2 * sizeof(MIB_IPNETROW); // give the benefit of the doubt
|
|
|
|
if(len == arpalloclen && nRes != NO_ERROR)
|
|
{
|
|
// Windows bug -- just assume the table is empty
|
|
pArpTable->dwNumEntries = 0;
|
|
LeaveCriticalSection(&csArpTable);
|
|
return -1;
|
|
}
|
|
|
|
if(len > arpalloclen)
|
|
{
|
|
// need to try that again
|
|
free(pArpTable);
|
|
pArpTable = (PMIB_IPNETTABLE)malloc(len);
|
|
arpalloclen = len;
|
|
if(!pArpTable) fatal("out of memory\n");
|
|
goto readarp; // please don't infinite loop!
|
|
}
|
|
}
|
|
|
|
pass0:
|
|
|
|
low = 0;
|
|
high = pArpTable->dwNumEntries - 1;
|
|
while(low <= high)
|
|
{
|
|
int i = low + (high - low) / 2;
|
|
if(pArpTable->table[i].dwAddr == ip
|
|
&& pArpTable->table[i].dwType != 2)
|
|
{
|
|
// we found it
|
|
if(pArpTable->table[i].dwIndex != winif)
|
|
{
|
|
fatal("lookupip: found ip on wrong interface\n"
|
|
"e-mail amluto@hotmail.com if you think this should have worked\n");
|
|
}
|
|
|
|
LeaveCriticalSection(&csArpTable);
|
|
return i;
|
|
}
|
|
|
|
// Otherwise, we need to narrow search region
|
|
if(pArpTable->table[i].dwAddr < ip)
|
|
low = i + 1;
|
|
else high = i - 1;
|
|
}
|
|
|
|
if(pass == 0) goto pass1;
|
|
|
|
LeaveCriticalSection(&csArpTable);
|
|
return -1;
|
|
}
|
|
|
|
static int SearchARP(DWORD ip, int ifi, BYTE *phys, int *physlen)
|
|
{
|
|
int i;
|
|
|
|
foo0("searcharp: try acquire csArpCache\n");
|
|
EnterCriticalSection(&csArpCache);
|
|
foo0("searcharp: acquired csArpCache\n");
|
|
|
|
// Is it in the ARP cache?
|
|
for(i = 0; i < ARPCACHELEN; i++)
|
|
{
|
|
if(arpcache[i].ip == ip && arpcache[i].ifi == ifi)
|
|
{
|
|
// we got it!
|
|
if(*physlen < arpcache[i].physlen)
|
|
fatal("searcharp: can't return the answer\n");
|
|
|
|
memcpy(phys, arpcache[i].phys, arpcache[i].physlen);
|
|
*physlen = arpcache[i].physlen;
|
|
#ifdef THREAD_DEBUG
|
|
printf("searcharp: found %s in cache\n", inet_ntoa(*(struct in_addr*)&ip));
|
|
#endif
|
|
LeaveCriticalSection(&csArpCache);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// else look it up
|
|
i = lookupip(ip, ifi);
|
|
|
|
LeaveCriticalSection(&csArpCache);
|
|
|
|
if(i == -1) return -1;
|
|
|
|
if(*physlen < pArpTable->table[i].dwPhysAddrLen)
|
|
fatal("insufficient space for physaddr\n");
|
|
|
|
*physlen = pArpTable->table[i].dwPhysAddrLen;
|
|
memcpy(phys, pArpTable->table[i].bPhysAddr,
|
|
pArpTable->table[i].dwPhysAddrLen);
|
|
|
|
#ifdef THREAD_DEBUG
|
|
printf("searcharp: found %s in system ARP table\n", inet_ntoa(*(struct in_addr*)&ip));
|
|
#endif
|
|
|
|
AddToARPCache(ip, ifi, phys, pArpTable->table[i].dwPhysAddrLen);
|
|
|
|
return 0;
|
|
}
|