├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── lib ├── reversed.rb └── reversed │ └── version.rb ├── reversed.gemspec └── test ├── reversed_test.rb └── test_helper.rb /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | - uses: ruby/setup-ruby@v1 9 | with: 10 | ruby-version: 3.4 11 | bundler-cache: true 12 | - run: bundle exec rake test 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.5.0 (2024-10-22) 2 | 3 | - Dropped support for Ruby < 3.1 4 | 5 | ## 0.4.2 (2023-01-10) 6 | 7 | - Fixed error with timeout with Ruby 3.1+ 8 | 9 | ## 0.4.1 (2022-12-28) 10 | 11 | - Added `fallback` option 12 | 13 | ## 0.4.0 (2022-06-12) 14 | 15 | - Dropped support for Ruby < 2.7 16 | 17 | ## 0.3.0 (2019-10-28) 18 | 19 | - Added support for machines with IPv6 disabled 20 | - Raise `ArgumentError` for invalid IP 21 | 22 | ## 0.2.1 (2017-05-08) 23 | 24 | - Added `timeout` and `nameservers` options 25 | 26 | ## 0.2.0 (2017-01-09) 27 | 28 | - Removed unnecessary dependency 29 | 30 | ## 0.1.1 (2016-11-14) 31 | 32 | - Use machine nameservers 33 | 34 | ## 0.1.0 (2016-11-14) 35 | 36 | - First release 37 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | gem "rake" 6 | gem "minitest", ">= 5" 7 | gem "logger" # for net-dns 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2023 Andrew Kane 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reversed 2 | 3 | :earth_americas: Reverse DNS / IP Lookup for Ruby 4 | 5 | ```ruby 6 | Reversed.lookup("8.8.4.4") 7 | # "dns.google" 8 | ``` 9 | 10 | Works with IPv4 and IPv6 11 | 12 | ```ruby 13 | Reversed.lookup("2a03:2880:2110:df07:face:b00c::1") 14 | # "a.ns.facebook.com" 15 | ``` 16 | 17 | [![Build Status](https://github.com/ankane/reversed/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/reversed/actions) 18 | 19 | ## Installation 20 | 21 | Add this line to your application’s Gemfile: 22 | 23 | ```ruby 24 | gem "reversed" 25 | ``` 26 | 27 | ## Reference 28 | 29 | Set timeout 30 | 31 | ```ruby 32 | Reversed.lookup(ip, timeout: 3) 33 | ``` 34 | 35 | Set nameservers 36 | 37 | ```ruby 38 | Reversed.lookup(ip, nameservers: ["1.1.1.1"]) 39 | ``` 40 | 41 | Disable SOA fallback (only check PTR record) 42 | 43 | ```ruby 44 | Reversed.lookup(ip, fallback: false) 45 | ``` 46 | 47 | ## History 48 | 49 | View the [changelog](https://github.com/ankane/reversed/blob/master/CHANGELOG.md) 50 | 51 | ## Contributing 52 | 53 | Everyone is encouraged to help improve this project. Here are a few ways you can help: 54 | 55 | - [Report bugs](https://github.com/ankane/reversed/issues) 56 | - Fix bugs and [submit pull requests](https://github.com/ankane/reversed/pulls) 57 | - Write, clarify, or fix documentation 58 | - Suggest or add new features 59 | 60 | To get started with development: 61 | 62 | ```sh 63 | git clone https://github.com/ankane/reversed.git 64 | cd reversed 65 | bundle install 66 | bundle exec rake test 67 | ``` 68 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList["test/**/*_test.rb"] 8 | t.warning = false # for net-dns 9 | end 10 | 11 | task default: :test 12 | -------------------------------------------------------------------------------- /lib/reversed.rb: -------------------------------------------------------------------------------- 1 | # dependencies 2 | require "net/dns" 3 | 4 | # stdlib 5 | require "ipaddr" 6 | 7 | # modules 8 | require_relative "reversed/version" 9 | 10 | module Reversed 11 | def self.lookup(ip, timeout: 5, nameservers: nil, fallback: true) 12 | ip = ip.to_s 13 | unless ip.empty? 14 | begin 15 | # ensure valid ip 16 | ip = IPAddr.new(ip) 17 | rescue IPAddr::InvalidAddressError 18 | raise ArgumentError, "Invalid IP" 19 | end 20 | 21 | options = { 22 | retry_number: 3, 23 | udp_timeout: timeout 24 | } 25 | options[:nameservers] = nameservers if nameservers 26 | begin 27 | resolver = Net::DNS::Resolver.new(options).search(ip.reverse, Net::DNS::PTR) 28 | answer = resolver.answer.first 29 | if !answer && fallback 30 | answer = resolver.authority.first 31 | end 32 | if answer && !answer.value.empty? 33 | answer.value.split(" ").first[0..-2] 34 | end 35 | rescue Net::DNS::Resolver::NoResponseError 36 | nil 37 | rescue Errno::EAFNOSUPPORT 38 | options[:use_tcp] = true 39 | options[:tcp_timeout] = timeout 40 | retry 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/reversed/version.rb: -------------------------------------------------------------------------------- 1 | module Reversed 2 | VERSION = "0.5.0" 3 | end 4 | -------------------------------------------------------------------------------- /reversed.gemspec: -------------------------------------------------------------------------------- 1 | require_relative "lib/reversed/version" 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "reversed" 5 | spec.version = Reversed::VERSION 6 | spec.summary = "Reverse DNS / IP Lookup for Ruby" 7 | spec.homepage = "https://github.com/ankane/reversed" 8 | spec.license = "MIT" 9 | 10 | spec.author = "Andrew Kane" 11 | spec.email = "andrew@ankane.org" 12 | 13 | spec.files = Dir["*.{md,txt}", "{lib}/**/*"] 14 | spec.require_path = "lib" 15 | 16 | spec.required_ruby_version = ">= 3.1" 17 | 18 | spec.add_dependency "net-dns", ">= 0.20" 19 | end 20 | -------------------------------------------------------------------------------- /test/reversed_test.rb: -------------------------------------------------------------------------------- 1 | require_relative "test_helper" 2 | 3 | class ReversedTest < Minitest::Test 4 | def test_ipv4 5 | assert_equal "dns.google", Reversed.lookup("8.8.4.4") 6 | end 7 | 8 | def test_nameservers 9 | assert_equal "dns.google", Reversed.lookup("8.8.4.4", nameservers: ["8.8.8.8"]) 10 | end 11 | 12 | def test_timeout 13 | assert_nil Reversed.lookup("10.255.255.1", timeout: 0.0001) 14 | end 15 | 16 | def test_fallback 17 | assert_equal "darl.ns.cloudflare.com", Reversed.lookup("103.21.244.0") 18 | assert_nil Reversed.lookup("103.21.244.0", fallback: false) 19 | end 20 | 21 | def test_ipv6 22 | assert_equal "one.one.one.one", Reversed.lookup("2606:4700:4700::1111") 23 | # returns nil on CI 24 | assert_equal "a.ns.facebook.com", Reversed.lookup("2a03:2880:2110:df07:face:b00c::1") unless ENV["CI"] 25 | end 26 | 27 | def test_nil 28 | assert_nil Reversed.lookup(nil) 29 | end 30 | 31 | def test_invalid_ip 32 | assert_raises(ArgumentError) do 33 | Reversed.lookup("dns.google") 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | Bundler.require(:default) 3 | require "minitest/autorun" 4 | require "minitest/pride" 5 | --------------------------------------------------------------------------------