unbound 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -36
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/examples/resolve_async.rb +47 -0
- data/lib/unbound.rb +4 -0
- data/lib/unbound/bindings.rb +56 -25
- data/lib/unbound/callback_array.rb +21 -0
- data/lib/unbound/callbacks_mixin.rb +54 -0
- data/lib/unbound/context.rb +14 -3
- data/lib/unbound/exceptions.rb +4 -0
- data/lib/unbound/query.rb +72 -0
- data/lib/unbound/resolver.rb +93 -14
- data/lib/unbound/result.rb +1 -9
- data/spec/callback_array_spec.rb +71 -0
- data/spec/context_spec.rb +17 -9
- data/spec/query_spec.rb +210 -0
- data/spec/resolver_spec.rb +156 -0
- data/spec/result_spec.rb +21 -0
- data/spec/spec_helper.rb +17 -2
- metadata +27 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9e071138d458da3ab632fd55f7208e686e55679
|
4
|
+
data.tar.gz: ffebd0663a8fe538b933c21cb35bd9f876ec64eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3190a931ff0e491a2c772056b486cad1c35b0e54ade6c6b6cf9429dfdd154157921fac796e62b34d0ea6ae699056a968b02776723179224c337bad716de2809d
|
7
|
+
data.tar.gz: c539edfe6095cf3dba1c51411782ee6dcfc5f8c73e611270ebcaaa39e855dd61256c650d1f79ca6f058133c3bdadba7f08d4f75271d0317fbf5643d2d71ed15a
|
data/README.md
CHANGED
@@ -6,42 +6,7 @@ These bindings allow for asynchronous and synchronous name resolution. You reall
|
|
6
6
|
|
7
7
|
## Code Example
|
8
8
|
|
9
|
-
|
10
|
-
require 'unbound/context'
|
11
|
-
require 'unbound/result'
|
12
|
-
|
13
|
-
ctx = Unbound::Context.new
|
14
|
-
|
15
|
-
CB = Proc.new do |mydata, err, result_ptr|
|
16
|
-
if (err == 0)
|
17
|
-
result = Unbound::Result.new(result_ptr)
|
18
|
-
puts "Got response rcode: #{result[:rcode]}, qname: #{result[:qname]}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
google_id = ctx.resolve_async("www.google.com", 1, 1, CB)
|
23
|
-
yahoo_id = ctx.resolve_async("www.yahoo.com", 1, 1, CB)
|
24
|
-
github_id = ctx.resolve_async("www.github.com", 1, 1, CB)
|
25
|
-
|
26
|
-
io = ctx.io
|
27
|
-
|
28
|
-
# Keep looping until we haven't gotten any
|
29
|
-
while (::IO.select([io], nil, nil, 5))
|
30
|
-
ctx.process()
|
31
|
-
end
|
32
|
-
|
33
|
-
ctx.close()
|
34
|
-
puts "all done!"
|
35
|
-
```
|
36
|
-
|
37
|
-
outputs:
|
38
|
-
```
|
39
|
-
Got response rcode: 0, qname: www.google.com
|
40
|
-
Got response rcode: 0, qname: www.github.com
|
41
|
-
Got response rcode: 0, qname: www.yahoo.com
|
42
|
-
all done!
|
43
|
-
```
|
44
|
-
|
9
|
+
See the examples directory.
|
45
10
|
|
46
11
|
## Motivation
|
47
12
|
|
data/Rakefile
CHANGED
@@ -20,6 +20,7 @@ Jeweler::Tasks.new do |gem|
|
|
20
20
|
gem.email = "falter@gmail.com"
|
21
21
|
gem.authors = ["Mike Ryan"]
|
22
22
|
gem.files = Dir.glob("lib/**/*.rb") +
|
23
|
+
Dir.glob("examples/*") +
|
23
24
|
Dir.glob("spec/{*.rb}") +
|
24
25
|
Dir.glob("spec/conf/{*.conf}") +
|
25
26
|
%w(LICENSE.txt Gemfile README.md Rakefile VERSION)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
1.0.0
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Example that resolves three hostnames in an aynchronous fashion.
|
2
|
+
#
|
3
|
+
# Outputs:
|
4
|
+
# Got result for www.google.com!
|
5
|
+
# Got result for github.com!
|
6
|
+
# Got result for www.yahoo.com!
|
7
|
+
# all done!
|
8
|
+
#
|
9
|
+
require 'unbound'
|
10
|
+
|
11
|
+
ctx = Unbound::Context.new
|
12
|
+
resolver = Unbound::Resolver.new(ctx)
|
13
|
+
|
14
|
+
at_exit do
|
15
|
+
resolver.close unless resolver.closed?
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add some callbacks to the resolver
|
19
|
+
resolver.on_answer do |q, result|
|
20
|
+
puts "Got result for #{q.name} : rcode: #{result[:rcode]}!"
|
21
|
+
end
|
22
|
+
resolver.on_cancel do |q|
|
23
|
+
puts "Query canceled: #{q.name}!"
|
24
|
+
end
|
25
|
+
resolver.on_error do |q, error_code|
|
26
|
+
puts "Query had error: #{q.name} #{error_code}!"
|
27
|
+
end
|
28
|
+
|
29
|
+
names = ["www.google.com", "www.yahoo.com", "github.com", "notadomain"]
|
30
|
+
names.each do |name|
|
31
|
+
query = Unbound::Query.new(name, 1, 1)
|
32
|
+
query.on_answer do |query, result|
|
33
|
+
puts "query-level callback for on_answer: #{name}"
|
34
|
+
end
|
35
|
+
resolver.send_query(query)
|
36
|
+
end
|
37
|
+
|
38
|
+
io = resolver.io
|
39
|
+
|
40
|
+
# Keep looping until we haven't gotten any
|
41
|
+
while resolver.outstanding_queries? && (::IO.select([io], nil, nil, 5))
|
42
|
+
ctx.process()
|
43
|
+
end
|
44
|
+
|
45
|
+
resolver.close()
|
46
|
+
puts "all done!"
|
47
|
+
|
data/lib/unbound.rb
CHANGED
data/lib/unbound/bindings.rb
CHANGED
@@ -1,15 +1,46 @@
|
|
1
1
|
require 'ffi'
|
2
|
+
require 'unbound/result'
|
2
3
|
module Unbound
|
3
4
|
module Bindings
|
4
5
|
extend FFI::Library
|
5
6
|
|
6
7
|
LIBNAMES = ['unbound', 'libunbound.so', 'libunbound.so.2']
|
7
8
|
if ENV['LIBUNBOUND']
|
9
|
+
# :nocov:
|
8
10
|
LIBNAMES.unshift(ENV['LIBUNBOUND'])
|
11
|
+
# :nocov:
|
9
12
|
end
|
10
13
|
|
11
14
|
ffi_lib LIBNAMES
|
12
15
|
|
16
|
+
enum :error_code,
|
17
|
+
[
|
18
|
+
# /** no error */
|
19
|
+
:noerror , 0,
|
20
|
+
# /** socket operation. Set to -1, so that if an error from _fd() is
|
21
|
+
# * passed (-1) it gives a socket error. */
|
22
|
+
:socket , -1,
|
23
|
+
# /** alloc failure */
|
24
|
+
:nomem , -2,
|
25
|
+
# /** syntax error */
|
26
|
+
:syntax , -3,
|
27
|
+
# /** DNS service failed */
|
28
|
+
:servfail , -4,
|
29
|
+
# /** fork() failed */
|
30
|
+
:forkfail , -5,
|
31
|
+
# /** cfg change after finalize() */
|
32
|
+
:afterfinal , -6,
|
33
|
+
# /** initialization failed (bad settings) */
|
34
|
+
:initfail , -7,
|
35
|
+
# /** error in pipe communication with async bg worker */
|
36
|
+
:pipe , -8,
|
37
|
+
# /** error reading from file (resolv.conf) */
|
38
|
+
:readfile , -9,
|
39
|
+
# /** error async_id does not exist or result already been delivered */
|
40
|
+
:noid , -10
|
41
|
+
]
|
42
|
+
|
43
|
+
|
13
44
|
|
14
45
|
typedef :pointer, :mydata
|
15
46
|
typedef :pointer, :ub_ctx_ptr
|
@@ -22,43 +53,43 @@ module Unbound
|
|
22
53
|
attach_function :ub_ctx_create, [], :ub_ctx_ptr
|
23
54
|
attach_function :ub_ctx_delete, [:ub_ctx_ptr], :void
|
24
55
|
|
25
|
-
attach_function :ub_ctx_set_option, [:ub_ctx_ptr, :string, :string], :
|
26
|
-
attach_function :ub_ctx_get_option, [:ub_ctx_ptr, :string, :pointer], :
|
27
|
-
attach_function :ub_ctx_config, [:ub_ctx_ptr, :string], :
|
56
|
+
attach_function :ub_ctx_set_option, [:ub_ctx_ptr, :string, :string], :error_code
|
57
|
+
attach_function :ub_ctx_get_option, [:ub_ctx_ptr, :string, :pointer], :error_code
|
58
|
+
attach_function :ub_ctx_config, [:ub_ctx_ptr, :string], :error_code
|
28
59
|
|
29
|
-
attach_function :ub_ctx_set_fwd, [:ub_ctx_ptr, :string], :
|
30
|
-
attach_function :ub_ctx_resolvconf, [:ub_ctx_ptr, :string], :
|
31
|
-
attach_function :ub_ctx_hosts, [:ub_ctx_ptr, :string], :
|
60
|
+
attach_function :ub_ctx_set_fwd, [:ub_ctx_ptr, :string], :error_code
|
61
|
+
attach_function :ub_ctx_resolvconf, [:ub_ctx_ptr, :string], :error_code
|
62
|
+
attach_function :ub_ctx_hosts, [:ub_ctx_ptr, :string], :error_code
|
32
63
|
|
33
|
-
attach_function :ub_ctx_add_ta, [:ub_ctx_ptr, :string], :
|
34
|
-
attach_function :ub_ctx_add_ta_file, [:ub_ctx_ptr, :string], :
|
64
|
+
attach_function :ub_ctx_add_ta, [:ub_ctx_ptr, :string], :error_code
|
65
|
+
attach_function :ub_ctx_add_ta_file, [:ub_ctx_ptr, :string], :error_code
|
35
66
|
|
36
|
-
attach_function :ub_ctx_trustedkeys, [:ub_ctx_ptr, :string], :
|
67
|
+
attach_function :ub_ctx_trustedkeys, [:ub_ctx_ptr, :string], :error_code
|
37
68
|
|
38
|
-
attach_function :ub_ctx_debugout, [:ub_ctx_ptr, :pointer], :
|
39
|
-
attach_function :ub_ctx_debuglevel, [:ub_ctx_ptr, :int], :
|
69
|
+
attach_function :ub_ctx_debugout, [:ub_ctx_ptr, :pointer], :error_code # TODO: FILE pointer
|
70
|
+
attach_function :ub_ctx_debuglevel, [:ub_ctx_ptr, :int], :error_code
|
40
71
|
|
41
|
-
attach_function :ub_ctx_async, [:ub_ctx_ptr, :int], :
|
72
|
+
attach_function :ub_ctx_async, [:ub_ctx_ptr, :int], :error_code
|
42
73
|
|
43
|
-
attach_function :ub_poll, [:ub_ctx_ptr], :
|
44
|
-
attach_function :ub_wait, [:ub_ctx_ptr], :
|
45
|
-
attach_function :ub_fd, [:ub_ctx_ptr], :
|
46
|
-
attach_function :ub_process, [:ub_ctx_ptr], :
|
74
|
+
attach_function :ub_poll, [:ub_ctx_ptr], :error_code
|
75
|
+
attach_function :ub_wait, [:ub_ctx_ptr], :error_code, :blocking => true
|
76
|
+
attach_function :ub_fd, [:ub_ctx_ptr], :error_code
|
77
|
+
attach_function :ub_process, [:ub_ctx_ptr], :error_code, :blocking => true
|
47
78
|
|
48
|
-
attach_function :ub_resolve, [:ub_ctx_ptr, :string, :int, :int, :ub_result_ptrptr], :
|
49
|
-
attach_function :ub_resolve_async, [:ub_ctx_ptr, :string, :int, :int, :mydata, :ub_callback_t, :pointer], :
|
50
|
-
attach_function :ub_cancel, [:ub_ctx_ptr, :int], :
|
79
|
+
attach_function :ub_resolve, [:ub_ctx_ptr, :string, :int, :int, :ub_result_ptrptr], :error_code, :blocking => true
|
80
|
+
attach_function :ub_resolve_async, [:ub_ctx_ptr, :string, :int, :int, :mydata, :ub_callback_t, :pointer], :error_code
|
81
|
+
attach_function :ub_cancel, [:ub_ctx_ptr, :int], :error_code
|
51
82
|
|
52
83
|
attach_function :ub_resolve_free, [:ub_result_ptr], :void
|
53
|
-
attach_function :ub_strerror, [:
|
84
|
+
attach_function :ub_strerror, [:error_code], :string
|
54
85
|
|
55
|
-
attach_function :ub_ctx_print_local_zones, [:ub_ctx_ptr], :
|
86
|
+
attach_function :ub_ctx_print_local_zones, [:ub_ctx_ptr], :error_code
|
56
87
|
|
57
|
-
attach_function :ub_ctx_zone_add, [:ub_ctx_ptr, :string, :string], :
|
58
|
-
attach_function :ub_ctx_zone_remove, [:ub_ctx_ptr, :string], :
|
88
|
+
attach_function :ub_ctx_zone_add, [:ub_ctx_ptr, :string, :string], :error_code
|
89
|
+
attach_function :ub_ctx_zone_remove, [:ub_ctx_ptr, :string], :error_code
|
59
90
|
|
60
|
-
attach_function :ub_ctx_data_add, [:ub_ctx_ptr, :string], :
|
61
|
-
attach_function :ub_ctx_data_remove, [:ub_ctx_ptr, :string], :
|
91
|
+
attach_function :ub_ctx_data_add, [:ub_ctx_ptr, :string], :error_code
|
92
|
+
attach_function :ub_ctx_data_remove, [:ub_ctx_ptr, :string], :error_code
|
62
93
|
|
63
94
|
## This wasn't added until unbound 1.4.15
|
64
95
|
# attach_function :ub_version, [], :string
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Unbound
|
2
|
+
class CallbackArray < Array
|
3
|
+
# Registers one or more callbacks
|
4
|
+
# @param [Array<#call>] cbs A splat of procs to register as callbacks
|
5
|
+
# @yield [] cb_block A block to register as a callback
|
6
|
+
# @raise [ArgumentError] if no callbacks are provided
|
7
|
+
def add_callback(*cbs, &cb_block)
|
8
|
+
cbs.push(cb_block) unless cb_block.nil?
|
9
|
+
raise(ArgumentError.new("Missing callback")) if cbs.empty?
|
10
|
+
concat(cbs)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Calls each registered callback with the provided arguments
|
14
|
+
# @param [Array] args A splat of arguments to pass to each callback via #call
|
15
|
+
def call(*args)
|
16
|
+
each do |cb|
|
17
|
+
cb.call(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'unbound/callback_array'
|
2
|
+
module Unbound
|
3
|
+
module CallbacksMixin
|
4
|
+
def init_callbacks
|
5
|
+
@callbacks_start = CallbackArray.new
|
6
|
+
@callbacks_answer = CallbackArray.new
|
7
|
+
@callbacks_error = CallbackArray.new
|
8
|
+
@callbacks_cancel = CallbackArray.new
|
9
|
+
@callbacks_finish = CallbackArray.new
|
10
|
+
end
|
11
|
+
private :init_callbacks
|
12
|
+
|
13
|
+
def clear_callbacks!
|
14
|
+
@callbacks_start.clear
|
15
|
+
@callbacks_answer.clear
|
16
|
+
@callbacks_error.clear
|
17
|
+
@callbacks_cancel.clear
|
18
|
+
@callbacks_finish.clear
|
19
|
+
end
|
20
|
+
private :clear_callbacks!
|
21
|
+
|
22
|
+
# Adds a callback that will be called just after the query is sent
|
23
|
+
# @param [Proc] cb
|
24
|
+
def on_start(*cbs, &cb_block)
|
25
|
+
@callbacks_start.add_callback(*cbs, &cb_block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Adds a callback that will be called when we receive an answer to the query
|
29
|
+
# @param [Proc] cb
|
30
|
+
def on_answer(*cbs, &cb_block)
|
31
|
+
@callbacks_answer.add_callback(*cbs, &cb_block)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Adds a callback that will be called when an error internal to unbound occurs
|
35
|
+
# @param [Proc] cb
|
36
|
+
def on_error(*cbs, &cb_block)
|
37
|
+
@callbacks_error.add_callback(*cbs, &cb_block)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Adds a callback that will be called if the query times out (dependant on
|
41
|
+
# the resolver)
|
42
|
+
# @param [Proc] cb
|
43
|
+
def on_cancel(*cbs, &cb_block)
|
44
|
+
@callbacks_cancel.add_callback(*cbs, &cb_block)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Adds a callback will *always* be called when the query is finished
|
48
|
+
# whether answerfully, in error, or due to cancel.
|
49
|
+
# @param [Proc] cb
|
50
|
+
def on_finish(*cbs, &cb_block)
|
51
|
+
@callbacks_finish.add_callback(*cbs, &cb_block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/unbound/context.rb
CHANGED
@@ -5,6 +5,15 @@ module Unbound
|
|
5
5
|
class Context
|
6
6
|
def initialize
|
7
7
|
@ub_ctx = Unbound::Bindings.ub_ctx_create()
|
8
|
+
@raise_on_noid = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def raise_on_noid=(b)
|
12
|
+
@raise_on_noid = (b == true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def raise_on_noid?
|
16
|
+
@raise_on_noid
|
8
17
|
end
|
9
18
|
|
10
19
|
def check_closed!
|
@@ -14,10 +23,12 @@ module Unbound
|
|
14
23
|
end
|
15
24
|
|
16
25
|
def raise_if_error!(retval)
|
17
|
-
if retval
|
18
|
-
|
26
|
+
return retval if retval == :noerror
|
27
|
+
|
28
|
+
if retval == :noid && @raise_on_noid == false
|
29
|
+
return retval
|
19
30
|
end
|
20
|
-
|
31
|
+
raise APIError.new(retval)
|
21
32
|
end
|
22
33
|
|
23
34
|
def closed?
|
data/lib/unbound/exceptions.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'unbound/exceptions'
|
2
|
+
require 'unbound/callbacks_mixin'
|
3
|
+
module Unbound
|
4
|
+
# A representation of a query, as used by Unbound::Resolver
|
5
|
+
class Query
|
6
|
+
include CallbacksMixin
|
7
|
+
attr_reader :name, :rrtype, :rrclass, :async_id
|
8
|
+
|
9
|
+
STATE_INIT = 0
|
10
|
+
STATE_STARTED = 1
|
11
|
+
STATE_FINISHED = 100
|
12
|
+
|
13
|
+
# @param [String] name
|
14
|
+
# @param [Integer] rrtype
|
15
|
+
# @param [Integer] rrclass
|
16
|
+
def initialize(name, rrtype, rrclass)
|
17
|
+
@name = name
|
18
|
+
@rrtype = rrtype
|
19
|
+
@rrclass = rrclass
|
20
|
+
@async_id = nil
|
21
|
+
|
22
|
+
@state = STATE_INIT
|
23
|
+
init_callbacks
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Boolean] whether the query has finished or not
|
27
|
+
def finished?
|
28
|
+
@state >= STATE_FINISHED
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Boolean] whether the query has been started or not
|
32
|
+
def started?
|
33
|
+
@state >= STATE_STARTED
|
34
|
+
end
|
35
|
+
|
36
|
+
# Called by the resolver just after it has sent the query, and received an
|
37
|
+
# asynchronous ID.
|
38
|
+
def start!(async_id)
|
39
|
+
@state = STATE_STARTED
|
40
|
+
@async_id = async_id
|
41
|
+
@callbacks_start.call(self)
|
42
|
+
@callbacks_start.clear
|
43
|
+
end
|
44
|
+
|
45
|
+
# Called by the resolver when it has received an answer
|
46
|
+
# @param [Unbound::Result] result The result structure
|
47
|
+
def answer!(result)
|
48
|
+
@callbacks_answer.call(self, result)
|
49
|
+
finish!()
|
50
|
+
end
|
51
|
+
|
52
|
+
# Called by the resolver when it has encountered an internal unbound error
|
53
|
+
# @param [Unbound::Bindings::error_codes] error_code
|
54
|
+
def error!(error_code)
|
55
|
+
@callbacks_error.call(self, error_code)
|
56
|
+
finish!()
|
57
|
+
end
|
58
|
+
|
59
|
+
# Called by the resolver after a cancel has occurred.
|
60
|
+
def cancel!()
|
61
|
+
@callbacks_cancel.call(self)
|
62
|
+
finish!()
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def finish!()
|
67
|
+
@state = STATE_FINISHED
|
68
|
+
@callbacks_finish.call(self)
|
69
|
+
clear_callbacks!
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/unbound/resolver.rb
CHANGED
@@ -1,36 +1,115 @@
|
|
1
1
|
require 'unbound/bindings'
|
2
2
|
require 'unbound/result'
|
3
3
|
require 'unbound/context'
|
4
|
+
require 'unbound/callbacks_mixin'
|
4
5
|
|
5
6
|
module Unbound
|
7
|
+
# A simple asynchronous resolver
|
6
8
|
class Resolver
|
7
|
-
|
8
|
-
@ctx = Unbound::Context.new
|
9
|
-
@ctx.load_config("unbound/etc/unbound.conf")
|
9
|
+
include CallbacksMixin
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
# @param [Unbound::Context] ctx The context around which we will wrap this
|
12
|
+
# resolver.
|
13
|
+
def initialize(ctx)
|
14
|
+
@ctx = ctx
|
15
|
+
@queries = {}
|
16
|
+
@resolve_callback_func = FFI::Function.new(
|
17
|
+
:void, [:pointer, :int, :pointer],
|
18
|
+
self.method(:resolve_callback))
|
19
|
+
|
20
|
+
init_callbacks
|
21
|
+
|
22
|
+
on_cancel do |query|
|
23
|
+
if query.async_id
|
24
|
+
@ctx.cancel_async_query(query.async_id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
on_finish do |query|
|
28
|
+
@queries.delete(query.object_id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Integer] the number of queries for which we are awaiting reply
|
33
|
+
def outstanding_queries
|
34
|
+
@queries.count
|
14
35
|
end
|
15
36
|
|
37
|
+
# @return [Boolean] true if there are any queries for which we are awaiting
|
38
|
+
# reply
|
39
|
+
def outstanding_queries?
|
40
|
+
@queries.count > 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def resolve_callback(oid_ptr, err, result_ptr)
|
44
|
+
return if oid_ptr.nil?
|
45
|
+
oid = oid_ptr.address
|
46
|
+
query = @queries[oid]
|
47
|
+
return if query.nil?
|
48
|
+
|
49
|
+
if err == 0
|
50
|
+
query.answer!(Unbound::Result.new(result_ptr))
|
51
|
+
else
|
52
|
+
# :nocov:
|
53
|
+
query.error!(err)
|
54
|
+
# :nocov:
|
55
|
+
end
|
56
|
+
ensure
|
57
|
+
if err == 0
|
58
|
+
# Always free the result pointer
|
59
|
+
Unbound::Bindings.ub_resolve_free(result_ptr)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
private :resolve_callback
|
63
|
+
|
64
|
+
# Cancel all outstanding queries.
|
65
|
+
def cancel_all
|
66
|
+
@queries.each_value do |query|
|
67
|
+
query.cancel!
|
68
|
+
end
|
69
|
+
@queries.clear
|
70
|
+
end
|
71
|
+
|
72
|
+
def closed?
|
73
|
+
@ctx.closed?
|
74
|
+
end
|
75
|
+
|
76
|
+
# Cancel all queries and close the resolver down.
|
16
77
|
def close
|
78
|
+
return if self.closed? == true
|
79
|
+
self.cancel_all
|
17
80
|
@ctx.close
|
18
81
|
end
|
19
82
|
|
20
|
-
|
21
|
-
|
83
|
+
# @see Unbound::Context#io
|
84
|
+
def io
|
85
|
+
@ctx.io
|
22
86
|
end
|
23
87
|
|
88
|
+
# @see Unbound::Context#process
|
24
89
|
def process
|
25
90
|
@ctx.process
|
26
91
|
end
|
27
92
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@
|
93
|
+
# @param [Unbound::Query] query
|
94
|
+
def send_query(query)
|
95
|
+
if query.started?
|
96
|
+
raise QueryAlreadyStarted.new
|
97
|
+
end
|
98
|
+
@queries[query.object_id] = query
|
99
|
+
# Add all of our callbacks, if any have been registered.
|
100
|
+
query.on_start(*@callbacks_start) unless @callbacks_start.empty?
|
101
|
+
query.on_answer(*@callbacks_answer) unless @callbacks_answer.empty?
|
102
|
+
query.on_error(*@callbacks_error) unless @callbacks_error.empty?
|
103
|
+
query.on_cancel(*@callbacks_cancel)
|
104
|
+
query.on_finish(*@callbacks_finish)
|
105
|
+
oid_ptr = FFI::Pointer.new query.object_id
|
106
|
+
async_id = @ctx.resolve_async(
|
107
|
+
query.name,
|
108
|
+
query.rrtype,
|
109
|
+
query.rrclass,
|
110
|
+
@resolve_callback_func,
|
111
|
+
oid_ptr)
|
112
|
+
query.start!(async_id)
|
34
113
|
end
|
35
114
|
end
|
36
115
|
end
|
data/lib/unbound/result.rb
CHANGED
@@ -50,15 +50,7 @@ module Unbound
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
class Result < FFI::
|
54
|
-
include ResultLayout
|
55
|
-
|
56
|
-
def self.release(ptr)
|
57
|
-
Unbound.ub_resolve_free(ptr)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class ResultCast < FFI::Struct
|
53
|
+
class Result < FFI::Struct
|
62
54
|
include ResultLayout
|
63
55
|
end
|
64
56
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Unbound::CallbackArray do
|
4
|
+
before :each do
|
5
|
+
@cba = Unbound::CallbackArray.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#add_callback" do
|
9
|
+
it "should raise an ArgumentError if no callback is provided" do
|
10
|
+
expect {
|
11
|
+
@cba.add_callback()
|
12
|
+
}.to raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
it "should accept a callback as an argument" do
|
15
|
+
proc1 = Proc.new {}
|
16
|
+
@cba.add_callback(proc1)
|
17
|
+
expect(@cba).to eq([proc1])
|
18
|
+
end
|
19
|
+
it "should accept a callback as block" do
|
20
|
+
proc1 = Proc.new {}
|
21
|
+
@cba.add_callback(&proc1)
|
22
|
+
expect(@cba).to eq([proc1])
|
23
|
+
end
|
24
|
+
it "should accept multiple callbacks as arguments in order" do
|
25
|
+
proc1 = Proc.new {}
|
26
|
+
proc2 = Proc.new {}
|
27
|
+
proc3 = Proc.new {}
|
28
|
+
proc4 = Proc.new {}
|
29
|
+
proc5 = Proc.new {}
|
30
|
+
@cba.add_callback(proc1, proc2, proc3, proc4, proc5)
|
31
|
+
expect(@cba).to eq([proc1, proc2, proc3, proc4, proc5])
|
32
|
+
end
|
33
|
+
it "should preserve order" do
|
34
|
+
proc1 = Proc.new {}
|
35
|
+
proc2 = Proc.new {}
|
36
|
+
proc3 = Proc.new {}
|
37
|
+
proc4 = Proc.new {}
|
38
|
+
proc5 = Proc.new {}
|
39
|
+
@cba.add_callback(proc1)
|
40
|
+
@cba.add_callback(proc2)
|
41
|
+
@cba.add_callback(proc3)
|
42
|
+
@cba.add_callback(proc4)
|
43
|
+
@cba.add_callback(proc5)
|
44
|
+
expect(@cba).to eq([proc1, proc2, proc3, proc4, proc5])
|
45
|
+
end
|
46
|
+
it "should arguments and blocks at the same time" do
|
47
|
+
proc1 = Proc.new {}
|
48
|
+
proc2 = Proc.new {}
|
49
|
+
proc3 = Proc.new {}
|
50
|
+
proc4 = Proc.new {}
|
51
|
+
proc5 = Proc.new {}
|
52
|
+
@cba.add_callback(proc1, proc2, proc3, proc4, &proc5)
|
53
|
+
expect(@cba).to eq([proc1, proc2, proc3, proc4, proc5])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
describe "#call" do
|
57
|
+
it "should arguments and blocks at the same time" do
|
58
|
+
expect { |cb1|
|
59
|
+
expect { |cb2|
|
60
|
+
expect { |cb3|
|
61
|
+
expect { |cb4|
|
62
|
+
@cba.add_callback(cb1.to_proc, cb2.to_proc, cb3.to_proc, cb4.to_proc)
|
63
|
+
@cba.call(1,2,3)
|
64
|
+
}.to yield_with_args(1,2,3)
|
65
|
+
}.to yield_with_args(1,2,3)
|
66
|
+
}.to yield_with_args(1,2,3)
|
67
|
+
}.to yield_with_args(1,2,3)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/spec/context_spec.rb
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'unbound/context'
|
3
|
-
require 'unbound/exceptions'
|
4
|
-
require 'unbound/result'
|
5
|
-
require 'pp'
|
6
2
|
|
7
3
|
describe Unbound::Context do
|
8
4
|
before :each do
|
@@ -13,6 +9,15 @@ describe Unbound::Context do
|
|
13
9
|
@ctx.close unless @ctx.closed?
|
14
10
|
end
|
15
11
|
|
12
|
+
subject { @ctx }
|
13
|
+
its(:raise_on_noid?) { should be_false }
|
14
|
+
|
15
|
+
|
16
|
+
it "should let me indicate that it should raise an error if the error code is :noid" do
|
17
|
+
@ctx.raise_on_noid = true
|
18
|
+
expect(@ctx.raise_on_noid?).to be_true
|
19
|
+
end
|
20
|
+
|
16
21
|
describe "#closed?" do
|
17
22
|
it "should indicate whether the context is closed or not" do
|
18
23
|
expect(@ctx.closed?).to be_false
|
@@ -74,7 +79,6 @@ describe Unbound::Context do
|
|
74
79
|
expect(1).to be(1)
|
75
80
|
|
76
81
|
@cb_result_ptr = nil
|
77
|
-
@cb_result = nil
|
78
82
|
@cb_err = nil
|
79
83
|
@cb_mydata = nil
|
80
84
|
|
@@ -82,9 +86,6 @@ describe Unbound::Context do
|
|
82
86
|
@cb_mydata = mydata
|
83
87
|
@cb_err = err
|
84
88
|
@cb_result_ptr = result_ptr
|
85
|
-
if (@cb_err == 0)
|
86
|
-
@cb_result = Unbound::Result.new(@cb_result_ptr)
|
87
|
-
end
|
88
89
|
end
|
89
90
|
end
|
90
91
|
|
@@ -120,14 +121,21 @@ describe Unbound::Context do
|
|
120
121
|
end
|
121
122
|
|
122
123
|
it "should cancel a query" do
|
123
|
-
expect(@ctx.cancel_async_query(@async_id)).to be(
|
124
|
+
expect(@ctx.cancel_async_query(@async_id)).to be(:noerror)
|
124
125
|
end
|
125
126
|
|
126
127
|
it "should raise an APIError when canceling a non-existent query" do
|
128
|
+
@ctx.raise_on_noid = true
|
129
|
+
expect(@ctx.raise_on_noid?).to be_true
|
127
130
|
expect(lambda do
|
128
131
|
@ctx.cancel_async_query(@async_id + 1)
|
129
132
|
end).to raise_error(Unbound::APIError)
|
130
133
|
end
|
134
|
+
|
135
|
+
it "#raise_on_noid=false should raise an APIError when canceling a non-existent query" do
|
136
|
+
expect(@ctx.raise_on_noid?).to be_false
|
137
|
+
expect(@ctx.cancel_async_query(@async_id + 1)).to eq(:noid)
|
138
|
+
end
|
131
139
|
end
|
132
140
|
end
|
133
141
|
|
data/spec/query_spec.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Unbound::Query do
|
4
|
+
context "a query for google.com IN A" do
|
5
|
+
before :each do
|
6
|
+
@rrtype = 1
|
7
|
+
@rrclass = 1
|
8
|
+
@name = "google.com"
|
9
|
+
@query = Unbound::Query.new(@name, @rrtype, @rrclass)
|
10
|
+
end
|
11
|
+
|
12
|
+
subject {@query}
|
13
|
+
its(:name) { should eq("google.com") }
|
14
|
+
its(:rrtype) { should eq(1) }
|
15
|
+
its(:rrclass) { should eq(1) }
|
16
|
+
|
17
|
+
describe "#finished?" do
|
18
|
+
it "should be false by default" do
|
19
|
+
expect(@query.finished?).to be_false
|
20
|
+
end
|
21
|
+
it "should be true after #cancel! is called" do
|
22
|
+
@query.cancel!
|
23
|
+
expect(@query.finished?).to be_true
|
24
|
+
end
|
25
|
+
it "should be true after #error! is called" do
|
26
|
+
@query.error!(1234)
|
27
|
+
expect(@query.finished?).to be_true
|
28
|
+
end
|
29
|
+
it "should be true after #answer! is called" do
|
30
|
+
result = double("Result")
|
31
|
+
@query.answer!(result)
|
32
|
+
expect(@query.finished?).to be_true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#start!" do
|
37
|
+
it "should set the async_id" do
|
38
|
+
expect(@query.async_id).to be_nil
|
39
|
+
@query.start!(1234)
|
40
|
+
expect(@query.async_id).to eq(1234)
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "the query should be started after calling" do
|
44
|
+
@query.start!(1234)
|
45
|
+
expect(@query).to be_started
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#started?" do
|
50
|
+
it "should be false by default" do
|
51
|
+
expect(@query.started?).to be_false
|
52
|
+
end
|
53
|
+
it "should be true after start! is called" do
|
54
|
+
@query.start!(1234)
|
55
|
+
expect(@query.started?).to be_true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#on_start" do
|
60
|
+
it "should be called after start! is called" do
|
61
|
+
expect { |cb|
|
62
|
+
@query.on_start(cb.to_proc)
|
63
|
+
@query.start!(1234)
|
64
|
+
}.to yield_with_args(@query)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#error!" do
|
69
|
+
it "should require an error code as an argument" do
|
70
|
+
expect {
|
71
|
+
@query.error!
|
72
|
+
}.to raise_error(ArgumentError)
|
73
|
+
expect {
|
74
|
+
@query.error!(1234)
|
75
|
+
}.to_not raise_error
|
76
|
+
end
|
77
|
+
|
78
|
+
specify "the query finished after being called" do
|
79
|
+
@query.error!(1234)
|
80
|
+
expect(@query).to be_finished
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#answer!" do
|
85
|
+
it "should require a result as an argument" do
|
86
|
+
expect {
|
87
|
+
@query.answer!
|
88
|
+
}.to raise_error(ArgumentError)
|
89
|
+
result = double("Result")
|
90
|
+
expect {
|
91
|
+
@query.answer!(result)
|
92
|
+
}.to_not raise_error
|
93
|
+
end
|
94
|
+
|
95
|
+
specify "the query finished after being called" do
|
96
|
+
result = double("Result")
|
97
|
+
@query.answer!(result)
|
98
|
+
expect(@query).to be_finished
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#cancel!" do
|
103
|
+
it "should require that no arguments be specified" do
|
104
|
+
expect {
|
105
|
+
@query.cancel!(1234)
|
106
|
+
}.to raise_error(ArgumentError)
|
107
|
+
expect {
|
108
|
+
@query.cancel!()
|
109
|
+
}.to_not raise_error
|
110
|
+
end
|
111
|
+
|
112
|
+
specify "the query finished after being called" do
|
113
|
+
@query.cancel!
|
114
|
+
expect(@query).to be_finished
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
describe "#on_error" do
|
120
|
+
specify "callbacks should be called upon #error!, but not others" do
|
121
|
+
expect { |answer_cb|
|
122
|
+
expect { |error_cb|
|
123
|
+
expect { |cancel_cb|
|
124
|
+
@query.on_answer(&answer_cb)
|
125
|
+
@query.on_error(&error_cb)
|
126
|
+
@query.on_cancel(&cancel_cb)
|
127
|
+
@query.error!(999)
|
128
|
+
}.to_not yield_control
|
129
|
+
}.to yield_control
|
130
|
+
}.to_not yield_control
|
131
|
+
end
|
132
|
+
|
133
|
+
specify "callbacks should be called with the query object and error code" do
|
134
|
+
expect { |error_cb|
|
135
|
+
@query.on_error(&error_cb)
|
136
|
+
@query.error!(999)
|
137
|
+
}.to yield_with_args(@query, 999)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#on_answer" do
|
142
|
+
specify "callbacks should be called upon #answer!, but not others" do
|
143
|
+
result = double("Result")
|
144
|
+
expect { |answer_cb|
|
145
|
+
expect { |error_cb|
|
146
|
+
expect { |cancel_cb|
|
147
|
+
@query.on_answer(&answer_cb)
|
148
|
+
@query.on_error(&error_cb)
|
149
|
+
@query.on_cancel(&cancel_cb)
|
150
|
+
@query.answer!(result)
|
151
|
+
}.to_not yield_control
|
152
|
+
}.to_not yield_control
|
153
|
+
}.to yield_control
|
154
|
+
end
|
155
|
+
|
156
|
+
specify "callbacks should be called with the query object and result object" do
|
157
|
+
result = double("Result")
|
158
|
+
expect { |answer_cb|
|
159
|
+
@query.on_answer(&answer_cb)
|
160
|
+
@query.answer!(result)
|
161
|
+
}.to yield_with_args(@query, result)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#on_cancel" do
|
166
|
+
specify "callbacks should be called upon #cancel!, but not others" do
|
167
|
+
expect { |answer_cb|
|
168
|
+
expect { |error_cb|
|
169
|
+
expect { |cancel_cb|
|
170
|
+
@query.on_answer(&answer_cb)
|
171
|
+
@query.on_error(&error_cb)
|
172
|
+
@query.on_cancel(&cancel_cb)
|
173
|
+
@query.cancel!()
|
174
|
+
}.to yield_control
|
175
|
+
}.to_not yield_control
|
176
|
+
}.to_not yield_control
|
177
|
+
end
|
178
|
+
|
179
|
+
specify "callbacks should be called with the query object" do
|
180
|
+
expect { |cancel_cb|
|
181
|
+
@query.on_cancel(&cancel_cb)
|
182
|
+
@query.cancel!()
|
183
|
+
}.to yield_with_args(@query)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "#on_finish" do
|
188
|
+
it "should be called after cancel! is called" do
|
189
|
+
expect { |cb|
|
190
|
+
@query.on_finish(cb.to_proc)
|
191
|
+
@query.cancel!()
|
192
|
+
}.to yield_with_args(@query)
|
193
|
+
end
|
194
|
+
it "should be called after answer! is called" do
|
195
|
+
result = double("Result")
|
196
|
+
expect { |cb|
|
197
|
+
@query.on_finish(cb.to_proc)
|
198
|
+
@query.answer!(result)
|
199
|
+
}.to yield_with_args(@query)
|
200
|
+
end
|
201
|
+
it "should be called after error! is called" do
|
202
|
+
expect { |cb|
|
203
|
+
@query.on_finish(cb.to_proc)
|
204
|
+
@query.error!(999)
|
205
|
+
}.to yield_with_args(@query)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Unbound::Resolver do
|
4
|
+
before :each do
|
5
|
+
@context = Unbound::Context.new
|
6
|
+
@resolver = Unbound::Resolver.new(@context)
|
7
|
+
end
|
8
|
+
|
9
|
+
after :each do
|
10
|
+
@resolver.close unless @resolver.closed?
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#cancel_all" do
|
14
|
+
it "should cancel all queries" do
|
15
|
+
query1 = Unbound::Query.new("localhost", 1, 1)
|
16
|
+
expect { |cb|
|
17
|
+
query1.on_cancel(cb.to_proc)
|
18
|
+
@resolver.send_query(query1)
|
19
|
+
@resolver.cancel_all
|
20
|
+
}.to yield_control
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#outstanding_queries" do
|
25
|
+
it "should return the number of outstanding queries" do
|
26
|
+
expect(@resolver.outstanding_queries).to eq(0)
|
27
|
+
10.times do
|
28
|
+
query = Unbound::Query.new("localhost", 1, 1)
|
29
|
+
@resolver.send_query(query)
|
30
|
+
end
|
31
|
+
expect(@resolver.outstanding_queries).to eq(10)
|
32
|
+
@resolver.cancel_all
|
33
|
+
expect(@resolver.outstanding_queries).to eq(0)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#outstanding_queries?" do
|
38
|
+
it "should be false if there are no outstanding queries" do
|
39
|
+
expect(@resolver.outstanding_queries?).to be_false
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be true if there are any outstanding queries" do
|
43
|
+
query = Unbound::Query.new("localhost", 1, 1)
|
44
|
+
@resolver.send_query(query)
|
45
|
+
expect(@resolver.outstanding_queries?).to be_true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#closed?" do
|
50
|
+
it "should not be closed by default" do
|
51
|
+
expect(@resolver.closed?).to be_false
|
52
|
+
end
|
53
|
+
it "should be closed after calling #close" do
|
54
|
+
@resolver.close
|
55
|
+
expect(@resolver.closed?).to be_true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#close" do
|
60
|
+
it "should call #cancel_all" do
|
61
|
+
@resolver.should_receive(:cancel_all).and_call_original
|
62
|
+
@resolver.close
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should close the context" do
|
66
|
+
@context.should_receive(:close).and_call_original
|
67
|
+
@resolver.close
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#io" do
|
72
|
+
it "should return an IO object" do
|
73
|
+
expect(@resolver.io).to be_a(::IO)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#process" do
|
78
|
+
it "should call our callback" do
|
79
|
+
query1 = Unbound::Query.new("localhost", 1, 1)
|
80
|
+
expect { |cb|
|
81
|
+
query1.on_answer(cb.to_proc)
|
82
|
+
@resolver.send_query(query1)
|
83
|
+
io = @resolver.io
|
84
|
+
expect(::IO.select([io], nil, nil, 5)).to_not be_nil
|
85
|
+
@resolver.process
|
86
|
+
}.to yield_control
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#send_query" do
|
91
|
+
it "should raise an exception if the same query object is submitted twice" do
|
92
|
+
query1 = Unbound::Query.new("localhost", 1, 1)
|
93
|
+
@resolver.send_query(query1)
|
94
|
+
expect {@resolver.send_query(query1)}.to raise_error
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#on_start" do
|
99
|
+
specify "callbacks should be called when send_query is called" do
|
100
|
+
query = Unbound::Query.new("localhost", 1, 1)
|
101
|
+
expect { |cb|
|
102
|
+
@resolver.on_start(&cb)
|
103
|
+
@resolver.send_query(query)
|
104
|
+
}.to yield_with_args(query)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#on_answer" do
|
109
|
+
specify "callbacks should be called if the query has been answered" do
|
110
|
+
query = Unbound::Query.new("localhost", 1, 1)
|
111
|
+
result = double("Result")
|
112
|
+
expect { |cb|
|
113
|
+
@resolver.on_answer(&cb)
|
114
|
+
@resolver.send_query(query)
|
115
|
+
query.answer!(result)
|
116
|
+
}.to yield_with_args(query, result)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#on_error" do
|
121
|
+
specify "callbacks should be called if the query has an error" do
|
122
|
+
query = Unbound::Query.new("localhost", 1, 1)
|
123
|
+
result = double("Result")
|
124
|
+
expect { |cb|
|
125
|
+
@resolver.on_error(&cb)
|
126
|
+
@resolver.send_query(query)
|
127
|
+
query.error!(1234)
|
128
|
+
}.to yield_with_args(query, 1234)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#on_cancel" do
|
133
|
+
specify "callbacks should be called if the query has been canceled" do
|
134
|
+
query = Unbound::Query.new("localhost", 1, 1)
|
135
|
+
result = double("Result")
|
136
|
+
expect { |cb|
|
137
|
+
@resolver.on_cancel(&cb)
|
138
|
+
@resolver.send_query(query)
|
139
|
+
query.cancel!()
|
140
|
+
}.to yield_with_args(query)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "#on_finish" do
|
145
|
+
specify "callbacks should be called if the query is finished for any reason" do
|
146
|
+
query = Unbound::Query.new("localhost", 1, 1)
|
147
|
+
result = double("Result")
|
148
|
+
expect { |cb|
|
149
|
+
@resolver.on_finish(&cb)
|
150
|
+
@resolver.send_query(query)
|
151
|
+
query.cancel!()
|
152
|
+
}.to yield_with_args(query)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
data/spec/result_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Unbound::Result do
|
4
|
+
extend UnboundHelper
|
5
|
+
UDP_RESPONSE = hex2bin("44438180000100060000000003777777057961686f6f03636f6d0000010001c00c0005000100000121000f0666642d667033037767310162c010c02b000500010000012100090664732d667033c032c046000500010000003100150e64732d616e792d6670332d6c666203776131c036c05b000500010000012100120f64732d616e792d6670332d7265616cc06ac07c00010001000000310004628bb718c07c00010001000000310004628bb495")
|
6
|
+
|
7
|
+
describe "#to_resolv" do
|
8
|
+
it "should return nil if there is no data" do
|
9
|
+
result = Unbound::Result.new
|
10
|
+
expect(result.to_resolv).to be_nil
|
11
|
+
end
|
12
|
+
it "should return a resolv object if there is data" do
|
13
|
+
result = Unbound::Result.new
|
14
|
+
packet_ptr = FFI::MemoryPointer.from_string(UDP_RESPONSE)
|
15
|
+
result[:answer_packet] = packet_ptr
|
16
|
+
result[:answer_len] = packet_ptr.size
|
17
|
+
expect(result.to_resolv).to be_a(Resolv::DNS::Message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -7,9 +7,24 @@ module UnboundHelper
|
|
7
7
|
SPEC_ROOT = Pathname.new(__FILE__).dirname.expand_path
|
8
8
|
PROJECT_ROOT = (SPEC_ROOT + '../').expand_path
|
9
9
|
CONF_ROOT = SPEC_ROOT + 'conf'
|
10
|
-
|
10
|
+
|
11
|
+
def config_file(name)
|
11
12
|
(CONF_ROOT + name).to_s
|
12
13
|
end
|
14
|
+
module_function :config_file
|
15
|
+
|
16
|
+
def hex2bin(hexstring)
|
17
|
+
ret = "\x00" * (hexstring.length / 2)
|
18
|
+
ret.force_encoding("BINARY")
|
19
|
+
offset = 0
|
20
|
+
while offset < hexstring.length
|
21
|
+
hex_byte = hexstring[offset..(offset+1)]
|
22
|
+
ret.setbyte(offset/2, hex_byte.to_i(16))
|
23
|
+
offset += 2
|
24
|
+
end
|
25
|
+
ret
|
26
|
+
end
|
27
|
+
module_function :hex2bin
|
13
28
|
end
|
14
29
|
|
15
30
|
require 'simplecov'
|
@@ -28,4 +43,4 @@ RSpec.configure do |config|
|
|
28
43
|
end
|
29
44
|
end
|
30
45
|
|
31
|
-
|
46
|
+
require 'unbound'
|
metadata
CHANGED
@@ -1,57 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unbound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Ryan
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
15
|
-
version_requirements: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - '>='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
15
|
requirement: !ruby/object:Gem::Requirement
|
21
16
|
requirements:
|
22
17
|
- - '>='
|
23
18
|
- !ruby/object:Gem::Version
|
24
19
|
version: '0'
|
25
|
-
prerelease: false
|
26
20
|
type: :runtime
|
27
|
-
|
28
|
-
name: jeweler
|
21
|
+
prerelease: false
|
29
22
|
version_requirements: !ruby/object:Gem::Requirement
|
30
23
|
requirements:
|
31
24
|
- - '>='
|
32
25
|
- !ruby/object:Gem::Version
|
33
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jeweler
|
34
29
|
requirement: !ruby/object:Gem::Requirement
|
35
30
|
requirements:
|
36
31
|
- - '>='
|
37
32
|
- !ruby/object:Gem::Version
|
38
33
|
version: '0'
|
39
|
-
prerelease: false
|
40
34
|
type: :development
|
41
|
-
|
42
|
-
name: rake
|
35
|
+
prerelease: false
|
43
36
|
version_requirements: !ruby/object:Gem::Requirement
|
44
37
|
requirements:
|
45
38
|
- - '>='
|
46
39
|
- !ruby/object:Gem::Version
|
47
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
44
|
requirements:
|
50
45
|
- - '>='
|
51
46
|
- !ruby/object:Gem::Version
|
52
47
|
version: '0'
|
53
|
-
prerelease: false
|
54
48
|
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
55
|
description: Unbound DNS resolver bindings for Ruby
|
56
56
|
email: falter@gmail.com
|
57
57
|
executables: []
|
@@ -65,21 +65,29 @@ files:
|
|
65
65
|
- README.md
|
66
66
|
- Rakefile
|
67
67
|
- VERSION
|
68
|
+
- examples/resolve_async.rb
|
68
69
|
- lib/unbound.rb
|
69
70
|
- lib/unbound/bindings.rb
|
71
|
+
- lib/unbound/callback_array.rb
|
72
|
+
- lib/unbound/callbacks_mixin.rb
|
70
73
|
- lib/unbound/context.rb
|
71
74
|
- lib/unbound/exceptions.rb
|
75
|
+
- lib/unbound/query.rb
|
72
76
|
- lib/unbound/resolver.rb
|
73
77
|
- lib/unbound/result.rb
|
78
|
+
- spec/callback_array_spec.rb
|
74
79
|
- spec/conf/local_zone.conf
|
75
80
|
- spec/conf/test_config.conf
|
76
81
|
- spec/context_spec.rb
|
82
|
+
- spec/query_spec.rb
|
83
|
+
- spec/resolver_spec.rb
|
84
|
+
- spec/result_spec.rb
|
77
85
|
- spec/spec_helper.rb
|
78
86
|
homepage: http://github.com/justfalter/unbound-ruby
|
79
87
|
licenses:
|
80
88
|
- MIT
|
81
89
|
metadata: {}
|
82
|
-
post_install_message:
|
90
|
+
post_install_message:
|
83
91
|
rdoc_options: []
|
84
92
|
require_paths:
|
85
93
|
- lib
|
@@ -94,9 +102,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
102
|
- !ruby/object:Gem::Version
|
95
103
|
version: '0'
|
96
104
|
requirements: []
|
97
|
-
rubyforge_project:
|
98
|
-
rubygems_version: 2.
|
99
|
-
signing_key:
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 2.0.14
|
107
|
+
signing_key:
|
100
108
|
specification_version: 4
|
101
109
|
summary: Unbound DNS resolver bindings for Ruby
|
102
110
|
test_files: []
|