Ping test

BlitzMax Forums/BlitzMax Programming/Ping test

Vertex(Posted 2007) [#1]
I write a ping function for my network module BNetEx and I can only test it on my Win2k x86. I don't know if it works correct on MacOS(with Motorola processor) or on Linux. It can be different byte orders or so on...

SuperStrict

Framework Pub.StdC

Extern "OS"
	Const INVALID_SOCKET_ : Int = -1
	Const SOCK_RAW_       : Int = 3
	Const IPPROTO_ICMP    : Int = 1

	?Win32
		Const SOL_SOCKET_ : Int = $FFFF

		Function inet_addr_:Int(Address$z) = "inet_addr@4"
		Function GetCurrentProcessId:Int() = "GetCurrentProcessId@0"

	?MacOS
		Const SOL_SOCKET_ : Int = $FFFF

		Function inet_addr_:Int(Address$z) = "inet_addr"
		Function GetCurrentProcessId:Int() = "getpid"

	?Linux
		Const SOL_SOCKET_ : Int = 1

		Function inet_addr_:Int(Address$z) = "inet_addr"
		Function GetCurrentProcessId:Int() = "getpid"
	?
End Extern

Type TICMP
	Field _Type     : Byte
	Field Code      : Byte
	Field Checksum  : Short
	Field ID        : Short
	Field Sequence  : Short

	Function BuildChecksum:Short(Buffer:Short Ptr, Size:Int)
		Local Checksum:Long

		While Size > 1
			Checksum :+ Buffer[0]
			Buffer :+ 1
			Size :- 2
		Wend
		If Size Then Checksum :+ (Byte Ptr(Buffer))[0]

		Checksum = (Checksum Shr 16) + (Checksum & $FFFF)
		Checksum :+ Checksum Shr 16
		Return htons_(~Checksum)
	End Function
End Type

Const ICMP_ECHOREPLY   : Byte = 0
Const ICMP_UNREACHABLE : Byte = 3
Const ICMP_ECHO        : Byte = 8

Const ICMP_CODE_NETWORK_UNREACHABLE : Byte = 0
Const ICMP_CODE_HOST_UNREACHABLE    : Byte = 1

Function Ping:Int(RemoteIP:Int, Data:Byte Ptr, Size:Int, Sequence:Int = 0, ..
                  Timeout:Int = 5000)
	Local Socket:Int, ProcessID:Int, ICMP:TICMP, Buffer:Byte Ptr, ..
	      Start:Int, Stop:Int, Result:Int, SenderIP:Int, SenderPort:Int, ..
	      IPSize:Int

	Socket = socket_(AF_INET_, SOCK_RAW_, IPPROTO_ICMP)
	If Socket = INVALID_SOCKET_ Then Return -1

	If setsockopt_(Socket, SOL_SOCKET_, SO_RCVTIMEO, Varptr(Timeout), 4) = ..
	   SOCKET_ERROR_ Then
		closesocket_(Socket)
		Return -1
	EndIf

	ProcessID = GetCurrentProcessID()

	ICMP = New TICMP
	ICMP._Type     = ICMP_ECHO
	ICMP.Code      = 0
	ICMP.Checksum  = 0
	ICMP.ID        = ProcessID
	ICMP.Sequence  = 0

	Buffer = MemAlloc(65536)
	MemCopy(Buffer, ICMP, 8)
	MemCopy(Buffer + 8, Data, Size)
	Short Ptr(Buffer)[1] = htons_(TICMP.BuildChecksum(Short Ptr(Buffer), 8 + Size))

	If sendto_(Socket, Buffer, 8 + Size, 0, RemoteIP, 0) = SOCKET_ERROR_ Then
		MemFree(Buffer)
		closesocket_(Socket)
		Return -1
	EndIf

	Start = MilliSecs()
	Repeat
		Result = recvfrom_(Socket, Buffer, 65536, 0, SenderIP, SenderPort)
		Stop = MilliSecs()
		If Result = SOCKET_ERROR_ Then
			MemFree(Buffer)
			closesocket_(Socket)
			Return -1
		EndIf

		?X86
			IPSize = (Buffer[0] & $0F)*4
		?PPC
			IPSize = (Buffer[0] & $F0)*4
		?
		MemCopy(ICMP, Buffer + IPSize, 8)

		If ICMP.ID <> ProcessID Then
			Continue
		ElseIf ICMP._Type = ICMP_UNREACHABLE Then
			If ICMP.Code = ICMP_CODE_HOST_UNREACHABLE Or ..
			   ICMP.Code = ICMP_CODE_NETWORK_UNREACHABLE Then
				MemFree(Buffer)
				closesocket_(Socket)
				Return -1
			EndIf
		ElseIf ICMP.Code = ICMP_ECHOREPLY Then
			Exit
		EndIf
	Forever

	MemFree(Buffer)
	closesocket_(Socket)
	Return Stop - Start
End Function

Function IntIP:Int(IP:String)
	Return htonl_(inet_addr_(IP))
End Function

Function GetHostIP:Int(HostName:String)
	Local Addresses:Byte Ptr Ptr, AddressType:Int, AddressLength:Int
	Local PAddress:Byte Ptr, Address:Int

	Addresses = gethostbyname_(HostName, AddressType, AddressLength)
	If (Not Addresses) Or AddressType <> AF_INET_ Or AddressLength <> 4 Then Return 0

	If Addresses[0] Then
		PAddress = Addresses[0]
		Address = (PAddress[0] Shl 24) | (PAddress[1] Shl 16) | ..
		          (PAddress[2] Shl 8) | PAddress[3]
		Return Address
	Else
		Return 0
	EndIf
End Function

Global RemoteIP : Int, ..
       Message  : String, ..
       Data     : Byte Ptr, ..
       Result   : Int

RemoteIP = GetHostIP("google.com")
If Not RemoteIP Then
	WriteStdout("Host not found~n")
	End
EndIf

Message = "Hello, world!"
Data = Message.ToCString()
Result = Ping(RemoteIP, Data, Message.Length)
If Result = -1 Then
	WriteStdout("Ping failed~n")
	MemFree(Data)
	End
EndIf

WriteStdout("Ping tooks " + Result + "ms~n")
MemFree(Data)
End

(Test is at the end of the sourcecode)

If it's work, please remove the ! from "Hello, world!" so that is an odd data size and test it again.

Thank you!

cu olli