Created
May 22, 2020 11:33
-
-
Save aziascreations/20180a03bab7ea3d97bc0becb497c1f9 to your computer and use it in GitHub Desktop.
PB Threaded ARP Requests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;{- Code Header | |
; ==- Basic Info -================================ | |
; Name: IPv4Helper.pbi | |
; Version: 0.0.1 | |
; Author: Herwin Bozet | |
; Create date: 21 June 2019, 19:24:33 | |
; | |
; Description: A basic set of utility procedures and macros to help with IPv4 addresses. | |
; | |
; ==- Compatibility -============================= | |
; Compiler version: PureBasic 5.62 & 5.70 (x64) (Other versions untested) | |
; Operating system: Windows (Other platforms untested) | |
; | |
; ==- Links & License -=========================== | |
; Github: https://github.com/aziascreations/PB-Networking-Playground | |
; License: Unlicense | |
; | |
;} | |
; | |
;- Compiler directives | |
;{ | |
;EnableExplicit | |
;} | |
; | |
;- Constants | |
;{ | |
#IPv4_IP_ERROR = $00000000 ; 0.0.0.0 | |
#IPv4_BITMASK_ERROR = $00000000 ; 0.0.0.0 | |
#IPv4_CIDRMASK_ERROR = 0 ; /0 | |
; TODO: D & E classes | |
#IPv4_BITMASK_CLASS_A = $000000FF ; 255.0.0.0 | |
#IPv4_BITMASK_CLASS_B = $0000FFFF ; 255.255.0.0 | |
#IPv4_BITMASK_CLASS_C = $00FFFFFF ; 255.255.255.0 | |
#IPv4_CIDRMASK_CLASS_A = 8 | |
#IPv4_CIDRMASK_CLASS_B = 16 | |
#IPv4_CIDRMASK_CLASS_C = 24 | |
;} | |
; | |
;- Procedures & Macros | |
;{ | |
; | |
;-> Validity checkers | |
;{ | |
; Returns non-zero if the bit mask is valid. | |
Procedure.b IsIPv4BitMaskValid(BitMask.l) | |
ProcedureReturn #True | |
EndProcedure | |
; Avoids useless procedure calls inside other procedure inside this include. | |
Macro _IsIPv4CIDRMaskValid(CIDRMask) | |
(CIDRMask >= 1 And CIDRMask<=31) | |
EndMacro | |
; Returns non-zero if the CIDR mask is valid. | |
Procedure.b IsIPv4CIDRMaskValid(CIDRMask.b) | |
ProcedureReturn Bool(_IsIPv4CIDRMaskValid(CIDRMask)) | |
EndProcedure | |
;} | |
; | |
;-> Mask transformers | |
;{ | |
; Returns the bit mask associated with the given CIDR mask. | |
; If the CIDR mask is invalid, it returns zero. (0.0.0.0) | |
Procedure.l GetIPv4BitMaskFromCIDRMask(CIDRMask.b) | |
Protected BitMask.l | |
If Not _IsIPv4CIDRMaskValid(CIDRMask) | |
ProcedureReturn #IPv4_BITMASK_ERROR | |
EndIf | |
; The endianness fucks it up without asm | |
BitMask = ($80000000 >> (CIDRMask - 1)) | |
EnableASM | |
MOV eax, BitMask | |
BSWAP eax | |
ProcedureReturn | |
DisableASM | |
EndProcedure | |
; Returns the CIDR mask associated with the given bit mask. | |
; If the bit mask is invalid, it returns zero. (/0) | |
Procedure.l GetIPv4CIDRMaskFromBitMask(BitMask.l) | |
If Not IsIPv4BitMaskValid(BitMask) | |
ProcedureReturn #IPv4_CIDRMASK_ERROR | |
EndIf | |
; ... | |
EndProcedure | |
;} | |
; | |
;-> Net-ID resolvers | |
;{ | |
Macro _GetIPv4NetworkIDByBitMask(IPv4Address, BitMask) | |
(IPv4Address & BitMask) | |
EndMacro | |
Macro _GetIPv4NetworkIDByCIDRMask(IPv4Address, CIDRMask) | |
(_GetIPv4NetworkIDByBitMask(IPv4Address, GetIPv4BitMaskFromCIDRMask(CIDRMask))) | |
EndMacro | |
Procedure.l GetIPv4NetworkIDByBitMask(IPv4Address.l, BitMask.l) | |
If IsIPv4BitMaskValid(BitMask) | |
ProcedureReturn _GetIPv4NetworkIDByBitMask(IPv4Address, BitMask) | |
Else | |
ProcedureReturn #IPv4_IP_ERROR | |
EndIf | |
EndProcedure | |
Procedure.l GetIPv4NetworkIDByCIDRMask(IPv4Address.l, CIDRMask.b) | |
If _IsIPv4CIDRMaskValid(CIDRMask) | |
ProcedureReturn _GetIPv4NetworkIDByBitMask(IPv4Address, GetIPv4BitMaskFromCIDRMask(CIDRMask)) | |
Else | |
ProcedureReturn #IPv4_IP_ERROR | |
EndIf | |
EndProcedure | |
;} | |
; | |
;-> Host-ID resolvers | |
;{ | |
Macro _GetIPv4HostIDByBitMask(IPv4Address, BitMask) | |
(IPv4Address & (~BitMask)) | |
EndMacro | |
Procedure.l GetIPv4HostIDByBitMask(IPv4Address.l, BitMask.l) | |
If IsIPv4BitMaskValid(BitMask) | |
ProcedureReturn _GetIPv4HostIDByBitMask(IPv4Address, BitMask) | |
Else | |
ProcedureReturn #IPv4_IP_ERROR | |
EndIf | |
EndProcedure | |
Procedure.l GetIPv4HostIDByCIDRMask(IPv4Address.l, CIDRMask.b) | |
If _IsIPv4CIDRMaskValid(CIDRMask) | |
ProcedureReturn _GetIPv4HostIDByBitMask(IPv4Address, GetIPv4BitMaskFromCIDRMask(CIDRMask)) | |
Else | |
ProcedureReturn #IPv4_IP_ERROR | |
EndIf | |
EndProcedure | |
;} | |
; | |
;-> Host-ID utils | |
;{ | |
Macro _GetIPv4RawHostCountFromBitMask(BitMask) | |
(_GetIPv4HostIDByBitMask($FFFFFFFF, BitMask) - 1) | |
EndMacro | |
Macro _GetIPv4RawHostCountFromCIDRMask(CIDRMask) | |
(Pow(2, 32-CIDRMask)) | |
EndMacro | |
; Returns non-zero is no error occured. | |
; FIXME: Is the broadcast @ removed ??? | |
Procedure GetIPv4MaxHostCountFromBitMask(BitMask.l) | |
If IsIPv4BitMaskValid(BitMask) | |
ProcedureReturn _GetIPv4RawHostCountFromBitMask(BitMask) | |
Else | |
ProcedureReturn #False ; 0 | |
EndIf | |
EndProcedure | |
Procedure GetIPv4MaxHostCountFromCIDRMask(CIDRMask.b) | |
If _IsIPv4CIDRMaskValid(CIDRMask) | |
ProcedureReturn _GetIPv4RawHostCountFromCIDRMask(CIDRMask) | |
Else | |
ProcedureReturn #False ; 0 | |
EndIf | |
EndProcedure | |
;} | |
; | |
;-> Misc | |
;{ | |
; Same as IPString(IPv4Address.l, #PB_Network_IPv4), but it doesn't require you to use InitNetwork(). | |
Procedure.s IPv4String(IPv4Address.l) | |
ProcedureReturn Str(PeekA(@IPv4Address)) + "." + Str(PeekA(@IPv4Address+1)) + "." + | |
Str(PeekA(@IPv4Address+2)) + "." + Str(PeekA(@IPv4Address+3)) | |
EndProcedure | |
;} | |
; Get nth client | |
; get next client(current, mask) | |
; Get broadcast | |
;Procedure.b IsIPv4MaskValid | |
; Get subnet mask | |
;} | |
; | |
;- Tests | |
;{ | |
CompilerIf #PB_Compiler_IsMainFile | |
;IP.l = GetIPv4BitMaskFromCIDRMask(24) | |
;IP.l = MakeIPAddress(156,48,133,96) | |
Define IP.l = MakeIPAddress(255, 255, 255, 255) | |
Define CIDR.b = 24 | |
Debug IPv4String(IP)+"/"+CIDR | |
Define BitMask.l = GetIPv4BitMaskFromCIDRMask(CIDR) | |
Debug IPv4String(BitMask) | |
Debug IPv4String(GetIPv4NetworkIDByBitMask(IP, BitMask)) | |
Debug IPv4String(GetIPv4HostIDByBitMask(IP, BitMask)) | |
Debug IPv4String(GetIPv4HostIDByCIDRMask(IP, CIDR)) | |
CompilerEndIf | |
;} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;{- Code Header | |
; ==- Basic Info -================================ | |
; Name: ThreadedSendARP.pb | |
; Version: N/A | |
; Authors: Herwin Bozet | |
; Create date: 11 October 2019, 03:15:22 | |
; | |
; Description: N/A | |
; | |
; ==- Requirements -============================= | |
; IPv4Helper.pbi: | |
; Version: 0.0.1 | |
; Link: https://github.com/aziascreations/PB-Networking-Playground | |
; License: Unlicense | |
; | |
; ==- Compatibility -============================= | |
; Compiler version: PureBasic 5.70 (x64) (Other versions untested) | |
; Operating system: Windows 10 (Other platforms untested) | |
; | |
; ==- Links & License -=========================== | |
; Github: https://github.com/aziascreations/PB-Networking-Playground | |
; License: Unlicense | |
; | |
; ==- Notes -===================================== | |
; The main loop appears to go above 254 even when it shouldn't. | |
; | |
;} | |
; | |
;- Compiler directives & imports | |
;{ | |
EnableExplicit | |
XIncludeFile "./Includes/IPv4Helper.pbi" | |
CompilerIf #PB_Compiler_Thread = 0 | |
CompilerError "Required: Thread-safe compiler flag." | |
CompilerEndIf | |
;} | |
; | |
;- Declarations | |
;{ | |
;-> Structures & Globals | |
Structure NetworkDeviceInfo | |
MACAddress.q | |
IPv4Address.i | |
EndStructure | |
; Because you can only give 1 param to a thread... | |
Structure ARPThreadParameterStruct | |
DestinationIPV4Address.i | |
SourceIPV4Address.i | |
DevicesListMutex.i ; Not sure about the type, don't care | |
EndStructure | |
; Couldn't find a way around that global... | |
Global NewList Devices.NetworkDeviceInfo() | |
;-> Procedures | |
Procedure ARPRequestIPV4WorkerThread(*Parameters.ARPThreadParameterStruct) | |
Protected *MACAddressBuffer = AllocateMemory(8) | |
If Not *MACAddressBuffer | |
ProcedureReturn | |
EndIf | |
; The fuck is that supposed to mean ? - The 6 bytes a mac@48 takes... | |
Protected MACAddressLength.l = 6 | |
Protected WasPresent.b = #False | |
If SendARP_(*Parameters\DestinationIPV4Address, *Parameters\SourceIPV4Address, *MACAddressBuffer, @MACAddressLength) = #NO_ERROR | |
LockMutex(*Parameters\DevicesListMutex) | |
ForEach Devices() | |
If Devices()\MACAddress = PeekL(*MACAddressBuffer) | |
WasPresent = #True | |
Break | |
EndIf | |
Next | |
If Not WasPresent | |
LastElement(Devices()) | |
If AddElement(Devices()) | |
Devices()\MACAddress = PeekQ(*MACAddressBuffer) | |
Devices()\IPv4Address = *Parameters\DestinationIPV4Address | |
Else | |
DebuggerError("Failed to allocate list cell !") | |
EndIf | |
EndIf | |
UnlockMutex(*Parameters\DevicesListMutex) | |
EndIf | |
EndProcedure | |
;} | |
; | |
;- Code | |
;{ | |
; The amount of threads allowed to run at the same time. | |
#WORKER_POOL_SIZE = 64 ; It helps to have a bigger one to not let the ones that are waiting for a timeout block the rest | |
#WORKER_POST_BIRTH_DELAY = 50 ; ms | |
; I could have used an array, but I don't want to deal with PB's bullshit around arrays. | |
NewList WorkerPoolThreadIDs.i() | |
NewList WorkerPoolThreadParams.i() ; Ptr list | |
; TODO: Pass the list as an arg to mutex it inside the thread ? | |
; Make it local | |
Define DevicesListMutex = CreateMutex() | |
Define Thread, SourceIPV4Address.i | |
Define *TemporaryParamsPtr.ARPThreadParameterStruct | |
Define IPV4AddressLSB.i = 1 ; This is used to simplify the main loop and to avoid making some annoying IP bit level shit. | |
Define i.i | |
; Init the worker pool list | |
For i = 0 To #WORKER_POOL_SIZE - 1 | |
InsertElement(WorkerPoolThreadIDs()) | |
WorkerPoolThreadIDs() = #Null | |
InsertElement(WorkerPoolThreadParams()) | |
WorkerPoolThreadParams() = #Null | |
Next | |
While IPV4AddressLSB < 255 - 1 | |
ForEach WorkerPoolThreadIDs() | |
If Not IsThread(WorkerPoolThreadIDs()) | |
; Cleaning up some garbage | |
SelectElement(WorkerPoolThreadParams(), ListIndex(WorkerPoolThreadIDs())) | |
If WorkerPoolThreadParams() | |
FreeMemory(WorkerPoolThreadParams()) | |
EndIf | |
WorkerPoolThreadParams() = AllocateMemory(SizeOf(ARPThreadParameterStruct)) | |
*TemporaryParamsPtr = WorkerPoolThreadParams() | |
*TemporaryParamsPtr\DestinationIPV4Address = MakeIPAddress(192,168,1,IPV4AddressLSB) | |
*TemporaryParamsPtr\SourceIPV4Address = MakeIPAddress(192,168,1,85) | |
*TemporaryParamsPtr\DevicesListMutex = DevicesListMutex | |
;*TemporaryParamsPtr\Devices() = Devices() | |
WorkerPoolThreadIDs() = CreateThread(@ARPRequestIPV4WorkerThread(), *TemporaryParamsPtr) | |
Debug IPV4AddressLSB | |
IPV4AddressLSB = IPV4AddressLSB + 1 | |
Delay(#WORKER_POST_BIRTH_DELAY) | |
EndIf | |
Next | |
Wend | |
; Waiting for the threads to finish | |
ForEach WorkerPoolThreadIDs() | |
If IsThread(WorkerPoolThreadIDs()) | |
WaitThread(WorkerPoolThreadIDs()) | |
EndIf | |
Next | |
; Last cleanup | |
ForEach WorkerPoolThreadParams() | |
If WorkerPoolThreadParams() | |
FreeMemory(WorkerPoolThreadParams()) | |
EndIf | |
Next | |
Debug "Done !" | |
Debug "Got "+Str(ListSize(Devices()))+" responses !" | |
ForEach Devices() | |
Debug IPv4String(Devices()\IPv4Address) | |
Debug "0x"+RSet(Hex(Devices()\MACAddress), 8*2, "0") | |
Debug "" | |
Next | |
;} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment