Check basic IPv6 connection ability using Ipv6 ping

Share your advanced PureBasic knowledge/code with the community.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Check basic IPv6 connection ability using Ipv6 ping

Post by SFSxOI »

I previously posted this for connection checks for IPv6 > http://www.purebasic.fr/english/viewtop ... 12&t=52402 > A possible issue with that is it directly accesses at socket level which makes it not suitable in every situation plus in some situations this direct approach can introduce a possible security vunlerability exploit or permissions situation.

The better way to do the same thing for just checking IPv6 connections is to use an IPv6 ping. Below is some simple code using API to do this. For windows Vista and above with winsock version 2.2 - tested Windows 7 x86 & Windows 8 x86.

(note: Yes i'm aware some of these API's have equals with the PureBasic underscore designation, e.g. "FunctionName_(x,x,x....)" and there would be no need to load up a library and call from the .dll directly)

Code: Select all

Prototype Pgetaddrinfo(pNodeName, pServiceName, pHints, ppResult)
Prototype PGetAddrInfoEx(pName, pServiceName, dwNameSpace, lpNspId, pHints, ppResult, timeout, lpOverlapped, lpCompletionRoutine, lpNameHandle)
Prototype PWSAStartup(wVersionRequested, lpWSAData)
Prototype PWSACleanup()
Prototype PInetNtop(Family, pAddr, pStringBuf, StringBufSize)
Prototype PRtlZeroMemory(mem, sze)
Prototype PFreeAddrInfo(ai)
Prototype PFreeAddrInfoEx(ai)
Prototype PIcmp6SendEcho2(IcmpHandle, Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData, RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout)
Prototype PIcmp6ParseReplies(ReplyBuffer, ReplySize)
Prototype PIcmp6CreateFile()
Prototype PInetPton(Family, pszAddrString, pAddrBuf)
Prototype Pgetnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
Prototype PNetWkstaGetInfo(servername, level, bufptr)
Prototype PNetApiBufferFree(Buffer_in)
Prototype PWSAGetLastError()

Global getaddrinfo.Pgetaddrinfo
Global GetAddrInfoEx.PGetAddrInfoEx
Global WSAStartup.PWSAStartup
Global WSACleanup.PWSACleanup
Global InetNtop.PInetNtop
Global ZeroMemory.PRtlZeroMemory
Global FreeAddrInfo.PFreeAddrInfo
Global FreeAddrInfoEx.PFreeAddrInfoEx
Global Icmp6SendEcho2.PIcmp6SendEcho2
Global Icmp6CreateFile.PIcmp6CreateFile
Global Icmp6ParseReplies.PIcmp6ParseReplies
Global InetPton.PInetPton
Global getnameinfo.Pgetnameinfo
Global NetWkstaGetInfo.PNetWkstaGetInfo
Global NetApiBufferFree.PNetApiBufferFree
Global WSAGetLastError.PWSAGetLastError

Structure IN6_ADDR ;->Structure in6_addr
  StructureUnion
    Byte.b[16]
    Word.w[8]
  EndStructureUnion
EndStructure

Structure sockaddr_in6 ;->Structure sockaddr_in6
  sin6_family.w
  sin6_port.w
  sin6_flowinfo.l
  sin6_addr.IN6_ADDR
  sin6_scope_id.l
EndStructure

Structure IPV6_ADDRESS_EX ;->Structure IPV6_ADDRESS_EX
  sin6_port.w
  sin6_flowinfo.l
  sin6_addr.w[8]
  sin6_scope_id.l
EndStructure

Structure ICMPV6_ECHO_REPLY ;->Structure ICMPV6_ECHO_REPLY
  Address.IPV6_ADDRESS_EX
  Status.l
  RoundTripTime.i
EndStructure

Structure ADDRINFOEX ;->Structure ADDRINFOEX
  ai_flags.l
  ai_family.l
  ai_socktype.l
  ai_protocol.l
  ai_addrlen.i
  *ai_canonname.c 
  *ai_addr.sockaddr_in6
  *ai_blob
  ai_bloblen.i
  ai_provider.l
  *ai_next.ADDRINFOEX
EndStructure

Structure WKSTA_INFO_100 ;->Structure WKSTA_INFO_100
  wki100_platform_id.l
  wki100_computername.l
  wki100_langroup.l
  wki100_ver_major.l
  wki100_ver_minor.l
EndStructure

Structure IO_STATUS_BLOCK
  StructureUnion
    Status.i
    Pointer.i
  EndStructureUnion
  Information.i
EndStructure

#DEFAULT_SEND_SIZE = 32
; added the IO_STATUS_BLOCK structure because the size of the structure will change between 32 and 64 bit when the .i become 8 bytes on 64 bit
#IO_STATUS_BLOCK_SIZE = SizeOf(IO_STATUS_BLOCK)  
#ICMPV6_ECHO_REPLY_SIZE = SizeOf(ICMPV6_ECHO_REPLY)
#ICMP_ERROR_MESSAGE_SIZE = 8
#REQUEST_DATA_SIZE = 32
; #DEFAULT_RECEIVE_SIZE is used for the reply buffer size. Per MS > http://msdn.microsoft.com/en-us/library/windows/desktop/aa366041(v=vs.85).aspx
;"The buffer must be large enough to hold at least one ICMPV6_ECHO_REPLY structure plus the number of bytes of data specified in the RequestSize parameter." 
;"This buffer should also be large enough to also hold 8 more bytes of data (the size of an ICMP error message) plus space for an IO_STATUS_BLOCK structure."
#DEFAULT_RECEIVE_SIZE = #ICMPV6_ECHO_REPLY_SIZE + #REQUEST_DATA_SIZE + #ICMP_ERROR_MESSAGE_SIZE + #IO_STATUS_BLOCK_SIZE
#DEFAULT_BUFFER_SIZE = 8184
#DEFAULT_TIMEOUT = 1000
#AF_INET6 = 23
#NS_DNS = 12
#NERR_Success = 0
#INET6_ADDRSTRLEN = 65
#NI_MAXHOST = 1025  ;Max size of a fully-qualified domain name
#NI_MAXSERV = 32 ;Max size of a service name
#MAX_OPT_SIZE = 40 ; Maximum length of IP options in bytes
#MAX_BUFFER_SIZE = SizeOf(ICMP_ECHO_REPLY) + $fff7 + #MAX_OPT_SIZE

Macro MAKEWORD(a, b)
  (a & $FF)|((b & $FF)<<8)
EndMacro

Procedure LoadLibraries()
  Global Lib_iphlpapi.l, Lib_Winsock.l, Lib_Lib_Kernel32.l, Lib_Netapi32.l
    
Lib_iphlpapi = OpenLibrary(#PB_Any,"iphlpapi.dll")
If IsLibrary(Lib_iphlpapi)<> 0
  Icmp6SendEcho2.PIcmp6SendEcho2=GetFunction(Lib_iphlpapi,"Icmp6SendEcho2")
  Icmp6CreateFile.PIcmp6CreateFile=GetFunction(Lib_iphlpapi,"Icmp6CreateFile")
  Icmp6ParseReplies.PIcmp6ParseReplies=GetFunction(Lib_iphlpapi,"Icmp6ParseReplies")
EndIf

Lib_Winsock = OpenLibrary(#PB_Any,"ws2_32.dll")
If IsLibrary(Lib_Winsock)<> 0
  WSAStartup.PWSAStartup=GetFunction(Lib_Winsock,"WSAStartup")
  WSACleanup.PWSACleanup=GetFunction(Lib_Winsock,"WSACleanup")
  WSAGetLastError.PWSAGetLastError=GetFunction(Lib_Winsock,"WSAGetLastError")
  CompilerIf #PB_Compiler_Unicode ; if compiled in unicode use this
    FreeAddrInfo.PFreeAddrInfo=GetFunction(Lib_Winsock,"FreeAddrInfoW")
    FreeAddrInfoEx.PFreeAddrInfoEx=GetFunction(Lib_Winsock,"FreeAddrInfoExW")
    getaddrinfo.Pgetaddrinfo=GetFunction(Lib_Winsock,"GetAddrInfoW")
    getnameinfo.Pgetnameinfo=GetFunction(Lib_Winsock,"GetNameInfoW")
    GetAddrInfoEx.PGetAddrInfoEx=GetFunction(Lib_Winsock,"GetAddrInfoExW")
    InetPton.PInetPton=GetFunction(Lib_Winsock,"InetPtonW")
    InetNtop.PInetNtop=GetFunction(Lib_Winsock,"InetNtopW")
  CompilerElse ; if not compiled in unicode use this
    FreeAddrInfo.PFreeAddrInfo=GetFunction(Lib_Winsock,"freeaddrinfo")
    FreeAddrInfoEx.PFreeAddrInfoEx=GetFunction(Lib_Winsock,"FreeAddrInfoEx")
    getaddrinfo.Pgetaddrinfo=GetFunction(Lib_Winsock,"getaddrinfo")
    getnameinfo.Pgetnameinfo=GetFunction(Lib_Winsock,"getnameinfo")
    GetAddrInfoEx.PGetAddrInfoEx=GetFunction(Lib_Winsock,"GetAddrInfoExA")
    InetPton.PInetPton=GetFunction(Lib_Winsock,"inet_pton")
    InetNtop.PInetNtop=GetFunction(Lib_Winsock,"inet_ntop")
  CompilerEndIf
EndIf

Lib_Kernel32 = OpenLibrary(#PB_Any,"Kernel32.dll")
If IsLibrary(Lib_Kernel32)<> 0
  ZeroMemory.PRtlZeroMemory=GetFunction(Lib_Kernel32,"RtlZeroMemory")
EndIf

Lib_Netapi32 = OpenLibrary(#PB_Any,"Netapi32.dll")
If IsLibrary(Lib_Netapi32)<> 0
  NetApiBufferFree.PNetApiBufferFree=GetFunction(Lib_Netapi32,"NetApiBufferFree")
  NetWkstaGetInfo.PNetWkstaGetInfo=GetFunction(Lib_Netapi32,"NetWkstaGetInfo")
EndIf

EndProcedure

Procedure.s GetCompName()
  Level.l = 100
  *WkstaBuf.WKSTA_INFO_100 = #Null
  If NetWkstaGetInfo(#Null, Level, @*WkstaBuf) = #NERR_Success
    comp$ = PeekS(*WkstaBuf\wki100_computername, -1, #PB_Unicode)
  Else
    comp$ = "Could not get local computer name due to error"
  EndIf
  NetApiBufferFree(*WkstaBuf)
  ProcedureReturn comp$
EndProcedure

Procedure Ipv6PingConnectTest(DestinationIPAddress$)
  Protected ICMPV6Sucess.b
  Protected senddatastrng$, compname$, resolvestr$, addrbuf.s, host_name.s, errmsg$
  Protected send_data_size.i, replySize.i, try_timeout.i
   
  *LocalComp.sockaddr_in6 = #Null
  *Destination.sockaddr_in6 = #Null
  *Resolve.sockaddr_in6 = #Null
  
  *LocalComp.sockaddr_in6 = AllocateMemory(SizeOf(sockaddr_in6))
  *Destination.sockaddr_in6 = AllocateMemory(SizeOf(sockaddr_in6))
  *Resolve.sockaddr_in6 = AllocateMemory(SizeOf(sockaddr_in6))
  *Resolve\sin6_family = #AF_INET6
  
  senddatastrng$ = "HELLO"
  *requestData.c = AllocateMemory(#REQUEST_DATA_SIZE)
  *prequestData = *requestData
  CopyMemoryString(@senddatastrng$, @*prequestData)
  CompilerIf #PB_Compiler_Unicode
    send_data_size = StringByteLength(PeekS(*requestData)) + 2
  CompilerElse
    send_data_size = StringByteLength(PeekS(*requestData)) + 1
  CompilerEndIf
  If send_data_size <= #DEFAULT_SEND_SIZE
    replySize = #DEFAULT_BUFFER_SIZE
  Else
    replySize = #MAX_BUFFER_SIZE
  EndIf
  
  *ReplyBuffer.c = AllocateMemory(replySize)
  *icmpEcho.ICMPV6_ECHO_REPLY = *ReplyBuffer
  
  try_timeout = 5000;#DEFAULT_TIMEOUT ; in milliseconds
  
  ;; ; the destination address *************************
  *dest_result.ADDRINFOEX = #Null
  hints.ADDRINFOEX
  ZeroMemory(@hints, SizeOf(hints))
  hints\ai_family = #AF_INET6
  hints\ai_socktype = #SOCK_STREAM
  ; if using GetAddrInfoEx then must use FreeAddrInfoEx to free
  res_getaddrinfo = GetAddrInfoEx(@DestinationIPAddress$, #Null, #NS_DNS, #Null, @hints, @*dest_result, #Null, #Null, #Null, #Null)
  ; can also use > getaddrinfo(@DestinationIPAddress$, #Null, @hints, @*dest_result)
  ; if using getaddrinfo then must use FreeAddrInfo to free
  If res_getaddrinfo > #NO_ERROR
    FreeAddrInfoEx(*dest_result)
    FreeMemory(*LocalComp)
    FreeMemory(*Destination)
    FreeMemory(*Resolve)
    ProcedureReturn -2
  EndIf
  *Destination\sin6_family = #AF_INET6
  *Destination\sin6_port = 0
  *Destination\sin6_addr = *dest_result\ai_addr\sin6_addr
  ;**************************************************************
  
  Icmp6File = Icmp6CreateFile()
  If Icmp6File = #INVALID_HANDLE_VALUE
    FreeAddrInfoEx(*dest_result)
    FreeMemory(*LocalComp)
    FreeMemory(*Destination)
    FreeMemory(*Resolve)
    ProcedureReturn #INVALID_HANDLE_VALUE
  EndIf
 
  numberOfReplies = Icmp6SendEcho2(Icmp6File, #Null, #Null, #Null, *LocalComp, *Destination, *requestData, send_data_size, #Null, *ReplyBuffer, replySize, try_timeout)
  icmp6parse = Icmp6ParseReplies(*ReplyBuffer, SizeOf(*ReplyBuffer))
  
  If numberOfReplies > 0 And icmp6parse = 1
    ICMPV6Sucess = #True
  Else
    ICMPV6Sucess = #False
  EndIf
    
  Select WSAGetLastError()
    Case 0
      errmsg$ = "Success"
    Case 1231
      errmsg$ = "The network location cannot be reached"
    Case 11001
      errmsg$ = "Buffer Too Small"
    Case 11002
      errmsg$ = "Destination Net Unreachable"
    Case 11003
      errmsg$ = "Destination Host Unreachable"
    Case 11004
      errmsg$ = "Destination Protocol Unreachable"
    Case 11005
      errmsg$ = "Destination Port Unreachable"
    Case 11006
      errmsg$ = "No Resources"
    Case 11007
      errmsg$ = "Bad Option"
    Case 11008
      errmsg$ = "Hardware Error"
    Case 11009
      errmsg$ = "Packet Too Big"
    Case 11010
      errmsg$ = "Request Timed Out"
    Case 11011
      errmsg$ = "Bad Request"
    Case 11012
      errmsg$ = "Bad Route"
    Case 11013
      errmsg$ = "TimeToLive Expired Transit"
    Case 11014
      errmsg$ = "TimeToLive Expired Reassembly"
    Case 11015
      errmsg$ = "Parameter Problem"
    Case 11016
      errmsg$ = "Source Quench"
    Case 11017
      errmsg$ = "Option Too Big"
    Case 11018
      errmsg$ = "Negotiating IPSEC"
    Case 11050
      errmsg$ = "General Failure"
    Default
      errmsg$ = "Unknown Status or Failure"
  EndSelect
  
  Debug "IPv6 address ping tested = " + DestinationIPAddress$
  addrbuf = Space(#INET6_ADDRSTRLEN)
  InetNtop(#AF_INET6, @*icmpEcho\Address\sin6_addr, @addrbuf, #INET6_ADDRSTRLEN)
  compname$ = GetCompName()
  Debug "IPv6 responding address = " + addrbuf
  
  InetPton(#AF_INET6, @addrbuf, *Resolve\sin6_addr) ; put addrbuf text address representation of address that responded to ping in *Resolve structure
  host_name = Space(#NI_MAXHOST)
  getresolveinfo = getnameinfo(*Resolve, SizeOf(sockaddr_in6), @host_name, #NI_MAXHOST, #Null, #Null, 0) ; get the host name
  
  If getresolveinfo = 0
    resolvestr$ = "IPv6 responding resolves to = "
  Else
    resolvestr$ = "IPv6 responding address could not be resolved = "
  EndIf
  
  If FindString(host_name, compname$, 1) = 1
    host_name = host_name + " (local computer)"
  EndIf
  
  Debug resolvestr$ + host_name
  Debug "Ping Status = " + errmsg$
  addrbuf = ""
  host_name = ""
  ; **********************************************************************
  
  IcmpCloseHandle_(Icmp6File)
  FreeAddrInfoEx(*dest_result)
  FreeMemory(*LocalComp)
  FreeMemory(*Destination)
  FreeMemory(*Resolve)
  
  ProcedureReturn ICMPV6Sucess
  
EndProcedure

LoadLibraries()
wVersionRequested.w = MAKEWORD(2,2)
WSAStart.i = WSAStartup(wVersionRequested,@wsaData.WSADATA)

Debug "**********************************************************"
Debug Ipv6PingConnectTest("2620:0:1cfe:face:b00c::3") ; facebook ipv6 hex address
Debug "**********************************************************"
Debug Ipv6PingConnectTest("www.v6.facebook.com") ; facebook named Ipv6 address
Debug "**********************************************************"
Debug Ipv6PingConnectTest("www.facebook.com") ; set up for both Ipv6 and Ipv4
Debug "**********************************************************"
Debug Ipv6PingConnectTest("www.google.com") ; set up for both Ipv6 and Ipv4
Debug "**********************************************************"
Debug Ipv6PingConnectTest("::1") ; test to local loopback IPv6 address
Debug "**********************************************************"
Debug Ipv6PingConnectTest("::3") ; intentional test to failure
Debug "**********************************************************"

WSACleanup()
If Lib_iphlpapi : CloseLibrary(Lib_iphlpapi) : EndIf
If Lib_Kernel32 : CloseLibrary(Lib_Kernel32) : EndIf
If Lib_Winsock : CloseLibrary(Lib_Winsock) : EndIf
If Lib_Netapi32 : CloseLibrary(Lib_Netapi32) : EndIf
Make sure also that you are using winsock version 2.2.

Edit note: Updated the code from previous post
Last edited by SFSxOI on Thu Jan 03, 2013 7:02 pm, edited 48 times in total.
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: Check basic IPv6 connection ability using Ipv6 ping

Post by ts-soft »

Works fine on x86 but not on x64
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Re: Check basic IPv6 connection ability using Ipv6 ping

Post by SFSxOI »

Haven't tested on 64 bit yet. I don't really know yet and haven't checked because i'm only doing x86 code at this time, however, I don't immediately see anything in here that would not work on 64 bit and there aren't any notable exceptions I know about right now that are specific to 64 bit only but... like I said I haven't checked out the 64 bit side yet.

What specific error(s) are you getting on 64 bit or what specifically doesn't work on 64 bit?

Edit note: Updated the code in the first post.
Last edited by SFSxOI on Sun Dec 30, 2012 2:22 am, edited 4 times in total.
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.
Post Reply