1
0
mirror of https://github.com/nmap/nmap.git synced 2026-02-04 20:46:33 +00:00
Files
nmap/mswin32/winip/pcapsend.c
2005-04-11 22:34:19 +00:00

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, &eth_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;
}