From 4e9697e55bf9e1c2b5be456cb19bbcf79d8064ec Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Sat, 30 Mar 2019 14:15:20 -0500 Subject: [PATCH] Added IPv6 functionality to getipaddr and add getipaddrs --- NEWS.md | 3 +- stdlib/Sockets/src/addrinfo.jl | 77 +++++++++++++++++++-------------- stdlib/Sockets/test/runtests.jl | 8 ++++ 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/NEWS.md b/NEWS.md index 0bff77b253102..3fb47f9296734 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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]). diff --git a/stdlib/Sockets/src/addrinfo.jl b/stdlib/Sockets/src/addrinfo.jl index fe306f23d6635..2685f9b9f96fb 100644 --- a/stdlib/Sockets/src/addrinfo.jl +++ b/stdlib/Sockets/src/addrinfo.jl @@ -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. @@ -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 @@ -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) diff --git a/stdlib/Sockets/test/runtests.jl b/stdlib/Sockets/test/runtests.jl index 5a795a29014f6..018b00b90f463 100644 --- a/stdlib/Sockets/test/runtests.jl +++ b/stdlib/Sockets/test/runtests.jl @@ -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