unbound 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|