ipaddress_2 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+