class UringMachine::DNSResolver
def do_resolve(hostname, type, try_count = 0)
def do_resolve(hostname, type, try_count = 0) fd = socket_fd req = prepare_request_packet(hostname, type) msg = req.encode @machine.send(fd, msg, msg.bytesize, 0) buf = +'' @machine.recv(fd, buf, 16384, 0) msg = Resolv::DNS::Message.decode buf addrs = [] msg.each_answer do |name, ttl, data| p [name, ttl, data] if data.kind_of?(Resolv::DNS::Resource::IN::A) || data.kind_of?(Resolv::DNS::Resource::IN::AAAA) addrs << data.address.to_s end end addrs end
def get_nameservers
def get_nameservers nameservers = [] IO.readlines('/etc/resolv.conf').each do |line| if line =~ /^nameserver (.+)$/ nameservers << $1.split(/\s+/).first end end nameservers end
def handle_requests_loop
def handle_requests_loop while true hostname, type, fiber = @machine.shift(@requests) res = do_resolve(hostname, type) @machine.schedule(fiber, res) end end
def initialize(machine)
def initialize(machine) @machine = machine @requests = UM::Queue.new @nameservers = get_nameservers @fiber = @machine.spin { handle_requests_loop } @last_id = 0 @cache = {} end
def msg_type(type)
def msg_type(type) # TODO: add support for other types Resolv::DNS::Resource::IN::A end
def prepare_request_packet(hostname, type)
def prepare_request_packet(hostname, type) msg = Resolv::DNS::Message.new msg.id = (@last_id += 1) msg.rd = 1 msg.add_question hostname, msg_type(type) msg end
def prepare_socket
def prepare_socket fd = @machine.socket(UM::AF_INET, UM::SOCK_DGRAM, 0, 0) @machine.bind(fd, '0.0.0.0', 0) @machine.connect(fd, @nameservers.sample, 53) fd end
def resolve(hostname, type)
def resolve(hostname, type) @machine.push(@requests, [hostname, type, Fiber.current]) @machine.yield end
def socket_fd
def socket_fd @socket_fd ||= prepare_socket end