From 7414eaae843411f4d9e333cf5cfa39b72934a187 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 23 Jul 2024 12:19:22 +0900 Subject: [PATCH 1/2] Rewrite IPAddr.new.include? with pure Ruby bit operation --- lib/uri/generic.rb | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb index bdd3666..a5c9598 100644 --- a/lib/uri/generic.rb +++ b/lib/uri/generic.rb @@ -10,7 +10,6 @@ require_relative 'common' autoload :IPSocket, 'socket' -autoload :IPAddr, 'ipaddr' module URI @@ -1564,15 +1563,26 @@ def self.use_proxy?(hostname, addr, port, no_proxy) # :nodoc: return false if dothostname.end_with?(".#{p_host.downcase}") end if addr - begin - return false if IPAddr.new(p_host).include?(addr) - rescue IPAddr::InvalidAddressError - next - end + return false if addr == p_host || in_ip_range?(addr, p_host) end end } true end + + private + + def self.ip_to_i(ip) + ip.split('.').map(&:to_i).pack('C*').unpack1('N') + end + + def self.in_ip_range?(ip, range) + return false unless range.include?('/') + + base, bits = range.split('/') + bits = bits.to_i + mask = (0xFFFFFFFF << (32 - bits)) & 0xFFFFFFFF + (ip_to_i(ip) & mask) == (ip_to_i(base) & mask) + end end end From e80b474e3000120bdb8b1cf5392b45a2073df244 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 26 Jul 2024 14:33:10 +0800 Subject: [PATCH 2/2] Support IPv6 range --- lib/uri/generic.rb | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb index a5c9598..683149f 100644 --- a/lib/uri/generic.rb +++ b/lib/uri/generic.rb @@ -1572,17 +1572,45 @@ def self.use_proxy?(hostname, addr, port, no_proxy) # :nodoc: private + def self.expand_ipv6(ip) + if ip.include?('::') + parts = ip.split('::') + left = parts[0].split(':') + right = parts[1] ? parts[1].split(':') : [] + middle = ['0'] * (8 - left.size - right.size) + (left + middle + right).join(':') + else + ip + end + end + def self.ip_to_i(ip) - ip.split('.').map(&:to_i).pack('C*').unpack1('N') + if ip.include?(':') + expanded_ip = expand_ipv6(ip) + segments = expanded_ip.split(':').map { |seg| seg.rjust(4, '0') } + hex_ip = segments.join + hex_ip.to_i(16) + else + ip.split('.').map(&:to_i).pack('C*').unpack1('N') + end end - + def self.in_ip_range?(ip, range) return false unless range.include?('/') - + base, bits = range.split('/') bits = bits.to_i - mask = (0xFFFFFFFF << (32 - bits)) & 0xFFFFFFFF - (ip_to_i(ip) & mask) == (ip_to_i(base) & mask) + + if base.include?(':') + # IPv6 + expanded_base = expand_ipv6(base) + mask = (1 << 128) - (1 << (128 - bits)) + (ip_to_i(ip) & mask) == (ip_to_i(expanded_base) & mask) + else + # IPv4 + mask = (0xFFFFFFFF << (32 - bits)) & 0xFFFFFFFF + (ip_to_i(ip) & mask) == (ip_to_i(base) & mask) + end end end end