Index: src/settings.cr ================================================================== --- src/settings.cr +++ src/settings.cr @@ -1,47 +1,69 @@ require "time" module AsyncDNS class Settings @local_domain : String? + @reload_period : Time::Span @last_reload : Time? - getter :static_hosts, :name_servers, :local_domain, :search_domains, - :timeout, :max_attempts, :abs_num_dots, :uses_tcp - setter :static_hosts, :name_servers, :local_domain, :search_domains, - :uses_tcp + property static_hosts, nameservers, local_domain, search_domains, uses_tcp + getter timeout, max_attempts, abs_num_dots + + def timeout=(timeout) + raise ArgumentError.new("timeout must be positive") if timeout < 0 + + @timeout = timeout + end + + def max_attempts=(max_attempts) + if max_attempts < 0 + raise ArgumentError.new("max_attempts must be positive") + end + + @max_attempts = max_attempts + end + + def abs_num_dots=(abs_num_dots) + if abs_num_dots < 0 + raise ArgumentError.new("abs_num_dots must be positive") + end + + @abs_num_dots = abs_num_dots + end def initialize @static_hosts = Hash(String, Array(String)).new - @name_servers = [] of String + @nameservers = [] of String @search_domains = [] of String @timeout = Time::Span.new(seconds: 2) - @max_attempts = 2 + @max_attempts = 3 @abs_num_dots = 1 @uses_tcp = false + @reload_period = Time::Span.new(seconds: 2) self.reload end def reload {% if flag?(:unix) %} - self.parse_hosts "/etc/hosts" + self.parse_hosts "/etc/hosts" + self.parse_resolv_conf "/etc/resolv.conf" {% else %} {% raise "Your OS is not supported by AsyncDNS" %} {% end %} end def parse_hosts(path) - file = File.open(path, "r") - @static_hosts.clear - file.each_line do |line| + + File.each_line(path, chomp: true) do |line| pos = line.index('#') line = line[0, pos] if pos split = line.split(/[ \t]/, remove_empty: true) - next unless split.size >= 2 + next if split.size < 2 address = split[0] split[1, split.size - 1].each do |host| addresses = @static_hosts[host]? if addresses.nil? @@ -51,7 +73,49 @@ addresses << address end end end + + def parse_resolv_conf(path) + @nameservers.clear + @local_domain = nil + @search_domains.clear + + File.each_line(path, chomp: true) do |line| + pos = line.index(/[#;]/) + line = line[0, pos] if pos + + split = line.split(/[ \t]/, remove_empty: true) + next if split.size < 2 + + case split[0] + when "nameserver" + next if split.size != 2 + @nameservers << split[1] + when "domain" + next if split.size != 2 + @local_domain = split[1] + when "search" + @search_domains = split[1, split.size - 1] + when "options" + split[1, split.size - 1].each { |option| parse_resolv_option(option) } + end + end + end + + private def parse_resolv_option(option) + if option.starts_with?("ndots:") + @abs_num_dots = option[6, option.size - 6].to_i + elsif option.starts_with?("timeout:") + @timeout = Time::Span.new(seconds: option[8, option.size - 8].to_i) + elsif option.starts_with?("attempts:") + @max_attempts = option[9, option.size - 9].to_i + elsif option.starts_with?("reload-period:") + @reload_period = Time::Span.new( + seconds: option[14, option.size - 14].to_i) + elsif option = "tcp" + @uses_tcp = true + end + end end end Index: tests/example.cr ================================================================== --- tests/example.cr +++ tests/example.cr @@ -3,5 +3,12 @@ AsyncDNS::Query.new("crystal-lang.org", AsyncDNS::DNSClass::IN, AsyncDNS::RRType::A) settings = AsyncDNS::Settings.new p settings.static_hosts +p settings.nameservers +p settings.local_domain +p settings.search_domains +p settings.uses_tcp +p settings.timeout +p settings.max_attempts +p settings.abs_num_dots