Skip to content

Commit ebe4436

Browse files
committed
implement getnameinfo
This function is the complement of getaddinfo.
1 parent 6bdf1bb commit ebe4436

File tree

6 files changed

+151
-10
lines changed

6 files changed

+151
-10
lines changed

base/exports.jl

+1
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,7 @@ export
10201020
flush,
10211021
getaddrinfo,
10221022
getalladdrinfo,
1023+
getnameinfo,
10231024
gethostname,
10241025
getipaddr,
10251026
getpeername,

base/libuv.jl

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ function reinit_stdio()
9292
global uv_jl_connectcb = cfunction(uv_connectcb, Void, Tuple{Ptr{Void}, Cint})
9393
global uv_jl_writecb_task = cfunction(uv_writecb_task, Void, Tuple{Ptr{Void}, Cint})
9494
global uv_jl_getaddrinfocb = cfunction(uv_getaddrinfocb, Void, Tuple{Ptr{Void}, Cint, Ptr{Void}})
95+
global uv_jl_getnameinfocb = cfunction(uv_getnameinfocb, Void, Tuple{Ptr{Void}, Cint, Cstring, Cstring})
9596
global uv_jl_recvcb = cfunction(uv_recvcb, Void, Tuple{Ptr{Void}, Cssize_t, Ptr{Void}, Ptr{Void}, Cuint})
9697
global uv_jl_sendcb = cfunction(uv_sendcb, Void, Tuple{Ptr{Void}, Cint})
9798
global uv_jl_return_spawn = cfunction(uv_return_spawn, Void, Tuple{Ptr{Void}, Int64, Int32})

base/socket.jl

+82
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,88 @@ end
698698
getaddrinfo(host::AbstractString, T::Type{<:IPAddr}) = getaddrinfo(String(host), T)
699699
getaddrinfo(host::AbstractString) = getaddrinfo(String(host), IPv4)
700700

701+
function uv_getnameinfocb(req::Ptr{Void}, status::Cint, hostname::Cstring, service::Cstring)
702+
data = uv_req_data(req)
703+
if data != C_NULL
704+
t = unsafe_pointer_to_objref(data)::Task
705+
uv_req_set_data(req, C_NULL)
706+
if status != 0
707+
schedule(t, UVError("getnameinfocb", status))
708+
else
709+
schedule(t, unsafe_string(hostname))
710+
end
711+
else
712+
# no owner for this req, safe to just free it
713+
Libc.free(req)
714+
end
715+
nothing
716+
end
717+
718+
"""
719+
getnameinfo(host::IPAddr) -> String
720+
721+
Performs a reverse-lookup for IP address to return a hostname and service
722+
using the operating system's underlying getnameinfo implementation.
723+
"""
724+
function getnameinfo(address::Union{IPv4, IPv6})
725+
req = Libc.malloc(_sizeof_uv_getnameinfo)
726+
uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call
727+
ev = eventloop()
728+
port = hton(UInt16(0))
729+
flags = 0
730+
uvcb = uv_jl_getnameinfocb::Ptr{Void}
731+
status = UV_EINVAL
732+
if address isa IPv4
733+
status = ccall(:jl_getnameinfo, Int32, (Ptr{Void}, Ptr{Void}, UInt32, UInt16, Cint, Ptr{Void}),
734+
ev, req, hton(address.host), port, flags, uvcb)
735+
elseif address isa IPv6
736+
status = ccall(:jl_getnameinfo6, Int32, (Ptr{Void}, Ptr{Void}, Ref{UInt128}, UInt16, Cint, Ptr{Void}),
737+
ev, req, hton(address.host), port, flags, uvcb)
738+
end
739+
if status < 0
740+
Libc.free(req)
741+
if status == UV_EINVAL
742+
throw(ArgumentError("Invalid getnameinfo argument"))
743+
elseif status == UV_ENOMEM || status == UV_ENOBUFS
744+
throw(OutOfMemoryError())
745+
end
746+
uv_error("getnameinfo", status)
747+
end
748+
ct = current_task()
749+
preserve_handle(ct)
750+
r = try
751+
uv_req_set_data(req, ct)
752+
wait()
753+
finally
754+
if uv_req_data(req) != C_NULL
755+
# req is still alive,
756+
# so make sure we don't get spurious notifications later
757+
uv_req_set_data(req, C_NULL)
758+
ccall(:uv_cancel, Int32, (Ptr{Void},), req) # try to let libuv know we don't care anymore
759+
else
760+
# done with req
761+
Libc.free(req)
762+
end
763+
unpreserve_handle(ct)
764+
end
765+
if isa(r, UVError)
766+
code = r.code
767+
if code in (UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS,
768+
UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL,
769+
UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME,
770+
UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE,
771+
UV_EAI_SOCKTYPE)
772+
throw(DNSError(repr(address), code))
773+
elseif code == UV_EAI_MEMORY
774+
throw(OutOfMemoryError())
775+
else
776+
throw(UVError("getnameinfo", code))
777+
end
778+
end
779+
return r::String
780+
end
781+
782+
701783
const _sizeof_uv_interface_address = ccall(:jl_uv_sizeof_interface_address,Int32,())
702784

703785
"""

doc/src/stdlib/io-network.md

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ Base.listen(::Any)
160160
Base.listen(::AbstractString)
161161
Base.getaddrinfo
162162
Base.getalladdrinfo
163+
Base.getnameinfo
163164
Base.getsockname
164165
Base.getpeername
165166
Base.IPv4

src/jl_uv.c

+27
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,33 @@ JL_DLLEXPORT int jl_getaddrinfo(uv_loop_t *loop, uv_getaddrinfo_t *req,
724724
return uv_getaddrinfo(loop, req, uvcb, host, service, &hints);
725725
}
726726

727+
JL_DLLEXPORT int jl_getnameinfo(uv_loop_t *loop, uv_getnameinfo_t *req,
728+
uint32_t host, uint16_t port, int flags, uv_getnameinfo_cb uvcb)
729+
{
730+
struct sockaddr_in addr;
731+
memset(&addr, 0, sizeof(addr));
732+
addr.sin_family = AF_INET;
733+
addr.sin_addr.s_addr = host;
734+
addr.sin_port = port;
735+
736+
req->data = NULL;
737+
return uv_getnameinfo(loop, req, uvcb, (struct sockaddr*)&addr, flags);
738+
}
739+
740+
JL_DLLEXPORT int jl_getnameinfo6(uv_loop_t *loop, uv_getnameinfo_t *req,
741+
void *host, uint16_t port, int flags, uv_getnameinfo_cb uvcb)
742+
{
743+
struct sockaddr_in6 addr;
744+
memset(&addr, 0, sizeof(addr));
745+
addr.sin6_family = AF_INET6;
746+
memcpy(&addr.sin6_addr, host, 16);
747+
addr.sin6_port = port;
748+
749+
req->data = NULL;
750+
return uv_getnameinfo(loop, req, uvcb, (struct sockaddr*)&addr, flags);
751+
}
752+
753+
727754
JL_DLLEXPORT struct sockaddr *jl_sockaddr_from_addrinfo(struct addrinfo *addrinfo)
728755
{
729756
return addrinfo->ai_addr;

test/socket.jl

+39-10
Original file line numberDiff line numberDiff line change
@@ -132,25 +132,51 @@ mktempdir() do tmpdir
132132
wait(tsk)
133133
end
134134

135-
@test !isempty(getalladdrinfo("localhost")::Vector{IPAddr})
136-
@test getaddrinfo("localhost", IPv4) === ip"127.0.0.1"
137-
@test getaddrinfo("localhost", IPv6) === ip"::1"
135+
# test some unroutable IP addresses (RFC 5737)
136+
@test getnameinfo(ip"192.0.2.1") == "192.0.2.1"
137+
@test getnameinfo(ip"198.51.100.1") == "198.51.100.1"
138+
@test getnameinfo(ip"203.0.113.1") == "203.0.113.1"
139+
@test getnameinfo(ip"0.1.1.1") == "0.1.1.1"
140+
@test getnameinfo(ip"::ffff:0.1.1.1") == "::ffff:0.1.1.1"
141+
@test getnameinfo(ip"::ffff:192.0.2.1") == "::ffff:192.0.2.1"
142+
@test getnameinfo(ip"2001:db8::1") == "2001:db8::1"
143+
144+
# test some valid IP addresses
145+
@test !isempty(getnameinfo(ip"::")::String)
146+
@test !isempty(getnameinfo(ip"0.0.0.0")::String)
147+
@test !isempty(getnameinfo(ip"10.1.0.0")::String)
148+
@test !isempty(getnameinfo(ip"10.1.0.255")::String)
149+
@test !isempty(getnameinfo(ip"10.1.255.1")::String)
150+
@test !isempty(getnameinfo(ip"255.255.255.255")::String)
151+
@test !isempty(getnameinfo(ip"255.255.255.0")::String)
152+
@test !isempty(getnameinfo(ip"192.168.0.1")::String)
153+
@test !isempty(getnameinfo(ip"::1")::String)
154+
155+
let localhost = getnameinfo(ip"127.0.0.1")::String
156+
@test !isempty(localhost) && localhost != "127.0.0.1"
157+
@test !isempty(getalladdrinfo(localhost)::Vector{IPAddr})
158+
@test getaddrinfo(localhost, IPv4)::IPv4 != ip"0.0.0.0"
159+
@test try
160+
getaddrinfo(localhost, IPv6)::IPv6 != ip"::"
161+
catch ex
162+
isa(ex, Base.DNSError) && ex.code == Base.UV_EAI_NONAME && ex.host == localhost
163+
end
164+
end
138165
@test_throws Base.DNSError getaddrinfo(".invalid")
139166
@test_throws ArgumentError getaddrinfo("localhost\0") # issue #10994
140-
@test_throws Base.UVError connect("localhost", 21452)
167+
@test_throws Base.UVError("connect", Base.UV_ECONNREFUSED) connect(ip"127.0.0.1", 21452)
141168

142169
# test invalid port
143-
@test_throws ArgumentError connect(ip"127.0.0.1",-1)
170+
@test_throws ArgumentError connect(ip"127.0.0.1", -1)
144171
@test_throws ArgumentError connect(ip"127.0.0.1", typemax(UInt16)+1)
145172
@test_throws ArgumentError connect(ip"0:0:0:0:0:ffff:127.0.0.1", -1)
146173
@test_throws ArgumentError connect(ip"0:0:0:0:0:ffff:127.0.0.1", typemax(UInt16)+1)
147174

148-
let
149-
p, server = listenany(defaultport)
175+
let (p, server) = listenany(defaultport)
150176
r = Channel(1)
151177
tsk = @async begin
152178
put!(r, :start)
153-
@test_throws Base.UVError accept(server)
179+
@test_throws Base.UVError("accept", Base.UV_ECONNABORTED) accept(server)
154180
end
155181
@test fetch(r) === :start
156182
close(server)
@@ -162,8 +188,8 @@ let
162188
randport, server = listenany(defaultport)
163189
@async connect("localhost", randport)
164190
s1 = accept(server)
165-
@test_throws ErrorException accept(server,s1)
166-
@test_throws Base.UVError listen(randport)
191+
@test_throws ErrorException("client TCPSocket is not in initialization state") accept(server, s1)
192+
@test_throws Base.UVError("listen", Base.UV_EADDRINUSE) listen(randport)
167193
port2, server2 = listenany(randport)
168194
@test randport != port2
169195
close(server)
@@ -207,6 +233,7 @@ let
207233
close(a)
208234
close(b)
209235
end
236+
210237
if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
211238
a = UDPSocket()
212239
b = UDPSocket()
@@ -221,6 +248,8 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
221248
end
222249
send(b, ip"::1", randport, "Hello World")
223250
wait(tsk)
251+
send(b, ip"::1", randport, "Hello World")
252+
wait(tsk)
224253
end
225254

226255
for (addr, porthint) in [

0 commit comments

Comments
 (0)