Index: src/asyncdns.cr ================================================================== --- src/asyncdns.cr +++ src/asyncdns.cr @@ -1,7 +1,8 @@ require "./query" require "./resolver" # AsyncDNS is an asynchronous DNS resolver. +# It is a port of ObjFW's OFDNSResolver & friends. module AsyncDNS VERSION = "0.1.0" end Index: src/query.cr ================================================================== --- src/query.cr +++ src/query.cr @@ -1,12 +1,11 @@ require "./dns_class" require "./rr_type" module AsyncDNS class Query - getter :domain_name, :dns_class, :rr_type + getter :name, :class, :type - def initialize(@domain_name : String, @dns_class : DNSClass, - @rr_type : RRType) + def initialize(@name : String, @class : DNSClass, @type : RRType) end end end Index: src/resolver.cr ================================================================== --- src/resolver.cr +++ src/resolver.cr @@ -1,13 +1,50 @@ require "./rr" require "./settings" +require "./response" module AsyncDNS class Resolver getter :settings - setter :settings + + enum Error + NO_NAME_SERVER + end + + private struct Context + def initialize(@id : UInt16, @settings : Settings, + @block : Response | Error ->) + end + end def initialize @settings = Settings.new + @queries = Hash(UInt16, Context).new + @tcp_queries = Hash(Socket, Context).new + end + + def resolve(query, &block : Response | Error ->) + id : UInt16 + while true + id = Random::Secure.rand(UInt16::MIN..UInt16::MAX) + break unless @queries.has_key?(id) + end + + if @settings.nameservers.empty? + yield Error::NO_NAME_SERVER + return + end + + send(Context.new(id, settings.dup, block)) + end + + def send(context : Context) + @queries[context.@id] = context + + # TODO + end + + def stop + # TODO end end end ADDED src/response.cr Index: src/response.cr ================================================================== --- src/response.cr +++ src/response.cr @@ -0,0 +1,11 @@ +require "./rr" + +module AsyncDNS + class Response + property :name, :answer, :authority, :additional + + def initialize(@name : String, @answer : Array(RR), @authority : Array(RR), + @additional : Array(RR)) + end + end +end Index: src/settings.cr ================================================================== --- src/settings.cr +++ src/settings.cr @@ -1,14 +1,17 @@ module AsyncDNS class Settings @local_domain : String? - property static_hosts, nameservers, local_domain, search_domains, uses_tcp - getter timeout, max_attempts, abs_num_dots + property :static_hosts, :nameservers, :local_domain, :search_domains, + :uses_tcp, :reload_period + getter :timeout, :max_attempts, :abs_num_dots def timeout=(timeout) - raise ArgumentError.new("timeout must be positive") if timeout < 0 + if timeout < Time::Span.zero + raise ArgumentError.new("timeout must be positive") + end @timeout = timeout end def max_attempts=(max_attempts) Index: tests/example.cr ================================================================== --- tests/example.cr +++ tests/example.cr @@ -1,9 +1,10 @@ require "../src/asyncdns" -AsyncDNS::Query.new("crystal-lang.org", AsyncDNS::DNSClass::IN, +AsyncDNS::RR::A.new("crystal-lang.org", Socket::IPAddress.new("127.0.0.1", 0), + 1234) + +query = AsyncDNS::Query.new("crystal-lang.org", AsyncDNS::DNSClass::IN, AsyncDNS::RRType::A) resolver = AsyncDNS::Resolver.new - -AsyncDNS::RR::A.new("crystal-lang.org", Socket::IPAddress.new("127.0.0.1", 0), - 1234) +resolver.resolve(query) { |response| p response }