├── .github └── dependabot.yml ├── .gitignore ├── .rspec ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── flareon ├── examples └── debug.rb ├── flareon.gemspec ├── flareon.png ├── lib ├── flareon.rb └── flareon │ └── version.rb └── spec ├── flareon_spec.rb └── spec_helper.rb /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: pry 11 | versions: 12 | - 0.14.0 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | rvm: 4 | - 2.5.0 5 | before_install: gem install bundler -v 1.16.1 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kgruber1@emich.edu. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in flareon.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | flareon (1.0.5) 5 | command_lion (= 2.0.1) 6 | httparty (>= 0.16.2, < 0.19.0) 7 | parallel (>= 1.12.1, < 1.21.0) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | coderay (1.1.2) 13 | command_lion (2.0.1) 14 | diff-lcs (1.4.4) 15 | httparty (0.18.1) 16 | mime-types (~> 3.0) 17 | multi_xml (>= 0.5.2) 18 | method_source (1.0.0) 19 | mime-types (3.3.1) 20 | mime-types-data (~> 3.2015) 21 | mime-types-data (3.2020.1104) 22 | multi_xml (0.6.0) 23 | parallel (1.20.0) 24 | pry (0.13.1) 25 | coderay (~> 1.1) 26 | method_source (~> 1.0) 27 | rake (13.0.3) 28 | rspec (3.10.0) 29 | rspec-core (~> 3.10.0) 30 | rspec-expectations (~> 3.10.0) 31 | rspec-mocks (~> 3.10.0) 32 | rspec-core (3.10.0) 33 | rspec-support (~> 3.10.0) 34 | rspec-expectations (3.10.0) 35 | diff-lcs (>= 1.2.0, < 2.0) 36 | rspec-support (~> 3.10.0) 37 | rspec-mocks (3.10.0) 38 | diff-lcs (>= 1.2.0, < 2.0) 39 | rspec-support (~> 3.10.0) 40 | rspec-support (3.10.0) 41 | 42 | PLATFORMS 43 | ruby 44 | 45 | DEPENDENCIES 46 | bundler (~> 2.0) 47 | flareon! 48 | pry 49 | rake (~> 13.0) 50 | rspec (~> 3.0) 51 | 52 | BUNDLED WITH 53 | 1.16.1 54 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Kent Gruber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flareon 2 | > A cloudflare DNS over HTTPs resolver client library. 3 | 4 |

5 | Pokemon, gott'a catch 'em all! 6 |

7 | 8 | Cloudflare’s DNS over HTTPs [endpoint](https://cloudflare-dns.com) supports [JSON format](https://developers.cloudflare.com/1.1.1.1/dns-over-https/json-format/) for querying [DNS](https://en.wikipedia.org/wiki/Domain_Name_System) data -- and this gem helps to use it! 9 | 10 | ## Installation 11 | 12 | $ gem install flareon 13 | 14 | ## Usage 15 | 16 | Perform a basic DNS query over HTTPs: 17 | ```ruby 18 | Flareon.query("google.com") 19 | # => { 20 | # "Status"=>0, 21 | # "TC"=>false, 22 | # "RD"=>true, 23 | # "RA"=>true, 24 | # "AD"=>false, 25 | # "CD"=>false, 26 | # "Question"=>[{"name"=>"google.com.", "type"=>1}], 27 | # "Answer"=>[{"name"=>"google.com.", "type"=>1, "TTL"=>83, "data"=>"172.217.1.46"}] 28 | # } 29 | ``` 30 | 31 | Single-threaded DNS query (IPv6) over HTTPs for multiple domains: 32 | ```ruby 33 | domains = ["google.com", "github.com", "microsoft.com", "apple.com"] 34 | 35 | results = Flareon.batch_query(domains, type: "AAAA") 36 | ``` 37 | 38 | Multi-threaded DNS query (IPv4) over HTTPs for multiple domains: 39 | ```ruby 40 | domains = ["google.com", "github.com", "microsoft.com", "apple.com"] 41 | 42 | results = Flareon.batch_query_multithreaded(domains, threads: 4) 43 | ``` 44 | 45 | Get the raw JSON response: 46 | ```ruby 47 | json = Flareon.query("google.com", json: true) 48 | ``` 49 | 50 | Specify [DNS query type](https://en.wikipedia.org/wiki/List_of_DNS_record_types): 51 | ```ruby 52 | Flareon.query("google.com", type: "A") 53 | Flareon.query("google.com", type: "AAAA") 54 | Flareon.query("google.com", type: "MX") 55 | ``` 56 | 57 | The `nslookup` method is an alias for the `query` method: 58 | ```ruby 59 | Flareon.nslookup("google.com") 60 | ``` 61 | 62 | The `dig` method is an alias for the `query` method: 63 | ```ruby 64 | Flareon.dig("google.com") 65 | ``` 66 | 67 | Check if a given name is resolvable: 68 | ```ruby 69 | Flareon.resolve?("google.com") 70 | # => true 71 | ``` 72 | 73 | Resolve a given domain to an IP address: 74 | ```ruby 75 | Flareon.resolve("google.com") 76 | # => "172.217.1.46" 77 | ``` 78 | 79 | Resolve a given domain to an IPv4 address: 80 | ```ruby 81 | Flareon.resolve("google.com") 82 | Flareon.resolve("google.com", type: "A") 83 | Flareon.resolve("google.com", type: 1) 84 | ``` 85 | 86 | Resolve a given domain to an IPv6 address: 87 | ```ruby 88 | Flareon.resolve("google.com", type: "AAAA") 89 | Flareon.resolve("google.com", type: 28) 90 | ``` 91 | 92 | Resolve a give domain to all IPv4 and IPv6 addresses: 93 | ```ruby 94 | ip_addresses = Flareon.resolve_all("google.com") 95 | 96 | # or 97 | 98 | Flareon.resolve_all("google.com") do |ip_address| 99 | # do something with each ip_address 100 | puts ip_address 101 | end 102 | ``` 103 | 104 | ## Inspiration 105 | 106 | Saw [hrbrmstr](https://github.com/hrbrmstr) working on [dnsflare](https://github.com/hrbrmstr/dnsflare) and wanted something similiar in Ruby. 107 | 108 | ## License 109 | 110 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 111 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /bin/flareon: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "command_lion" 5 | require "flareon" 6 | require "yaml" 7 | 8 | CommandLion::App.run do 9 | name "flareon" 10 | version Flareon::VERSION 11 | description "A cloudflare DNS over HTTPs resolver command-line client." 12 | 13 | command :query do 14 | description "Query cloudflare's DNS over HTTPs resolver for the given domain(s)." 15 | type :strings 16 | action do 17 | threads = options[:threads].argument 18 | type = options[:type].argument 19 | json = options[:json].given? ? true : false 20 | stream = options[:stream].given? ? true : false 21 | 22 | if stream 23 | Flareon.batch_query_multithreaded(arguments, type: type, threads: threads) do |result| 24 | if json 25 | puts result.to_json 26 | else 27 | puts result.to_yaml 28 | end 29 | end 30 | else 31 | if json 32 | puts Flareon.batch_query_multithreaded(arguments, type: type, threads: threads).to_json 33 | else 34 | puts Flareon.batch_query_multithreaded(arguments, type: type, threads: threads).to_yaml 35 | end 36 | end 37 | end 38 | 39 | option :type do 40 | description "specify the type of record to query for (A, AAAA, MX, ect)" 41 | flag "--type" 42 | type :string 43 | default "A" 44 | end 45 | 46 | option :threads do 47 | description "specify the number of threads to use" 48 | flag "--threads" 49 | type :integer 50 | default 1 51 | end 52 | 53 | option :json do 54 | description "use json output instead of YAML" 55 | flag "--json" 56 | end 57 | 58 | option :stream do 59 | description "stream results to STDOUT as they are available" 60 | flag "--stream" 61 | end 62 | end 63 | 64 | command :resolve do 65 | description "Resolve the given domain(s) to an IPv4 ( or IPv6 ) address." 66 | type :strings 67 | 68 | action do 69 | type = options[:ipv6].given? ? "AAAA" : "A" 70 | all = options[:all].given? ? true : false 71 | json = options[:json].given? ? true : false 72 | stream = options[:stream].given? ? true : false 73 | 74 | if stream 75 | arguments.each do |argument| 76 | result = {} 77 | result[argument] = Flareon.resolve(argument, type: type) 78 | if json 79 | puts result.to_json 80 | else 81 | puts result.to_yaml 82 | end 83 | end 84 | else 85 | results = {} 86 | arguments.each do |argument| 87 | results[argument] = Flareon.resolve(argument, type: type) 88 | end 89 | if json 90 | puts results.to_json 91 | else 92 | puts results.to_yaml 93 | end 94 | end 95 | end 96 | 97 | option :ipv6 do 98 | description "specify only ipv6" 99 | flag "--ipv6" 100 | end 101 | 102 | option :all do 103 | description "resolve all possible IP addresses (both ipv4 and ipv6)" 104 | flag "--all" 105 | end 106 | 107 | option :json do 108 | description "use json output instead of YAML" 109 | flag "--json" 110 | end 111 | 112 | option :stream do 113 | description "stream results to STDOUT as they are available" 114 | flag "--stream" 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /examples/debug.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'flareon' 3 | require 'pry' 4 | 5 | binding.pry 6 | -------------------------------------------------------------------------------- /flareon.gemspec: -------------------------------------------------------------------------------- 1 | 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "flareon/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "flareon" 8 | spec.version = Flareon::VERSION 9 | spec.authors = ["Kent 'picat' Gruber"] 10 | spec.email = ["kgruber1@emich.edu"] 11 | 12 | spec.summary = %q{A cloudflare DNS resolver client library.} 13 | #spec.description = %q{TODO: Write a longer description or delete this line.} 14 | spec.homepage = "https://github.com/picatz/flareon" 15 | spec.license = "MIT" 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 18 | f.match(%r{^(test|spec|features)/}) 19 | end 20 | spec.bindir = "bin" 21 | spec.executable = "flareon" 22 | spec.require_paths = ['lib'] 23 | 24 | spec.add_dependency 'httparty', ">= 0.16.2", "< 0.19.0" 25 | spec.add_dependency 'parallel', ">= 1.12.1", "< 1.21.0" 26 | spec.add_dependency 'command_lion', "2.0.1" 27 | 28 | spec.add_development_dependency "bundler", "~> 2.0" 29 | spec.add_development_dependency "rake", "~> 13.0" 30 | spec.add_development_dependency "rspec", "~> 3.0" 31 | spec.add_development_dependency "pry" 32 | end 33 | -------------------------------------------------------------------------------- /flareon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/picatz/flareon/8c88fda7bbd90e67d643793ad3d02e2bdfe3eb68/flareon.png -------------------------------------------------------------------------------- /lib/flareon.rb: -------------------------------------------------------------------------------- 1 | require "httparty" 2 | require "parallel" 3 | require "flareon/version" 4 | 5 | module Flareon 6 | # Base URL for cloudflare's DNS over HTTPs endpoint. 7 | URL = "https://cloudflare-dns.com/dns-query".freeze 8 | # Special *ct* value to add to every query. 9 | CT = "application/dns-json".freeze 10 | # Header sent in every query. 11 | HEADER = { "Content-Type": "application/json" }.freeze 12 | 13 | # Query the DNS over HTTPs endpoint. 14 | # 15 | # == IPv4 DNS query 16 | # result = Flareon.query("google.com") 17 | # # or 18 | # result = Flareon.query("google.com", type: "A") 19 | # # or 20 | # result = Flareon.query("google.com", type: 1) 21 | # 22 | # == IPv6 DNS query 23 | # result = Flareon.query("google.com", type: "AAAA") 24 | # # or 25 | # result = Flareon.query("google.com", type: 28) 26 | # 27 | # == Mail exchange record query 28 | # result = Flareon.query("google.com", type: "MX") 29 | # # or 30 | # result = Flareon.query("google.com", type: 15) 31 | # == Raw JSON response ( not parsed ) 32 | # result = Flareon.query("google.com", json: true) 33 | # 34 | def self.query(name, type: "A", json: false) 35 | buffer = StringIO.new 36 | query = { name: name, type: type, ct: CT } 37 | response = HTTParty.get(URL, stream_body: true, query: query, headers: HEADER) do |fragment| 38 | buffer << fragment 39 | end 40 | if response.success? 41 | buffer = buffer.string 42 | if json 43 | return buffer 44 | else 45 | return JSON.parse(buffer) 46 | end 47 | else 48 | raise "Got HTTP response code #{response.code}" 49 | end 50 | end 51 | 52 | # Alias the query method with both nslookup and dig. 53 | # This keeps a similiar API to: github.com/hrbrmstr/dnsflare 54 | class << self 55 | alias nslookup query 56 | alias dig query 57 | end 58 | 59 | # Check if a given domain name is resolvable to an IPv4 or IPv6 address. 60 | def self.resolve?(name, type: "A") 61 | return true if Flareon.resolve(name, type: type) 62 | false 63 | rescue 64 | return false 65 | end 66 | 67 | # Resolve a given domain name to a IP address. 68 | def self.resolve(name, type: "A") 69 | unless type == "A" || type == "AAAA" 70 | raise "Unsupported resolve type!" 71 | end 72 | resp = Flareon.query(name, type: type) 73 | if resp["Status"] == 0 74 | return nil unless resp.has_key? "Answer" 75 | return resp["Answer"][0]["data"] 76 | else 77 | raise resp 78 | end 79 | end 80 | 81 | # Resolve a given domain name to all addresses (IPv4/IPv6). 82 | # 83 | # == Resolve all IP addresses 84 | # results = Flareon.resolve_all("google.com") 85 | # # or 86 | # results = Flareon.resolve_all("google.com", type: :both) 87 | # # or 88 | # Flareon.resolve_all("google.com") do |ip_address| 89 | # # do something with the ip_address 90 | # puts ip_address 91 | # end 92 | # == Resolve all IPv4 addresses 93 | # results = Flareon.resolve_all("google.com", type: 1) 94 | # # or 95 | # results = Flareon.resolve_all("google.com", type: "A") 96 | # 97 | def self.resolve_all(name, type: :both) 98 | unless type == "A" || type == "AAAA" || type == :both 99 | raise "Unsupported resolve type!" 100 | end 101 | results = [] unless block_given? 102 | case type 103 | when "A", "AAAA" 104 | resp = Flareon.query(name, type: type) 105 | if resp["Status"] == 0 106 | resp["Answer"].each do |answer| 107 | if block_given? 108 | yield answer["data"] 109 | else 110 | results << answer["data"] 111 | end 112 | end 113 | else 114 | raise resp 115 | end 116 | when :both 117 | if block_given? 118 | Flareon.resolve_all(name, type: "A") { |ip| yield ip } 119 | Flareon.resolve_all(name, type: "AAAA") { |ip| yield ip } 120 | else 121 | Flareon.resolve_all(name, type: "A") { |ip| results << ip } 122 | Flareon.resolve_all(name, type: "AAAA") { |ip| results << ip } 123 | end 124 | end 125 | return results unless block_given? 126 | end 127 | 128 | def self.batch_query(names, type: "A", json: false) 129 | results = [] unless block_given? 130 | names.each do |name| 131 | if block_given? 132 | yield Flareon.query(name, type: type, json: json) 133 | else 134 | results << Flareon.query(name, type: type, json: json) 135 | end 136 | end 137 | return results unless block_given? 138 | end 139 | 140 | def self.batch_query_multithreaded(names, type: "A", json: false, threads: 4) 141 | results = Parallel.map(names, in_threads: threads) do |name| 142 | Flareon.query(name, type: type, json: json) 143 | end 144 | if block_given? 145 | results.each { |result| yield result } 146 | end 147 | return results 148 | end 149 | 150 | end 151 | -------------------------------------------------------------------------------- /lib/flareon/version.rb: -------------------------------------------------------------------------------- 1 | module Flareon 2 | VERSION = "1.0.5" 3 | end 4 | -------------------------------------------------------------------------------- /spec/flareon_spec.rb: -------------------------------------------------------------------------------- 1 | require 'resolv' 2 | 3 | RSpec.describe Flareon do 4 | it "has a version number" do 5 | expect(Flareon::VERSION).not_to be nil 6 | end 7 | 8 | it "can query google.com" do 9 | resp = Flareon.query("google.com") 10 | expect(resp["Status"]).to eq(0) 11 | expect(resp["TC"]).to be(true).or be(false) 12 | expect(resp["RD"]).to be(true).or be(false) 13 | expect(resp["RA"]).to be(true).or be(false) 14 | expect(resp["AD"]).to be(true).or be(false) 15 | expect(resp["CD"]).to be(true).or be(false) 16 | expect(resp["Question"]).to be_a(Array) 17 | expect(resp["Question"][0]).to be_a(Hash) 18 | expect(resp["Answer"]).to be_a(Array) 19 | expect(resp["Answer"][0]).to be_a(Hash) 20 | end 21 | 22 | it "can resolve google.com" do 23 | resp = Flareon.resolve?("google.com") 24 | expect(resp).to eq(true) 25 | end 26 | 27 | it "can resolve google.com to an IPv4 address" do 28 | case Flareon.resolve("google.com") 29 | when Resolv::IPv4::Regex 30 | true 31 | else 32 | false 33 | end 34 | end 35 | 36 | it "can resolve google.com to an IPv6 address" do 37 | case Flareon.resolve("google.com", type: "AAAA") 38 | when Resolv::IPv4::Regex 39 | true 40 | else 41 | false 42 | end 43 | end 44 | 45 | it "can query for google.com and return the raw JSON response" do 46 | resp = Flareon.query("google.com", json: true) 47 | hash = JSON.parse(resp) 48 | expect(hash["Status"]).to eq(0) 49 | end 50 | 51 | it "has alias 'dig' for query method" do 52 | resp1 = Flareon.query("google.com") 53 | resp2 = Flareon.dig("google.com") 54 | expect(resp1["Status"]).to eq(resp2["Status"]) 55 | expect(resp1["TC"]).to eq(resp2["TC"]) 56 | expect(resp1["RD"]).to eq(resp2["RD"]) 57 | expect(resp1["RA"]).to eq(resp2["RA"]) 58 | expect(resp1["AD"]).to eq(resp2["AD"]) 59 | expect(resp1["CD"]).to eq(resp2["CD"]) 60 | expect(resp1["CD"]).to eq(resp2["CD"]) 61 | expect(resp1["CD"]).to eq(resp2["CD"]) 62 | expect(resp1["Question"]).to eq(resp2["Question"]) 63 | expect(resp1["Answer"][0]["name"]).to eq(resp2["Answer"][0]["name"]) 64 | end 65 | 66 | it "has alias 'nslookup' for query method" do 67 | resp1 = Flareon.query("google.com") 68 | resp2 = Flareon.dig("google.com") 69 | expect(resp1["Status"]).to eq(resp2["Status"]) 70 | expect(resp1["TC"]).to eq(resp2["TC"]) 71 | expect(resp1["RD"]).to eq(resp2["RD"]) 72 | expect(resp1["RA"]).to eq(resp2["RA"]) 73 | expect(resp1["AD"]).to eq(resp2["AD"]) 74 | expect(resp1["CD"]).to eq(resp2["CD"]) 75 | expect(resp1["CD"]).to eq(resp2["CD"]) 76 | expect(resp1["CD"]).to eq(resp2["CD"]) 77 | expect(resp1["Question"]).to eq(resp2["Question"]) 78 | expect(resp1["Answer"][0]["name"]).to eq(resp2["Answer"][0]["name"]) 79 | end 80 | 81 | it "has a single-threaded batch query method" do 82 | domains = ["google.com", "github.com", "microsoft.com", "apple.com"] 83 | results = Flareon.batch_query(domains) 84 | expect(results.size).to eq(domains.size) 85 | results.each do |result| 86 | expect(result["Status"]).to eq(0) 87 | end 88 | end 89 | 90 | it "has a multi-threaded batch query method" do 91 | domains = ["google.com", "github.com", "microsoft.com", "apple.com"] 92 | results = Flareon.batch_query_multithreaded(domains, threads: 4) do |result| 93 | expect(result["Status"]).to eq(0) 94 | end 95 | expect(results.size).to eq(domains.size) 96 | end 97 | 98 | end 99 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "flareon" 3 | 4 | RSpec.configure do |config| 5 | # Enable flags like --only-failures and --next-failure 6 | config.example_status_persistence_file_path = ".rspec_status" 7 | 8 | # Disable RSpec exposing methods globally on `Module` and `main` 9 | config.disable_monkey_patching! 10 | 11 | config.expect_with :rspec do |c| 12 | c.syntax = :expect 13 | end 14 | end 15 | --------------------------------------------------------------------------------