ip_address_simple 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,15 @@
1
+ == IP Address Simple
2
+ IpAddress is a class that makes common IP address handling a doddle.
3
+ It provides support for...
4
+
5
+ * interchange between string and integer IP representations
6
+ * masking / sub-netting
7
+ * IPv4 and IPv6
8
+
9
+ == Author
10
+ Copyright 2007 Andre Ben Hamou
11
+ mailto:andre@prfsa.com
12
+
13
+ == License
14
+ This library and all associated materials are release under the terms of
15
+ version 3 of the GNU Public License (http://www.gnu.org/copyleft/gpl.html).
data/lib/ip_address.rb ADDED
@@ -0,0 +1,118 @@
1
+ require "ip_address_range"
2
+
3
+ class IpAddress
4
+ IP_BITS = {4 => 32, 6 => 128}
5
+ IP_MAX = {4 => (1 << 32) - 1, 6 => (1 << 128) - 1}
6
+
7
+ # Takes an Integer or a String representation of an IP address and coerces it to a representation of the requested type (:integer / :string).
8
+ # If the version (4 / 6) is not specified then it will be guessed with guess_version.
9
+ def self.coerce(value, to_type, version = nil)
10
+ raise "unknown type #{to_type.inspect} requested" unless [:integer, :string].include?(to_type)
11
+ version ||= guess_version(value)
12
+
13
+ case value
14
+ when Integer
15
+ if to_type == :integer then value
16
+ elsif version == 4 then [24, 16, 8, 0].map { |shift| (value >> shift) & 255 }.join(".")
17
+ else sprintf("%.32x", value).scan(/.{4}/).join(":")
18
+ end
19
+ when String
20
+ if to_type == :string then value
21
+ elsif version == 4 then value.split(".").inject(0) { |total, octet| (total << 8) + octet.to_i }
22
+ else value.delete(":").to_i(16)
23
+ end
24
+ end
25
+ end
26
+
27
+ # Takes an Integer or a String and guesses whether it represents an IPv4 or IPv6 address.
28
+ # For an Integer, IPv4 is assumed unless the value is greater than IP_MAX[4].
29
+ # For a String, IPv6 is assumed if it contains at least one colon (:).
30
+ def self.guess_version(value)
31
+ case value
32
+ when Integer then value > IP_MAX[4] ? 6 : 4
33
+ when String then value =~ /:/ ? 6 : 4
34
+ end
35
+ end
36
+
37
+ # Takes an Integer bitcount (bits) and returns an appropriate masking Integer.
38
+ # For example, a /24 network (in IPv4) corresponds to a mask of 255.255.255.0 or the number 4294967040.
39
+ # If the version (4 / 6) is not specified then it will be assumed to be 4 unless bits > 32.
40
+ def self.mask_from_slash_bits(bits, version = nil)
41
+ raise "bits > 128" if bits > 128
42
+ version ||= bits > 32 ? 6 : 4
43
+
44
+ max = IP_MAX[version]
45
+ left_shift = IP_BITS[version] - bits
46
+ (max << left_shift) & max
47
+ end
48
+
49
+ # Takes an Integer or a String representation of a network mask and returns the number of addresses it encodes.
50
+ # If the version (4 / 6) is not specified then it will be guessed with guess_version.
51
+ def self.mask_size(value, version = nil)
52
+ version ||= guess_version(value)
53
+ (coerce(value, :integer, version) ^ IP_MAX[version]) + 1
54
+ end
55
+
56
+ include Comparable
57
+
58
+ attr_reader :integer, :string, :version
59
+
60
+ # Takes an Integer or a String representation of an IP address and creates a new IpAddress object with it.
61
+ # If the version (4 / 6) is not specified then it will be guessed with guess_version.
62
+ def initialize(value, version = nil)
63
+ @version = version || IpAddress.guess_version(value)
64
+ @integer = IpAddress.coerce(value, :integer, @version)
65
+ @string = IpAddress.coerce(value, :string, @version)
66
+ end
67
+
68
+ # Adds the specified Integer value to that of the IpAddress and returns a new IpAddress based on the sum.
69
+ def +(value)
70
+ IpAddress.new(@integer + value, @version)
71
+ end
72
+
73
+ # Subtracts the specified Integer value from that of the IpAddress and returns a new IpAddress based on the difference.
74
+ def -(value)
75
+ IpAddress.new(@integer - value, @version)
76
+ end
77
+
78
+ # Returns the range of IpAddresses in the specified /bits network. Basically a convenience wrapper around mask.
79
+ def /(bits)
80
+ mask(IpAddress.mask_from_slash_bits(bits, @version))
81
+ end
82
+
83
+ # Compares one IpAddress with another based on the Integer representation of their values.
84
+ def <=>(other)
85
+ @integer <=> other.integer
86
+ end
87
+
88
+ # Takes an Integer or a String representation of a network mask and returns the range of IpAddresses in that network.
89
+ def mask(value)
90
+ base_int = @integer & IpAddress.coerce(value, :integer, @version)
91
+ IpAddressRange.new(IpAddress.new(base_int, @version), IpAddress.new(base_int + IpAddress.mask_size(value, @version) - 1))
92
+ end
93
+
94
+ # Returns the next IpAddress after this one.
95
+ def succ
96
+ self + 1
97
+ end
98
+
99
+ def to_i
100
+ @integer
101
+ end
102
+
103
+ def to_s
104
+ @string
105
+ end
106
+ end
107
+
108
+ class Integer
109
+ def to_ip(version = nil)
110
+ IpAddress.new(self, version)
111
+ end
112
+ end
113
+
114
+ class String
115
+ def to_ip(version = nil)
116
+ IpAddress.new(self, version)
117
+ end
118
+ end
@@ -0,0 +1,5 @@
1
+ class IpAddressRange < Range
2
+ def size
3
+ last.integer - first.integer + 1
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ require "test/unit"
2
+ require "ip_address"
3
+
4
+ class TestIpAddress < Test::Unit::TestCase # :nodoc:
5
+ def setup
6
+ @ip = IpAddress.new("10.0.0.1")
7
+ end
8
+
9
+ def test_basics
10
+ [@ip, IpAddress.new(167772161)].each do |ip|
11
+ assert_equal(4, ip.version)
12
+ assert_equal(167772161, ip.integer)
13
+ assert_equal(167772161, ip.to_i)
14
+ assert_equal("10.0.0.1", ip.string)
15
+ assert_equal("10.0.0.1", ip.to_s)
16
+ end
17
+
18
+ ip = IpAddress.new(182735, 6)
19
+ assert_equal(6, ip.version)
20
+ assert_equal(182735, ip.integer)
21
+ assert_equal("0000:0000:0000:0000:0000:0000:0002:c9cf", ip.string)
22
+ end
23
+
24
+ def test_arithmetic
25
+ other = @ip + 256
26
+ assert_kind_of(IpAddress, other)
27
+ assert_equal("10.0.1.1", other.string)
28
+ other = @ip - 256
29
+ assert_kind_of(IpAddress, other)
30
+ assert_equal("9.255.255.1", other.string)
31
+ end
32
+
33
+ def test_comparison
34
+ other = @ip + 20
35
+ assert(@ip < other)
36
+ end
37
+
38
+ def test_conversion
39
+ assert_equal(@ip, "10.0.0.1".to_ip)
40
+ assert_equal(@ip, 167772161.to_ip)
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ require "test/unit"
2
+ require "ip_address"
3
+
4
+ class TestIpAddressRange < Test::Unit::TestCase # :nodoc:
5
+ def setup
6
+ @ip = IpAddress.new("10.0.0.1")
7
+ end
8
+
9
+ def test_range_masking
10
+ [@ip.mask("255.255.255.252"), @ip / 30].each do |range|
11
+ assert_kind_of(IpAddressRange, range)
12
+ assert_kind_of(IpAddress, range.first)
13
+ assert_equal("10.0.0.0", range.first.string)
14
+ assert_kind_of(IpAddress, range.last)
15
+ assert_equal("10.0.0.3", range.last.string)
16
+ assert_equal(4, range.size)
17
+ end
18
+ end
19
+ end
data/test/ts_all.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "test/unit"
2
+ require "tc_ip_address"
3
+ require "tc_ip_address_range"
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: ip_address_simple
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2007-10-13 00:00:00 +01:00
8
+ summary: Simple IP address manipulation library
9
+ require_paths:
10
+ - lib
11
+ email: andre@prfsa.com
12
+ homepage:
13
+ rubyforge_project: ip-addr-simple
14
+ description:
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Andre Ben Hamou
31
+ files:
32
+ - README
33
+ - lib/ip_address.rb
34
+ - lib/ip_address_range.rb
35
+ - test/tc_ip_address.rb
36
+ - test/tc_ip_address_range.rb
37
+ - test/ts_all.rb
38
+ test_files:
39
+ - test/tc_ip_address.rb
40
+ - test/tc_ip_address_range.rb
41
+ rdoc_options:
42
+ - --main
43
+ - README
44
+ extra_rdoc_files:
45
+ - README
46
+ executables: []
47
+
48
+ extensions: []
49
+
50
+ requirements: []
51
+
52
+ dependencies: []
53
+