Skip to content

Instantly share code, notes, and snippets.

@aziascreations
Created May 22, 2020 11:33
Show Gist options
  • Save aziascreations/20180a03bab7ea3d97bc0becb497c1f9 to your computer and use it in GitHub Desktop.
Save aziascreations/20180a03bab7ea3d97bc0becb497c1f9 to your computer and use it in GitHub Desktop.
PB Threaded ARP Requests
;{- 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
;}
;{- 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