Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getaddrinfo, getnameinfo #23596

Merged
merged 2 commits into from
Sep 19, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
implement getnameinfo
This function is the complement of getaddinfo.
  • Loading branch information
vtjnash committed Sep 18, 2017
commit ebe443685e8fde2d8c969842f7f7cf1955265c56
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,7 @@ export
flush,
getaddrinfo,
getalladdrinfo,
getnameinfo,
gethostname,
getipaddr,
getpeername,
Expand Down
1 change: 1 addition & 0 deletions base/libuv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function reinit_stdio()
global uv_jl_connectcb = cfunction(uv_connectcb, Void, Tuple{Ptr{Void}, Cint})
global uv_jl_writecb_task = cfunction(uv_writecb_task, Void, Tuple{Ptr{Void}, Cint})
global uv_jl_getaddrinfocb = cfunction(uv_getaddrinfocb, Void, Tuple{Ptr{Void}, Cint, Ptr{Void}})
global uv_jl_getnameinfocb = cfunction(uv_getnameinfocb, Void, Tuple{Ptr{Void}, Cint, Cstring, Cstring})
global uv_jl_recvcb = cfunction(uv_recvcb, Void, Tuple{Ptr{Void}, Cssize_t, Ptr{Void}, Ptr{Void}, Cuint})
global uv_jl_sendcb = cfunction(uv_sendcb, Void, Tuple{Ptr{Void}, Cint})
global uv_jl_return_spawn = cfunction(uv_return_spawn, Void, Tuple{Ptr{Void}, Int64, Int32})
Expand Down
82 changes: 82 additions & 0 deletions base/socket.jl
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,88 @@ end
getaddrinfo(host::AbstractString, T::Type{<:IPAddr}) = getaddrinfo(String(host), T)
getaddrinfo(host::AbstractString) = getaddrinfo(String(host), IPv4)

function uv_getnameinfocb(req::Ptr{Void}, status::Cint, hostname::Cstring, service::Cstring)
data = uv_req_data(req)
if data != C_NULL
t = unsafe_pointer_to_objref(data)::Task
uv_req_set_data(req, C_NULL)
if status != 0
schedule(t, UVError("getnameinfocb", status))
else
schedule(t, unsafe_string(hostname))
end
else
# no owner for this req, safe to just free it
Libc.free(req)
end
nothing
end

"""
getnameinfo(host::IPAddr) -> String

Performs a reverse-lookup for IP address to return a hostname and service
using the operating system's underlying getnameinfo implementation.
"""
function getnameinfo(address::Union{IPv4, IPv6})
req = Libc.malloc(_sizeof_uv_getnameinfo)
uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call
ev = eventloop()
port = hton(UInt16(0))
flags = 0
uvcb = uv_jl_getnameinfocb::Ptr{Void}
status = UV_EINVAL
if address isa IPv4
status = ccall(:jl_getnameinfo, Int32, (Ptr{Void}, Ptr{Void}, UInt32, UInt16, Cint, Ptr{Void}),
ev, req, hton(address.host), port, flags, uvcb)
elseif address isa IPv6
status = ccall(:jl_getnameinfo6, Int32, (Ptr{Void}, Ptr{Void}, Ref{UInt128}, UInt16, Cint, Ptr{Void}),
ev, req, hton(address.host), port, flags, uvcb)
end
if status < 0
Libc.free(req)
if status == UV_EINVAL
throw(ArgumentError("Invalid getnameinfo argument"))
elseif status == UV_ENOMEM || status == UV_ENOBUFS
throw(OutOfMemoryError())
end
uv_error("getnameinfo", status)
end
ct = current_task()
preserve_handle(ct)
r = try
uv_req_set_data(req, ct)
wait()
finally
if uv_req_data(req) != C_NULL
# req is still alive,
# so make sure we don't get spurious notifications later
uv_req_set_data(req, C_NULL)
ccall(:uv_cancel, Int32, (Ptr{Void},), req) # try to let libuv know we don't care anymore
else
# done with req
Libc.free(req)
end
unpreserve_handle(ct)
end
if isa(r, UVError)
code = r.code
if code in (UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS,
UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL,
UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME,
UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE,
UV_EAI_SOCKTYPE)
throw(DNSError(repr(address), code))
elseif code == UV_EAI_MEMORY
throw(OutOfMemoryError())
else
throw(UVError("getnameinfo", code))
end
end
return r::String
end


const _sizeof_uv_interface_address = ccall(:jl_uv_sizeof_interface_address,Int32,())

"""
Expand Down
1 change: 1 addition & 0 deletions doc/src/stdlib/io-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ Base.listen(::Any)
Base.listen(::AbstractString)
Base.getaddrinfo
Base.getalladdrinfo
Base.getnameinfo
Base.getsockname
Base.getpeername
Base.IPv4
Expand Down
27 changes: 27 additions & 0 deletions src/jl_uv.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,33 @@ JL_DLLEXPORT int jl_getaddrinfo(uv_loop_t *loop, uv_getaddrinfo_t *req,
return uv_getaddrinfo(loop, req, uvcb, host, service, &hints);
}

JL_DLLEXPORT int jl_getnameinfo(uv_loop_t *loop, uv_getnameinfo_t *req,
uint32_t host, uint16_t port, int flags, uv_getnameinfo_cb uvcb)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = host;
addr.sin_port = port;

req->data = NULL;
return uv_getnameinfo(loop, req, uvcb, (struct sockaddr*)&addr, flags);
}

JL_DLLEXPORT int jl_getnameinfo6(uv_loop_t *loop, uv_getnameinfo_t *req,
void *host, uint16_t port, int flags, uv_getnameinfo_cb uvcb)
{
struct sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, host, 16);
addr.sin6_port = port;

req->data = NULL;
return uv_getnameinfo(loop, req, uvcb, (struct sockaddr*)&addr, flags);
}


JL_DLLEXPORT struct sockaddr *jl_sockaddr_from_addrinfo(struct addrinfo *addrinfo)
{
return addrinfo->ai_addr;
Expand Down
49 changes: 39 additions & 10 deletions test/socket.jl
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,51 @@ mktempdir() do tmpdir
wait(tsk)
end

@test !isempty(getalladdrinfo("localhost")::Vector{IPAddr})
@test getaddrinfo("localhost", IPv4) === ip"127.0.0.1"
@test getaddrinfo("localhost", IPv6) === ip"::1"
# test some unroutable IP addresses (RFC 5737)
@test getnameinfo(ip"192.0.2.1") == "192.0.2.1"
@test getnameinfo(ip"198.51.100.1") == "198.51.100.1"
@test getnameinfo(ip"203.0.113.1") == "203.0.113.1"
@test getnameinfo(ip"0.1.1.1") == "0.1.1.1"
@test getnameinfo(ip"::ffff:0.1.1.1") == "::ffff:0.1.1.1"
@test getnameinfo(ip"::ffff:192.0.2.1") == "::ffff:192.0.2.1"
@test getnameinfo(ip"2001:db8::1") == "2001:db8::1"

# test some valid IP addresses
@test !isempty(getnameinfo(ip"::")::String)
@test !isempty(getnameinfo(ip"0.0.0.0")::String)
@test !isempty(getnameinfo(ip"10.1.0.0")::String)
@test !isempty(getnameinfo(ip"10.1.0.255")::String)
@test !isempty(getnameinfo(ip"10.1.255.1")::String)
@test !isempty(getnameinfo(ip"255.255.255.255")::String)
@test !isempty(getnameinfo(ip"255.255.255.0")::String)
@test !isempty(getnameinfo(ip"192.168.0.1")::String)
@test !isempty(getnameinfo(ip"::1")::String)

let localhost = getnameinfo(ip"127.0.0.1")::String
@test !isempty(localhost) && localhost != "127.0.0.1"
@test !isempty(getalladdrinfo(localhost)::Vector{IPAddr})
@test getaddrinfo(localhost, IPv4)::IPv4 != ip"0.0.0.0"
@test try
getaddrinfo(localhost, IPv6)::IPv6 != ip"::"
catch ex
isa(ex, Base.DNSError) && ex.code == Base.UV_EAI_NONAME && ex.host == localhost
end
end
@test_throws Base.DNSError getaddrinfo(".invalid")
@test_throws ArgumentError getaddrinfo("localhost\0") # issue #10994
@test_throws Base.UVError connect("localhost", 21452)
@test_throws Base.UVError("connect", Base.UV_ECONNREFUSED) connect(ip"127.0.0.1", 21452)

# test invalid port
@test_throws ArgumentError connect(ip"127.0.0.1",-1)
@test_throws ArgumentError connect(ip"127.0.0.1", -1)
@test_throws ArgumentError connect(ip"127.0.0.1", typemax(UInt16)+1)
@test_throws ArgumentError connect(ip"0:0:0:0:0:ffff:127.0.0.1", -1)
@test_throws ArgumentError connect(ip"0:0:0:0:0:ffff:127.0.0.1", typemax(UInt16)+1)

let
p, server = listenany(defaultport)
let (p, server) = listenany(defaultport)
r = Channel(1)
tsk = @async begin
put!(r, :start)
@test_throws Base.UVError accept(server)
@test_throws Base.UVError("accept", Base.UV_ECONNABORTED) accept(server)
end
@test fetch(r) === :start
close(server)
Expand All @@ -162,8 +188,8 @@ let
randport, server = listenany(defaultport)
@async connect("localhost", randport)
s1 = accept(server)
@test_throws ErrorException accept(server,s1)
@test_throws Base.UVError listen(randport)
@test_throws ErrorException("client TCPSocket is not in initialization state") accept(server, s1)
@test_throws Base.UVError("listen", Base.UV_EADDRINUSE) listen(randport)
port2, server2 = listenany(randport)
@test randport != port2
close(server)
Expand Down Expand Up @@ -207,6 +233,7 @@ let
close(a)
close(b)
end

if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
a = UDPSocket()
b = UDPSocket()
Expand All @@ -221,6 +248,8 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
end
send(b, ip"::1", randport, "Hello World")
wait(tsk)
send(b, ip"::1", randport, "Hello World")
wait(tsk)
end

for (addr, porthint) in [
Expand Down