Network Get Address From Host (All OS)

Share your advanced PureBasic knowledge/code with the community.
User avatar
mk-soft
Always Here
Always Here
Posts: 6246
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Network Get Address From Host (All OS)

Post by mk-soft »

Returns the IP addresses of the host.

After it ran immediately on Windows and macOS, it did not run on Linux at first.
When I noticed the next day that the structures were minimally different, it also worked. :wink:

update v1.01.2
- Fixed structures

update v1.01.4
- Fixed memory-leak

Update v1.02.0
- Some fixed and optimization

Code: Select all

;-TOP

; *************************************************
; Comment   : Network Get Address From Host (All OS)
; Authors   : mk-soft, infratec
; Version   : v1.02.0
; Create    : 31.12.2022
; Update    : 12.08.2023
; Link      : https://www.purebasic.fr/english/viewtopic.php?t=80387

; Description
;  Family : PF_UNSPEC IPv4 and IPv6
;  Family : PF_INET   IPv4
;  Family : PF_INET6  IPv6

EnableExplicit

;-- Imports

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Import ""
CompilerElse
  ImportC ""
CompilerEndIf
  getaddrinfo(NodeName.p-Ascii, ServiceName.p-Ascii, *pHints, *ppResult)
  freeaddrinfo(*ppResult)
  inet_ntop(Family, *pAddr, pStringBuf, StringBufSize)
EndImport

;-- Contants

#AF_UNSPEC = 0
#AF_INET = 2
CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
    #AF_INET6 = 23
  CompilerCase #PB_OS_Linux
    #AF_INET6 = 10
  CompilerCase #PB_OS_MacOS
    #AF_INET6 = 30
CompilerEndSelect

#PF_UNSPEC = #AF_UNSPEC
#PF_INET = #AF_INET
#PF_INET6 = #AF_INET6

#AI_CANONNAME = 2

#SOCK_STREAM = 1

;-- Structures

CompilerIf Not Defined(in_addr, #PB_Structure)
  Structure in_addr Align #PB_Structure_AlignC
    StructureUnion
      s_b.a[4]
      s_w.u[2]
      s_addr.l
    EndStructureUnion
  EndStructure
CompilerEndIf

CompilerIf Not Defined(in6_addr, #PB_Structure)
  Structure in6_addr Align #PB_Structure_AlignC
    s6_addr.a[16]
  EndStructure
CompilerEndIf

CompilerIf Not Defined(SOCKADDR, #PB_Structure)
  Structure SOCKADDR Align #PB_Structure_AlignC
    sa_family.u
    sa_data.a[14]
  EndStructure
CompilerEndIf

CompilerIf Not Defined(SOCKADDR_IN4, #PB_Structure)
  Structure SOCKADDR_IN4 Align #PB_Structure_AlignC
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      sin_len.a
      sin_family.a
    CompilerElse
      sin_family.u
    CompilerEndIf
    sin_port.u
    sin_addr.in_addr
    sin_zero.a[8]
  EndStructure
CompilerEndIf

CompilerIf Not Defined(SOCKADDR_IN6, #PB_Structure)
  Structure SOCKADDR_IN6 Align #PB_Structure_AlignC
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      sin6_len.a
      sin6_family.a
    CompilerElse
      sin6_family.u
    CompilerEndIf
    sin6_port.u
    sin6_flowinfo.l
    sin6_addr.in6_addr
    sin6_scope_id.l
  EndStructure
CompilerEndIf

CompilerIf #PB_Compiler_OS = #PB_OS_Linux
  
  Structure addrinfo Align #PB_Structure_AlignC
    ai_flags.l
    ai_family.l
    ai_socktype.l
    ai_protocol.l
    ai_addrlen.l
    StructureUnion
      *ai_addr.SOCKADDR
      *ai_addr_in.SOCKADDR_IN4
      *ai_addr_in6.SOCKADDR_IN6
    EndStructureUnion
    *ai_canonname
    *ai_next.addrinfo
  EndStructure
  
CompilerElse
  
  Structure addrinfo Align #PB_Structure_AlignC
    ai_flags.l
    ai_family.l
    ai_socktype.l
    ai_protocol.l
    ai_addrlen.l
    *ai_canonname
    StructureUnion
      *ai_addr.SOCKADDR
      *ai_addr_in.SOCKADDR_IN4
      *ai_addr_in6.SOCKADDR_IN6
    EndStructureUnion
    *ai_next.addrinfo
  EndStructure
  
CompilerEndIf

;-- Functions

Procedure.s GetAddrString(HostName.s, Family = #AF_UNSPEC, CanonName = #False)
  Protected r1.s
  Protected hints.addrinfo, *res.addrinfo, *result.addrinfo
  Protected errcode.l
  Protected *addrstr
  Protected *ptr4.in_addr, *ptr6.in6_addr
  Protected name.s
  
  
  hints\ai_family = Family
  hints\ai_socktype = #SOCK_STREAM
  If CanonName
    hints\ai_flags = #AI_CANONNAME
  Else
    hints\ai_flags = 0
  EndIf
  
  errcode = getaddrinfo(Hostname, #Empty$, hints, @*result)
  If errcode = 0
    *addrstr = AllocateMemory(258)
    *res = *result
    While *res
      Select (*res\ai_family)
        Case #AF_INET
          If *res\ai_addr
            *ptr4 = @*res\ai_addr_in\sin_addr
            If inet_ntop(*res\ai_family, *ptr4, *addrstr, 256)
              r1 + PeekS(*addrstr, -1, #PB_Ascii)
            EndIf
          EndIf
        Case #AF_INET6
          If *res\ai_addr
            *ptr6 = @*res\ai_addr_in6\sin6_addr
            If inet_ntop(*res\ai_family, *ptr6, *addrstr, 256)
              r1 + PeekS(*addrstr, -1, #PB_Ascii)
            EndIf
          EndIf
      EndSelect
      
      If CanonName And *res\ai_canonname
        name = PeekS(*res\ai_canonname, -1, #PB_Ascii)
      EndIf
      *res = *res\ai_next
      If *res
        r1 + ";"
      EndIf
    Wend
    
    If CanonName
      r1 + ";" + name
    EndIf
    FreeMemory(*addrstr)
    freeaddrinfo(*result)
  Else
    ; Error
  EndIf
  
  ProcedureReturn r1
EndProcedure

; ****

CompilerIf #PB_Compiler_IsMainFile
  
  ;- Example
  Define r1.s, host.s
  
  host = "localhost" ; local network
  r1.s = GetAddrString(host, #PF_INET6, #True)
  Debug host + "/ " + r1
  
  host = "raspberrypi" ; local network
  r1.s = GetAddrString(host, #PF_UNSPEC, #True)
  Debug host + "/ " + r1
  
  host = "michaels-mini-2" ; local network
  r1.s = GetAddrString(host, #PF_INET, #True)
  Debug host + "/ " + r1
  
  host = "www.purebasic.fr"
  r1.s = GetAddrString(host, #PF_UNSPEC, #True)
  Debug host + "/ " + r1
  
  host = "raspberry.com" ; internet
  r1.s = GetAddrString(host, #PF_UNSPEC, #True)
  Debug host + "/ " + r1
  
CompilerEndIf
Last edited by mk-soft on Sat Aug 12, 2023 11:35 am, edited 5 times in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
idle
Always Here
Always Here
Posts: 5901
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Network Get Address From Host (All OS)

Post by idle »

I tested it on windows 11, there's was a leak freeing resources but if an address is unresolvable or doesn't exist, the errors not clearing even calling wsacleanup_() Not sure what the issue is.

Code: Select all

Procedure.s GetAddrString(HostName.s, Family = #AF_UNSPEC, CanonName = #False)
  Protected r1.s, *host
  Protected hints.addrinfo, *res.addrinfo, *result.addrinfo
  Protected errcode.l
  Protected *addrstr
  Protected *ptr4.in_addr, *ptr6.in6_addr
  Protected name.s
  
  *host = Ascii(Hostname)
  *addrstr = AllocateMemory(258)
  
  hints\ai_family = Family
  hints\ai_socktype = #SOCK_STREAM
  If CanonName
    hints\ai_flags = #AI_CANONNAME
  Else
    hints\ai_flags = 0
  EndIf
  
  errcode = getaddrinfo(*host, #Empty$, hints, @*result)
  If errcode <> 0
     Goto ERROR: 
   EndIf
  
  *res = *result
  While *res
    Select (*res\ai_family)
      Case #AF_INET
        If *res\ai_addr
          *ptr4 = @*res\ai_addr_in\sin_addr
          If inet_ntop(*res\ai_family, *ptr4, *addrstr, 256)
            r1 + PeekS(*addrstr, -1, #PB_Ascii)
          EndIf
        EndIf
      Case #AF_INET6
        If *res\ai_addr
          *ptr6 = @*res\ai_addr_in6\sin6_addr
          If inet_ntop(*res\ai_family, *ptr6, *addrstr, 256)
            r1 + PeekS(*addrstr, -1, #PB_Ascii)
          EndIf
        EndIf
    EndSelect
    
    If CanonName And *res\ai_canonname
      name = PeekS(*res\ai_canonname, -1, #PB_Ascii)
    EndIf
    *res = *res\ai_next
    If *res
      r1 + ";"
    EndIf
  Wend
  
  If CanonName
    r1 + ";" + name
  EndIf
  
  freeaddrinfo(*result);
  Error: 
  FreeMemory(*addrstr)
  FreeMemory(*host)
  
  ProcedureReturn r1
EndProcedure
but if an address is unresolved the error isn't cleared and I don't know why.

Code: Select all

host = "raspberrypi"
  r1.s = GetAddrString(host, #AF_INET, #True)
  Debug host + ": " + r1
  
  host = "www.purebasic.fr"
  r1.s = GetAddrString(host, #AF_INET, #True)
  Debug host + ": " + r1

; second run 
 host = "raspberrypi.com"
  r1.s = GetAddrString(host, #AF_INET, #True)
  Debug host + ": " + r1
  
  host = "www.purebasic.fr"
  r1.s = GetAddrString(host, #AF_INET, #True)
  Debug host + ": " + r1

Result as

raspberrypi:
www.purebasic.fr:

;second run
raspberrypi.com: 93.93.130.37;raspberrypi.com
www.purebasic.fr: 163.172.93.88;www.purebasic.fr
User avatar
mk-soft
Always Here
Always Here
Posts: 6246
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Network Get Address From Host (All OS)

Post by mk-soft »

update v1.01.4
- Fixed memory-leak

I overlooked the memory leak. Thanks for reporting it.
If an error like the address could not be resolved, no result memory is created.

The query with "raspberrypi" without a result is correct for you. I have a Raspberry in my network for testing and therefore also the IPv6 address.

It also seems that the timeout time is very short.
raspberrypi/ 2003:e1:bf3f:5a00:XXXX:XXXX:XXXX:XXXX;192.168.XXX.XXX;raspberrypi.fritz.box
www.purebasic.fr/ 163.172.93.88;purebasic.fr
raspberry.com/ 178.62.51.113;raspberry.com
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
idle
Always Here
Always Here
Posts: 5901
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Network Get Address From Host (All OS)

Post by idle »

windows timeout is at least 3 seconds, it can be a very slow function.

I'm still not sure why the error isn't clearing, the call to "raspberrypi" doesn.t resolve to an address on my network which is correct and returns the error #WSAHOST_NOT_FOUND = 11001
but the next call to purebasic.fr also fails when it shouldn't so somethings not quite right.
I think it a windows bug.
User avatar
mk-soft
Always Here
Always Here
Posts: 6246
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Network Get Address From Host (All OS)

Post by mk-soft »

ah,

You also have a Raspberry PI on the net.
It works for me under Windows 10, macOS Ventura and Linux Mint.
My router is a Fritzbox 5590.

Try the programme with the Raspberry.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Network Get Address From Host (All OS)

Post by Kwai chang caine »

Hello TsSoft

Thanks for sharing 8)
I try your nice code, but i have an IMA in line 164 with W10 and v6 beta9

Code: Select all

  Select (*res\ai_family)
ImageThe happiness is a road...
Not a destination
User avatar
mk-soft
Always Here
Always Here
Posts: 6246
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Network Get Address From Host (All OS)

Post by mk-soft »

No Problem with PB v6.0 Release ... ( why beta 9)
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
mk-soft
Always Here
Always Here
Posts: 6246
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Network Get Address From Host (All OS)

Post by mk-soft »

Update v1.02.0
- Some fixed and optimization

Thanks to infratec

Testing with:
- Windows 10 PB x86 and x64
- Linux Debian 9 x86, Debian 11 x64
- macOS 13.5 x64, m1
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
infratec
Always Here
Always Here
Posts: 7619
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Network Get Address From Host (All OS)

Post by infratec »

Unfortunately it does not always return what I want (macOS)

If I try to get the IPs (v4 and v6) and LAN is connected to IPv4 and WLAN is connected to IPv4/v6 I get no IPv6 address.
If I disconnect LAN it works.
To specify AF_INET6 makes no difference.

nslookup works always at least if I use -type=AAAA

So it is not a problem of the DNS.
User avatar
mk-soft
Always Here
Always Here
Posts: 6246
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Network Get Address From Host (All OS)

Post by mk-soft »

I don't get an IP6 result even with only WLAN. Even if I switch off IPv4.
Even if I make the query from the Raspberry.
Is it possibly due to macOS?
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
infratec
Always Here
Always Here
Posts: 7619
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Network Get Address From Host (All OS)

Post by infratec »

If I'm only in an IPv4/v6 network I get for mit.edu one IPv4 address and 2 IPv6 addresses (macOS)
User avatar
mk-soft
Always Here
Always Here
Posts: 6246
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Network Get Address From Host (All OS)

Post by mk-soft »

infratec wrote: Sat Aug 12, 2023 12:19 pm If I'm only in an IPv4/v6 network I get for mit.edu one IPv4 address and 2 IPv6 addresses (macOS)
Same here ...
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply