Page 1 of 1

simple ping on Linux uses raw sockets

Posted: Sat Apr 30, 2005 12:36 pm
by alban read
This example works but still needs improvement.
I thought I would post it anyway.

Code: Select all


; Simple ping for linux (in pure basic.) 
; Alban Read. 2005.
; beware that the time in milliseconds does not seem accurate 

#AF_INET=2
#SOCK_RAW=3
#EPERM =1
#ICMP_ECHO=8
#SOL_SOCKET=1

#F_VERBOSE=$100
#IP_HEADER_LEN=20


; fd_set management 
#FD_SET_SIZE= 128 ; bytes
#NFD_BITS = 32 

Procedure.l FD_ISSET( n.l, fd_set.l )
 wordPtr= (fd_set+(4*(n/#NFD_BITS)))
 ProcedureReturn (PeekL(wordPtr) & (1 << ((n) % #NFD_BITS)))
EndProcedure
;
Procedure FD_SET( n.l, fd_set.l )
  wordPtr= (fd_set+(4*(n/#NFD_BITS)))
  PokeL( wordPtr, PeekL( wordPtr) | (1 << ((n) % #NFD_BITS)))
EndProcedure
;
Procedure FD_CLR( n.l, fd_set.l )
  wordPtr= (fd_set+(4*(n/#NFD_BITS)))
  PokeL( wordPtr, PeekL( wordPtr) & (~(1 << ((n) % #NFD_BITS))))
EndProcedure
;
Procedure FD_ZERO( fd_set.l)
  For i=0 To 28 Step 4
    PokeL(fd_set,0)
  Next i
EndProcedure

Global sequence.w
Global replyIP.l


Procedure.w inverted_checksum( buffer.l, count.l)
  i.l=0
  sum.l=0
  For i=0 To count Step 2
    sum = sum + (PeekW( buffer+i) & $ffff)
  Next i
; should add odd size check.  
  While sum > $ffff
    sum = (sum >> 16) + ( sum & $ffff)
  Wend
  sum = ( (~sum) & $ffff)
  ProcedureReturn sum
EndProcedure

Procedure.l sendPing( s.l, arg1$, string.s)

; resolve ip address or name.
; this can take a long while to timeout.
; 
ip.l=inet_addr_(arg1$)
If ip.l=-1
  hp.l = gethostbyname_( arg1$) 
  If hp.l>0 
    fullname.s = PeekS(PeekL(hp))
    ip.l=(PeekL(PeekL(PeekL(hp+16))))
    Else
    ; PrintN("host name unknown")
    pingResult.l=0
    ProcedureReturn pingResult
  EndIf
EndIf
  
    
  ; --------------------------------------------------------------
  ; set up destination address
  ;   struct sockaddr_in {
  ;   sa_family_t sin_family; /* address family: AF_INET */
  ;   u_int16_t sin_port; /* port in network byte order*/
  ;   struct in_addr sin_addr; /* internet address */
  ;   dont forget the unused.b[8] as THEY DO MATTER.
  ; }; 
  
  lendata.l = Len( string)
  whereTo.l=AllocateMemory(16)
  PokeW(whereTo.l+0000,#AF_INET)
  PokeW(whereTo.l+0002, 0)    ; port not used.
  PokeL(whereTo.l+0004,ip.l)
  PokeL(whereTo.l+0008, 0)    ; the unused 8 bytes - are essential!
  PokeL(whereTo.l+0012, 0)
  
  address.s=PeekS(inet_ntoa_(PeekL(whereTo.l+0004)))
  
  ; PrintN("Familly ="+Str(PeekW(whereTo.l+0000)))
  ; PrintN("Port ="+Str(PeekW(whereTo.l+0002)))
  ; PrintN("Address ="+address.s)
  
  ; build a packet to send. 
  ; struct icmphdr {
  ;   __u8          type;
  ;   __u8          code;
  ;   __u16         checksum;
  ;   union {
  ;         struct {
  ;                 __u16   id;
  ;                 __u16   sequence;
  ;         } echo;
  ;         __u32   gateway;
  ;         struct {
  ;                 __u16   __unused;
  ;                 __u16   mtu;
  ;         } frag;
  ;   } un;
  ; };
  cc.l = lendata.l+8+8
  socklen.l=16
  ident.w = getpid_()
  packetMemory.l=AllocateMemory(64*1024)
  ptr.l=packetMemory
  ; 8 bytes of ICMP header.
  PokeB(ptr, #ICMP_ECHO)  ; type
  ptr=ptr+1
  PokeB(ptr, 0)           ; code
  ptr=ptr+1: chkptr.l=ptr
  PokeW(ptr, 0)           ; checksum holder.
  ptr=ptr+2
  PokeW(ptr, ident )      ; ident - will be changed in the reply though.
  ptr=ptr+2
  PokeW(ptr, sequence)    ; sequence number.
  ptr=ptr+2
  timePtr=ptr
  ;PokeL(ptr, ElapsedMilliseconds() ) ;time stampA should be done just before we send..
  ptr=ptr+4
  PokeL(ptr, ident)       ; this should be unchanged as part of the reply
  ptr=ptr+4
  PokeS(ptr, string)
  ptr=ptr+lendata
  pktsize.l=ptr-packetMemory
  
  ; PrintN( "packet size="+Str(pktsize))
  ; PrintN( "type="+ Str( PeekB(packetMemory.l+0000)))
  ; PrintN( "code="+ Str( PeekB(packetMemory.l+0001)))
  ; PrintN( "chksum="+ Str( PeekW(packetMemory.l+0002)))
  ; PrintN( "ident="+ Str( PeekW(packetMemory.l+0004)))
  ; PrintN( "sequence="+ Str( PeekW(packetMemory.l+0006)))
  ; PrintN( "data=<"+PeekS(packetMemory+0016)+">")
  
  PokeL(timePtr, ElapsedMilliseconds() ) ;time stampA 
  ; checksum needs to be done last of all
  PokeW(chkptr, inverted_checksum(packetMemory, pktsize) )
  
  result.l = sendto_( s, packetMemory, pktsize, 0, whereTo, socklen  )
  ; perror_("") 
  ; PrintN( Str(result.l ))
  
  sequence.w=sequence.w+1
  ; our packet should hopefully come back now!
  ; free the buffers
  
  FreeMemory(packetMemory)
  FreeMemory(whereTo)
  
  pingResult=-1
  ProcedureReturn pingResult

EndProcedure



Procedure.l GetReply(s)
whereFrom.l=AllocateMemory(16)
PokeW(whereFrom.l+0000, #AF_INET) ; protocol
PokeW(whereFrom.l+0002, 0)
PokeL(whereFrom.l+0004, 0) ; IP address here is ignored.
PokeL(whereFrom.l+0008, 0) ; the unused 8 bytes - are essential!
PokeL(whereFrom.l+0012, 0)
whereLen=16
packetMemory.l = AllocateMemory(64*1024)
fd_set = AllocateMemory(#FD_SET_SIZE)
; set the timeout to wait for a reply
timeout.l=AllocateMemory(8)
PokeL( timeout.l+0,3) ;seconds
PokeL( timeout.l+8,0) ;milliseconds
FD_ZERO( fd_set)
FD_SET( s, fd_set)
memset_(packetMemory,0,64*1024) 
sel.l = select_(s +1, fd_set, 0, 0, timeout )
If sel.l > 0 
  size.l = recvfrom_(s, packetMemory, 64*1024, 0, whereFrom, @whereLen )
  recvTime.l= ElapsedMilliseconds()
  address.s=PeekS(inet_ntoa_(PeekL(whereFrom+0004)))
  packetMemory=packetMemory+#IP_HEADER_LEN ; skip the ip header.
  sentTime.l = PeekL( packetMemory+8)
  localId.w = PeekL( packetMemory+12)
  ; - information can be retrieved from the packet.  
;   If localId.w = getpid_()
;     PrintN("Our packet")
;   Else 
;    PrintN("Not Our packet")
;   EndIf  
;   PrintN("Reply from Address ="+address)
;   PrintN( "type="+ Hex( PeekB(packetMemory.l+0000)))
;   PrintN( "code="+ Hex( PeekB(packetMemory.l+0001)))
;   PrintN( "chksum="+ Hex( PeekW(packetMemory.l+0002)))
;   PrintN( "remote ident="+ Hex( PeekW(packetMemory.l+0004)))
;   PrintN( "sequence="+ Hex( PeekW(packetMemory.l+0006)))
;   PrintN( "sent time (ms)="+Str(sentTime.l))
;   PrintN( "data=<"+PeekS(packetMemory+0016)+">")
;   PrintN( "Time ellapsed ="+Str( recvTime-sentTime))
;   PrintN( "local id="+Str( PeekL( packetMemory+12)))
;   
 If localId.w = getpid_() 
    pingResult= 1
 EndIf
Else
;   PrintN("Reply timed out")
  pingResult=0
EndIf

; tidy up memory etc
FreeMemory(packetMemory-#IP_HEADER_LEN)
FreeMemory(timeout)
FreeMemory(whereFrom)

ProcedureReturn pingResult
EndProcedure

Procedure.l openRawSocketAsRoot()

; run as root for as little time as possible

; Now the number 1 and the letters icmp 
; are brought to you by..
proto.l = getprotobyname_("ICMP")
If proto = 0 
  PrintN("ICMP protocol unknown")
  End
Else 
 proto$ = PeekS(PeekL(proto))
 proto=proto+8
 protocolICMP.l=PeekL(proto)
EndIf
; is there even any point at all in getprotobyname?

; RAW sockets must run as root - 
; so you must set root permission on the executable to do this or sudo it.
s.l = socket_ (#AF_INET, #SOCK_RAW, protocolICMP  )
If s.l<0 
    perror_("") 
    End
EndIf
; drop root now that the raw socket is open.
setuid_( getuid_())

ProcedureReturn s

EndProcedure


; console example of simple LINUX ping
s.l = openRawSocketAsRoot()

result.l = OpenConsole()
If result = 0
  End
EndIf 

arg1$=ProgramParameter()
If arg1$ = ""
  PrintN("Ping nodename or ipaddress")
  End
EndIf

If sendPing(s, arg1$,"This is a message ")
  If GetReply(s) 
    PrintN( "Ping succeeded")
    Else 
    PrintN("No Reply: Ping failed")
  EndIf
Else 
  PrintN("Unable to ping")
EndIf

End


Posted: Sat Apr 30, 2005 6:11 pm
by KarLKoX
Very interessting, good job ! :D

Re: simple ping on Linux uses raw sockets

Posted: Thu Sep 12, 2013 3:46 pm
by Joel
Some new version for PB 5.x?

With PB 5.20

Code: Select all

gethostbyname_
is not >0. Someone an idea?