/* * 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 #include #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 #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("")) != 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= 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; iBuffer)) 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