IPv6 and IPv4 Connection Check

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.....

IPv6 and IPv4 Connection Check

Post by SFSxOI »

For Windows Vista, Windows 7, and now Windows 8 (and Windows Server 2008 and up), only tested in x86 32 bit versions. Works in either ANSI or Unicode

Just a quick implementation using the API WSAConnectByName. I'm doing some IPv6 stuff, needed to test connectivity to IPv6 sites but also wanted something to do IPv4 at the same time so leveraging the dual winsock stack capabilitites present in Windows Vista and up the WSAConnectByName came in handy to do this. Its the MSDN sample (http://msdn.microsoft.com/en-us/library ... s.85).aspx) converted for PureBasic (which does not at this time do IPv6).

The _ thing for an API function, you can use that too in some of the API calls with socket_, setsockopt_, closesocket_, WSAStartup_, and WSACleanup_, if you prefer :) and cut down the number of calls to the .dll but ya can't do it with WSAConnectByName and will still need to call it from ws2_32.dll.

Winsock version 2.2 is started up first, then the procedure is called with the site named address (in the form 'www.somesite.com') and the port service name (e.g. http which is the name for port 80) or the actual port number (in a string). Then a dual use socket for both Ipv4 and Ipv6 is created and WSAConnectByName is used to make a fast connection to the named site to check for IPv4 or IPv6 connectivity (depending on the site). If connection is sucessful it returns #True, then the socket is closed and the procedure returns.

Code: Select all

#IPV6_V6ONLY = 27 ;Treat wildcard bind as AF_INET6-only
#SO_UPDATE_CONNECT_CONTEXT = $7010
#IPPROTO_ICMPV6 = 58
#IPPROTO_TCP = 6
#AF_INET6 =  23
#IPPROTO_IPV6  = 41
#IPV6_V6ONLY = 27

Prototype Psocket(af, type, protocol)
Prototype Psetsockopt(s, level, optname, optval, optlen)
Prototype Pclosesocket(s)
Prototype PWSAStartup(wVersionRequested, lpWSAData)
Prototype PWSACleanup()
Prototype PWSAGetLastError()
Prototype PWSAConnectByName(s, nodename, servicename, LocalAddressLength, LocalAddress, RemoteAddressLength, RemoteAddress, timeout, Reserved)
Global socket.Psocket
Global setsockopt.Psetsockopt
Global closesocket.Pclosesocket
Global WSAStartup.PWSAStartup
Global WSACleanup.PWSACleanup
Global WSAGetLastError.PWSAGetLastError
Global WSAConnectByName.PWSAConnectByName

Lib_Winsock = OpenLibrary(#PB_Any,"ws2_32.dll")
If IsLibrary(Lib_Winsock)<> 0
  init_Winsock = #True
  WSAStartup.PWSAStartup=GetFunction(Lib_Winsock,"WSAStartup")
  WSACleanup.PWSACleanup=GetFunction(Lib_Winsock,"WSACleanup")
  WSAGetLastError.PWSAGetLastError=GetFunction(Lib_Winsock,"WSAGetLastError")
  socket.Psocket=GetFunction(Lib_Winsock,"socket")
  setsockopt.Psetsockopt=GetFunction(Lib_Winsock,"setsockopt")
  closesocket.Pclosesocket=GetFunction(Lib_Winsock,"closesocket")
  CompilerIf #PB_Compiler_Unicode ; if compiled in unicode use this
    WSAConnectByName.PWSAConnectByName=GetFunction(Lib_Winsock,"WSAConnectByNameW")
  CompilerElse ; if not compiled in unicode use this
    WSAConnectByName.PWSAConnectByName=GetFunction(Lib_Winsock,"WSAConnectByNameA")
  CompilerEndIf
EndIf

; Structure SOCKADDR_STORAGE ;->Structure SOCKADDR_STORAGE
;   ss_family.l
;   __ss_pad1.c[6]
;   __ss_align.q
;   __ss_pad2.c[112]
; EndStructure

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

Procedure OpenAndConnect(Node_Name$)
  
  ConnSocket = #INVALID_SOCKET
  ipv6only = 0 ; 0 = IPv4 and IPv6 ------ 1 = IPv6 only
  iResult.i
  iResultopt.i
  iResultoptII.i
  bSuccess.b
  ;LocalAddr.SOCKADDR_STORAGE ; uncomment if needed for getting addresses
  ;RemoteAddr.SOCKADDR_STORAGE
  ;dwLocalAddr.i = SizeOf(LocalAddr)
  ;dwRemoteAddr.i = SizeOf(RemoteAddr)
  NodeName$ = Node_Name$
  PortName$ = "http"
  timeout = 10000
  
  ConnSocket = socket(#AF_INET6, #SOCK_STREAM, 0)
  If ConnSocket = #INVALID_SOCKET
    ProcedureReturn #INVALID_SOCKET
  EndIf
  
  iResultopt = setsockopt(ConnSocket, #IPPROTO_IPV6, #IPV6_V6ONLY, @ipv6only, SizeOf(ipv6only) )
  If iResultopt = #SOCKET_ERROR
    bSuccess = #False
  Else 
    ; note - read the MSDN, if the remote and local address are #Null the dont need the structure or dwLocalAddr and dwRemoteAddr variables as they are ignored in this case
    ; uncomment to use and put the LocalAddr and RemoteAddr back in the function
    bSuccess = WSAConnectByName(ConnSocket, @NodeName$, @PortName$, @dwLocalAddr, #Null, @dwRemoteAddr, #Null, #Null, #Null)
    If bSuccess = #False
      bSuccess = #False
    Else
      bSuccess = #True
    EndIf
  EndIf
  
  iResultoptII = setsockopt(ConnSocket, #SOL_SOCKET, #SO_UPDATE_CONNECT_CONTEXT, #Null, 0)
  If iResultoptII = #SOCKET_ERROR
    bSuccess = #False
  Else
    bSuccess = #True
  EndIf
  closesocket(ConnSocket)
  ProcedureReturn bSuccess
  
EndProcedure
;url_d$ = "www.ipv6forum.com"
wVersionRequested.w = MAKEWORD(2,2)
WSAStart.i = WSAStartup(wVersionRequested,@wsaData.WSADATA) ; starts up winsock v 2.2 in wVersionRequested
PortName$ = "http"
NodeName$ = "www.google.com"
Debug OpenAndConnect(NodeName$)
; NodeName$ = "www.yahoo.com"
; Debug OpenAndConnect(NodeName$)
NodeName$ = "ipv6.google.com"
Debug OpenAndConnect(NodeName$)
NodeName$ = "www.ipv6ready.org"
Debug OpenAndConnect(NodeName$)
NodeName$ = "test-ipv6.com"
Debug OpenAndConnect(NodeName$)
NodeName$ = "2607:f8b0:4002:c05::67" ; ipv6 address for ipv6.google.com
Debug OpenAndConnect(NodeName$)
WSACleanup()
If Lib_Winsock : CloseLibrary(Lib_Winsock) : EndIf
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.