ipcalc 1.0.0

This diff has not been reviewed by any users.
Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ require_relative 'ipcalc/Ip'
@@ -0,0 +1,438 @@
1
+ # This file implements the class Ip.
2
+ # Author:: Denis BEURIVE (mailto:denis.beurive@gmail.com)
3
+ # Copyright:: Copyright (c) 2012 Denis BEURIVE
4
+ # License:: Distributes under the same terms as Ruby
5
+
6
+
7
+ require_relative "Iptools"
8
+
9
+ # This class handles common operations on IP addresses.
10
+ # o Create an IP address from a given string (ex: "192.168.0.12").
11
+ # o Create an IP address from a given integer value (ex: 16909060 or 0b00000001000000100000001100000100 or 0xaabbccdd)
12
+ # o Get the string representation of the IP address.
13
+ # o Get the decimal representation of the IP address.
14
+ # o Get the binary representation of the IP address.
15
+ # o Get the hexa representation of the IP address.
16
+ # o Increment an IP address (ex: 198.168.0.12 + 3).
17
+ # o Decrement an IP address (ex: 198.168.0.12 - 5).
18
+ # o Compare 2 IP addresses (ex: 198.168.0.12 > 198.168.0.13).
19
+ # o Manipulate an IP address by "dots" (ex: if ip="198.168.0.12", then ip[0]=192 produces ip="192.168.0.12" of ip[3] returns 12).
20
+ # o Operate binary operations <<, >>, |, &, ^ (ex: "198.168.0.12" & "200.168.0.15").
21
+ # o Calculate the subnet of a given class that contains the IP address (ex: What is the subnet of class 24 that includes this IP address?).
22
+ # o Test if the IP address is included in a given subnet (ex: is "192.168.10.12" included into "123.154.12.13/24"?).
23
+
24
+ class Ip
25
+ attr_reader :dots, :version
26
+
27
+ # Maximum value for an IP V4 address.
28
+ IPMAX_V4 = (2**32) - 1
29
+
30
+ # Maximum value for an IP V6 address.
31
+ IPMAX_V6 = (2**48) - 1
32
+
33
+ # Create an IP address V4 or V6.
34
+ #
35
+ # [in_ip] String, or integer, that represents the IP address.
36
+ # Example: 0b10101101010101011111010101000001 or 2908091713 or "173.85.245.65"
37
+ # [in_v] IP version (4 or 6).
38
+
39
+ def initialize(in_ip, in_v=4)
40
+
41
+ if in_ip.is_a?(String)
42
+ @version = Ip.version(in_ip)
43
+ @ip_string = in_ip
44
+ elsif in_ip.is_a?(Integer)
45
+ if in_ip > Ip::IPMAX_V4
46
+ @version = 6
47
+ else
48
+ @version = in_v
49
+ end
50
+ @ip_string = Iptools.i_to_dots(in_ip, @version)
51
+ else
52
+ raise RuntimeError, "Invalid IP addresse #{in_ip} (should be a string or an interger)"
53
+ end
54
+
55
+ @dots = Ip.valid?(@ip_string, @version)
56
+
57
+ raise RuntimeError, "Invalid IP addresse #{in_ip}" if (@dots.nil?)
58
+ @ip_int = to_i
59
+ end
60
+
61
+ # Return the textual representation of the IP address.
62
+
63
+ def to_s
64
+ @ip_string
65
+ end
66
+
67
+ # Return the integer representation of the IP address.
68
+
69
+ def to_i
70
+ return @ip_int unless (@ip_int.nil?);
71
+ Iptools.dots_to_i(@dots)
72
+ end
73
+
74
+ # Return a string that represents the IP address' binary value.
75
+ # The returned string may contain bytes' separators (dots).
76
+ # Example: "1.2.3.4" => "00000001000000100000001100000100" or "00000001.00000010.00000011.00000100"
77
+ #
78
+ # [in_pretty] If TRUE, then the returned string will contain dots to separated bytes.
79
+ #
80
+ # The method returns a string that represents a binary value.
81
+
82
+ def to_bin(in_pretty=false)
83
+ s = @ip_int.to_s(2).rjust(@version*8, '0')
84
+ return s unless in_pretty
85
+
86
+ if (@version == 4)
87
+ /^((?:0|1){8})((?:0|1){8})((?:0|1){8})((?:0|1){8})$/.match(s)[1,4].join('.')
88
+ else
89
+ /^((?:0|1){8})((?:0|1){8})((?:0|1){8})((?:0|1){8})((?:0|1){8})((?:0|1){8})$/.match(s)[1,6].join('.')
90
+ end
91
+ end
92
+
93
+ # Return a string that represents the IP address' hexa value.
94
+ # The returned string may contain bytes' separators (dots).
95
+ # Example: "1.2.3.4" => "01020304" or "01.02.03.04"
96
+ #
97
+ # [in_pretty] If TRUE, then the returned string will contain dots to separate bytes.
98
+ #
99
+ # The method returns a string that represents a hexa value.
100
+
101
+ def to_hex(in_pretty=false)
102
+ s = @ip_int.to_s(16).rjust(@version*2, '0')
103
+ return s unless in_pretty
104
+
105
+ if (@version == 4)
106
+ /^([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})$/.match(s)[1,4].join('.')
107
+ else
108
+ /^([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})$/.match(s)[1,6].join('.')
109
+ end
110
+ end
111
+
112
+ # Operator "+" (and +=)
113
+
114
+ def +(in_delta)
115
+ v = @ip_int + in_delta
116
+ raise RuntimeError, "+: Invalid operand #{in_delta}" if (v > MAX())
117
+ Ip.new(v, @version)
118
+ end
119
+
120
+ # Operator "-" (and -=)
121
+
122
+ def -(in_delta)
123
+ v = @ip_int - in_delta
124
+ raise RuntimeError, "-: Invalid operand #{in_delta}" if v < 0
125
+ Ip.new(v, @version)
126
+ end
127
+
128
+ # Operators <=>
129
+
130
+ def <=>(in_ip)
131
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
132
+ @ip_int <=> in_ip.to_i
133
+ end
134
+
135
+ # Operators >
136
+
137
+ def >(in_ip)
138
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
139
+ @ip_int > in_ip.to_i
140
+ end
141
+
142
+ # Operators <
143
+
144
+ def <(in_ip)
145
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
146
+ @ip_int < in_ip.to_i
147
+ end
148
+
149
+ # Operators >=
150
+
151
+ def >=(in_ip)
152
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
153
+ @ip_int >= in_ip.to_i
154
+ end
155
+
156
+ # Operators <=
157
+
158
+ def <=(in_ip)
159
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
160
+ @ip_int <= in_ip.to_i
161
+ end
162
+
163
+ # Operators ==
164
+
165
+ def ==(in_ip)
166
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
167
+ @ip_int == in_ip.to_i
168
+ end
169
+
170
+ # Operators !=
171
+
172
+ def !=(in_ip)
173
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
174
+ @ip_int != in_ip.to_i
175
+ end
176
+
177
+ # Return a given dot value extracted from an IP address.
178
+ # Example: If ip is "192.168.0.15", ip[0] is 192, ip[1] is 168...
179
+
180
+ def [](in_index)
181
+ raise RuntimeError, "[]: Invalid index #{in_index}" if ((in_index < 0) || (in_index > MAX_DOTS_INDEX()))
182
+ @dots[in_index]
183
+ end
184
+
185
+ # Assign a value to a given IP's dot.
186
+ # Example: Ip ip is "192.168.0.15", ip[0]=193 then ip="193.168.0.15"
187
+
188
+ def []=(in_index, in_value)
189
+ raise RuntimeError, "[]: Invalid index #{in_index}" if ((in_index < 0) || (in_index > MAX_DOTS_INDEX()))
190
+ raise RuntimeError, "[]: Invalid value #{in_value}" if ((in_value < 0) || (in_value > 255))
191
+ @dots[in_index] = in_value
192
+
193
+ # Updtate values.
194
+ @ip_int = Iptools.dots_to_i(@dots)
195
+ @ip_string = Iptools.i_to_dots(@ip_int, @version)
196
+ end
197
+
198
+ # Operator >>
199
+
200
+ def >>(in_decal)
201
+ Ip.new(@ip_int >> in_decal, @version)
202
+ end
203
+
204
+ # Operator <<
205
+
206
+ def <<(in_decal)
207
+ Ip.new((@ip_int << in_decal) & MAX(), @version)
208
+ end
209
+
210
+ # Operator | (binary OR)
211
+
212
+ def |(in_ip)
213
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
214
+ Ip.new(@ip_int | in_ip.to_i, @version)
215
+ end
216
+
217
+ # Operator & (binary AND)
218
+
219
+ def &(in_ip)
220
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
221
+ Ip.new(@ip_int & in_ip.to_i, @version)
222
+ end
223
+
224
+ # Operator ^ (binary XOR)
225
+
226
+ def ^(in_ip)
227
+ raise RuntimeError, "Can not compare IPV4 with IPV6!" if @version != in_ip.version
228
+ Ip.new(@ip_int ^ in_ip.to_i, @version)
229
+ end
230
+
231
+ # Return the CIDR of a given subnet's class that contains this IP address.
232
+ #
233
+ # [in_class] Class of the subnet (example: 26).
234
+ #
235
+ # The method returns the CIDR of the given class that contains this IP address.
236
+
237
+ def cidr(in_class)
238
+ max_class = MAX_CLASS()
239
+ raise RuntimeError, "Invalid subnet class /#{in_class}" if (in_class > max_class || in_class < 1)
240
+ mask = (2**in_class - 1) << (max_class - in_class)
241
+ Iptools.i_to_dots(@ip_int & mask, @version) + "/#{in_class}"
242
+ end
243
+
244
+ # Test if the IP address is part of a given subnet.
245
+ #
246
+ # [in_subnet] The subnet (example: "173.85.245.32/27").
247
+ #
248
+ # The method returns TRUE or FALSE, whether the given IP address is included into the given subnet or not.
249
+
250
+ def included_in?(in_subnet)
251
+ m = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\.\d{1,3}\.\d{1,3})?)\s*\/\s*(\d{1,2})$/.match(in_subnet)
252
+ raise RuntimeError, "Invalid subnet #{in_subnet}" if m.nil?
253
+
254
+ ipv6tag = m[2]
255
+
256
+ cidrv = 6
257
+ if (ipv6tag.nil?)
258
+ cidrv = 4
259
+ end
260
+
261
+ raise RuntimeError, "Subnet #{in_subnet} is IP version #{cidrv} and current IP is IP version #{@version}" if @version != cidrv
262
+
263
+ snet = m[1]
264
+ classe = m[3]
265
+ s = cidr(classe.to_i)
266
+ # puts s + " --- " + snet + "/" + classe
267
+ s == snet + "/" + classe
268
+ end
269
+
270
+ # -------------------------------------------------------------
271
+ # Statics
272
+ # -------------------------------------------------------------
273
+
274
+ # Test if a given string represents an IP address.
275
+ #
276
+ # [in_ip] String to test.
277
+ # [in_version] IP version (should be 4 or 6)
278
+ #
279
+ # If the given string represents an IP address, that the method returns an array that represents the IP address.
280
+ # Otherwise, the method returns the value nil.
281
+
282
+ def self.valid?(in_ip, in_version=4)
283
+ if (in_version == 4)
284
+ return Ip.validV4?(in_ip)
285
+ end
286
+ if (in_version == 6)
287
+ return Ip.validV6?(in_ip)
288
+ end
289
+ raise RuntimeError, "Invalid IP addresse version #{in_version}"
290
+ end
291
+
292
+ # Test if a given string represents an IP V4 address.
293
+ #
294
+ # [in_ip] String to test.
295
+ #
296
+ # If the given string represents an IP V4 address, that the method returns an array that represents the IP address.
297
+ # Otherwise, the method returns the value nil.
298
+
299
+ def self.validV4?(in_ip)
300
+ dots = [];
301
+ m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.match(in_ip)
302
+ return nil if m.nil?
303
+
304
+ for i in 1..4
305
+ dot = m[i].to_i;
306
+ return nil if dot > 255
307
+ dots.push(dot)
308
+ end
309
+ dots
310
+ end
311
+
312
+ # Test if a given string represents an IP V6 address.
313
+ #
314
+ # [in_ip] String to test.
315
+ #
316
+ # If the given string represents an IP V6 address, that the method returns an array that represents the IP address.
317
+ # Otherwise, the method returns the value nil.
318
+
319
+ def self.validV6?(in_ip)
320
+ dots = [];
321
+ m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.match(in_ip)
322
+ return nil if m.nil?
323
+
324
+ for i in 1..6
325
+ dot = m[i].to_i;
326
+ return nil if dot > 255
327
+ dots.push(dot)
328
+ end
329
+ dots
330
+ end
331
+
332
+ # Convert a string that represents an IP address into an integer.
333
+ # Example: "1.2.3.4" => 16909060
334
+ #
335
+ # [in_ip] String to convert.
336
+ #
337
+ # The method returns an integer that represents the given IP address.
338
+
339
+ def self.to_i(in_ip)
340
+ dots = Ip.valid?(in_ip, Ip.version(in_ip));
341
+ raise RuntimeError, "Invalid IP addresse #{in_ip}" if (dots.nil?)
342
+ Iptools.dots_to_i(dots)
343
+ end
344
+
345
+ # Convert a string that represents an IP address into a string that represents a binary value.
346
+ # The returned string may contain bytes' separators (dots).
347
+ # Example: "1.2.3.4" => "00000001000000100000001100000100" or "00000001.00000010.00000011.00000100"
348
+ #
349
+ # [in_ip] String to convert.
350
+ # [in_pretty] If TRUE, then the returned string will contain dots to separated bytes.
351
+ #
352
+ # The method returns a string that represents a binary value.
353
+
354
+ def self.to_bin(in_ip, in_pretty=false)
355
+ version = Ip.version(in_ip)
356
+ s = Ip.to_i(in_ip).to_s(2).rjust(Ip.MAX_CLASS(version), '0')
357
+ return s unless in_pretty
358
+
359
+ if (4 == version)
360
+ return /^((?:0|1){8})((?:0|1){8})((?:0|1){8})((?:0|1){8})$/.match(s)[1,4].join('.')
361
+ end
362
+ /^((?:0|1){8})((?:0|1){8})((?:0|1){8})((?:0|1){8})((?:0|1){8})((?:0|1){8})$/.match(s)[1,6].join('.')
363
+ end
364
+
365
+ # Convert a string that represents an IP address into a string that represents a hexa value.
366
+ # The returned string may contain bytes' separators (dots).
367
+ # Example: "1.2.3.4" => "01020304" or "01.02.03.04"
368
+ #
369
+ # [in_ip] String to convert.
370
+ # [in_pretty] If TRUE, then the returned string will contain dots to separate bytes.
371
+ #
372
+ # The method returns a string that represents a hexa value.
373
+
374
+ def self.to_hex(in_ip, in_pretty=false)
375
+ version = Ip.version(in_ip)
376
+ s = Ip.to_i(in_ip).to_s(16).rjust(version == 4 ? 8 : 12, '0')
377
+ return s unless in_pretty
378
+ if (4 == version)
379
+ return /^([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})$/.match(s)[1,4].join('.')
380
+ end
381
+ /^([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})$/.match(s)[1,6].join('.')
382
+ end
383
+
384
+ # Given a string that represents an IP address, return the IP version (4 or 6).
385
+ #
386
+ # [in_ip] String that represents an IP address.
387
+ #
388
+ # The method returns the IP version of the IP address. The returned value is 4 or 6.
389
+
390
+ def self.version(in_ip)
391
+ m = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\.\d{1,3}\.\d{1,3})?$/.match(in_ip)
392
+ raise RuntimeError, "Invalid IP address #{in_ip}" if m[1].nil?
393
+ return 4 if m[2].nil?
394
+ return 6
395
+ end
396
+
397
+ # -------------------------------------------------------------
398
+ # Make private operators that have no sense for an IP address
399
+ # -------------------------------------------------------------
400
+
401
+ private
402
+
403
+ def self.MAX_CLASS(in_version)
404
+ return in_version == 4 ? 32 : 48
405
+ end
406
+
407
+ def MAX
408
+ return @version == 4 ? Ip::IPMAX_V4 : Ip::IPMAX_V6
409
+ end
410
+
411
+ def MAX_DOTS_INDEX
412
+ return @version == 4 ? 3 : 5
413
+ end
414
+
415
+ def MAX_CLASS
416
+ Ip.MAX_CLASS(@version)
417
+ end
418
+
419
+ def /(other)
420
+ end
421
+ def *(other)
422
+ end
423
+ def %(other)
424
+ end
425
+ def and(other)
426
+ end
427
+ def **(other)
428
+ end
429
+ def =~(other)
430
+ end
431
+ def -@
432
+ end
433
+ def +@
434
+ end
435
+
436
+ end
437
+
438
+
@@ -0,0 +1,47 @@
1
+ # This file implements the module Iptools.
2
+ # Author:: Denis BEURIVE (mailto:denis.beurive@gmail.com)
3
+ # Copyright:: Copyright (c) 2012 Denis BEURIVE
4
+ # License:: Distributes under the same terms as Ruby
5
+
6
+
7
+ # This module contains various utilities that are used by all classes that manipulate IP addresses.
8
+
9
+ module Iptools
10
+
11
+ # Convert an array that represents an IP address into an integer.
12
+ # Example: [173, 85, 245, 65] => 2908091713.
13
+ #
14
+ # [in_dots] the array that will be converted into an integer (ex: [192, 168, 0, 10]).
15
+ #
16
+ # The method returns an integer.
17
+
18
+ def self.dots_to_i(in_dots)
19
+ bint = 0;
20
+ rdx = 1;
21
+ in_dots.reverse.each do |dot|
22
+ bint += dot * rdx
23
+ rdx *= 256
24
+ end
25
+ bint
26
+ end
27
+
28
+ # Convert an integer that represents an IP address into an array.
29
+ # Example: 2908091713 => [173, 85, 245, 65]
30
+ #
31
+ # [in_int] Integer to convert.
32
+ # [in_v] IP version (should be 4 or 6).
33
+ #
34
+ # The method returns an array (with 4 or 6 elements).
35
+
36
+ def self.i_to_dots(in_int, in_v=4)
37
+ res = [];
38
+ raise RuntimeError, "Invalid IP version #{in_v} (should be 4 or 6)" if (in_v != 4 && in_v != 6)
39
+ in_v.times do
40
+ r = in_int % 256;
41
+ in_int = (in_int - r) / 256;
42
+ res.unshift(r.to_s);
43
+ end
44
+ res.join('.');
45
+ end
46
+
47
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ipcalc
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - denis BEURIVE
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-30 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: This gem provides classes that help IP manipulations.
15
+ email: denis.beurive@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/ipcalc.rb
21
+ - lib/ipcalc/Iptools.rb
22
+ - lib/ipcalc/Ip.rb
23
+ homepage: https://github.com/denis-beurive/ipcalc
24
+ licenses: []
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: 1.9.1
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 1.8.24
44
+ signing_key:
45
+ specification_version: 3
46
+ summary: IP calculation.
47
+ test_files: []