# 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 'ipaddr' module Ronin module Support module Network class IPRange # # Represents CIDR notation IP ranges. # # ## Examples # # cidr = Network::IP::CIDR.new('10.0.0.1/24') # cidr.each { |ip puts } # # 10.0.0.0 # # 10.0.0.1 # # ... # # 10.0.0.254 # # 10.0.0.255 # # @api public # # @since 1.0.0 # class CIDR < IPAddr include Enumerable # The CIDR IP range string. # # @return [String] attr_reader :string # # Initializes and parses the CIDR range. # # @param [String] string # The CIDR range string to parse. # # @param [Integer] family # The address family for the CIDR range. This is mainly for # backwards compatibility with `IPAddr#initialize`. # def initialize(string,family=Socket::AF_UNSPEC) super(string,family) @string = string end # # Alias for {#initialize new}. # # @param [String] string # The CIDR range string to parse. # # @return [CIDR] # The parsed CIDR range. # # @see #initialize # def self.parse(string) new(string) end # Socket families and IP address sizes SIZES = { Socket::AF_INET => 32, Socket::AF_INET6 => 128 } # Socket families and IP address masks MASKS = { Socket::AF_INET => IN4MASK, Socket::AF_INET6 => IN6MASK } # # Calcualtes the CIDR range between two IP addresses. # # @param [String, IPAddr] first # The first IP address in the CIDR range. # # @param [String, IPAddr] last # The last IP Address in the CIDR range. # # @return [CIDR] # The CIDR range between the two IP addresses. # # @example # IPRange::CIDR.range("1.1.1.1","1.1.1.255") # # => # # def self.range(first,last) first_ip = case first when IPAddr then first else IPAddr.new(first) end last_ip = case last when IPAddr then last else IPAddr.new(last) end unless (first_ip.family == last_ip.family) raise(ArgumentError,"must specify two IPv4 or IPv6 addresses: #{first.inspect} #{last.inspect}") end num_bits = SIZES.fetch(first_ip.family) diff_bits = first_ip.to_i ^ last_ip.to_i if diff_bits > 0 prefix_length = num_bits - Math.log2(diff_bits).ceil return new("#{first_ip}/#{prefix_length}") else return new(first_ip.to_s) end end # # Enumerates over each IP address that is included in the addresses # netmask. Supports both IPv4 and IPv6 addresses. # # @param [String] string # The CIDR range string to parse and enumerate over. # # @yield [ip] # The block which will be passed every IP address covered be the # netmask of the IPAddr object. # # @yieldparam [String] ip # An IP address. # # @example # IPRange::CIDR.each('10.0.0.1/24') { |ip| puts ip } # # 10.0.0.0 # # 10.0.0.1 # # ... # # 10.0.0.254 # # 10.0.0.255 # def self.each(string,&block) new(string).each(&block) end # # Iterates over each IP address that is included in the addresses # netmask. Supports both IPv4 and IPv6 addresses. # # @yield [ip] # The block which will be passed every IP address covered be the # netmask of the IPAddr object. # # @yieldparam [String] ip # An IP address. # # @return [self] # # @example # cidr = IPAddr.new('10.1.1.1/24') # cidr.each { |ip| puts ip } # # 10.0.0.0 # # 10.0.0.1 # # ... # # 10.0.0.254 # # 10.0.0.255 # def each return enum_for(__method__) unless block_given? family_mask = MASKS[@family] (0..((~@mask_addr) & family_mask)).each do |i| yield _to_string(@addr | i) end return self end # # The first IP address of the CIDR Range. # # @return [String] # def first _to_string(@addr) end # # The last IP address of the CIDR range. # # @return [String] # def last _to_string(@addr | ~@mask_addr) end # # Converts the CIDR range back into a String. # # @return [String] # def to_s @string end # # Inspects the CIDR range. # # @return [String] # def inspect "#<#{self.class}: #{@string}>" end end end end end end