WinUAE/od-win32/win32_uaenet.cpp

559 lines
14 KiB
C++

/*
* UAE - The Un*x Amiga Emulator
*
* Win32 uaenet emulation
*
* Copyright 2007 Toni Wilen
*/
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <Iphlpapi.h>
#include "sysconfig.h"
#include "sysdeps.h"
#include <stdio.h>
#define HAVE_REMOTE
#define WPCAP
#define PCAP_DONT_INCLUDE_PCAP_BPF_H
#include "pcap.h"
#include "packet32.h"
#include "pcap/dlt.h"
#include "ntddndis.h"
#include "options.h"
#include "traps.h"
#include "sana2.h"
#include "threaddep/thread.h"
#include "win32_uaenet.h"
#include "win32.h"
int log_ethernet;
static struct netdriverdata tds[MAX_TOTAL_NET_DEVICES];
static int enumerated;
static int ethernet_paused;
typedef int(_cdecl *PCAP_FINDALLDEVS_EX)(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf);
static PCAP_FINDALLDEVS_EX ppcap_findalldevs_ex;
typedef void(_cdecl *PCAP_FREEALLDEVS)(pcap_if_t *);
static PCAP_FREEALLDEVS ppcap_freealldevs;
typedef pcap_t *(_cdecl *PCAP_OPEN)(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);
static PCAP_OPEN ppcap_open;
typedef void (_cdecl *PCAP_CLOSE)(pcap_t *);
static PCAP_CLOSE ppcap_close;
typedef int (_cdecl *PCAP_DATALINK)(pcap_t *);
static PCAP_DATALINK ppcap_datalink;
typedef int (_cdecl *PCAP_SENDPACKET)(pcap_t *, const u_char *, int);
static PCAP_SENDPACKET ppcap_sendpacket;
typedef int(_cdecl *PCAP_NEXT_EX)(pcap_t *, struct pcap_pkthdr **, const u_char **);
static PCAP_NEXT_EX ppcap_next_ex;
typedef const char *(_cdecl *PCAP_LIB_VERSION)(void);
static PCAP_LIB_VERSION ppcap_lib_version;
typedef LPADAPTER(_cdecl *PACKETOPENADAPTER)(PCHAR AdapterName);
static PACKETOPENADAPTER pPacketOpenAdapter;
typedef VOID(_cdecl *PACKETCLOSEADAPTER)(LPADAPTER lpAdapter);
static PACKETCLOSEADAPTER pPacketCloseAdapter;
typedef BOOLEAN (_cdecl *PACKETREQUEST)(LPADAPTER AdapterObject, BOOLEAN Set, PPACKET_OID_DATA OidData);
static PACKETREQUEST pPacketRequest;
static HMODULE wpcap, packet;
struct uaenetdatawin32
{
HANDLE evttw;
void *readdata, *writedata;
uae_sem_t change_sem;
volatile int threadactiver;
uae_thread_id tidr;
uae_sem_t sync_semr;
volatile int threadactivew;
uae_thread_id tidw;
uae_sem_t sync_semw;
void *user;
struct netdriverdata *tc;
uae_u8 *readbuffer;
uae_u8 *writebuffer;
int mtu;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *fp;
uaenet_gotfunc *gotfunc;
uaenet_getfunc *getfunc;
};
int uaenet_getdatalenght (void)
{
return sizeof (struct uaenetdatawin32);
}
static void uaeser_initdata (struct uaenetdatawin32 *sd, void *user)
{
memset (sd, 0, sizeof (struct uaenetdatawin32));
sd->evttw = 0;
sd->user = user;
sd->fp = NULL;
}
#if 0
static void *uaenet_trap_thread (void *arg)
{
struct uaenetdatawin32 *sd = arg;
HANDLE handles[4];
int cnt, towrite;
int readactive, writeactive;
DWORD actual;
uae_set_thread_priority (NULL, 2);
sd->threadactive = 1;
uae_sem_post (&sd->sync_sem);
readactive = 0;
writeactive = 0;
while (sd->threadactive == 1) {
int donotwait = 0;
uae_sem_wait (&sd->change_sem);
if (readactive) {
if (GetOverlappedResult (sd->hCom, &sd->olr, &actual, FALSE)) {
readactive = 0;
uaenet_gotdata (sd->user, sd->readbuffer, actual);
donotwait = 1;
}
}
if (writeactive) {
if (GetOverlappedResult (sd->hCom, &sd->olw, &actual, FALSE)) {
writeactive = 0;
donotwait = 1;
}
}
if (!readactive) {
if (!ReadFile (sd->hCom, sd->readbuffer, sd->mtu, &actual, &sd->olr)) {
DWORD err = GetLastError();
if (err == ERROR_IO_PENDING)
readactive = 1;
} else {
uaenet_gotdata (sd->user, sd->readbuffer, actual);
donotwait = 1;
}
}
towrite = 0;
if (!writeactive && uaenet_getdata (sd->user, sd->writebuffer, &towrite)) {
donotwait = 1;
if (!WriteFile (sd->hCom, sd->writebuffer, towrite, &actual, &sd->olw)) {
DWORD err = GetLastError();
if (err == ERROR_IO_PENDING)
writeactive = 1;
}
}
uae_sem_post (&sd->change_sem);
if (!donotwait) {
cnt = 0;
handles[cnt++] = sd->evtt;
if (readactive)
handles[cnt++] = sd->olr.hEvent;
if (writeactive)
handles[cnt++] = sd->olw.hEvent;
WaitForMultipleObjects(cnt, handles, FALSE, INFINITE);
}
}
sd->threadactive = 0;
uae_sem_post (&sd->sync_sem);
return 0;
}
#endif
static void uaenet_trap_threadr (void *arg)
{
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)arg;
struct pcap_pkthdr *header;
const u_char *pkt_data;
uae_set_thread_priority (NULL, 1);
sd->threadactiver = 1;
uae_sem_post (&sd->sync_semr);
while (sd->threadactiver == 1) {
int r;
r = ppcap_next_ex(sd->fp, &header, &pkt_data);
if (r == 1 && !ethernet_paused) {
uae_sem_wait (&sd->change_sem);
sd->gotfunc ((struct s2devstruct*)sd->user, pkt_data, header->len);
uae_sem_post (&sd->change_sem);
}
if (r < 0) {
write_log (_T("pcap_next_ex failed, err=%d\n"), r);
break;
}
}
sd->threadactiver = 0;
uae_sem_post (&sd->sync_semr);
}
static void uaenet_trap_threadw (void *arg)
{
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)arg;
uae_set_thread_priority (NULL, 1);
sd->threadactivew = 1;
uae_sem_post (&sd->sync_semw);
while (sd->threadactivew == 1) {
int donotwait = 0;
int towrite = sd->mtu;
uae_sem_wait (&sd->change_sem);
if (sd->getfunc ((struct s2devstruct*)sd->user, sd->writebuffer, &towrite)) {
if (log_ethernet & 1) {
TCHAR out[1600 * 2], *p;
p = out;
for (int i = 0; i < towrite && i < 1600; i++) {
_stprintf(p, _T("%02x"), sd->writebuffer[i]);
p += 2;
*p = 0;
}
write_log(_T("OUT %4d: %s\n"), towrite, out);
}
ppcap_sendpacket(sd->fp, sd->writebuffer, towrite);
donotwait = 1;
}
uae_sem_post (&sd->change_sem);
if (!donotwait)
WaitForSingleObject (sd->evttw, INFINITE);
}
sd->threadactivew = 0;
uae_sem_post (&sd->sync_semw);
}
void uaenet_trigger (void *vsd)
{
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)vsd;
if (!sd)
return;
SetEvent (sd->evttw);
}
// locally administered unicast MAC: U<<1, A<<1, E<<1
static const uae_u8 uaemac[] = { 0xaa, 0x82, 0x8a, 0x00, 0x00, 0x00 };
int uaenet_open (void *vsd, struct netdriverdata *tc, void *user, uaenet_gotfunc *gotfunc, uaenet_getfunc *getfunc, int promiscuous, const uae_u8 *mac)
{
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)vsd;
char *s;
s = ua (tc->name);
if (mac)
memcpy(tc->mac, mac, 6);
if (memcmp(tc->mac, tc->originalmac, 6)) {
promiscuous = 1;
}
sd->fp = ppcap_open(s, 65536, (promiscuous ? PCAP_OPENFLAG_PROMISCUOUS : 0) | PCAP_OPENFLAG_MAX_RESPONSIVENESS, 100, NULL, sd->errbuf);
xfree (s);
if (sd->fp == NULL) {
TCHAR *ss = au (sd->errbuf);
write_log (_T("'%s' failed to open: %s\n"), tc->name, ss);
xfree (ss);
return 0;
}
sd->tc = tc;
sd->user = user;
sd->evttw = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!sd->evttw)
goto end;
sd->mtu = tc->mtu;
sd->readbuffer = xmalloc (uae_u8, sd->mtu);
sd->writebuffer = xmalloc (uae_u8, sd->mtu);
sd->gotfunc = gotfunc;
sd->getfunc = getfunc;
uae_sem_init (&sd->change_sem, 0, 1);
uae_sem_init (&sd->sync_semr, 0, 0);
uae_start_thread (_T("uaenet_win32r"), uaenet_trap_threadr, sd, &sd->tidr);
uae_sem_wait (&sd->sync_semr);
uae_sem_init (&sd->sync_semw, 0, 0);
uae_start_thread (_T("uaenet_win32w"), uaenet_trap_threadw, sd, &sd->tidw);
uae_sem_wait (&sd->sync_semw);
write_log (_T("uaenet_win32 initialized\n"));
return 1;
end:
uaenet_close (sd);
return 0;
}
void uaenet_close (void *vsd)
{
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)vsd;
if (!sd)
return;
if (sd->threadactiver) {
sd->threadactiver = -1;
}
if (sd->threadactivew) {
sd->threadactivew = -1;
SetEvent (sd->evttw);
}
if (sd->threadactiver) {
while (sd->threadactiver)
Sleep(10);
write_log (_T("uaenet_win32 thread %d killed\n"), sd->tidr);
uae_end_thread (&sd->tidr);
}
if (sd->threadactivew) {
while (sd->threadactivew)
Sleep(10);
CloseHandle (sd->evttw);
write_log (_T("uaenet_win32 thread %d killed\n"), sd->tidw);
uae_end_thread (&sd->tidw);
}
xfree (sd->readbuffer);
xfree (sd->writebuffer);
if (sd->fp)
ppcap_close (sd->fp);
uaeser_initdata (sd, sd->user);
write_log (_T("uaenet_win32 closed\n"));
}
void uaenet_enumerate_free (void)
{
int i;
for (i = 0; i < MAX_TOTAL_NET_DEVICES; i++) {
tds[i].active = 0;
}
enumerated = 0;
}
static struct netdriverdata *enumit (const TCHAR *name)
{
int cnt;
if (name == NULL)
return tds;
for (cnt = 0; cnt < MAX_TOTAL_NET_DEVICES; cnt++) {
TCHAR mac[20];
struct netdriverdata *tc = tds + cnt;
_stprintf (mac, _T("%02X:%02X:%02X:%02X:%02X:%02X"),
tc->mac[0], tc->mac[1], tc->mac[2], tc->mac[3], tc->mac[4], tc->mac[5]);
if (tc->active && name && (!_tcsicmp (name, tc->name) || !_tcsicmp (name, mac)))
return tc;
}
return NULL;
}
struct netdriverdata *uaenet_enumerate (const TCHAR *name)
{
static int done;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *alldevs, *d;
int cnt;
LPADAPTER lpAdapter = 0;
PPACKET_OID_DATA OidData;
struct netdriverdata *tc, *tcp;
pcap_t *fp;
int val;
TCHAR *ss;
bool npcap = true;
TCHAR sname[MAX_DPATH];
int isdll;
if (enumerated) {
return enumit (name);
}
tcp = tds;
int len = GetSystemDirectory(sname, MAX_DPATH);
if (len) {
_tcscat(sname, _T("\\Npcap"));
SetDllDirectory(sname);
}
wpcap = LoadLibrary(_T("wpcap.dll"));
packet = LoadLibrary(_T("packet.dll"));
SetDllDirectory(_T(""));
if (wpcap == NULL) {
FreeLibrary(packet);
int err = GetLastError();
wpcap = LoadLibrary (_T("wpcap.dll"));
packet = LoadLibrary(_T("packet.dll"));
if (wpcap == NULL) {
write_log (_T("uaenet: npcap/winpcap not installed (wpcap.dll)\n"));
return NULL;
}
npcap = false;
}
if (packet == NULL) {
write_log (_T("uaenet: npcap/winpcap not installed (packet.dll)\n"));
FreeLibrary(wpcap);
wpcap = NULL;
return NULL;
}
ppcap_lib_version = (PCAP_LIB_VERSION)GetProcAddress(wpcap, "pcap_lib_version");
ppcap_findalldevs_ex = (PCAP_FINDALLDEVS_EX)GetProcAddress(wpcap, "pcap_findalldevs_ex");
ppcap_freealldevs = (PCAP_FREEALLDEVS)GetProcAddress(wpcap, "pcap_freealldevs");
ppcap_open = (PCAP_OPEN)GetProcAddress(wpcap, "pcap_open");
ppcap_close = (PCAP_CLOSE)GetProcAddress(wpcap, "pcap_close");
ppcap_datalink = (PCAP_DATALINK)GetProcAddress(wpcap, "pcap_datalink");
ppcap_sendpacket = (PCAP_SENDPACKET)GetProcAddress(wpcap, "pcap_sendpacket");
ppcap_next_ex = (PCAP_NEXT_EX)GetProcAddress(wpcap, "pcap_next_ex");
pPacketOpenAdapter = (PACKETOPENADAPTER)GetProcAddress(packet, "PacketOpenAdapter");
pPacketCloseAdapter = (PACKETCLOSEADAPTER)GetProcAddress(packet, "PacketCloseAdapter");
pPacketRequest = (PACKETREQUEST)GetProcAddress(packet, "PacketRequest");
if (!ppcap_lib_version || !ppcap_findalldevs_ex || !ppcap_freealldevs ||
!ppcap_open || !ppcap_close || !ppcap_datalink || !ppcap_sendpacket ||
!ppcap_next_ex || !pPacketOpenAdapter || !pPacketCloseAdapter || !pPacketRequest) {
write_log(_T("uaenet: too old npcap/winpcap, missing function(s).\n"));
return NULL;
}
ss = au (ppcap_lib_version());
if (!done)
write_log (_T("uaenet: %s\n"), ss);
xfree (ss);
if (ppcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) {
ss = au (errbuf);
write_log (_T("uaenet: failed to get interfaces: %s\n"), ss);
xfree (ss);
return NULL;
}
PIP_ADAPTER_ADDRESSES aa = NULL;
DWORD aasize = 0;
DWORD err = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
NULL, NULL, &aasize);
if (err == ERROR_BUFFER_OVERFLOW) {
aa = (IP_ADAPTER_ADDRESSES*)xcalloc(uae_u8, aasize);
if (GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
NULL, aa, &aasize) != ERROR_SUCCESS) {
xfree(aa);
aa = NULL;
}
}
OidData = (PPACKET_OID_DATA)xcalloc(uae_u8, 6 + sizeof(PACKET_OID_DATA));
OidData->Length = 6;
OidData->Oid = OID_802_3_CURRENT_ADDRESS;
if (!done)
write_log (_T("uaenet: detecting interfaces\n"));
for(cnt = 0, d = alldevs; d != NULL; d = d->next) {
char *n2;
TCHAR *ss2;
tc = tcp + cnt;
if (cnt >= MAX_TOTAL_NET_DEVICES) {
write_log (_T("buffer overflow\n"));
break;
}
ss = au (d->name);
ss2 = d->description ? au (d->description) : _T("(no description)");
write_log (_T("%s\n- %s\n"), ss, ss2);
xfree (ss2);
xfree (ss);
n2 = d->name;
if (strlen (n2) <= strlen (PCAP_SRC_IF_STRING)) {
write_log (_T("- corrupt name\n"));
continue;
}
fp = ppcap_open(d->name, 65536, 0, 0, NULL, errbuf);
if (!fp) {
ss = au (errbuf);
write_log (_T("- pcap_open() failed: %s\n"), ss);
xfree (ss);
continue;
}
val = ppcap_datalink(fp);
ppcap_close (fp);
if (val != DLT_EN10MB) {
if (!done)
write_log (_T("- not an ethernet adapter (%d)\n"), val);
continue;
}
lpAdapter = pPacketOpenAdapter(n2 + strlen (PCAP_SRC_IF_STRING));
if (lpAdapter == NULL) {
if (!done)
write_log (_T("- PacketOpenAdapter() failed\n"));
continue;
}
memset(tc->originalmac, 0, sizeof tc->originalmac);
memcpy(tc->mac, uaemac, 6);
if (pPacketRequest (lpAdapter, FALSE, OidData)) {
memcpy(tc->mac, OidData->Data, 6);
memcpy(tc->originalmac, tc->mac, 6);
} else {
PIP_ADAPTER_ADDRESSES aap = aa;
while (aap) {
char *name1 = aap->AdapterName;
char *name2 = d->name;
while (*name2 && *name2 != '{')
name2++;
if (*name2 && !stricmp(name1, name2)) {
memcpy(tc->mac, aap->PhysicalAddress, 6);
memcpy(tc->originalmac, tc->mac, 6);
break;
}
aap = aap->Next;
}
}
if (!done)
write_log(_T("- MAC %02X:%02X:%02X:%02X:%02X:%02X -> %02X:%02X:%02X:%02X:%02X:%02X\n"),
tc->mac[0], tc->mac[1], tc->mac[2], tc->mac[3], tc->mac[4], tc->mac[5],
uaemac[0], uaemac[1], uaemac[2], tc->mac[3], tc->mac[4], tc->mac[5]);
memcpy(tc->mac, uaemac, 3);
tc->type = UAENET_PCAP;
tc->active = 1;
tc->mtu = 1522;
tc->name = au(d->name);
tc->desc = au(d->description);
cnt++;
pPacketCloseAdapter(lpAdapter);
}
if (!done)
write_log (_T("uaenet: end of detection, %d devices found.\n"), cnt);
done = 1;
ppcap_freealldevs(alldevs);
xfree(OidData);
xfree(aa);
enumerated = 1;
return enumit (name);
}
void uaenet_close_driver (struct netdriverdata *tc)
{
int i;
if (!tc)
return;
for (i = 0; i < MAX_TOTAL_NET_DEVICES; i++) {
tc[i].active = 0;
}
}
void ethernet_pause(int pause)
{
ethernet_paused = pause;
}
void ethernet_reset(void)
{
ethernet_paused = 0;
}