macemu/BasiliskII/src/Windows/b2ether/packet32.cpp

694 lines
18 KiB
C++
Executable File

/*
* packet32.cpp
*
* Basilisk II (C) 1997-2008 Christian Bauer
*
* Windows platform specific code copyright (C) Lauri Pesonen
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "main.h"
#include "util_windows.h"
#include <windowsx.h>
#include <winioctl.h>
#include "cpu_emulation.h"
// VC6 does not have this, Platform SDK has.
// In case of errors, try to comment out, the needed
// definitions are below (#ifndef _NTDDNDIS_)
// Most people don't have the Platform SDK, so I take this one out.
// #include <ntddndis.h>
#include "inc/ntddpack.h"
#include "ether.h"
#include "ether_defs.h"
#include "b2ether/multiopt.h"
#include "b2ether/inc/b2ether_hl.h"
#ifndef _NTDDNDIS_
#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
#define OID_802_3_PERMANENT_ADDRESS 0x01010101
#define OID_802_3_CURRENT_ADDRESS 0x01010102
#define OID_802_3_MULTICAST_LIST 0x01010103
#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
#endif
#define DEBUG_PACKETS 0
#define DEBUG 0
#include "debug.h"
#ifdef __cplusplus
extern "C" {
#endif
#if DEBUG
#pragma optimize("",off)
#endif
#define MAX_MULTICAST 100
#define MAX_MULTICAST_SZ (20*ETH_802_3_ADDRESS_LENGTH)
static ULONG packet_filter = 0;
LPADAPTER PacketOpenAdapter( LPCTSTR AdapterName, int16 mode )
{
LPADAPTER lpAdapter;
BOOLEAN Result = TRUE;
D(bug("Packet32: PacketOpenAdapter\n"));
// May fail if user is not an Administrator.
StartPacketDriver( TEXT("B2ether") );
lpAdapter = (LPADAPTER)GlobalAllocPtr( GMEM_MOVEABLE|GMEM_ZEROINIT, sizeof(ADAPTER) );
if (lpAdapter==NULL) {
D(bug("Packet32: PacketOpenAdapter GlobalAlloc Failed\n"));
return NULL;
}
TCHAR device_name[256];
_sntprintf(lpAdapter->SymbolicLink, lengthof(lpAdapter->SymbolicLink), TEXT("\\\\.\\B2ether_%s"), AdapterName );
_sntprintf(device_name, lengthof(device_name), TEXT("\\Device\\B2ether_%s"), AdapterName );
// Work around one subtle NT4 bug.
DefineDosDevice(
DDD_REMOVE_DEFINITION,
&lpAdapter->SymbolicLink[4],
NULL
);
DefineDosDevice(
DDD_RAW_TARGET_PATH,
&lpAdapter->SymbolicLink[4],
device_name
);
packet_filter = NDIS_PACKET_TYPE_DIRECTED |
NDIS_PACKET_TYPE_MULTICAST |
NDIS_PACKET_TYPE_BROADCAST;
if(mode == ETHER_MULTICAST_ALL) packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
if(mode == ETHER_MULTICAST_PROMISCUOUS) packet_filter |= NDIS_PACKET_TYPE_PROMISCUOUS;
if (Result) {
lpAdapter->hFile = CreateFile(lpAdapter->SymbolicLink,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
// (os == VER_PLATFORM_WIN32_NT) ? CREATE_ALWAYS : OPEN_EXISTING,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0
);
if (lpAdapter->hFile != INVALID_HANDLE_VALUE) {
if(*AdapterName && _tcscmp(AdapterName,TEXT("<None>")) != 0) {
PacketSetFilter( lpAdapter, packet_filter );
}
return lpAdapter;
}
}
D(bug("Packet32: PacketOpenAdapter Could not open adapter\n"));
GlobalFreePtr( lpAdapter );
return NULL;
}
VOID PacketCloseAdapter( LPADAPTER lpAdapter )
{
D(bug("Packet32: PacketCloseAdapter\n"));
if(lpAdapter) {
if(lpAdapter->hFile) {
CloseHandle(lpAdapter->hFile);
}
GlobalFreePtr(lpAdapter);
}
}
LPPACKET PacketAllocatePacket( LPADAPTER AdapterObject, UINT Length )
{
LPPACKET lpPacket;
lpPacket = (LPPACKET)GlobalAllocPtr( GMEM_MOVEABLE|GMEM_ZEROINIT, sizeof(PACKET) );
if(lpPacket==NULL) {
D(bug("Packet32: PacketAllocatePacket: GlobalAlloc Failed\n"));
return NULL;
}
lpPacket->OverLapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
if(!lpPacket->OverLapped.hEvent) {
D(bug("Packet32: PacketAllocatePacket: CreateEvent Failed\n"));
GlobalFreePtr(lpPacket);
return NULL;
}
lpPacket->Buffer = GlobalAllocPtr(GMEM_MOVEABLE,2048); // 1514
if(!lpPacket->Buffer) {
D(bug("Packet32: PacketAllocatePacket: GlobalAllocPtr Failed\n"));
if(lpPacket->OverLapped.hEvent) CloseHandle(lpPacket->OverLapped.hEvent);
GlobalFreePtr(lpPacket);
return NULL;
}
lpPacket->OverLapped.Offset = 0;
lpPacket->OverLapped.OffsetHigh = 0;
lpPacket->Length = Length;
lpPacket->BytesReceived = 0;
lpPacket->bIoComplete = FALSE;
lpPacket->free = TRUE;
return lpPacket;
}
VOID PacketFreePacket( LPPACKET lpPacket )
{
if(lpPacket) {
if(lpPacket->Buffer) GlobalFreePtr(lpPacket->Buffer);
if(lpPacket->OverLapped.hEvent) CloseHandle(lpPacket->OverLapped.hEvent);
GlobalFreePtr(lpPacket);
}
}
BOOLEAN PacketDeviceIoControl(
LPADAPTER lpAdapterObject,
LPPACKET lpPacket,
ULONG ulIoctl,
BOOLEAN bSync
)
{
BOOLEAN Result;
lpPacket->OverLapped.Offset = 0;
lpPacket->OverLapped.OffsetHigh = 0;
lpPacket->BytesReceived = 0;
if ( !ResetEvent( lpPacket->OverLapped.hEvent ) ) {
lpPacket->bIoComplete = FALSE;
D(bug( "Packet32: PacketDeviceIoControl failed to reset event\r\n", GetLastError() ));
return FALSE;
}
Result = DeviceIoControl(
lpAdapterObject->hFile,
ulIoctl,
lpPacket->Buffer,
lpPacket->Length,
lpPacket->Buffer,
lpPacket->Length,
&(lpPacket->BytesReceived),
&(lpPacket->OverLapped) );
if( !Result && bSync ) {
if (GetLastError() == ERROR_IO_PENDING) {
Result = GetOverlappedResult( lpAdapterObject->hFile,
&(lpPacket->OverLapped),
&(lpPacket->BytesReceived),
TRUE );
} else {
D(bug( "Packet32: unsupported API call returned error 0x%x\r\n", GetLastError() ));
}
}
lpPacket->bIoComplete = Result;
return Result;
}
VOID CALLBACK PacketSendCompletionRoutine(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
)
{
LPPACKET lpPacket = CONTAINING_RECORD(lpOverlapped,PACKET,OverLapped);
#if DEBUG_PACKETS
D(bug("PacketSendCompletionRoutine %d\n",dwNumberOfBytesTransfered));
#endif
lpPacket->bIoComplete = TRUE;
// lpPacket->free = TRUE;
// PacketFreePacket(lpPacket);
recycle_write_packet(lpPacket);
}
BOOLEAN PacketSendPacket(
LPADAPTER AdapterObject,
LPPACKET lpPacket,
BOOLEAN Sync,
BOOLEAN RecyclingAllowed
)
{
BOOLEAN Result;
#if DEBUG_PACKETS
D(bug("Packet32: PacketSendPacket bytes=%d, sync=%d\n",lpPacket->Length,Sync));
#endif
lpPacket->OverLapped.Offset = 0;
lpPacket->OverLapped.OffsetHigh = 0;
lpPacket->bIoComplete = FALSE;
if(Sync) {
Result = WriteFile(
AdapterObject->hFile,
lpPacket->Buffer,
lpPacket->Length,
&lpPacket->BytesReceived,
&lpPacket->OverLapped
);
if(Result) {
Result = GetOverlappedResult(
AdapterObject->hFile,
&lpPacket->OverLapped,
&lpPacket->BytesReceived,
TRUE
);
} else {
D(bug("Packet32: PacketSendPacket WriteFile failed, err=%d\n",(int)GetLastError()));
}
lpPacket->bIoComplete = TRUE;
if(RecyclingAllowed) PacketFreePacket(lpPacket);
#if DEBUG_PACKETS
D(bug("Packet32: PacketSendPacket result=%d, bytes=%d\n",(int)Result,(int)lpPacket->BytesReceived));
#endif
} else {
// don't care about the result
Result = WriteFileEx(
AdapterObject->hFile,
lpPacket->Buffer,
lpPacket->Length,
&lpPacket->OverLapped,
PacketSendCompletionRoutine
);
#if DEBUG_PACKETS
D(bug("Packet32: PacketSendPacket result=%d\n",(int)Result));
#endif
if(!Result && RecyclingAllowed) {
recycle_write_packet(lpPacket);
}
}
return Result;
}
BOOLEAN PacketReceivePacket(
LPADAPTER AdapterObject,
LPPACKET lpPacket,
BOOLEAN Sync
)
{
BOOLEAN Result;
lpPacket->OverLapped.Offset=0;
lpPacket->OverLapped.OffsetHigh=0;
lpPacket->bIoComplete = FALSE;
#if DEBUG_PACKETS
D(bug("Packet32: PacketReceivePacket\n"));
#endif
if (Sync) {
Result = ReadFile(
AdapterObject->hFile,
lpPacket->Buffer,
lpPacket->Length,
&lpPacket->BytesReceived,
&lpPacket->OverLapped
);
if(Result) {
Result = GetOverlappedResult(
AdapterObject->hFile,
&lpPacket->OverLapped,
&lpPacket->BytesReceived,
TRUE
);
if(Result)
lpPacket->bIoComplete = TRUE;
else
lpPacket->free = TRUE;
}
} else {
Result = ReadFileEx(
AdapterObject->hFile,
lpPacket->Buffer,
lpPacket->Length,
&lpPacket->OverLapped,
packet_read_completion
);
}
if(!Result) lpPacket->BytesReceived = 0;
#if DEBUG_PACKETS
D(bug("Packet32: PacketReceivePacket got %d bytes, result=%d\n",lpPacket->BytesReceived,(int)Result));
#endif
return Result;
}
BOOLEAN PacketRequest(
LPADAPTER lpAdapterObject,
LPPACKET lpPacket,
BOOLEAN bSet
)
{
BOOLEAN Result = FALSE;
Result = PacketDeviceIoControl(
lpAdapterObject,
lpPacket,
(ULONG) ((bSet) ? IOCTL_PROTOCOL_SET_OID : IOCTL_PROTOCOL_QUERY_OID),
TRUE );
if ( lpPacket->BytesReceived == 0 ) {
D(bug( "Packet32: Ndis returned error to OID\r\n"));
Result = FALSE;
}
return Result;
}
LPPACKET PacketQueryOid(
LPADAPTER lpAdapter,
ULONG ulOid,
ULONG ulLength
)
{
ULONG ioctl;
LPPACKET lpPacket;
#define pOidData ((PPACKET_OID_DATA)(lpPacket->Buffer))
lpPacket = PacketAllocatePacket( lpAdapter, sizeof(PACKET_OID_DATA)-1+ulLength );
if( lpPacket ) {
ioctl = IOCTL_PROTOCOL_QUERY_OID;
pOidData->Oid = ulOid;
pOidData->Length = ulLength;
if (PacketRequest( lpAdapter, lpPacket, FALSE )) {
return lpPacket;
}
PacketFreePacket( lpPacket );
}
#undef pOidData
return 0;
}
BOOLEAN PacketGetMAC( LPADAPTER AdapterObject, LPBYTE address, BOOL permanent )
{
BOOLEAN Status;
LPPACKET lpPacket;
lpPacket = PacketQueryOid(
AdapterObject,
permanent ? OID_802_3_PERMANENT_ADDRESS : OID_802_3_CURRENT_ADDRESS,
ETH_802_3_ADDRESS_LENGTH
);
if(lpPacket) {
memcpy( address,
((BYTE *)(lpPacket->Buffer)) + sizeof(PACKET_OID_DATA) - 1,
ETH_802_3_ADDRESS_LENGTH );
PacketFreePacket( lpPacket );
Status = TRUE;
} else {
Status = FALSE;
}
return Status;
}
// There are other ways to do this.
BOOLEAN PacketAddMulticast( LPADAPTER AdapterObject, LPBYTE address )
{
BOOLEAN Status = FALSE;
LPBYTE p;
int i, count;
LPPACKET lpPacket;
D(bug("PacketAddMulticast\n"));
/*
if(packet_filter & (NDIS_PACKET_TYPE_ALL_MULTICAST|NDIS_PACKET_TYPE_PROMISCUOUS)) {
D(bug("PacketAddMulticast: already listening for all multicast\n"));
return TRUE;
}
*/
lpPacket = PacketQueryOid( AdapterObject, OID_802_3_MULTICAST_LIST, MAX_MULTICAST_SZ );
#define OidData ((PPACKET_OID_DATA)(lpPacket->Buffer))
if(lpPacket) {
count = OidData->Length / ETH_802_3_ADDRESS_LENGTH;
D(bug("PacketAddMulticast: %d old addresses\n",count));
p = (LPBYTE)OidData->Data;
for( i=0; i<count; i++ ) {
if(memcmp(p,address,ETH_802_3_ADDRESS_LENGTH) == 0) {
// This multicast is already defined -- error or not?
Status = TRUE;
D(bug("PacketAddMulticast: address already defined\n"));
break;
}
p += ETH_802_3_ADDRESS_LENGTH;
}
if(i == count) {
if(i >= MAX_MULTICAST) {
D(bug("PacketAddMulticast: too many addresses\n"));
Status = FALSE;
} else {
D(bug("PacketAddMulticast: adding a new address\n"));
// ULONG IoCtlBufferLength = (sizeof(PACKET_OID_DATA)+ETH_802_3_ADDRESS_LENGTH*1-1);
ULONG IoCtlBufferLength = (sizeof(PACKET_OID_DATA)+ETH_802_3_ADDRESS_LENGTH*(count+1)-1);
LPPACKET lpPacket2 = PacketAllocatePacket( AdapterObject, IoCtlBufferLength );
#define OidData2 ((PPACKET_OID_DATA)(lpPacket2->Buffer))
if ( lpPacket2 ) {
OidData2->Oid = OID_802_3_MULTICAST_LIST;
// OidData2->Length = ETH_802_3_ADDRESS_LENGTH*1;
// memcpy( OidData2->Data, address, ETH_802_3_ADDRESS_LENGTH );
memcpy( OidData2->Data, OidData->Data, ETH_802_3_ADDRESS_LENGTH*count );
memcpy( OidData2->Data+ETH_802_3_ADDRESS_LENGTH*count, address, ETH_802_3_ADDRESS_LENGTH );
OidData2->Length = ETH_802_3_ADDRESS_LENGTH*(count+1);
Status = PacketRequest( AdapterObject, lpPacket2, TRUE );
PacketFreePacket( lpPacket2 );
}
#undef OidData2
}
}
PacketFreePacket( lpPacket );
}
#undef OidData
// return Status;
return TRUE;
}
// It seems that the last multicast address is never deleted. Why?
// Don't know the reason, but luckily this is not fatal.
// Hard to examine return codes. See NE2000 sources, always returns ok.
BOOLEAN PacketDelMulticast( LPADAPTER AdapterObject, LPBYTE address )
{
BOOLEAN Status = FALSE;
LPBYTE p;
int i, count;
LPPACKET lpPacket, lpPacket2;
D(bug("PacketDelMulticast\n"));
if(packet_filter & (NDIS_PACKET_TYPE_ALL_MULTICAST|NDIS_PACKET_TYPE_PROMISCUOUS)) {
D(bug("PacketDelMulticast: already listening for all multicast\n"));
return TRUE;
}
lpPacket = PacketQueryOid( AdapterObject, OID_802_3_MULTICAST_LIST, MAX_MULTICAST_SZ );
#define OidData ((PPACKET_OID_DATA)(lpPacket->Buffer))
if(lpPacket) {
count = OidData->Length / ETH_802_3_ADDRESS_LENGTH;
D(bug("PacketDelMulticast: %d old addresses\n",count));
Status = FALSE;
p = (LPBYTE)OidData->Data;
for( i=0; i<count; i++ ) {
int tail_len;
if(memcmp(p,address,ETH_802_3_ADDRESS_LENGTH) == 0) {
D(bug("PacketDelMulticast: address found, deleting\n"));
ULONG IoCtlBufferLength = (sizeof(PACKET_OID_DATA)+ETH_802_3_ADDRESS_LENGTH*(count-1)-1);
lpPacket2 = PacketAllocatePacket( AdapterObject, IoCtlBufferLength );
#define OidData2 ((PPACKET_OID_DATA)(lpPacket2->Buffer))
if ( lpPacket2 ) {
OidData2->Oid = OID_802_3_MULTICAST_LIST;
OidData2->Length = ETH_802_3_ADDRESS_LENGTH*(count-1);
tail_len = ETH_802_3_ADDRESS_LENGTH * (count-i-1);
if(tail_len) memmove( p, p+ETH_802_3_ADDRESS_LENGTH, tail_len );
if(OidData2->Length) memcpy( OidData2->Data, OidData->Data, OidData2->Length );
if(count == 1) memset( OidData2->Data, 0, ETH_802_3_ADDRESS_LENGTH ); // eh...
Status = PacketRequest( AdapterObject, lpPacket2, TRUE );
PacketFreePacket( lpPacket2 );
D(bug("PacketDelMulticast: PacketRequest returned status 0x%X, last error = 0x%X\n",Status,GetLastError()));
}
break;
#undef OidData2
}
p += ETH_802_3_ADDRESS_LENGTH;
}
if( i == count ) {
D(bug("PacketDelMulticast: cannot delete, was not defined\n"));
}
PacketFreePacket( lpPacket );
#undef OidData
}
// return Status;
return TRUE;
}
BOOLEAN PacketSetFilter( LPADAPTER AdapterObject, ULONG Filter )
{
BOOLEAN Status;
ULONG IoCtlBufferLength = (sizeof(PACKET_OID_DATA)+sizeof(ULONG)-1);
LPPACKET lpPacket;
lpPacket = PacketAllocatePacket( AdapterObject, IoCtlBufferLength );
#define lpOidData ((PPACKET_OID_DATA)(lpPacket->Buffer))
if ( lpPacket ) {
lpOidData->Oid = OID_GEN_CURRENT_PACKET_FILTER;
lpOidData->Length = sizeof(ULONG);
*((PULONG)lpOidData->Data) = Filter;
Status = PacketRequest( AdapterObject, lpPacket, TRUE );
PacketFreePacket( lpPacket );
} else {
Status = FALSE;
}
#undef lpOidData
return Status;
}
BOOLEAN StartPacketDriver( LPCTSTR ServiceName )
{
BOOLEAN Status = FALSE;
SC_HANDLE SCManagerHandle;
SC_HANDLE SCServiceHandle;
SCManagerHandle = OpenSCManager(
NULL,
NULL,
SC_MANAGER_ALL_ACCESS);
if(SCManagerHandle == NULL) {
D(bug("Could not open Service Control Manager\r\n"));
} else {
SCServiceHandle = OpenService(SCManagerHandle,ServiceName,SERVICE_START);
if (SCServiceHandle == NULL) {
D(bug(TEXT("Could not open service %s\r\n"),ServiceName));
} else {
Status = StartService( SCServiceHandle, 0, NULL );
if(!Status) {
if (GetLastError()==ERROR_SERVICE_ALREADY_RUNNING) {
Status = TRUE;
}
}
BOOL waiting = TRUE;
// loop until the service is fully started.
while (waiting) {
SERVICE_STATUS ServiceStatus;
if (QueryServiceStatus(SCServiceHandle, &ServiceStatus)) {
switch(ServiceStatus.dwCurrentState) {
case SERVICE_RUNNING:
waiting = FALSE;
Status = TRUE;
break;
case SERVICE_START_PENDING:
Sleep(500);
break;
default:
waiting = FALSE;
break;
}
} else {
waiting = FALSE;
}
}
CloseServiceHandle(SCServiceHandle);
}
CloseServiceHandle(SCManagerHandle);
}
return Status;
}
ULONG PacketGetAdapterNames( LPADAPTER lpAdapter, LPTSTR pStr, PULONG BufferSize )
{
LONG Status;
HKEY hKey;
DWORD RegType;
Status = RegOpenKey(
HKEY_LOCAL_MACHINE,
TEXT("SYSTEM\\CurrentControlSet\\Services\\B2Ether\\Linkage"),
&hKey
);
if( Status == ERROR_SUCCESS ) {
Status = RegQueryValueEx(
hKey,
TEXT("Export"),
NULL,
&RegType,
(LPBYTE)pStr,
BufferSize
);
RegCloseKey(hKey);
}
return Status;
}
#ifdef __cplusplus
}
#endif
#if DEBUG
#pragma optimize("",on)
#endif