unbound 0.0.1
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 +7 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +20 -0
- data/README.md +77 -0
- data/Rakefile +35 -0
- data/VERSION +1 -0
- data/lib/unbound/bindings.rb +66 -0
- data/lib/unbound/context.rb +77 -0
- data/lib/unbound/exceptions.rb +26 -0
- data/lib/unbound/resolver.rb +36 -0
- data/lib/unbound/result.rb +66 -0
- data/lib/unbound.rb +3 -0
- data/spec/conf/local_zone.conf +5 -0
- data/spec/conf/test_config.conf +3 -0
- data/spec/context_spec.rb +148 -0
- data/spec/spec_helper.rb +31 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 44bef7bb342a48227c09e4c002a815bd4b7a4b47
|
4
|
+
data.tar.gz: a78682e4f9191e66a78e9a9c30da43602ec618ae
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2ac8d61b74fa8fc29e1f2d414d54782e70ad33acfdff9bc55e09b6d89b34f6557c21c5b2db2044b8873988d232707f3b8412accf2fe04c85cdb42231d4c8ba9b
|
7
|
+
data.tar.gz: 8a9af9603f3f472dad7d2fe38902fa5316f6288ca6e6c92acc2c8076db8449ee8e9b09c81279681061863ffe9f568f0318ae8c2db25cce204ab6f019c3e4e46e
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Michael Ryan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
## Synopsis
|
2
|
+
|
3
|
+
Ruby FFI bindings for [Unbound](http://www.unbound.net/), a validating, recursive, and caching DNS resolver. Specifically, we are binding to the [libunbound](http://www.unbound.net/documentation/libunbound.html) API, allowing a for a full Unbound DNS resolver to be embedded within a Ruby application.
|
4
|
+
|
5
|
+
These bindings allow for asynchronous and synchronous name resolution. You really should familiarize yourself with [libunbound](http://www.unbound.net/documentation/libunbound.html) in order to leverage this library. The current bindings will support versions of versions of libunbound distributed with Unbound 1.4.2 or newer.
|
6
|
+
|
7
|
+
## Code Example
|
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
|
+
fd = ctx.fd
|
23
|
+
io = IO.for_fd(fd)
|
24
|
+
|
25
|
+
google_id = ctx.resolve_async("www.google.com", 1, 1, CB)
|
26
|
+
yahoo_id = ctx.resolve_async("www.yahoo.com", 1, 1, CB)
|
27
|
+
github_id = ctx.resolve_async("www.github.com", 1, 1, CB)
|
28
|
+
|
29
|
+
# Keep looping until we haven't gotten any
|
30
|
+
while (::IO.select([io], nil, nil, 5))
|
31
|
+
ctx.process()
|
32
|
+
end
|
33
|
+
|
34
|
+
ctx.close()
|
35
|
+
puts "all done!"
|
36
|
+
```
|
37
|
+
|
38
|
+
outputs:
|
39
|
+
```
|
40
|
+
Got response rcode: 0, qname: www.google.com
|
41
|
+
Got response rcode: 0, qname: www.github.com
|
42
|
+
Got response rcode: 0, qname: www.yahoo.com
|
43
|
+
all done!
|
44
|
+
```
|
45
|
+
|
46
|
+
|
47
|
+
## Motivation
|
48
|
+
|
49
|
+
I needed to resolve millions of DNS queries, and didn't want to spend more than 5 seconds waiting for answers to each question. I had found myself subject to timeouts imposed by my recursive resolver, specifically that these were longer than the 5 seconds.My resolver was only capable of handling so many queries concurrently, so it was very easy to knock it over. I needed a way to cancel queries, freeing resources on my resolver, something that is not possible through the typical UDP DNS interface.
|
50
|
+
|
51
|
+
By creating bindings for libunbound, I gained access to the ability to cancel DNS queries (specifically ub_cancel), allowing my name resolution process to move forward at a fair clip.
|
52
|
+
|
53
|
+
## Installation
|
54
|
+
|
55
|
+
You'll need libunbound for Unbound 1.4.2 or greater. This can typically be achieved by installing the 'unbound' DNS package for your distribution.
|
56
|
+
|
57
|
+
To install this gem:
|
58
|
+
```
|
59
|
+
gem install unbound
|
60
|
+
```
|
61
|
+
|
62
|
+
## API Reference
|
63
|
+
|
64
|
+
|
65
|
+
## Tests
|
66
|
+
|
67
|
+
```
|
68
|
+
rake spec
|
69
|
+
```
|
70
|
+
|
71
|
+
## Contributors
|
72
|
+
|
73
|
+
* [Mike Ryan](https://github.com/justfalter)
|
74
|
+
|
75
|
+
## License
|
76
|
+
|
77
|
+
See LICENSE.txt
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development, :test)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "unbound"
|
16
|
+
gem.homepage = "http://github.com/justfalter/unbound-ruby"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Unbound DNS resolver bindings for Ruby}
|
19
|
+
gem.description = %Q{Unbound DNS resolver bindings for Ruby}
|
20
|
+
gem.email = "falter@gmail.com"
|
21
|
+
gem.authors = ["Mike Ryan"]
|
22
|
+
gem.files = Dir.glob("lib/**/*.rb") +
|
23
|
+
Dir.glob("spec/{*.rb}") +
|
24
|
+
Dir.glob("spec/conf/{*.conf}") +
|
25
|
+
%w(LICENSE.txt Gemfile README.md Rakefile VERSION)
|
26
|
+
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rspec/core'
|
31
|
+
require 'rspec/core/rake_task'
|
32
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
33
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
module Unbound
|
3
|
+
module Bindings
|
4
|
+
extend FFI::Library
|
5
|
+
|
6
|
+
LIBNAMES = ['unbound', 'libunbound.so', 'libunbound.so.2']
|
7
|
+
if ENV['LIBUNBOUND']
|
8
|
+
LIBNAMES.unshift(ENV['LIBUNBOUND'])
|
9
|
+
end
|
10
|
+
|
11
|
+
ffi_lib LIBNAMES
|
12
|
+
|
13
|
+
|
14
|
+
typedef :pointer, :mydata
|
15
|
+
typedef :pointer, :ub_ctx_ptr
|
16
|
+
typedef :pointer, :ub_result_ptr
|
17
|
+
typedef :pointer, :ub_result_ptrptr
|
18
|
+
|
19
|
+
callback :ub_callback_t, [:mydata, :int, :ub_result_ptr], :void
|
20
|
+
|
21
|
+
|
22
|
+
attach_function :ub_ctx_create, [], :ub_ctx_ptr
|
23
|
+
attach_function :ub_ctx_delete, [:ub_ctx_ptr], :void
|
24
|
+
|
25
|
+
attach_function :ub_ctx_set_option, [:ub_ctx_ptr, :string, :string], :int
|
26
|
+
attach_function :ub_ctx_get_option, [:ub_ctx_ptr, :string, :pointer], :int
|
27
|
+
attach_function :ub_ctx_config, [:ub_ctx_ptr, :string], :int
|
28
|
+
|
29
|
+
attach_function :ub_ctx_set_fwd, [:ub_ctx_ptr, :string], :int
|
30
|
+
attach_function :ub_ctx_resolvconf, [:ub_ctx_ptr, :string], :int
|
31
|
+
attach_function :ub_ctx_hosts, [:ub_ctx_ptr, :string], :int
|
32
|
+
|
33
|
+
attach_function :ub_ctx_add_ta, [:ub_ctx_ptr, :string], :int
|
34
|
+
attach_function :ub_ctx_add_ta_file, [:ub_ctx_ptr, :string], :int
|
35
|
+
|
36
|
+
attach_function :ub_ctx_trustedkeys, [:ub_ctx_ptr, :string], :int
|
37
|
+
|
38
|
+
attach_function :ub_ctx_debugout, [:ub_ctx_ptr, :pointer], :int # TODO: FILE pointer
|
39
|
+
attach_function :ub_ctx_debuglevel, [:ub_ctx_ptr, :int], :int
|
40
|
+
|
41
|
+
attach_function :ub_ctx_async, [:ub_ctx_ptr, :int], :int
|
42
|
+
|
43
|
+
attach_function :ub_poll, [:ub_ctx_ptr], :int
|
44
|
+
attach_function :ub_wait, [:ub_ctx_ptr], :int, :blocking => true
|
45
|
+
attach_function :ub_fd, [:ub_ctx_ptr], :int
|
46
|
+
attach_function :ub_process, [:ub_ctx_ptr], :int, :blocking => true
|
47
|
+
|
48
|
+
attach_function :ub_resolve, [:ub_ctx_ptr, :string, :int, :int, :ub_result_ptrptr], :int, :blocking => true
|
49
|
+
attach_function :ub_resolve_async, [:ub_ctx_ptr, :string, :int, :int, :mydata, :ub_callback_t, :pointer], :int
|
50
|
+
attach_function :ub_cancel, [:ub_ctx_ptr, :int], :int
|
51
|
+
|
52
|
+
attach_function :ub_resolve_free, [:ub_result_ptr], :void
|
53
|
+
attach_function :ub_strerror, [:int], :string
|
54
|
+
|
55
|
+
attach_function :ub_ctx_print_local_zones, [:ub_ctx_ptr], :int
|
56
|
+
|
57
|
+
attach_function :ub_ctx_zone_add, [:ub_ctx_ptr, :string, :string], :int
|
58
|
+
attach_function :ub_ctx_zone_remove, [:ub_ctx_ptr, :string], :int
|
59
|
+
|
60
|
+
attach_function :ub_ctx_data_add, [:ub_ctx_ptr, :string], :int
|
61
|
+
attach_function :ub_ctx_data_remove, [:ub_ctx_ptr, :string], :int
|
62
|
+
|
63
|
+
## This wasn't added until unbound 1.4.15
|
64
|
+
# attach_function :ub_version, [], :string
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'unbound/bindings'
|
2
|
+
require 'unbound/exceptions'
|
3
|
+
|
4
|
+
module Unbound
|
5
|
+
class Context
|
6
|
+
def initialize
|
7
|
+
@ub_ctx = Unbound::Bindings.ub_ctx_create()
|
8
|
+
end
|
9
|
+
|
10
|
+
def check_closed!
|
11
|
+
if @ub_ctx.nil?
|
12
|
+
raise ContextClosedError.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def raise_if_error!(retval)
|
17
|
+
if retval != 0
|
18
|
+
raise APIError.new(retval)
|
19
|
+
end
|
20
|
+
return retval
|
21
|
+
end
|
22
|
+
|
23
|
+
def closed?
|
24
|
+
@ub_ctx.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_config(filename)
|
28
|
+
check_closed!
|
29
|
+
raise_if_error!(Unbound::Bindings.ub_ctx_config(@ub_ctx, filename))
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_option(varname)
|
33
|
+
check_closed!
|
34
|
+
ret_ptr = FFI::MemoryPointer.new(:pointer)
|
35
|
+
raise_if_error!(Unbound::Bindings.ub_ctx_get_option(@ub_ctx, varname, ret_ptr))
|
36
|
+
ret_ptr.read_pointer().read_string()
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_option(varname, val)
|
40
|
+
check_closed!
|
41
|
+
unless varname.end_with?(':')
|
42
|
+
varname = varname + ":"
|
43
|
+
end
|
44
|
+
raise_if_error!(Unbound::Bindings.ub_ctx_set_option(@ub_ctx, varname, val))
|
45
|
+
end
|
46
|
+
|
47
|
+
def close
|
48
|
+
check_closed!
|
49
|
+
Unbound::Bindings.ub_ctx_delete(@ub_ctx)
|
50
|
+
@ub_ctx = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def process
|
54
|
+
check_closed!
|
55
|
+
raise_if_error!(Unbound::Bindings.ub_process(@ub_ctx))
|
56
|
+
end
|
57
|
+
|
58
|
+
def cancel_async_query(async_id)
|
59
|
+
check_closed!
|
60
|
+
raise_if_error!(Unbound::Bindings.ub_cancel(@ub_ctx, async_id))
|
61
|
+
end
|
62
|
+
|
63
|
+
def fd
|
64
|
+
check_closed!
|
65
|
+
Unbound::Bindings.ub_fd(@ub_ctx)
|
66
|
+
end
|
67
|
+
|
68
|
+
def resolve_async(name, rrtype, rrclass, callback, private_data = nil)
|
69
|
+
check_closed!
|
70
|
+
async_id_ptr = FFI::MemoryPointer.new :int
|
71
|
+
raise_if_error!(
|
72
|
+
Unbound::Bindings.ub_resolve_async(
|
73
|
+
@ub_ctx, name, rrtype, rrclass, private_data, callback, async_id_ptr))
|
74
|
+
return async_id_ptr.get_int(0)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'unbound/bindings'
|
2
|
+
|
3
|
+
module Unbound
|
4
|
+
class Error < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
# An APIError is the result of a failure in an unbound api call. It expects
|
8
|
+
# an error code and translates that into a human-readable string.
|
9
|
+
class APIError < Error
|
10
|
+
attr_reader :error_code
|
11
|
+
def initialize(error_code)
|
12
|
+
@error_code = error_code
|
13
|
+
msg = Unbound::Bindings.ub_strerror(@error_code)
|
14
|
+
super(msg)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Indicates that an API call was attempted, but no context was provided.
|
19
|
+
class MissingContextError < Error
|
20
|
+
end
|
21
|
+
|
22
|
+
# Indicates that the context was closed (for Unbound::Context)
|
23
|
+
class ContextClosedError < MissingContextError
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'unbound/bindings'
|
2
|
+
require 'unbound/result'
|
3
|
+
require 'unbound/context'
|
4
|
+
|
5
|
+
module Unbound
|
6
|
+
class Resolver
|
7
|
+
def initialize()
|
8
|
+
@ctx = Unbound::Context.new
|
9
|
+
@ctx.load_config("unbound/etc/unbound.conf")
|
10
|
+
|
11
|
+
cb = Proc.new { |a,b,c| nil }
|
12
|
+
self.query("localhost", 1, 1, cb)
|
13
|
+
@ctx.wait()
|
14
|
+
end
|
15
|
+
|
16
|
+
def close
|
17
|
+
@ctx.close
|
18
|
+
end
|
19
|
+
|
20
|
+
def fd
|
21
|
+
@ctx.fd
|
22
|
+
end
|
23
|
+
|
24
|
+
def process
|
25
|
+
@ctx.process
|
26
|
+
end
|
27
|
+
|
28
|
+
def cancel(async_id)
|
29
|
+
@ctx.cancel_async_query(async_id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def query(name, rrtype, rrclass, callback, private_data = nil)
|
33
|
+
@ctx.resolve_async(name, rrtype, rrclass, callback, private_data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'resolv'
|
3
|
+
module Unbound
|
4
|
+
module ResultLayout
|
5
|
+
# TODO Figure out how to determine version of libunbound and,
|
6
|
+
# thus, proper structure of ub_result
|
7
|
+
def self.included(base)
|
8
|
+
base.class_eval do
|
9
|
+
layout(
|
10
|
+
:qname, :string, # char* qname
|
11
|
+
:qtype, :int, # int qtype
|
12
|
+
:qclass, :int, # int qclass
|
13
|
+
|
14
|
+
:data, :pointer, # char** data
|
15
|
+
:len, :pointer, # int* len
|
16
|
+
|
17
|
+
:canonname, :string, # char* canonname
|
18
|
+
|
19
|
+
:rcode, :int, # int rcode
|
20
|
+
|
21
|
+
:answer_packet, :pointer, # void* answer_packet
|
22
|
+
:answer_len, :int, # int answer_len
|
23
|
+
|
24
|
+
:havedata, :int, # int havedata
|
25
|
+
|
26
|
+
:secure, :int, # int secure
|
27
|
+
|
28
|
+
:bogus, :int, # int bogus
|
29
|
+
:why_bogus, :string, # char* why_bogus
|
30
|
+
|
31
|
+
# This wasn't added until unbound version 1.4.20
|
32
|
+
#:ttl, :int # int ttl
|
33
|
+
)
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def answer_packet
|
39
|
+
if self[:answer_len] <= 0
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
self[:answer_packet].read_bytes(self[:answer_len])
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_resolv
|
46
|
+
if ap = answer_packet()
|
47
|
+
return Resolv::DNS::Message.decode(ap)
|
48
|
+
end
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Result < FFI::ManagedStruct
|
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
|
62
|
+
include ResultLayout
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
data/lib/unbound.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'unbound/context'
|
3
|
+
require 'unbound/exceptions'
|
4
|
+
require 'unbound/result'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
describe Unbound::Context do
|
8
|
+
before :each do
|
9
|
+
@ctx = Unbound::Context.new
|
10
|
+
end
|
11
|
+
|
12
|
+
after :each do
|
13
|
+
@ctx.close unless @ctx.closed?
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#closed?" do
|
17
|
+
it "should indicate whether the context is closed or not" do
|
18
|
+
expect(@ctx.closed?).to be_false
|
19
|
+
@ctx.close
|
20
|
+
expect(@ctx.closed?).to be_true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#close" do
|
25
|
+
it "should delete the underlying context" do
|
26
|
+
Unbound::Bindings.should_receive(:ub_ctx_delete)
|
27
|
+
@ctx.close
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should raise an Unbound::ContextClosedError if we try to close it more than once" do
|
31
|
+
@ctx.close
|
32
|
+
expect(lambda do
|
33
|
+
@ctx.close
|
34
|
+
end).to raise_error(Unbound::ContextClosedError)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should raise an Unbound::ContextClosedError if we make an API call after the context is closed." do
|
38
|
+
@ctx.close
|
39
|
+
expect(lambda do
|
40
|
+
@ctx.fd
|
41
|
+
end).to raise_error(Unbound::ContextClosedError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should let me set and retrieve unbound configuration options" do
|
46
|
+
@ctx.set_option("do-ip6", "no")
|
47
|
+
expect(@ctx.get_option("do-ip6")).to eq("no")
|
48
|
+
@ctx.set_option("do-ip6", "yes")
|
49
|
+
expect(@ctx.get_option("do-ip6")).to eq("yes")
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#load_config" do
|
53
|
+
it "should load a proper unbound configuration file" do
|
54
|
+
@ctx.set_option("do-ip6", "no")
|
55
|
+
expect(@ctx.get_option("do-ip6")).to eq("no")
|
56
|
+
@ctx.load_config(UnboundHelper.config_file("test_config.conf"))
|
57
|
+
expect(@ctx.get_option("do-ip6")).to eq("yes")
|
58
|
+
expect(@ctx.get_option("version")).to eq("test config loaded!")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should raise an Unbound::APIError if the configuration file was not found" do
|
62
|
+
expect(lambda do
|
63
|
+
@ctx.load_config(UnboundHelper.config_file("this_config_doesnt_exist.conf"))
|
64
|
+
end).to raise_error(Unbound::APIError)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
shared_examples_for "an asynchronous name resolution method" do
|
69
|
+
# expects:
|
70
|
+
# @processing_proc = A Proc
|
71
|
+
# @ctx
|
72
|
+
before :each do
|
73
|
+
@ctx.load_config(UnboundHelper.config_file("local_zone.conf"))
|
74
|
+
expect(1).to be(1)
|
75
|
+
|
76
|
+
@cb_result_ptr = nil
|
77
|
+
@cb_result = nil
|
78
|
+
@cb_err = nil
|
79
|
+
@cb_mydata = nil
|
80
|
+
|
81
|
+
@cb = Proc.new do |mydata, err, result_ptr|
|
82
|
+
@cb_mydata = mydata
|
83
|
+
@cb_err = err
|
84
|
+
@cb_result_ptr = result_ptr
|
85
|
+
if (@cb_err == 0)
|
86
|
+
@cb_result = Unbound::Result.new(@cb_result_ptr)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "resolving mycomputer.local IN A" do
|
92
|
+
before :each do
|
93
|
+
@async_id = @ctx.resolve_async("mycomputer.local", 1, 1, @cb)
|
94
|
+
@processing_proc.call()
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should have an error in the callback" do
|
98
|
+
expect(@cb_err).to be(0)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should have a result_ptr" do
|
102
|
+
expect(@cb_result_ptr).not_to be_nil
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "result.to_resolv" do
|
106
|
+
subject {Unbound::Result.new(@cb_result_ptr).to_resolv}
|
107
|
+
its(:rcode) {should eq(0)}
|
108
|
+
|
109
|
+
it "should have the proper answer" do
|
110
|
+
expect(subject.answer.length).to be(1)
|
111
|
+
answer = subject.answer[0]
|
112
|
+
expect(answer[0].to_s).to eq("mycomputer.local")
|
113
|
+
expect(answer[2].address.to_s).to eq("192.0.2.51")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
describe "#cancel_async_query" do
|
118
|
+
before :each do
|
119
|
+
@async_id = @ctx.resolve_async("mycomputer.local", 1, 1, @cb)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should cancel a query" do
|
123
|
+
expect(@ctx.cancel_async_query(@async_id)).to be(0)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should raise an APIError when canceling a non-existent query" do
|
127
|
+
expect(lambda do
|
128
|
+
@ctx.cancel_async_query(@async_id + 1)
|
129
|
+
end).to raise_error(Unbound::APIError)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
describe "asynchronous query processing via select/process" do
|
136
|
+
before :each do
|
137
|
+
@processing_proc = lambda do
|
138
|
+
fd = @ctx.fd
|
139
|
+
io = IO.for_fd(fd)
|
140
|
+
ret = ::IO.select([io], nil, nil, 5)
|
141
|
+
expect(ret).not_to be_nil
|
142
|
+
@ctx.process
|
143
|
+
end
|
144
|
+
end
|
145
|
+
it_should_behave_like "an asynchronous name resolution method"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup :default, :test
|
4
|
+
|
5
|
+
module UnboundHelper
|
6
|
+
require 'pathname'
|
7
|
+
SPEC_ROOT = Pathname.new(__FILE__).dirname.expand_path
|
8
|
+
PROJECT_ROOT = (SPEC_ROOT + '../').expand_path
|
9
|
+
CONF_ROOT = SPEC_ROOT + 'conf'
|
10
|
+
def self.config_file(name)
|
11
|
+
(CONF_ROOT + name).to_s
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'simplecov'
|
16
|
+
SimpleCov.start
|
17
|
+
|
18
|
+
|
19
|
+
SimpleCov.start do
|
20
|
+
project_root = RSpec::Core::RubyProject.root
|
21
|
+
add_filter UnboundHelper::PROJECT_ROOT.join('spec').to_s
|
22
|
+
add_filter UnboundHelper::PROJECT_ROOT.join('.gem').to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
RSpec.configure do |config|
|
26
|
+
config.expect_with :rspec do |c|
|
27
|
+
c.syntax = :expect
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unbound
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Ryan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jeweler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
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
|
+
description: Unbound DNS resolver bindings for Ruby
|
56
|
+
email: falter@gmail.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files:
|
60
|
+
- LICENSE.txt
|
61
|
+
- README.md
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- VERSION
|
68
|
+
- lib/unbound.rb
|
69
|
+
- lib/unbound/bindings.rb
|
70
|
+
- lib/unbound/context.rb
|
71
|
+
- lib/unbound/exceptions.rb
|
72
|
+
- lib/unbound/resolver.rb
|
73
|
+
- lib/unbound/result.rb
|
74
|
+
- spec/conf/local_zone.conf
|
75
|
+
- spec/conf/test_config.conf
|
76
|
+
- spec/context_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
homepage: http://github.com/justfalter/unbound-ruby
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.0.14
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Unbound DNS resolver bindings for Ruby
|
102
|
+
test_files: []
|