# frozen_string_literal: true # # Copyright (c) 2006-2023 Hal Brodigan (postmodern.mod3 at gmail.com) # # ronin-support is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ronin-support is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ronin-support. If not, see . # require 'ronin/support/network/ip' require 'ipaddr' module Ronin module Support module Network class IPRange # # Represents an arbitrary range of IP addresses. # # ## Examples # # range = Network::IPRange::Range.new('128.2.3.4','200.10.1.255') # range.each { |ip| puts ip } # # 128.2.3.4 # # 128.2.3.5 # # ... # # 200.10.1.254 # # 200.10.1.255 # # @since 1.0.0 # # @api public # class Range include Enumerable # The first IP address of the ASN IP range. # # @return [IP] attr_reader :begin alias first begin # The last IP address of the ASN IP range. # # @return [IP] attr_reader :end alias last end # The common prefix shared by the first and last IP addresses in the # ASN IP range. # # @return [String] attr_reader :prefix # The address family of the ASN IP range. # # @return [Socket::AF_INET, Socket::AF_INET6] attr_reader :family # # Initializes the ASN IP range. # # @param [String] first # The first IP address in the ASN IP range. # # @param [String] last # The last IP address in the ASN IP range. # # @raise [ArgumentError] # Both IP addresses must be either IPv4 addresses or IPv6 addresses. # # @api private # def initialize(first,last) @begin = IP.new(first) @end = IP.new(last) if (@begin.ipv4? && !@end.ipv4?) || (@begin.ipv6? && !@end.ipv6?) raise(ArgumentError,"must specify two IPv4 or IPv6 addresses: #{first.inspect} #{last.inspect}") end @prefix = self.class.prefix(first,last) @family = @begin.family @begin_uint = @begin.to_i @end_uint = @end.to_i end # # Finds the common prefix of two IP addresses. # # @param [String] first # The first IP address of the ASN IP range. # # @param [String] last # The last IP address of the ASN IP range. # # @return [String] # The common prefix shared by both IPs. # # @api private # def self.prefix(first,last) min_length = [first.length, last.length].min min_length.times do |index| if first[index] != last[index] return first[0,index] end end return first end # # Determines whether the IP range is an IPv4 range. # # @return [Boolean] # def ipv4? @begin.ipv4? end # # Determines whether the IP range is an IPv6 range. # # @return [Boolean] # def ipv6? @end.ipv6? end # # Determines if the given IP belongs to the ASN IP range. # # @param [IPAddr, String] ip # The IP to test. # # @return [Boolean] # Specifies whether the IP is or is not within the ASN IP range. # def include?(ip) ip = IPAddr.new(ip) unless ip.kind_of?(IPAddr) ip_uint = ip.to_i return (ip_uint >= @begin_uint) && (ip_uint <= @end_uint) end # # Enumerates over every IP address in the ASN IP range. # # @yield [ip] # If a block is given, it will be passed every IP within the ASN # IP range. # # @yieldparam [IP] ip # An IP within the ASN IP range. # # @return [Enumerator] # If no block is given, an Enumerator will be returned. # # @example # range = IPRange::Range.new('1.1.1.1','1.1.3.42') # range.each { |ip| puts ip } # # 1.1.1.1 # # 1.1.1.2 # # ... # # 1.1.3.41 # # 1.1.3.42 # def each return enum_for(__method__) unless block_given? ipaddr = @begin.clone (@begin_uint..@end_uint).each do |ip_uint| ipaddr.send(:set,ip_uint) yield ipaddr.to_s end return self end # # Compares the IP range to another object. # # @param [Object] other # The other object to compare to. # # @return [Boolean] # def ==(other) self.class == other.class && self.begin == other.begin && self.end == other.end end # # Converts the IP range to a String. # # @return [String] # def to_s "#{@begin} - #{@end}" end # # Inspects the IP range. # # @return [String] # def inspect "#<#{self.class}: #{self}>" end end end end end end