Skip to content

Commit d989d3e

Browse files
authored
Merge pull request #23596 from JuliaLang/jn/21555
provide improved getaddrinfo, getnameinfo
2 parents 417331c + ebe4436 commit d989d3e

File tree

7 files changed

+244
-64
lines changed

7 files changed

+244
-64
lines changed

base/deprecated.jl

+9
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,15 @@ end
18151815
@deprecate get_creds!(cache::CachedCredentials, credid, default) get!(cache, credid, default)
18161816
end
18171817

1818+
@noinline function getaddrinfo(callback::Function, host::AbstractString)
1819+
depwarn("getaddrinfo with a callback function is deprecated, wrap code in @async instead for deferred execution", :getaddrinfo)
1820+
@async begin
1821+
r = getaddrinfo(host)
1822+
callback(r)
1823+
end
1824+
nothing
1825+
end
1826+
18181827
# END 0.7 deprecations
18191828

18201829
# BEGIN 1.0 deprecations

base/exports.jl

+2
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,8 @@ export
10191019
fdio,
10201020
flush,
10211021
getaddrinfo,
1022+
getalladdrinfo,
1023+
getnameinfo,
10221024
gethostname,
10231025
getipaddr,
10241026
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

+160-48
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ end
583583
##
584584

585585
struct DNSError <: Exception
586-
host::AbstractString
586+
host::String
587587
code::Int32
588588
end
589589

@@ -592,81 +592,193 @@ function show(io::IO, err::DNSError)
592592
" (", uverrorname(err.code), ")")
593593
end
594594

595-
callback_dict = ObjectIdDict()
596-
597595
function uv_getaddrinfocb(req::Ptr{Void}, status::Cint, addrinfo::Ptr{Void})
598-
data = ccall(:jl_uv_getaddrinfo_data, Ptr{Void}, (Ptr{Void},), req)
599-
data == C_NULL && return
600-
cb = unsafe_pointer_to_objref(data)::Function
601-
pop!(callback_dict,cb) # using pop forces an error if cb not in callback_dict
602-
if status != 0 || addrinfo == C_NULL
603-
invokelatest(cb, UVError("uv_getaddrinfocb received an unexpected status code", status))
604-
else
605-
freeaddrinfo = addrinfo
606-
while addrinfo != C_NULL
607-
sockaddr = ccall(:jl_sockaddr_from_addrinfo, Ptr{Void}, (Ptr{Void},), addrinfo)
608-
if ccall(:jl_sockaddr_is_ip4, Int32, (Ptr{Void},), sockaddr) == 1
609-
invokelatest(cb, IPv4(ntoh(ccall(:jl_sockaddr_host4, UInt32, (Ptr{Void},), sockaddr))))
610-
break
611-
#elseif ccall(:jl_sockaddr_is_ip6, Int32, (Ptr{Void},), sockaddr) == 1
612-
# host = Vector{UInt128}(1)
613-
# scope_id = ccall(:jl_sockaddr_host6, UInt32, (Ptr{Void}, Ptr{UInt128}), sockaddr, host)
614-
# invokelatest(cb, IPv6(ntoh(host[1])))
615-
# break
596+
data = uv_req_data(req)
597+
if data != C_NULL
598+
t = unsafe_pointer_to_objref(data)::Task
599+
uv_req_set_data(req, C_NULL)
600+
if status != 0 || addrinfo == C_NULL
601+
schedule(t, UVError("getaddrinfocb", status))
602+
else
603+
freeaddrinfo = addrinfo
604+
addrs = IPAddr[]
605+
while addrinfo != C_NULL
606+
sockaddr = ccall(:jl_sockaddr_from_addrinfo, Ptr{Void}, (Ptr{Void},), addrinfo)
607+
if ccall(:jl_sockaddr_is_ip4, Int32, (Ptr{Void},), sockaddr) == 1
608+
ip4addr = ccall(:jl_sockaddr_host4, UInt32, (Ptr{Void},), sockaddr)
609+
push!(addrs, IPv4(ntoh(ip4addr)))
610+
elseif ccall(:jl_sockaddr_is_ip6, Int32, (Ptr{Void},), sockaddr) == 1
611+
ip6addr = Ref{UInt128}()
612+
scope_id = ccall(:jl_sockaddr_host6, UInt32, (Ptr{Void}, Ptr{UInt128}), sockaddr, ip6addr)
613+
push!(addrs, IPv6(ntoh(ip6addr[])))
614+
end
615+
addrinfo = ccall(:jl_next_from_addrinfo, Ptr{Void}, (Ptr{Void},), addrinfo)
616616
end
617-
addrinfo = ccall(:jl_next_from_addrinfo, Ptr{Void}, (Ptr{Void},), addrinfo)
617+
ccall(:uv_freeaddrinfo, Void, (Ptr{Void},), freeaddrinfo)
618+
schedule(t, addrs)
618619
end
619-
ccall(:uv_freeaddrinfo, Void, (Ptr{Void},), freeaddrinfo)
620+
else
621+
# no owner for this req, safe to just free it
622+
Libc.free(req)
620623
end
621-
Libc.free(req)
622624
nothing
623625
end
624626

625-
function getaddrinfo(cb::Function, host::String)
627+
"""
628+
getalladdrinfo(host::AbstractString) -> Vector{IPAddr}
629+
630+
Gets all of the IP addresses of the `host`.
631+
Uses the operating system's underlying getaddrinfo implementation, which may do a DNS lookup.
632+
"""
633+
function getalladdrinfo(host::String)
626634
isascii(host) || error("non-ASCII hostname: $host")
627-
callback_dict[cb] = cb
628-
status = ccall(:jl_getaddrinfo, Int32, (Ptr{Void}, Cstring, Ptr{UInt8}, Any, Ptr{Void}),
629-
eventloop(), host, C_NULL, cb, uv_jl_getaddrinfocb::Ptr{Void})
630-
if status == UV_EINVAL
631-
throw(ArgumentError("Invalid uv_getaddrinfo() agument"))
632-
elseif status in [UV_ENOMEM, UV_ENOBUFS]
633-
throw(OutOfMemoryError())
634-
elseif status < 0
635-
throw(UVError("uv_getaddrinfo returned an unexpected error code", status))
635+
req = Libc.malloc(_sizeof_uv_getaddrinfo)
636+
uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call
637+
status = ccall(:jl_getaddrinfo, Int32, (Ptr{Void}, Ptr{Void}, Cstring, Ptr{Void}, Ptr{Void}),
638+
eventloop(), req, host, #=service=#C_NULL, uv_jl_getaddrinfocb::Ptr{Void})
639+
if status < 0
640+
Libc.free(req)
641+
if status == UV_EINVAL
642+
throw(ArgumentError("Invalid getaddrinfo argument"))
643+
elseif status == UV_ENOMEM || status == UV_ENOBUFS
644+
throw(OutOfMemoryError())
645+
end
646+
uv_error("getaddrinfo", status)
647+
end
648+
ct = current_task()
649+
preserve_handle(ct)
650+
r = try
651+
uv_req_set_data(req, ct)
652+
wait()
653+
finally
654+
if uv_req_data(req) != C_NULL
655+
# req is still alive,
656+
# so make sure we don't get spurious notifications later
657+
uv_req_set_data(req, C_NULL)
658+
ccall(:uv_cancel, Int32, (Ptr{Void},), req) # try to let libuv know we don't care anymore
659+
else
660+
# done with req
661+
Libc.free(req)
662+
end
663+
unpreserve_handle(ct)
636664
end
637-
return nothing
665+
if isa(r, UVError)
666+
code = r.code
667+
if code in (UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS,
668+
UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL,
669+
UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME,
670+
UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE,
671+
UV_EAI_SOCKTYPE)
672+
throw(DNSError(host, code))
673+
elseif code == UV_EAI_MEMORY
674+
throw(OutOfMemoryError())
675+
else
676+
throw(UVError("getaddrinfo", code))
677+
end
678+
end
679+
return r::Vector{IPAddr}
638680
end
639-
getaddrinfo(cb::Function, host::AbstractString) = getaddrinfo(cb, String(host))
681+
getalladdrinfo(host::AbstractString) = getalladdrinfo(String(host))
640682

641683
"""
642-
getaddrinfo(host::AbstractString) -> IPAddr
684+
getalladdrinfo(host::AbstractString, IPAddr=IPv4) -> IPAddr
643685
644-
Gets the IP address of the `host` (may have to do a DNS lookup)
686+
Gets the first IP address of the `host` of the specified IPAddr type.
687+
Uses the operating system's underlying getaddrinfo implementation, which may do a DNS lookup.
645688
"""
646-
function getaddrinfo(host::String)
647-
c = Condition()
648-
getaddrinfo(host) do IP
649-
notify(c,IP)
689+
function getaddrinfo(host::String, T::Type{<:IPAddr})
690+
addrs = getalladdrinfo(host)
691+
for addr in addrs
692+
if addr isa T
693+
return addr
694+
end
695+
end
696+
throw(DNSError(host, UV_EAI_NONAME))
697+
end
698+
getaddrinfo(host::AbstractString, T::Type{<:IPAddr}) = getaddrinfo(String(host), T)
699+
getaddrinfo(host::AbstractString) = getaddrinfo(String(host), IPv4)
700+
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)
650764
end
651-
r = wait(c)
652765
if isa(r, UVError)
653-
r = r::UVError
654766
code = r.code
655767
if code in (UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS,
656768
UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL,
657769
UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME,
658770
UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE,
659771
UV_EAI_SOCKTYPE)
660-
throw(DNSError(host, code))
772+
throw(DNSError(repr(address), code))
661773
elseif code == UV_EAI_MEMORY
662774
throw(OutOfMemoryError())
663775
else
664-
throw(SystemError("uv_getaddrinfocb", -code))
776+
throw(UVError("getnameinfo", code))
665777
end
666778
end
667-
return r::IPAddr
779+
return r::String
668780
end
669-
getaddrinfo(host::AbstractString) = getaddrinfo(String(host))
781+
670782

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

doc/src/stdlib/io-network.md

+2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ Base.connect(::AbstractString)
159159
Base.listen(::Any)
160160
Base.listen(::AbstractString)
161161
Base.getaddrinfo
162+
Base.getalladdrinfo
163+
Base.getnameinfo
162164
Base.getsockname
163165
Base.getpeername
164166
Base.IPv4

src/jl_uv.c

+31-9
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ JL_DLLEXPORT size_t jl_uv_buf_len(const uv_buf_t *buf) { return buf->len; }
118118
JL_DLLEXPORT void jl_uv_buf_set_base(uv_buf_t *buf, char *b) { buf->base = b; }
119119
JL_DLLEXPORT void jl_uv_buf_set_len(uv_buf_t *buf, size_t n) { buf->len = n; }
120120
JL_DLLEXPORT void *jl_uv_connect_handle(uv_connect_t *connect) { return connect->handle; }
121-
JL_DLLEXPORT void *jl_uv_getaddrinfo_data(uv_getaddrinfo_t *req) { return req->data; }
122121
JL_DLLEXPORT uv_file jl_uv_file_handle(jl_uv_file_t *f) { return f->file; }
123122
JL_DLLEXPORT void *jl_uv_req_data(uv_req_t *req) { return req->data; }
124123
JL_DLLEXPORT void jl_uv_req_set_data(uv_req_t *req, void *data) { req->data = data; }
@@ -712,23 +711,46 @@ JL_DLLEXPORT struct sockaddr_in *jl_uv_interface_address_sockaddr(uv_interface_a
712711
return &ifa->address.address4;
713712
}
714713

715-
JL_DLLEXPORT int jl_getaddrinfo(uv_loop_t *loop, const char *host,
716-
const char *service, jl_function_t *cb,
717-
uv_getaddrinfo_cb uvcb)
714+
JL_DLLEXPORT int jl_getaddrinfo(uv_loop_t *loop, uv_getaddrinfo_t *req,
715+
const char *host, const char *service, uv_getaddrinfo_cb uvcb)
718716
{
719-
uv_getaddrinfo_t *req = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t));
720717
struct addrinfo hints;
721-
722-
memset (&hints, 0, sizeof (hints));
718+
memset(&hints, 0, sizeof(hints));
723719
hints.ai_family = PF_UNSPEC;
724720
hints.ai_socktype = SOCK_STREAM;
725721
hints.ai_flags |= AI_CANONNAME;
726722

727-
req->data = cb;
723+
req->data = NULL;
724+
return uv_getaddrinfo(loop, req, uvcb, host, service, &hints);
725+
}
728726

729-
return uv_getaddrinfo(loop,req,uvcb,host,service,&hints);
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);
730738
}
731739

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+
732754
JL_DLLEXPORT struct sockaddr *jl_sockaddr_from_addrinfo(struct addrinfo *addrinfo)
733755
{
734756
return addrinfo->ai_addr;

0 commit comments

Comments
 (0)