ipcalc 1.0.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 @@
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: []