Skip to content

Instantly share code, notes, and snippets.

@rdp
Created November 20, 2019 21:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rdp/c776c1ea95dfd666d1d828eba4caabc3 to your computer and use it in GitHub Desktop.
Save rdp/c776c1ea95dfd666d1d828eba4caabc3 to your computer and use it in GitHub Desktop.
require "socket" # works with 0.31.1 FWIW
struct Socket::Addrinfo
class DnsRequestCbArg
getter value : Int32 | Pointer(LibC::Addrinfo) | Nil
@fiber : Fiber
def initialize
@fiber = Fiber.current
end
def value=(val)
@value = val
end
end
def self.getaddrinfo(host, port, family, socktype, protocol = Protocol::IP, timeout = nil)
hints = LibC::Addrinfo.new
hints.ai_family = (family || Family::UNSPEC).to_i32
hints.ai_socktype = socktype
hints.ai_protocol = protocol
hints.ai_flags = 0
dns_req = DnsRequestCbArg.new
# may fire immediately or on the next event loop
req = create_dns_request(host, port.to_s, pointerof(hints), dns_req) do |err, addr, data|
dreq = data.as(DnsRequestCbArg)
if err == 0
dreq.value = addr
else
dreq.value = err
end
end
if timeout && req
spawn do
sleep timeout.not_nil!
req.not_nil!.cancel unless dns_req.value
end
end
success = false
value = dns_req.value
# BUG: not thread safe. change when threads are implemented
unless value
Fiber.yield
value = dns_req.value
end
if value.is_a?(LibC::Addrinfo*)
begin
cur_addr = value
while cur_addr
success = yield new(cur_addr)
break if success
cur_addr = cur_addr.value.ai_next
end
ensure
LibEvent2.evutil_freeaddrinfo value
end
elsif value.is_a?(Int)
if value == LibEvent2::EVUTIL_EAI_CANCEL
raise IO::Timeout.new("Failed to resolve #{host} in #{timeout} seconds")
end
error_message = String.new(LibC.gai_strerror(value))
raise Socket::Error.new("getaddrinfo: #{error_message}")
else
raise "unknown type #{value.inspect}"
end
# shouldn't raise
raise Socket::Error.new("getaddrinfo: unspecified error") unless success
end
def self.create_dns_request(nodename, servname, hints, data, &callback : LibEvent2::DnsGetAddrinfoCallback)
Thread.current.event_base.new_dns_base.getaddrinfo(nodename, servname, hints, data, &callback)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment