How to check if an IP address is private (IPv6, IPv4, cidr)

```ruby require 'resolv' require 'ipaddr' class PrivateIP # https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces IPV4_NETWORKS = %w[0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.0.8/32 192.0.0.9/32 192.0.0.170/32 192.0.0.171/32 192.0.2.0/24 192.31.196.0/24 192.52.193.0/24 192.88.99.0/24 192.168.0.0/16 192.175.48.0/24 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24 240.0.0.0/4 255.255.255.255/32 224.0.0.0/24 239.0.0.0/8].map { |cidr| IPAddr.new(cidr) } # https://en.wikipedia.org/wiki/Unique_local_address IPV6_NETWORKS = %w[fd00::/8 fc00::/8 0000:0000:0000:0000:0000:0000:0000:0000/64].map { |cidr| IPAddr.new(cidr) } class InvalidHost < ArgumentError; end # Examples: # private?('127.0.0.1') => true # private?('localhost') => true # private?('google.com') => false def self.private?(ip_or_host) address = IPAddr.new(ip_or_host) if address.ipv4? IPV4_NETWORKS.any? { |cidr| cidr.include?(ip_or_host) } elsif address.ipv6? IPV6_NETWORKS.any? { |cidr| cidr.include?(ip_or_host) } else false end rescue IPAddr::InvalidAddressError private_host?(ip_or_host) end # Example: private_host?('localhost') => true def self.private_host?(host) host_ips(host).any? do |type, ips| ips.any? { |ip| private?(ip) } end end # Example: host_ips('localhost') => {ipv4: ['127.0.0.1'], ipv6: []} def self.host_ips(host) ipv4 = Resolv::DNS.new.getresources(host, Resolv::DNS::Resource::IN::A) ipv6 = Resolv::DNS.new.getresources(host, Resolv::DNS::Resource::IN::AAAA) raise InvalidHost, "unknown host: #{host}" if ipv4.empty? && ipv6.empty? { ipv4: ipv4.map { |r| r.address.to_s }, ipv6: ipv6.map { |r| r.address.to_s } } end end ``` ```ruby require 'test_helper' require 'private_ip' class PrivateIPTest < ActiveSupport::TestCase test "private?" do assert PrivateIP.private?('localhost') assert PrivateIP.private?('127.0.0.1') assert PrivateIP.private?('0.0.0.0') refute PrivateIP.private?('google.com') refute PrivateIP.private?('209.216.230.240') end test "private? (IPV6)" do assert PrivateIP.private?('fd7b:5886:20a0:11a0:1111:2222:3333:4444') assert PrivateIP.private?('::1') # Try http://[::1]:3000 in browser assert PrivateIP.private?('::') assert PrivateIP.private?('0:0:0:0:0:0:0:1') end test "private? (CIDR)" do PrivateIP::IPV4_NETWORKS.each do |cidr| assert PrivateIP.private?(cidr.to_s) end PrivateIP::IPV6_NETWORKS.each do |cidr| assert PrivateIP.private?(cidr.to_s) end end test "private_host?" do assert PrivateIP.private_host?('localhost') refute PrivateIP.private_host?('google.com') end test "host_ips" do assert_equal({ ipv4: ["127.0.0.1"], ipv6: [] }, PrivateIP.host_ips('localhost')) assert_equal({ ipv4: ["209.216.230.240"], ipv6: []}, PrivateIP.host_ips('news.ycombinator.com')) end test "host_ips (invalid)" do assert_raise PrivateIP::InvalidHost do PrivateIP.host_ips('https://google.com') end assert_raise PrivateIP::InvalidHost do PrivateIP.host_ips('127.0.0.1') end assert_raise PrivateIP::InvalidHost do PrivateIP.host_ips('::1') end assert_raise PrivateIP::InvalidHost do PrivateIP.host_ips('[::1]') end assert_raise PrivateIP::InvalidHost do PrivateIP.host_ips('::') end end end ```