diff options
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb')
| -rw-r--r-- | vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb new file mode 100644 index 0000000..1d2d7aa --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb @@ -0,0 +1,232 @@ +module EventMachine + module DNS + class Resolver + + def self.windows? + if RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/ + require 'win32/resolv' + true + else + false + end + end + + HOSTS_FILE = windows? ? Win32::Resolv.get_hosts_path : '/etc/hosts' + + @hosts = nil + @nameservers = nil + @socket = nil + + def self.resolve(hostname) + Request.new(socket, hostname) + end + + def self.socket + if @socket && @socket.error? + @socket = Socket.open + else + @socket ||= Socket.open + end + end + + def self.nameservers=(ns) + @nameservers = ns + end + + def self.nameservers + return @nameservers if @nameservers + + if windows? + _, ns = Win32::Resolv.get_resolv_info + return @nameservers = ns || [] + end + + @nameservers = [] + IO.readlines('/etc/resolv.conf').each do |line| + if line =~ /^nameserver (.+)$/ + @nameservers << $1.split(/\s+/).first + end + end + + @nameservers + rescue + @nameservers = [] + end + + def self.nameserver + nameservers.shuffle.first + end + + def self.hosts + return @hosts if @hosts + + @hosts = {} + IO.readlines(HOSTS_FILE).each do |line| + next if line =~ /^#/ + addr, host = line.split(/\s+/) + + next unless addr && host + @hosts[host] ||= [] + @hosts[host] << addr + end + + @hosts + rescue + @hosts = {} + end + end + + class RequestIdAlreadyUsed < RuntimeError; end + + class Socket < EventMachine::Connection + def self.open + EventMachine::open_datagram_socket('0.0.0.0', 0, self) + end + + def initialize + @nameserver = nil + end + + def post_init + @requests = {} + end + + def start_timer + @timer ||= EM.add_periodic_timer(0.1, &method(:tick)) + end + + def stop_timer + EM.cancel_timer(@timer) + @timer = nil + end + + def unbind + end + + def tick + @requests.each do |id,req| + req.tick + end + end + + def register_request(id, req) + if @requests.has_key?(id) + raise RequestIdAlreadyUsed + else + @requests[id] = req + end + + start_timer + end + + def deregister_request(id, req) + @requests.delete(id) + stop_timer if @requests.length == 0 + end + + def send_packet(pkt) + send_datagram(pkt, nameserver, 53) + end + + def nameserver=(ns) + @nameserver = ns + end + + def nameserver + @nameserver || Resolver.nameserver + end + + # Decodes the packet, looks for the request and passes the + # response over to the requester + def receive_data(data) + msg = nil + begin + msg = Resolv::DNS::Message.decode data + rescue + else + req = @requests[msg.id] + if req + @requests.delete(msg.id) + stop_timer if @requests.length == 0 + req.receive_answer(msg) + end + end + end + end + + class Request + include Deferrable + attr_accessor :retry_interval, :max_tries + + def initialize(socket, hostname) + @socket = socket + @hostname = hostname + @tries = 0 + @last_send = Time.at(0) + @retry_interval = 3 + @max_tries = 5 + + if addrs = Resolver.hosts[hostname] + succeed addrs + else + EM.next_tick { tick } + end + end + + def tick + # Break early if nothing to do + return if @last_send + @retry_interval > Time.now + if @tries < @max_tries + send + else + @socket.deregister_request(@id, self) + fail 'retries exceeded' + end + end + + def receive_answer(msg) + addrs = [] + msg.each_answer do |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 + + if addrs.empty? + fail "rcode=#{msg.rcode}" + else + succeed addrs + end + end + + private + + def send + @tries += 1 + @last_send = Time.now + @socket.send_packet(packet.encode) + end + + def id + begin + @id = rand(65535) + @socket.register_request(@id, self) + rescue RequestIdAlreadyUsed + retry + end unless defined?(@id) + + @id + end + + def packet + msg = Resolv::DNS::Message.new + msg.id = id + msg.rd = 1 + msg.add_question @hostname, Resolv::DNS::Resource::IN::A + msg + end + + end + end +end |
