Class: Dnsruby::Resolver
- Inherits:
-
Object
- Object
- Dnsruby::Resolver
- Defined in:
- lib/dnsruby/resolver.rb
Overview
Description
Dnsruby::Resolver is a DNS stub resolver.
This class performs queries with retries across multiple nameservers.
The system configured resolvers are used by default.
The retry policy is a combination of the Net::DNS and dnsjava approach, and has the option of :
* A total timeout for the query (defaults to 0, meaning "no total timeout")
* A retransmission system that targets the namervers concurrently once the first query round is
complete, but in which the total time per query round is split between the number of nameservers
targetted for the first round. and total time for query round is doubled for each query round
Note that, if a total timeout is specified, then that will apply regardless of the retry policy
(i.e. it may cut retries short).
Note also that these timeouts are distinct from the SingleResolver's packet_timeout
Timeouts apply to the initial query and response. If DNSSEC validation is to
be performed, then additional queries may be required (these are performed automatically
by Dnsruby). Each additional query will be performed with its own timeouts.
So, even with a query_timeout of 5 seconds, a response which required extensive
validation may take several times that long.
(Future versions of Dnsruby may expose finer-grained events for client tracking of
responses and validation)
== Methods
=== Synchronous
These methods raise an exception or return a response message with rcode==NOERROR
* Dnsruby::Resolver#send_message(msg)
* Dnsruby::Resolver#query(name [, type [, klass]])
There are "!" versions of these two methods that return an array [response, error]
instead of raising an error on failure. They can be called as follows:
response, error = resolver.send_message!(...)
response, error = resolver.query!(...)
If the request succeeds, response will contain the Dnsruby::Message response
and error will be nil.
If the request fails, response will be nil and error will contain the error raised.
=== Asynchronous
These methods use a response queue to return the response and the error
* Dnsruby::Resolver#send_async(msg, response_queue, query_id)
== Event Loop
Dnsruby runs a pure Ruby event loop to handle I/O in a single thread.
Support for EventMachine has been deprecated.
Direct Known Subclasses
Defined Under Namespace
Classes: EventType
Constant Summary collapse
- DefaultQueryTimeout =
0
- DefaultPacketTimeout =
5
- DefaultRetryTimes =
1
- DefaultRetryDelay =
5
- DefaultPipeLiningMaxQueries =
5
- DefaultPort =
53
- DefaultDnssec =
false
- AbsoluteMinDnssecUdpSize =
1220
- MinDnssecUdpSize =
4096
- DefaultUDPSize =
MinDnssecUdpSize
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
The current Config.
-
#dnssec ⇒ Object
Use DNSSEC for this Resolver.
-
#do_caching ⇒ Object
Defines whether we will cache responses, or pass every request to the upstream resolver.
-
#do_validation ⇒ Object
Defines whether validation is performed by default on this Resolver when the query method is called.
-
#ignore_truncation ⇒ Object
Should truncation be ignored? i.e.
-
#no_tcp ⇒ Object
If no_tcp==true, then ONLY UDP will be used as a transport.
-
#packet_timeout ⇒ Object
The timeout for any individual packet.
-
#port ⇒ Object
The port to send queries to on the resolver.
-
#query_timeout ⇒ Object
Note that this timeout represents the total time a query may run for - multiple packets can be sent to multiple nameservers in this time.
-
#recurse ⇒ Object
Should the Recursion Desired bit be set?.
-
#retry_delay ⇒ Object
The query will be tried across nameservers retry_times times, with a delay of retry_delay seconds between each retry.
-
#retry_times ⇒ Object
The query will be tried across nameservers retry_times times, with a delay of retry_delay seconds between each retry.
-
#src_address ⇒ Object
The source address to send queries from for IPv4.
-
#src_address6 ⇒ Object
The source address to send queries from for IPv6.
-
#tcp_pipelining ⇒ Object
If tcp_pipelining==true, then we reuse the TCP connection.
-
#tcp_pipelining_max_queries ⇒ Object
How many times (number of messages) to reuse the pipelining connection before closing, :infinite for infinite number of requests per connection.
-
#tsig ⇒ Object
Returns the value of attribute tsig.
-
#udp_size ⇒ Object
The maximum UDP size to be used.
-
#use_tcp ⇒ Object
Should TCP be used as a transport rather than UDP? If use_tcp==true, then ONLY TCP will be used as a transport.
Class Method Summary collapse
- .check_port(p, src_port = []) ⇒ Object
- .get_ports_from(p) ⇒ Object
- .get_tsig(args) ⇒ Object
- .port_in_range(p) ⇒ Object
Instance Method Summary collapse
-
#add_config_nameservers ⇒ Object
:nodoc: all.
-
#add_server(server) ⇒ Object
# Add a new SingleResolver to the list of resolvers this Resolver object will # query.
-
#add_src_port(p) ⇒ Object
Can be a single Integer or a Range or an Array If an invalid port is selected (one reserved by IANA), then an ArgumentError will be raised.
-
#close ⇒ Object
Close the Resolver.
-
#generate_timeouts(base = 0) ⇒ Object
:nodoc: all.
-
#initialize(*args) ⇒ Resolver
constructor
Create a new Resolver object.
- #nameserver=(n) ⇒ Object
- #nameservers=(ns) ⇒ Object
- #persistent_tcp=(on) ⇒ Object
- #persistent_udp=(on) ⇒ Object
-
#query(name, type = Types.A, klass = Classes.IN, set_cd = @dnssec) ⇒ Object
Query for a name.
-
#query!(name, type = Types.A, klass = Classes.IN, set_cd = @dnssec) ⇒ Object
Like query, but does not raise an error when an error occurs.
-
#query_no_validation_or_recursion(name, type = Types.A, klass = Classes.IN) ⇒ Object
:nodoc: all.
-
#query_raw(message, error_strategy = :return) ⇒ Object
Sends a message with send_plain_message.
-
#reset_attributes ⇒ Object
:nodoc: all.
-
#send_async(msg, client_queue, client_query_id = nil) ⇒ Object
Asynchronously send a Message to the server.
-
#send_message(message) ⇒ Object
Send a message, and wait for the response.
-
#send_message!(message) ⇒ Object
Like send_message, but does not raise an error when an error occurs.
-
#send_plain_message(message) ⇒ Object
This method takes a Message (supplied by the client), and sends it to the configured nameservers.
- #set_config_nameserver(n) ⇒ Object
-
#single_res_mutex ⇒ Object
:nodoc: all.
-
#single_resolvers ⇒ Object
}.
-
#single_resolvers=(s) ⇒ Object
The array of SingleResolvers used for sending query messages attr_accessor :single_resolvers # :nodoc:.
-
#src_port ⇒ Object
The source port to send queries from Returns either a single Integer or an Array e.g.
-
#src_port=(p) ⇒ Object
Can be a single Integer or a Range or an Array If an invalid port is selected (one reserved by IANA), then an ArgumentError will be raised.
-
#update ⇒ Object
:nodoc: all.
- #update_internal_res(res) ⇒ Object
Constructor Details
#initialize(*args) ⇒ Resolver
Create a new Resolver object. If no parameters are passed in, then the default
system configuration will be used. Otherwise, a Hash may be passed in with the
following optional elements :
* :port
* :use_tcp
* :tsig
* :ignore_truncation
* :src_address
* :src_address6
* :src_port
* :recurse
* :udp_size
* :config_info - see Config
* :nameserver - can be either a String or an array of Strings
* :packet_timeout
* :query_timeout
* :retry_times
* :retry_delay
* :do_caching
* :tcp_pipelining
* :tcp_pipelining_max_queries - can be a number or :infinite symbol
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/dnsruby/resolver.rb', line 436 def initialize(*args) # @TODO@ Should we allow :namesver to be an RRSet of NS records? Would then need to randomly order them? @resolver_ruby = nil @src_address = nil @src_address6 = nil @single_res_mutex = Mutex.new @configured = false @do_caching = true @config = Config.new() reset_attributes # Process args if args.length == 1 if args[0].class == Hash args[0].keys.each do |key| begin if key == :config_info @config.set_config_info(args[0][:config_info]) elsif key == :nameserver set_config_nameserver(args[0][:nameserver]) elsif key == :nameservers set_config_nameserver(args[0][:nameservers]) else send(key.to_s + '=', args[0][key]) end rescue Exception => e Dnsruby.log.error{"Argument #{key} not valid : #{e}\n"} end end elsif args[0].class == String set_config_nameserver(args[0]) elsif args[0].class == Config # also accepts a Config object from Dnsruby::Resolv @config = args[0] end else # Anything to do? end update end |
Instance Attribute Details
#config ⇒ Object (readonly)
The current Config
129 130 131 |
# File 'lib/dnsruby/resolver.rb', line 129 def config @config end |
#dnssec ⇒ Object
Use DNSSEC for this Resolver
167 168 169 |
# File 'lib/dnsruby/resolver.rb', line 167 def dnssec @dnssec end |
#do_caching ⇒ Object
Defines whether we will cache responses, or pass every request to the
upstream resolver. This is only really useful when querying authoritative
servers (as the upstream recursive resolver is likely to cache)
134 135 136 |
# File 'lib/dnsruby/resolver.rb', line 134 def do_caching @do_caching end |
#do_validation ⇒ Object
Defines whether validation is performed by default on this Resolver when the
query method is called.
Note that send_message and send_async expect a
Message object to be passed in, which is already configured to the callers
requirements.
174 175 176 |
# File 'lib/dnsruby/resolver.rb', line 174 def do_validation @do_validation end |
#ignore_truncation ⇒ Object
Should truncation be ignored?
i.e. the TC bit is ignored and thus the resolver will not requery over TCP if TC is set
114 115 116 |
# File 'lib/dnsruby/resolver.rb', line 114 def ignore_truncation @ignore_truncation end |
#no_tcp ⇒ Object
If no_tcp==true, then ONLY UDP will be used as a transport.
This should not generally be used, but is provided as a debugging aid.
107 108 109 |
# File 'lib/dnsruby/resolver.rb', line 107 def no_tcp @no_tcp end |
#packet_timeout ⇒ Object
The timeout for any individual packet. This is the timeout used by SingleResolver
152 153 154 |
# File 'lib/dnsruby/resolver.rb', line 152 def packet_timeout @packet_timeout end |
#port ⇒ Object
The port to send queries to on the resolver
92 93 94 |
# File 'lib/dnsruby/resolver.rb', line 92 def port @port end |
#query_timeout ⇒ Object
Note that this timeout represents the total time a query may run for - multiple packets
can be sent to multiple nameservers in this time.
This is distinct from the SingleResolver per-packet timeout
The query_timeout is not required - it will default to 0, which means "do not use query_timeout".
If this is the case then the timeout will be dictated by the retry_times and retry_delay attributes
159 160 161 |
# File 'lib/dnsruby/resolver.rb', line 159 def query_timeout @query_timeout end |
#recurse ⇒ Object
Should the Recursion Desired bit be set?
123 124 125 |
# File 'lib/dnsruby/resolver.rb', line 123 def recurse @recurse end |
#retry_delay ⇒ Object
The query will be tried across nameservers retry_times times, with a delay of retry_delay seconds
between each retry. The first time round, retry_delay will be divided by the number of nameservers
being targetted, and a new nameserver will be queried with the resultant delay.
164 165 166 |
# File 'lib/dnsruby/resolver.rb', line 164 def retry_delay @retry_delay end |
#retry_times ⇒ Object
The query will be tried across nameservers retry_times times, with a delay of retry_delay seconds
between each retry. The first time round, retry_delay will be divided by the number of nameservers
being targetted, and a new nameserver will be queried with the resultant delay.
164 165 166 |
# File 'lib/dnsruby/resolver.rb', line 164 def retry_times @retry_times end |
#src_address ⇒ Object
The source address to send queries from for IPv4
117 118 119 |
# File 'lib/dnsruby/resolver.rb', line 117 def src_address @src_address end |
#src_address6 ⇒ Object
The source address to send queries from for IPv6
120 121 122 |
# File 'lib/dnsruby/resolver.rb', line 120 def src_address6 @src_address6 end |
#tcp_pipelining ⇒ Object
If tcp_pipelining==true, then we reuse the TCP connection
99 100 101 |
# File 'lib/dnsruby/resolver.rb', line 99 def tcp_pipelining @tcp_pipelining end |
#tcp_pipelining_max_queries ⇒ Object
How many times (number of messages) to reuse the pipelining connection before closing, :infinite for infinite number of requests per connection
103 104 105 |
# File 'lib/dnsruby/resolver.rb', line 103 def tcp_pipelining_max_queries @tcp_pipelining_max_queries end |
#tsig ⇒ Object
Returns the value of attribute tsig.
110 111 112 |
# File 'lib/dnsruby/resolver.rb', line 110 def tsig @tsig end |
#udp_size ⇒ Object
The maximum UDP size to be used
126 127 128 |
# File 'lib/dnsruby/resolver.rb', line 126 def udp_size @udp_size end |
#use_tcp ⇒ Object
Should TCP be used as a transport rather than UDP?
If use_tcp==true, then ONLY TCP will be used as a transport.
96 97 98 |
# File 'lib/dnsruby/resolver.rb', line 96 def use_tcp @use_tcp end |
Class Method Details
.check_port(p, src_port = []) ⇒ Object
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 |
# File 'lib/dnsruby/resolver.rb', line 653 def Resolver.check_port(p, src_port=[]) unless p.is_a?(Integer) tmp_src_ports = Array.new(src_port) p.each do |x| unless Resolver.check_port(x, tmp_src_ports) return false end tmp_src_ports.push(x) end return true end if Resolver.port_in_range(p) return ! ((p == 0) && (src_port.length > 0)) else Dnsruby.log.error("Illegal port (#{p})") Dnsruby.log_and_raise("Illegal port #{p}", ArgumentError) end end |
.get_ports_from(p) ⇒ Object
676 677 678 679 680 681 682 683 684 685 686 |
# File 'lib/dnsruby/resolver.rb', line 676 def Resolver.get_ports_from(p) a = [] if p.is_a?(Integer) a = [p] else p.each do |x| a.push(x) end end a end |
.get_tsig(args) ⇒ Object
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 |
# File 'lib/dnsruby/resolver.rb', line 735 def Resolver.get_tsig(args) tsig = nil if args.length == 1 if args[0] if args[0].instance_of?(RR::TSIG) tsig = args[0] elsif args[0].instance_of?(Array) tsig = RR.new_from_hash((*args[0])) end else # Dnsruby.log.debug{'TSIG signing switched off'} return nil end else tsig = RR.new_from_hash((args)) end Dnsruby.log.info{"TSIG signing now using #{tsig.name}, key=#{tsig.key}"} tsig end |
.port_in_range(p) ⇒ Object
672 673 674 |
# File 'lib/dnsruby/resolver.rb', line 672 def Resolver.port_in_range(p) (p == 0) || ((p >= 50000) && (p <= 65535)) end |
Instance Method Details
#add_config_nameservers ⇒ Object
:nodoc: all
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
# File 'lib/dnsruby/resolver.rb', line 477 def add_config_nameservers # :nodoc: all unless @configured @config.get_ready end @configured = true @single_res_mutex.synchronize { # Add the Config nameservers @config.nameserver.each do |ns| res = PacketSender.new({ server: ns, port: @port, dnssec: @dnssec, use_tcp: @use_tcp, no_tcp: @no_tcp, tcp_pipelining: @tcp_pipelining, tcp_pipelining_max_queries: @tcp_pipelining_max_queries, packet_timeout: @packet_timeout, tsig: @tsig, ignore_truncation: @ignore_truncation, src_address: @src_address, src_address6: @src_address6, src_port: @src_port, recurse: @recurse, udp_size: @udp_size}) @single_resolvers.push(res) if res end } end |
#add_server(server) ⇒ Object
# Add a new SingleResolver to the list of resolvers this Resolver object will
# query.
def add_resolver(internal) # :nodoc:
# @TODO@ Make a new PacketSender from this SingleResolver!!
@single_resolvers.push(internal)
end
565 566 567 568 569 570 571 |
# File 'lib/dnsruby/resolver.rb', line 565 def add_server(server)# :nodoc: @configured = true res = PacketSender.new(server) Dnsruby.log_and_raise("Can't create server #{server}", ArgumentError) unless res update_internal_res(res) @single_res_mutex.synchronize { @single_resolvers.push(res) } end |
#add_src_port(p) ⇒ Object
Can be a single Integer or a Range or an Array
If an invalid port is selected (one reserved by
IANA), then an ArgumentError will be raised.
"0" means "any valid port" - this is only a viable
option if it is the only port in the list.
An ArgumentError will be raised if "0" is added to
an existing set of source ports.
res.add_src_port(60000)
res.add_src_port([60001,60005,60010])
res.add_src_port(60015..60115)
639 640 641 642 643 644 645 646 647 648 649 650 651 |
# File 'lib/dnsruby/resolver.rb', line 639 def add_src_port(p) if Resolver.check_port(p, @src_port) a = Resolver.get_ports_from(p) a.each do |x| if (@src_port.length > 0) && (x == 0) Dnsruby.log_and_raise("src_port of 0 only allowed as only src_port value (currently #{@src_port.length} values", ArgumentError) end @src_port.push(x) end end update end |
#close ⇒ Object
Close the Resolver. Unfinished queries are terminated with OtherResolvError.
409 410 411 |
# File 'lib/dnsruby/resolver.rb', line 409 def close @resolver_ruby.close if @resolver_ruby end |
#generate_timeouts(base = 0) ⇒ Object
:nodoc: all
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 |
# File 'lib/dnsruby/resolver.rb', line 818 def generate_timeouts(base=0) # :nodoc: all # These should be be pegged to the single_resolver they are targetting : # e.g. timeouts[timeout1]=nameserver timeouts = {} retry_delay = @retry_delay # @single_res_mutex.synchronize { @retry_times.times do |retry_count| if retry_count > 0 retry_delay *= 2 end @single_resolvers.delete(nil) # Just in case... @single_resolvers.each_index do |i| res = @single_resolvers[i] offset = (i * @retry_delay.to_f / @single_resolvers.length) if retry_count == 0 timeouts[base + offset]=[res, retry_count] else if timeouts.has_key?(base + retry_delay + offset) Dnsruby.log_and_raise('Duplicate timeout key!') end timeouts[base + retry_delay + offset]=[res, retry_count] end end end # } timeouts end |
#nameserver=(n) ⇒ Object
586 587 588 589 590 591 |
# File 'lib/dnsruby/resolver.rb', line 586 def nameserver=(n) @configured = true @single_res_mutex.synchronize { @single_resolvers=[] } set_config_nameserver(n) add_config_nameservers end |
#nameservers=(ns) ⇒ Object
582 583 584 |
# File 'lib/dnsruby/resolver.rb', line 582 def nameservers=(ns) self.nameserver=(ns) end |
#persistent_tcp=(on) ⇒ Object
778 779 780 781 |
# File 'lib/dnsruby/resolver.rb', line 778 def persistent_tcp=(on) @persistent_tcp = on update end |
#persistent_udp=(on) ⇒ Object
783 784 785 786 |
# File 'lib/dnsruby/resolver.rb', line 783 def persistent_udp=(on) @persistent_udp = on update end |
#query(name, type = Types.A, klass = Classes.IN, set_cd = @dnssec) ⇒ Object
Query for a name. If a valid Message is received, then it is returned
to the caller. Otherwise an exception (a Dnsruby::ResolvError or Dnsruby::ResolvTimeout) is raised.
require 'dnsruby'
res = Dnsruby::Resolver.new
response = res.query('example.com') # defaults to Types.A, Classes.IN
response = res.query('example.com', Types.MX)
response = res.query('208.77.188.166') # IPv4 address so PTR query will be made
response = res.query('208.77.188.166', Types.PTR)
191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/dnsruby/resolver.rb', line 191 def query(name, type=Types.A, klass=Classes.IN, set_cd=@dnssec) msg = Message.new msg.do_caching = @do_caching msg.header.rd = 1 msg.add_question(name, type, klass) msg.do_validation = @do_validation if @dnssec msg.header.cd = set_cd # We do our own validation by default end (msg) end |
#query!(name, type = Types.A, klass = Classes.IN, set_cd = @dnssec) ⇒ Object
Like query, but does not raise an error when an error occurs.
Instead, it returns it.
@return a 2 element array: [response, error]
206 207 208 209 210 211 212 213 214 |
# File 'lib/dnsruby/resolver.rb', line 206 def query!(name, type=Types.A, klass=Classes.IN, set_cd=@dnssec) response = nil; error = nil begin response = query(name, type, klass, set_cd) rescue => e error = e end [response, error] end |
#query_no_validation_or_recursion(name, type = Types.A, klass = Classes.IN) ⇒ Object
:nodoc: all
216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/dnsruby/resolver.rb', line 216 def query_no_validation_or_recursion(name, type=Types.A, klass=Classes.IN) # :nodoc: all msg = Message.new msg.do_caching = @do_caching msg.header.rd = false msg.do_validation = false msg.add_question(name, type, klass) if @dnssec msg.header.cd = true # We do our own validation by default end (msg) end |
#query_raw(message, error_strategy = :return) ⇒ Object
Sends a message with send_plain_message. Effectively a wrapper around send_plain_message, but adds the ability to configure whether an error will be raised or returned if it occurs.
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
# File 'lib/dnsruby/resolver.rb', line 281 def query_raw(, error_strategy = :return) unless [:return, :raise].include?(error_strategy) raise ArgumentError.new('error_strategy should be one of [:return, :raise].') end response, error = () if error_strategy == :return [response, error] else raise error if error response end end |
#reset_attributes ⇒ Object
:nodoc: all
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/dnsruby/resolver.rb', line 515 def reset_attributes # :nodoc: all @resolver_ruby.reset_attributes if @resolver_ruby # Attributes # do_validation tells the Resolver whether to try to validate the response # with DNSSEC. This should work for NSEC-signed domains, but NSEC3 # validation is not currently supported. This attribute now defaults to # false. Please let me know if you require NSEC3 validation. @do_validation = false @query_timeout = DefaultQueryTimeout @retry_delay = DefaultRetryDelay @retry_times = DefaultRetryTimes @packet_timeout = DefaultPacketTimeout @port = DefaultPort @udp_size = DefaultUDPSize @dnssec = DefaultDnssec @do_caching= true @use_tcp = false @no_tcp = false @tcp_pipelining = false @tcp_pipelining_max_queries = DefaultPipeLiningMaxQueries @tsig = nil @ignore_truncation = false @config = Config.new() @src_address = nil @src_address6 = nil @src_port = [0] @recurse = true @single_res_mutex.synchronize { @single_resolvers=[] } @configured = false end |
#send_async(msg, client_queue, client_query_id = nil) ⇒ Object
Asynchronously send a Message to the server. The send can be done using just
Dnsruby. Support for EventMachine has been deprecated.
== Dnsruby pure Ruby event loop :
A client_queue is supplied by the client,
along with an optional client_query_id to identify the response. The client_query_id
is generated, if not supplied, and returned to the client.
When the response is known,
a tuple of (query_id, response_message, exception) will be added to the client_queue.
The query is sent synchronously in the caller's thread. The select thread is then used to
listen for and process the response (up to pushing it to the client_queue). The client thread
is then used to retrieve the response and deal with it.
Takes :
* msg - the message to send
* client_queue - a Queue to push the response to, when it arrives
* client_query_id - an optional ID to identify the query to the client
* use_tcp - whether to use only TCP (defaults to SingleResolver.use_tcp)
Returns :
* client_query_id - to identify the query response to the client. This ID is
generated if it is not passed in by the client
=== Example invocations :
id = res.send_async(msg, queue)
NOT SUPPORTED : id = res.send_async(msg, queue, use_tcp)
id = res.send_async(msg, queue, id)
id = res.send_async(msg, queue, id, use_tcp)
=== Example code :
require 'dnsruby'
res = Dnsruby::Resolver.newsend
query_id = 10 # can be any object you like
query_queue = Queue.new
res.send_async(Message.new('example.com', Types.MX), query_queue, query_id)
query_id_2 = res.send_async(Message.new('example.com', Types.A), query_queue)
# ...do a load of other stuff here...
2.times do
response_id, response, exception = query_queue.pop
# You can check the ID to see which query has been answered
if exception == nil
# deal with good response
else
# deal with problem
end
end
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/dnsruby/resolver.rb', line 389 def send_async(msg, client_queue, client_query_id = nil) unless @configured add_config_nameservers end # @single_res_mutex.synchronize { unless @resolver_ruby # @TODO@ Synchronize this? @resolver_ruby = ResolverRuby.new(self) end # } client_query_id = @resolver_ruby.send_async(msg, client_queue, client_query_id) if @single_resolvers.length == 0 Thread.start { sleep(@query_timeout == 0 ? 1 : @query_timeout) client_queue.push([client_query_id, nil, ResolvTimeout.new('Query timed out - no nameservers configured')]) } end client_query_id end |
#send_message(message) ⇒ Object
Send a message, and wait for the response. If a valid Message is received, then it is returned
to the caller. Otherwise an exception (a Dnsruby::ResolvError or Dnsruby::ResolvTimeout) is raised.
send_async is called internally.
example :
require 'dnsruby'
include Dnsruby
res = Dnsruby::Resolver.new
begin
response = res.send_message(Message.new('example.com', Types.MX))
rescue ResolvError
# ...
rescue ResolvTimeout
# ...
end
245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/dnsruby/resolver.rb', line 245 def () Dnsruby.log.debug{'Resolver : sending message'} q = Queue.new send_async(, q) _id, result, error = q.pop if error error.response = result if error.is_a?(ResolvError) raise error else result end end |
#send_message!(message) ⇒ Object
Like send_message, but does not raise an error when an error occurs.
Instead, it returns it.
@return a 2 element array: [response, error]
263 264 265 266 267 268 269 270 271 |
# File 'lib/dnsruby/resolver.rb', line 263 def () response = nil; error = nil begin response = () rescue => e error = e end [response, error] end |
#send_plain_message(message) ⇒ Object
This method takes a Message (supplied by the client), and sends it to
the configured nameservers. No changes are made to the Message before it
is sent (TSIG signatures will be applied if configured on the Resolver).
Retries are handled as the Resolver is configured to do.
Incoming responses to the query are not cached or validated (although TCP
fallback will be performed if the TC bit is set and the (Single)Resolver has
ignore_truncation set to false).
Note that the Message is left untouched - this means that no OPT records are
added, even if the UDP transport for the server is specified at more than 512
bytes. If it is desired to use EDNS for this packet, then you should call
the Dnsruby::PacketSender#prepare_for_dnssec(msg), or
Dnsruby::PacketSender#add_opt_rr(msg)
The return value from this method is the [response, error] tuple. Either of
these values may be nil - it is up to the client to check.
example :
require 'dnsruby'
include Dnsruby
res = Dnsruby::Resolver.new
response, error = res.send_plain_message(Message.new('example.com', Types.MX))
if error
print "Error returned : #{error}\n"
else
process_response(response)
end
323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/dnsruby/resolver.rb', line 323 def () Dnsruby::TheLog.debug('Resolver : send_plain_message') .do_caching = false .do_validation = false .send_raw = true q = Queue.new send_async(, q) _id, result, error = q.pop error.response = result if !error.nil? && error.is_a?(ResolvError) [result, error] end |
#set_config_nameserver(n) ⇒ Object
506 507 508 509 510 511 512 513 |
# File 'lib/dnsruby/resolver.rb', line 506 def set_config_nameserver(n) # @TODO@ Should we allow NS RRSet here? If so, then .sort_by {rand} @config.get_ready unless @configured @configured = true @config.nameserver = n.kind_of?(String) ? [n] : n add_config_nameservers end |
#single_res_mutex ⇒ Object
:nodoc: all
814 815 816 |
# File 'lib/dnsruby/resolver.rb', line 814 def single_res_mutex # :nodoc: all @single_res_mutex end |
#single_resolvers ⇒ Object
}
144 145 146 147 148 149 |
# File 'lib/dnsruby/resolver.rb', line 144 def single_resolvers # :nodoc: unless @configured add_config_nameservers end @single_resolvers end |
#single_resolvers=(s) ⇒ Object
The array of SingleResolvers used for sending query messages
attr_accessor :single_resolvers # :nodoc:
138 139 140 141 142 143 |
# File 'lib/dnsruby/resolver.rb', line 138 def single_resolvers=(s) # :nodoc: @configured = true # @single_res_mutex.synchronize { @single_resolvers = s # } end |
#src_port ⇒ Object
The source port to send queries from
Returns either a single Integer or an Array
e.g. '0', or '[60001, 60002, 60007]'
Defaults to 0 - random port
608 609 610 |
# File 'lib/dnsruby/resolver.rb', line 608 def src_port @src_port.length == 1 ? @src_port[0] : @src_port end |
#src_port=(p) ⇒ Object
Can be a single Integer or a Range or an Array
If an invalid port is selected (one reserved by
IANA), then an ArgumentError will be raised.
res.src_port=0
res.src_port=[60001,60005,60010]
res.src_port=60015..60115
620 621 622 623 624 625 |
# File 'lib/dnsruby/resolver.rb', line 620 def src_port=(p) if Resolver.check_port(p) @src_port = Resolver.get_ports_from(p) update end end |
#update ⇒ Object
:nodoc: all
550 551 552 553 554 555 556 |
# File 'lib/dnsruby/resolver.rb', line 550 def update # :nodoc: all # Update any resolvers we have with the latest config @single_res_mutex.synchronize do @single_resolvers.delete(nil) # Just in case... @single_resolvers.each { |res| update_internal_res(res) } end end |
#update_internal_res(res) ⇒ Object
573 574 575 576 577 578 579 580 |
# File 'lib/dnsruby/resolver.rb', line 573 def update_internal_res(res) [:port, :use_tcp, :no_tcp, :tcp_pipelining, :tcp_pipelining_max_queries, :tsig, :ignore_truncation, :packet_timeout, :src_address, :src_address6, :src_port, :recurse, :udp_size, :dnssec].each do |param| res.send(param.to_s + '=', instance_variable_get('@' + param.to_s)) end end |