Created
October 3, 2009 02:51
-
-
Save danielsdeleo/200358 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require "ipaddr" | |
| require "ip_error" | |
| module IpAddrsHelper | |
| def self.convert_port_string(port) | |
| port = port.to_s | |
| case port | |
| when /\A\d+\s*:\s*\d+\Z/ | |
| Range.new(port.split(':').first.to_i, port.split(':').last.to_i) | |
| when /\A\d+[\s+\d,]+\d+\Z/ | |
| port.split(',').collect {|port| port.strip.to_i } | |
| when /\A\d+\Z/ | |
| port.to_i | |
| else | |
| nil #makes validation fail if model validates_presence_of (which is a good thing) | |
| end | |
| end | |
| def self.normalize_ip_address(string) | |
| begin | |
| ip_addr = assert_valid_ip_addr(string) | |
| ip_addr.to_string_with_cidr | |
| rescue IpError::InvalidAddressString | |
| nil # makes validation fail if model validates_presence_of | |
| end | |
| end | |
| # raises an ArgumentError if +string+ is not a valid IP Address, | |
| # returns a new IPAddr object otherwise | |
| def self.assert_valid_ip_addr(string) | |
| begin | |
| IPAddr.new(string) | |
| rescue ArgumentError | |
| raise IpError::InvalidAddressString, "IP address was not valid" | |
| end | |
| end | |
| module IpAddrInstanceMethods | |
| #returns the IP Address as a string in CIDR notation, such as: "192.168.123.0/24" | |
| def to_string_with_cidr | |
| return "#{self.to_s}/#{self.cidr_mask}" | |
| end | |
| #returns the CIDR format mask as an integer between 0 and 32 for IPv4, | |
| #between 0 and 128 for IPv6. | |
| def cidr_mask | |
| if cidr_mask = @mask_addr.to_s(2).index('0') | |
| return cidr_mask | |
| else | |
| eval_for_ip4_ip6(Proc.new {32}, Proc.new {128}) | |
| end | |
| end | |
| def host_digits | |
| eval_for_ip4_ip6(Proc.new {32 - cidr_mask}, Proc.new {128 - cidr_mask}) | |
| end | |
| def network_name_as_int | |
| self.to_i - self.to_i.modulo(host_digits) | |
| end | |
| def network_name | |
| eval_for_ip4_ip6(Proc.new {network_name_as_int.to_ip4_addr}, Proc.new {network_name_as_int.to_ip6_addr}) | |
| end | |
| def broadcast_address_as_int | |
| network_name_as_int + ((2 ** host_digits) -1) | |
| end | |
| def broadcast_address | |
| eval_for_ip4_ip6(Proc.new {broadcast_address_as_int.to_ip4_addr}, Proc.new {broadcast_address_as_int.to_ip6_addr}) | |
| end | |
| # use instead of IPAddr's include? method, which incorrectly returns true for things like: | |
| # IPAddr.new("10.0.0.0/24").include?(IPAddr.new("10.0.0.0/13")) | |
| def include_network?(other_ip_addr) | |
| (self.network_name_as_int <= other_ip_addr.network_name_as_int) && (self.broadcast_address_as_int >= other_ip_addr.network_name_as_int) | |
| end | |
| def overlaps_network?(other_ip_addr) | |
| (self.network_name_as_int <= other_ip_addr.network_name_as_int) && (self.broadcast_address_as_int >= other_ip_addr.network_name_as_int) || | |
| (self.network_name_as_int <= other_ip_addr.broadcast_address_as_int) && (self.broadcast_address_as_int >= other_ip_addr.network_name_as_int) | |
| end | |
| private | |
| # takes two procs as arguments, calling the first for IPv4 addresses and the second for IPv6 | |
| def eval_for_ip4_ip6(ip4_proc, ip6_proc) | |
| case @family | |
| when ::Socket::AF_INET | |
| ip4_proc.call | |
| when ::Socket::AF_INET6 | |
| ip6_proc.call | |
| else | |
| raise ArgumentError, "Unsupported address family" | |
| end | |
| end | |
| end | |
| module StringInstanceMethods | |
| def to_ip4_range | |
| ip_string_pieces = self.split("/") | |
| lower_bound = IPAddr.new(ip_string_pieces.first).to_i | |
| if ip_string_pieces.length == 2 | |
| ip_range = (2 ** (32 - ip_string_pieces.last.to_i)) - 1 # we subtract 1 b/c 10.0.0.0/24 = 10.0.0.0 to 10.0.0.255 | |
| upper_bound = lower_bound + ip_range | |
| else | |
| upper_bound = lower_bound | |
| end | |
| return (lower_bound..upper_bound) | |
| end | |
| end | |
| module IntegerInstanceMethods | |
| def to_ip6_addr | |
| octet_array = Array.new #not really octets | |
| for i in 1..8 | |
| octet = (self.modulo(16 ** (4 * i))) / (16 ** ((i - 1) * 4)) | |
| octet_array.unshift(octet.to_s(16)) | |
| end | |
| # this needs a regexp to turn dead:0:0:beef to dead::beef, best implemented as an extension to IPAddr | |
| return IPAddr.new(octet_array.join(":")).to_s | |
| end | |
| def to_ip4_addr | |
| octet_array = Array.new | |
| for i in 1..4 | |
| octet_array.unshift((self.modulo(16 ** (2 * i))) / (16 ** ((i - 1) * 2))) | |
| end | |
| return octet_array.join(".") | |
| end | |
| end | |
| end | |
| IPAddr.send(:include, IpAddrsHelper::IpAddrInstanceMethods) | |
| String.send(:include, IpAddrsHelper::StringInstanceMethods) | |
| Integer.send(:include, IpAddrsHelper::IntegerInstanceMethods) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module IpError | |
| class InvalidAddressString < StandardError | |
| end | |
| class InvalidRoute < StandardError | |
| end | |
| class InvalidGateway < StandardError | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment