Skip to content

Commit

Permalink
Added IPv6 functionality to getipaddr and add getipaddrs (#30609)
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanKarpinski authored Apr 3, 2019
2 parents 609aa7d + 4e9697e commit e8a88c9
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 33 deletions.
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ Command-line option changes
New library functions
---------------------

* `getipaddrs()` function returns all the IP addresses of the local machine ([#30349])
* `getipaddrs()` function returns all the IP addresses of the local machine, with IPv4 addresses sorting before IPv6 addresses ([#30349, #30604])
* `getipaddr(addr_type)` and `getipaddrs(addr_type)` functions returns an IP address(es) of the desired type of the local machine ([#30604])
* Added `Base.hasproperty` and `Base.hasfield` ([#28850]).
* One argument `!=(x)`, `>(x)`, `>=(x)`, `<(x)`, `<=(x)` has been added for currying,
similar to the existing `==(x)` and `isequal(x)` methods ([#30915]).
Expand Down
77 changes: 45 additions & 32 deletions stdlib/Sockets/src/addrinfo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -215,47 +215,41 @@ const _sizeof_uv_interface_address = ccall(:jl_uv_sizeof_interface_address,Int32
"""
getipaddr() -> IPAddr
Get the IP address of the local machine.
Get an IP address of the local machine, preferring IPv4 over IPv6. Throws if no
addresses are available.
getipaddr(addr_type::Type{T}) where T<:IPAddr -> T
Get an IP address of the local machine of the specified type. Throws if no
addresses of the specified type are available.
# Examples
```julia-repl
julia> getipaddr()
ip"192.168.1.28"
julia> getipaddr(IPv6)
ip"fe80::9731:35af:e1c5:6e49"
```
"""
function getipaddr()
addr_ref = Ref{Ptr{UInt8}}(C_NULL)
count_ref = Ref{Int32}(1)
lo_present = false
err = ccall(:jl_uv_interface_addresses, Int32, (Ref{Ptr{UInt8}}, Ref{Int32}), addr_ref, count_ref)
uv_error("getlocalip", err)
addr, count = addr_ref[], count_ref[]
for i = 0:(count-1)
current_addr = addr + i*_sizeof_uv_interface_address
if 1 == ccall(:jl_uv_interface_address_is_internal, Int32, (Ptr{UInt8},), current_addr)
lo_present = true
continue
end
sockaddr = ccall(:jl_uv_interface_address_sockaddr, Ptr{Cvoid}, (Ptr{UInt8},), current_addr)
if ccall(:jl_sockaddr_in_is_ip4, Int32, (Ptr{Cvoid},), sockaddr) == 1
rv = IPv4(ntoh(ccall(:jl_sockaddr_host4, UInt32, (Ptr{Cvoid},), sockaddr)))
ccall(:uv_free_interface_addresses, Cvoid, (Ptr{UInt8}, Int32), addr, count)
return rv
# Uncomment to enbable IPv6
#elseif ccall(:jl_sockaddr_in_is_ip6, Int32, (Ptr{Cvoid},), sockaddr) == 1
# host = Vector{UInt128}(undef, 1)
# ccall(:jl_sockaddr_host6, UInt32, (Ptr{Cvoid}, Ptr{UInt128}), sockaddrr, host)
# return IPv6(ntoh(host[1]))
end
function getipaddr(addr_type::Type{T}) where T<:IPAddr
addrs = getipaddrs(addr_type)
if length(addrs) == 0
error("No networking interface available")
end
ccall(:uv_free_interface_addresses, Cvoid, (Ptr{UInt8}, Int32), addr, count)
return lo_present ? localhost : error("No networking interface available")
return addrs[1]
end
getipaddr() = getipaddr(IPv4)


"""
getipaddrs(include_lo::Bool=false) -> Vector{IPv4}
getipaddrs(include_lo::Bool=false) -> Vector{IPAddr}
Get the IP addresses of the local machine.
Get the IPv4 addresses of the local machine.
getipaddrs(addr_type::Type{T}, include_lo::Bool=false) where T<:IPAddr -> Vector{T}
Get the IP addresses of the local machine of the specified type.
!!! compat "Julia 1.2"
This function is available as of Julia 1.2.
Expand All @@ -266,10 +260,15 @@ julia> getipaddrs()
2-element Array{IPv4,1}:
ip"10.255.0.183"
ip"172.17.0.1"
julia> getipaddrs(IPv6)
2-element Array{IPv6,1}:
ip"fe80::9731:35af:e1c5:6e49"
ip"fe80::445e:5fff:fe5d:5500"
```
"""
function getipaddrs(include_lo::Bool=false)
addresses = IPv4[]
function getipaddrs(addr_type::Type{T}, include_lo::Bool=false) where T<:IPAddr
addresses = T[]
addr_ref = Ref{Ptr{UInt8}}(C_NULL)
count_ref = Ref{Int32}(1)
lo_present = false
Expand All @@ -285,10 +284,24 @@ function getipaddrs(include_lo::Bool=false)
end
end
sockaddr = ccall(:jl_uv_interface_address_sockaddr, Ptr{Cvoid}, (Ptr{UInt8},), current_addr)
if ccall(:jl_sockaddr_in_is_ip4, Int32, (Ptr{Cvoid},), sockaddr) == 1
if IPv4 <: T && ccall(:jl_sockaddr_in_is_ip4, Int32, (Ptr{Cvoid},), sockaddr) == 1
push!(addresses, IPv4(ntoh(ccall(:jl_sockaddr_host4, UInt32, (Ptr{Cvoid},), sockaddr))))
elseif IPv6 <: T && ccall(:jl_sockaddr_in_is_ip6, Int32, (Ptr{Cvoid},), sockaddr) == 1
addr6 = Ref{UInt128}()
scope_id = ccall(:jl_sockaddr_host6, UInt32, (Ptr{Cvoid}, Ref{UInt128},), sockaddr, addr6)
push!(addresses, IPv6(ntoh(addr6[])))
end
end
ccall(:uv_free_interface_addresses, Cvoid, (Ptr{UInt8}, Int32), addr, count)
sort!(addresses, lt=(addr1,addr2) -> begin
if addr1 isa IPv4 && addr2 isa IPv6
return true
elseif addr1 isa IPv6 && addr2 isa IPv4
return false
else
return addr1 < addr2
end
end)
return addresses
end
getipaddrs(include_lo::Bool=false) = getipaddrs(IPAddr, include_lo)
8 changes: 8 additions & 0 deletions stdlib/Sockets/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,17 @@ end

@testset "getipaddrs" begin
@test getipaddr() in getipaddrs()
try
getipaddr(IPv6) in getipaddrs(IPv6)
catch
if !isempty(getipaddrs(IPv6))
@test "getipaddr(IPv6) errored when it shouldn't have!"
end
end

@testset "include lo" begin
@test issubset(getipaddrs(), getipaddrs(true))
@test issubset(getipaddrs(IPv6), getipaddrs(IPv6, true))
end
end

Expand Down

0 comments on commit e8a88c9

Please sign in to comment.