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 6, 2017
1 parent e73c3d1 commit 1915931
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 59 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 @@ -161,6 +161,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
138 changes: 79 additions & 59 deletions test/socket.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,42 +109,61 @@ 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 invalid IP addresses
@test getnameinfo(ip"10.1.0.0") == "10.1.0.0"
@test getnameinfo(ip"10.1.0.255") == "10.1.0.255"
@test getnameinfo(ip"10.1.255.1") == "10.1.255.1"
@test getnameinfo(ip"0.1.1.1") == "0.1.1.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 some valid IP addresses
@test !isempty(getnameinfo(ip"::")::String)
@test !isempty(getnameinfo(ip"0.0.0.0")::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 getaddrinfo(localhost, IPv6)::IPv6 != ip"::"
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)

p, server = listenany(defaultport)
r = Channel(1)
tsk = @async begin
put!(r, :start)
@test_throws Base.UVError accept(server)
let (p, server) = listenany(defaultport)
r = Channel(1)
tsk = @async begin
put!(r, :start)
@test_throws Base.UVError("accept", Base.UV_ECONNABORTED) accept(server)
end
@test fetch(r) === :start
close(server)
wait(tsk)
end

let (port, server) = listenany(defaultport)
@async connect("localhost", port)
s1 = accept(server)
@test_throws ErrorException accept(server, s1)
@test_throws Base.UVError listen(port)
port2, server2 = listenany(port)
@test port != port2
close(server)
close(server2)
end
@test fetch(r) === :start
close(server)
wait(tsk)

port, server = listenany(defaultport)
@async connect("localhost",port)
s1 = accept(server)
@test_throws ErrorException accept(server,s1)
@test_throws Base.UVError listen(port)
port2, server2 = listenany(port)
@test port != port2
close(server)
close(server2)

@test_throws Base.DNSError connect(".invalid",80)

begin
let port = defaultport
a = UDPSocket()
b = UDPSocket()
bind(a, ip"127.0.0.1", port)
Expand Down Expand Up @@ -174,57 +193,58 @@ begin
send(b, ip"127.0.0.1", port, "Hello World")
wait(tsk)

@test_throws MethodError bind(UDPSocket(),port)
@test_throws MethodError bind(UDPSocket(), port)

close(a)
close(b)
end
if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
a = UDPSocket()
b = UDPSocket()
bind(a, ip"::1", UInt16(port))
bind(b, ip"::1", UInt16(port+1))

tsk = @async begin
@test begin
(addr, data) = recvfrom(a)
addr == ip"::1" && String(data) == "Hello World"
if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
let port = defaultport
a = UDPSocket()
b = UDPSocket()
bind(a, ip"::1", UInt16(port))
bind(b, ip"::1", UInt16(port + 1))

tsk = @async begin
@test begin
(addr, data) = recvfrom(a)
addr == ip"::1" && String(data) == "Hello World"
end
end
send(b, ip"::1", port, "Hello World")
wait(tsk)
end
send(b, ip"::1", port, "Hello World")
wait(tsk)
end

begin
for (addr, porthint) in [(IPv4("127.0.0.1"), UInt16(11011)),
(IPv6("::1"), UInt16(11012)), (getipaddr(), UInt16(11013))]
port, listen_sock = listenany(addr, porthint)
gsn_addr, gsn_port = getsockname(listen_sock)
for (addr, porthint) in [(IPv4("127.0.0.1"), UInt16(11011)),
(IPv6("::1"), UInt16(11012)), (getipaddr(), UInt16(11013))]
port, listen_sock = listenany(addr, porthint)
gsn_addr, gsn_port = getsockname(listen_sock)

@test addr == gsn_addr
@test port == gsn_port
@test addr == gsn_addr
@test port == gsn_port

@test_throws MethodError getpeername(listen_sock)
@test_throws MethodError getpeername(listen_sock)

# connect to it
client_sock = connect(addr, port)
server_sock = accept(listen_sock)
# connect to it
client_sock = connect(addr, port)
server_sock = accept(listen_sock)

self_client_addr, self_client_port = getsockname(client_sock)
peer_client_addr, peer_client_port = getpeername(client_sock)
self_srvr_addr, self_srvr_port = getsockname(server_sock)
peer_srvr_addr, peer_srvr_port = getpeername(server_sock)
self_client_addr, self_client_port = getsockname(client_sock)
peer_client_addr, peer_client_port = getpeername(client_sock)
self_srvr_addr, self_srvr_port = getsockname(server_sock)
peer_srvr_addr, peer_srvr_port = getpeername(server_sock)

@test self_client_addr == peer_client_addr == self_srvr_addr == peer_srvr_addr
@test self_client_addr == peer_client_addr == self_srvr_addr == peer_srvr_addr

@test peer_client_port == self_srvr_port
@test peer_srvr_port == self_client_port
@test self_srvr_port != self_client_port
@test peer_client_port == self_srvr_port
@test peer_srvr_port == self_client_port
@test self_srvr_port != self_client_port

close(listen_sock)
close(client_sock)
close(server_sock)
end
close(listen_sock)
close(client_sock)
close(server_sock)
end

# Local-machine broadcast
Expand Down

0 comments on commit 1915931

Please sign in to comment.