Skip to content

Commit

Permalink
implement getnameinfo
Browse files Browse the repository at this point in the history
This function is the complement of getaddinfo.
  • Loading branch information
vtjnash committed Sep 18, 2017
1 parent 6bdf1bb commit ebe4436
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 10 deletions.
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

0 comments on commit ebe4436

Please sign in to comment.