ipaddress_2 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,75 @@
1
+ module IPAddress
2
+
3
+ #
4
+ # Mongoid field serialization
5
+ #
6
+ # IPAddress objects are converted to String
7
+ #
8
+ # IPAddress.mongoize IPAddress.parse("172.16.10.1")
9
+ # #=> "172.16.10.1"
10
+ #
11
+ # Prefix will be removed from host adresses
12
+ #
13
+ # IPAddress.mongoize "172.16.10.1/32"
14
+ # #=> "172.16.10.1"
15
+ #
16
+ # Prefix will be kept for network addresses
17
+ #
18
+ # IPAddress.mongoize "172.16.10.1/24"
19
+ # #=> "172.16.10.1/24"
20
+ #
21
+ # IPv6 addresses will be stored uncompressed to ease DB search and sorting
22
+ #
23
+ # IPAddress.mongoize "2001:db8::8:800:200c:417a"
24
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
25
+ # IPAddress.mongoize "2001:db8::8:800:200c:417a/64"
26
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
27
+ #
28
+ # Invalid addresses will be serialized as nil
29
+ #
30
+ # IPAddress.mongoize "invalid"
31
+ # #=> nil
32
+ # IPAddress.mongoize ""
33
+ # #=> nil
34
+ # IPAddress.mongoize 1
35
+ # #=> nil
36
+ # IPAddress.mongoize nil
37
+ # #=> nil
38
+ #
39
+ def self.mongoize(ipaddress)
40
+ ipaddress = self.parse(ipaddress) unless ipaddress.is_a?(IPAddress)
41
+ if ipaddress.bits.length == ipaddress.prefix
42
+ ipaddress.address
43
+ elsif ipaddress.is_a?(IPAddress::IPv6)
44
+ ipaddress.to_string_uncompressed
45
+ else
46
+ ipaddress.to_string
47
+ end
48
+ rescue ArgumentError
49
+ nil
50
+ end
51
+
52
+ #
53
+ # Mongoid field deserialization
54
+ #
55
+ def self.demongoize(string)
56
+ parse(string)
57
+ rescue ArgumentError
58
+ nil
59
+ end
60
+
61
+ #
62
+ # Delegates to IPAddress.mongoize
63
+ #
64
+ def self.evolve(ipaddress)
65
+ mongoize(ipaddress)
66
+ end
67
+
68
+ #
69
+ # Sends self object to IPAddress#mongoize
70
+ #
71
+ def mongoize
72
+ IPAddress.mongoize(self)
73
+ end
74
+
75
+ end
@@ -0,0 +1,285 @@
1
+ module IPAddress
2
+
3
+ #
4
+ # =NAME
5
+ #
6
+ # IPAddress::Prefix
7
+ #
8
+ # =SYNOPSIS
9
+ #
10
+ # Parent class for Prefix32 and Prefix128
11
+ #
12
+ # =DESCRIPTION
13
+ #
14
+ # IPAddress::Prefix is the parent class for IPAddress::Prefix32
15
+ # and IPAddress::Prefix128, defining some modules in common for
16
+ # both the subclasses.
17
+ #
18
+ # IPAddress::Prefix shouldn't be accesses directly, unless
19
+ # for particular needs.
20
+ #
21
+ class Prefix
22
+
23
+ include Comparable
24
+
25
+ attr_reader :prefix
26
+
27
+ #
28
+ # Creates a new general prefix
29
+ #
30
+ def initialize(num)
31
+ @prefix = num.to_i
32
+ end
33
+
34
+ #
35
+ # Returns a string with the prefix
36
+ #
37
+ def to_s
38
+ "#@prefix"
39
+ end
40
+ alias_method :inspect, :to_s
41
+
42
+ #
43
+ # Returns the prefix
44
+ #
45
+ def to_i
46
+ @prefix
47
+ end
48
+ alias_method :to_int, :to_i
49
+
50
+ #
51
+ # Provides support for Ruby type coercion
52
+ #
53
+ def coerce(other)
54
+ case other
55
+ when Integer
56
+ [other, @prefix]
57
+ else
58
+ other.coerce(@prefix).reverse!
59
+ end
60
+ end
61
+
62
+ #
63
+ # Compare the prefix
64
+ #
65
+ def <=>(oth)
66
+ if oth.is_a? Integer
67
+ @prefix <=> oth
68
+ else
69
+ x, y = oth.coerce(@prefix)
70
+ x <=> y
71
+ end
72
+ end
73
+
74
+ #
75
+ # Sums two prefixes or a prefix to a
76
+ # number, returns a Integer
77
+ #
78
+ def +(oth)
79
+ if oth.is_a? Integer
80
+ self.prefix + oth
81
+ else
82
+ x, y = oth.coerce(@prefix)
83
+ x + y
84
+ end
85
+ end
86
+
87
+ #
88
+ # Returns the difference between two
89
+ # prefixes, or a prefix and a number,
90
+ # as a Integer
91
+ #
92
+ def -(oth)
93
+ if oth.is_a? Integer
94
+ self.prefix - oth
95
+ else
96
+ x, y = oth.coerce(@prefix)
97
+ x - y
98
+ end
99
+ end
100
+
101
+ end # class Prefix
102
+
103
+
104
+ class Prefix32 < Prefix
105
+
106
+ IN4MASK = 0xffffffff
107
+
108
+ #
109
+ # Creates a new prefix object for 32 bits IPv4 addresses
110
+ #
111
+ # prefix = IPAddress::Prefix32.new 24
112
+ # #=> 24
113
+ #
114
+ def initialize(num)
115
+ unless (0..32).include? num
116
+ raise ArgumentError, "Prefix must be in range 0..32, got: #{num}"
117
+ end
118
+ super(num)
119
+ end
120
+
121
+ #
122
+ # Returns the length of the host portion
123
+ # of a netmask.
124
+ #
125
+ # prefix = Prefix32.new 24
126
+ #
127
+ # prefix.host_prefix
128
+ # #=> 8
129
+ #
130
+ def host_prefix
131
+ 32 - @prefix
132
+ end
133
+
134
+ #
135
+ # Transforms the prefix into a string of bits
136
+ # representing the netmask
137
+ #
138
+ # prefix = IPAddress::Prefix32.new 24
139
+ #
140
+ # prefix.bits
141
+ # #=> "11111111111111111111111100000000"
142
+ #
143
+ def bits
144
+ "%.32b" % to_u32
145
+ end
146
+
147
+ #
148
+ # Gives the prefix in IPv4 dotted decimal format,
149
+ # i.e. the canonical netmask we're all used to
150
+ #
151
+ # prefix = IPAddress::Prefix32.new 24
152
+ #
153
+ # prefix.to_ip
154
+ # #=> "255.255.255.0"
155
+ #
156
+ def to_ip
157
+ [bits].pack("B*").unpack("CCCC").join(".")
158
+ end
159
+
160
+ #
161
+ # An array of octets of the IPv4 dotted decimal
162
+ # format
163
+ #
164
+ # prefix = IPAddress::Prefix32.new 24
165
+ #
166
+ # prefix.octets
167
+ # #=> [255, 255, 255, 0]
168
+ #
169
+ def octets
170
+ to_ip.split(".").map{|i| i.to_i}
171
+ end
172
+
173
+ #
174
+ # Unsigned 32 bits decimal number representing
175
+ # the prefix
176
+ #
177
+ # prefix = IPAddress::Prefix32.new 24
178
+ #
179
+ # prefix.to_u32
180
+ # #=> 4294967040
181
+ #
182
+ def to_u32
183
+ (IN4MASK >> host_prefix) << host_prefix
184
+ end
185
+
186
+ #
187
+ # Shortcut for the octecs in the dotted decimal
188
+ # representation
189
+ #
190
+ # prefix = IPAddress::Prefix32.new 24
191
+ #
192
+ # prefix[2]
193
+ # #=> 255
194
+ #
195
+ def [](index)
196
+ octets[index]
197
+ end
198
+
199
+ #
200
+ # The hostmask is the contrary of the subnet mask,
201
+ # as it shows the bits that can change within the
202
+ # hosts
203
+ #
204
+ # prefix = IPAddress::Prefix32.new 24
205
+ #
206
+ # prefix.hostmask
207
+ # #=> "0.0.0.255"
208
+ #
209
+ def hostmask
210
+ [~to_u32].pack("N").unpack("CCCC").join(".")
211
+ end
212
+
213
+ #
214
+ # Creates a new prefix by parsing a netmask in
215
+ # dotted decimal form
216
+ #
217
+ # prefix = IPAddress::Prefix32::parse_netmask "255.255.255.0"
218
+ # #=> 24
219
+ #
220
+ def self.parse_netmask(netmask)
221
+ octets = netmask.split(".").map{|i| i.to_i}
222
+ num = octets.pack("C"*octets.size).unpack("B*").first.count "1"
223
+ return self.new(num)
224
+ end
225
+
226
+ end # class Prefix32 < Prefix
227
+
228
+ class Prefix128 < Prefix
229
+
230
+ #
231
+ # Creates a new prefix object for 128 bits IPv6 addresses
232
+ #
233
+ # prefix = IPAddress::Prefix128.new 64
234
+ # #=> 64
235
+ #
236
+ def initialize(num=128)
237
+ unless (0..128).include? num.to_i
238
+ raise ArgumentError, "Prefix must be in range 0..128, got: #{num}"
239
+ end
240
+ super(num.to_i)
241
+ end
242
+
243
+ #
244
+ # Transforms the prefix into a string of bits
245
+ # representing the netmask
246
+ #
247
+ # prefix = IPAddress::Prefix128.new 64
248
+ #
249
+ # prefix.bits
250
+ # #=> "1111111111111111111111111111111111111111111111111111111111111111"
251
+ # "0000000000000000000000000000000000000000000000000000000000000000"
252
+ #
253
+ def bits
254
+ "1" * @prefix + "0" * (128 - @prefix)
255
+ end
256
+
257
+ #
258
+ # Unsigned 128 bits decimal number representing
259
+ # the prefix
260
+ #
261
+ # prefix = IPAddress::Prefix128.new 64
262
+ #
263
+ # prefix.to_u128
264
+ # #=> 340282366920938463444927863358058659840
265
+ #
266
+ def to_u128
267
+ bits.to_i(2)
268
+ end
269
+
270
+ #
271
+ # Returns the length of the host portion
272
+ # of a netmask.
273
+ #
274
+ # prefix = Prefix128.new 96
275
+ #
276
+ # prefix.host_prefix
277
+ # #=> 32
278
+ #
279
+ def host_prefix
280
+ 128 - @prefix
281
+ end
282
+
283
+ end # class Prefix123 < Prefix
284
+
285
+ end # module IPAddress
@@ -0,0 +1,3 @@
1
+ module IPAddress
2
+ VERSION = "0.11.0"
3
+ end
@@ -0,0 +1,293 @@
1
+ #
2
+ # = IPAddress
3
+ #
4
+ # A ruby library to manipulate IPv4 and IPv6 addresses
5
+ #
6
+ #
7
+ # Package:: IPAddress
8
+ # Author:: Marco Ceresa <ceresa@ieee.org>
9
+ # License:: Ruby License
10
+ #
11
+ #--
12
+ #
13
+ #++
14
+
15
+ require 'ipaddress_2/ipv4'
16
+ require 'ipaddress_2/ipv6'
17
+ require 'ipaddress_2/mongoid' if defined?(Mongoid)
18
+
19
+ module IPAddress
20
+
21
+ NAME = "IPAddress"
22
+ GEM = "ipaddress_2"
23
+ AUTHORS = ["Marco Ceresa <ceresa@ieee.org>"]
24
+
25
+ #
26
+ # Parse the argument string to create a new
27
+ # IPv4, IPv6 or Mapped IP object
28
+ #
29
+ # ip = IPAddress.parse 167837953 # 10.1.1.1
30
+ # ip = IPAddress.parse "172.16.10.1/24"
31
+ # ip6 = IPAddress.parse "2001:db8::8:800:200c:417a/64"
32
+ # ip_mapped = IPAddress.parse "::ffff:172.16.10.1/128"
33
+ #
34
+ # All the object created will be instances of the
35
+ # correct class:
36
+ #
37
+ # ip.class
38
+ # #=> IPAddress::IPv4
39
+ # ip6.class
40
+ # #=> IPAddress::IPv6
41
+ # ip_mapped.class
42
+ # #=> IPAddress::IPv6::Mapped
43
+ #
44
+ def IPAddress::parse(str)
45
+
46
+ # Check if an int was passed
47
+ if str.kind_of? Integer
48
+ return IPAddress::IPv4.new(ntoa(str))
49
+ end
50
+
51
+ case str
52
+ when /:.+\./
53
+ IPAddress::IPv6::Mapped.new(str)
54
+ when /\./
55
+ IPAddress::IPv4.new(str)
56
+ when /:/
57
+ IPAddress::IPv6.new(str)
58
+ else
59
+ raise ArgumentError, "Unknown IP Address #{str}"
60
+ end
61
+ end
62
+
63
+ #
64
+ # Converts a unit32 to IPv4
65
+ #
66
+ # IPAddress::ntoa(167837953)
67
+ # #-> "10.1.1.1"
68
+ #
69
+ def self.ntoa(uint)
70
+ unless(uint.is_a? Numeric and uint <= 0xffffffff and uint >= 0)
71
+ raise(::ArgumentError, "not a long integer: #{uint.inspect}")
72
+ end
73
+ ret = []
74
+ 4.times do
75
+ ret.unshift(uint & 0xff)
76
+ uint >>= 8
77
+ end
78
+ ret.join('.')
79
+ end
80
+
81
+ #
82
+ # True if the object is an IPv4 address
83
+ #
84
+ # ip = IPAddress("192.168.10.100/24")
85
+ #
86
+ # ip.ipv4?
87
+ # #-> true
88
+ #
89
+ def ipv4?
90
+ self.kind_of? IPAddress::IPv4
91
+ end
92
+
93
+ #
94
+ # True if the object is an IPv6 address
95
+ #
96
+ # ip = IPAddress("192.168.10.100/24")
97
+ #
98
+ # ip.ipv6?
99
+ # #-> false
100
+ #
101
+ def ipv6?
102
+ self.kind_of? IPAddress::IPv6
103
+ end
104
+
105
+
106
+ #
107
+ # Checks if the given string is either a valid IP, either a valid IPv4 subnet
108
+ #
109
+ # Example:
110
+ #
111
+ # IPAddress::valid? "10.0.0.0/24"
112
+ # #=> true
113
+ #
114
+ # IPAddress::valid? "2002::1"
115
+ # #=> true
116
+ #
117
+ # IPAddress::valid? "10.0.0.256"
118
+ # #=> false
119
+ #
120
+ # IPAddress::valid? "10.0.0.0/999"
121
+ # #=> false
122
+ #
123
+ def self.valid?(addr)
124
+ valid_ip?(addr) || valid_ipv4_subnet?(addr) || valid_ipv6_subnet?(addr)
125
+ end
126
+
127
+ #
128
+ # Checks if the given string is a valid IP address,
129
+ # either IPv4 or IPv6
130
+ #
131
+ # Example:
132
+ #
133
+ # IPAddress::valid_ip? "2002::1"
134
+ # #=> true
135
+ #
136
+ # IPAddress::valid_ip? "10.0.0.256"
137
+ # #=> false
138
+ #
139
+ def self.valid_ip?(addr)
140
+ valid_ipv4?(addr) || valid_ipv6?(addr)
141
+ end
142
+
143
+ #
144
+ # Checks if the given string is a valid IPv4 subnet
145
+ #
146
+ # Example:
147
+ #
148
+ # IPAddress::valid_ipv4_subnet? "10.0.0.0/24"
149
+ # #=> true
150
+ #
151
+ # IPAddress::valid_ipv4_subnet? "10.0.0.0/255.255.255.0"
152
+ # #=> true
153
+ #
154
+ # IPAddress::valid_ipv4_subnet? "10.0.0.0/64"
155
+ # #=> false
156
+ #
157
+ def self.valid_ipv4_subnet?(addr)
158
+ ip, netmask = addr.split("/")
159
+
160
+ valid_ipv4?(ip) && (!(netmask =~ /\A([12]?\d|3[0-2])\z/).nil? || valid_ipv4_netmask?(netmask))
161
+ end
162
+
163
+ #
164
+ # Checks if the given string is a valid IPv6 subnet
165
+ #
166
+ # Example:
167
+ #
168
+ # IPAddress::valid_ipv6_subnet? "::/0"
169
+ # #=> true
170
+ #
171
+ # IPAddress::valid_ipv6_subnet? "dead:beef:cafe:babe::/64"
172
+ # #=> true
173
+ #
174
+ # IPAddress::valid_ipv6_subnet? "2001::1/129"
175
+ # #=> false
176
+ #
177
+ def self.valid_ipv6_subnet?(addr)
178
+ ip, netmask = addr.split("/")
179
+
180
+ netmask = Integer(netmask, 10)
181
+
182
+ valid_ipv6?(ip) && netmask >= 0 && netmask <= 128
183
+ rescue ArgumentError
184
+ false
185
+ end
186
+
187
+ #
188
+ # Checks if the given string is a valid IPv4 address
189
+ #
190
+ # Example:
191
+ #
192
+ # IPAddress::valid_ipv4? "2002::1"
193
+ # #=> false
194
+ #
195
+ # IPAddress::valid_ipv4? "172.16.10.1"
196
+ # #=> true
197
+ #
198
+ def self.valid_ipv4?(addr)
199
+ if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
200
+ return $~.captures.all? {|i| i.to_i < 256}
201
+ end
202
+ false
203
+ end
204
+
205
+ #
206
+ # Checks if the argument is a valid IPv4 netmask
207
+ # expressed in dotted decimal format.
208
+ #
209
+ # IPAddress.valid_ipv4_netmask? "255.255.0.0"
210
+ # #=> true
211
+ #
212
+ def self.valid_ipv4_netmask?(addr)
213
+ arr = addr.split(".").map{|i| i.to_i}.pack("CCCC").unpack("B*").first.scan(/01/)
214
+ arr.empty? && valid_ipv4?(addr)
215
+ rescue
216
+ return false
217
+ end
218
+
219
+ #
220
+ # Checks if the given string is a valid IPv6 address
221
+ #
222
+ # Example:
223
+ #
224
+ # IPAddress::valid_ipv6? "2002::1"
225
+ # #=> true
226
+ #
227
+ # IPAddress::valid_ipv6? "2002::DEAD::BEEF"
228
+ # #=> false
229
+ #
230
+ def self.valid_ipv6?(addr)
231
+ # https://gist.github.com/cpetschnig/294476
232
+ # http://forums.intermapper.com/viewtopic.php?t=452
233
+ return true if /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/ =~ addr
234
+ false
235
+ end
236
+
237
+ #
238
+ # Deprecate method
239
+ #
240
+ def self.deprecate(message = nil) # :nodoc:
241
+ message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
242
+ warn("DEPRECATION WARNING: #{message}")
243
+ end
244
+
245
+ end # module IPAddress
246
+
247
+ #
248
+ # IPAddress is a wrapper method built around
249
+ # IPAddress's library classes. Its purpouse is to
250
+ # make you indipendent from the type of IP address
251
+ # you're going to use.
252
+ #
253
+ # For example, instead of creating the three types
254
+ # of IP addresses using their own contructors
255
+ #
256
+ # ip = IPAddress::IPv4.new "172.16.10.1/24"
257
+ # ip6 = IPAddress::IPv6.new "2001:db8::8:800:200c:417a/64"
258
+ # ip_mapped = IPAddress::IPv6::Mapped "::ffff:172.16.10.1/128"
259
+ #
260
+ # you can just use the IPAddress wrapper:
261
+ #
262
+ # ip = IPAddress "172.16.10.1/24"
263
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
264
+ # ip_mapped = IPAddress "::ffff:172.16.10.1/128"
265
+ #
266
+ # All the object created will be instances of the
267
+ # correct class:
268
+ #
269
+ # ip.class
270
+ # #=> IPAddress::IPv4
271
+ # ip6.class
272
+ # #=> IPAddress::IPv6
273
+ # ip_mapped.class
274
+ # #=> IPAddress::IPv6::Mapped
275
+ #
276
+ def IPAddress(str)
277
+ IPAddress::parse str
278
+ end
279
+
280
+ #
281
+ # Compatibility with Ruby 1.8
282
+ #
283
+ if RUBY_VERSION =~ /^1\.8/
284
+ class Hash # :nodoc:
285
+ alias :key :index
286
+ end
287
+ module Math # :nodoc:
288
+ def Math.log2(n)
289
+ log(n) / log(2)
290
+ end
291
+ end
292
+ end
293
+