ipaddress 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,10 @@
1
+ == master
2
+
3
+ NEW:: IPAddress::IPv6#include?
4
+ NEW:: IPAddress::IPv6#network_u128
5
+ NEW:: Modified IPAddress::IPv6::Mapped to accept IPv4 mapped addresses in IPv6 format
6
+ NEW:: IPAddress::IPv4#private?
7
+ NEW:: IPAddress::IPv4::parse_classful
1
8
 
2
9
  == ipaddress 0.6.0
3
10
 
@@ -43,8 +43,12 @@ Some quick examples of things you can't do with IPAddr:
43
43
  * iterate over hosts
44
44
  * perform subnetting or network aggregation
45
45
 
46
- Moreover, many methods and procedures are so old that they have been
47
- declared deprecated by the IETF.
46
+ Many methods and procedures are so old that they have been
47
+ declared deprecated by the IETF, and some others have bugs in their
48
+ implementation.
49
+
50
+ Moreover, IPAddress is more robust and is already around 50% faster than IPAddr,
51
+ in addition to provide an organic API with logical separation and OO structure.
48
52
 
49
53
  We hope that IPAddress will address all these issues and meet all your
50
54
  needs in network programming.
@@ -77,20 +81,15 @@ documentation with Rake:
77
81
  ipaddress$ rake rdoc
78
82
 
79
83
  The latest documentation can be found online at
80
- {this address}[http://marcoceresa.com/ipaddress]
81
-
82
- == Usage
83
-
84
- In this section I will illustrate how to use the IPAddress library
85
- through some examples of common tasks.
86
-
87
- === IPv4
84
+ {this address}[http://rubydoc.info/github/bluemonk/ipaddress/master/frames]
85
+
86
+ == IPv4
88
87
 
89
88
  Class IPAddress::IPv4 is used to handle IPv4 type addresses. IPAddress
90
89
  is similar to other IP Addresses libraries, like Ruby's own
91
90
  IPAddr. However it works slightly different, as we will see.
92
91
 
93
- ==== Create a new IPv4 address
92
+ === Create a new IPv4 address
94
93
 
95
94
  The usual way to express an IP Address is using its dotted decimal
96
95
  form, such as 172.16.10.1, and a prefix, such as 24, separated by a
@@ -102,7 +101,15 @@ To create a new IPv4 object, you can use IPv4 own class
102
101
 
103
102
  ip = IPAddress::IPv4.new "172.16.10.1/24"
104
103
 
105
- or, in a easier way, using the IPAddress wrapper method
104
+ or, in a easier way, using the IPAddress parse method
105
+
106
+ ip = IPAddress.parse "172.16.10.1/24"
107
+
108
+ which accepts and parses any kind of IP (IPv4, IPV6 and
109
+ IPv4 IPv6 Mapped addresses).
110
+
111
+ If you like sintactic sugar, you can use the wrapper method
112
+ IPAddress(), which is built around IPAddress::parse:
106
113
 
107
114
  ip = IPAddress "172.16.10.1/24"
108
115
 
@@ -114,40 +121,20 @@ You can specify an IPv4 address in any of two ways:
114
121
  In this example, prefix /24 and netmask 255.255.255.0 are the same and
115
122
  you have the flexibility to use either one of them.
116
123
 
117
- ==== Classful networks
118
-
119
- If you don't specify a prefix (or a subnet mask), then the library
120
- will create an object base on the CLASSFUL network from the given
121
- IP. Remember the CLASSFUL network are the following (RFC 791)
122
-
123
- * Class A, from 0.0.0.0 to 127.255.255.255
124
- * Class B, from 128.0.0.0 to 191.255.255.255
125
- * Class C, from 192.0.0.0 to 255.255.255.255
126
-
127
- Since classful networks here are only considered to calculate the default
128
- prefix number, classes D and E are not considered.
129
-
130
- You can easily check which CLASSFUL network the IP belongs:
124
+ If you don't explicitly specify the prefix (or the subnet mask),
125
+ IPAddress thinks you're dealing with host addresses and not with
126
+ networks. Therefore, the default prefix will be /32, or
127
+ 255.255.255.255. For example:
131
128
 
132
- ip = IPAddress("10.0.0.1/24")
133
- ip.a?
134
- #=> true
135
-
136
- ip = IPAddress("172.16.10.1/24")
137
- ip.b?
138
- #=> true
129
+ # let's declare an host address
130
+ host = IPAddress::IPv4.new "10.1.1.1."
139
131
 
140
- ip = IPAddress("192.168.1.1/30")
141
- ip.c?
142
- #=> true
143
-
144
- These methods are only checking the address portion of an IP, and are
145
- indipendent from its prefix.
132
+ The new created object will have prefix /32, which is the same
133
+ as we created the following:
146
134
 
147
- For more information on CLASSFUL networks visit the
148
- {Wikipedia page}[http://en.wikipedia.org/wiki/Classful_network]
149
-
150
- ==== Handling the IPv4 address
135
+ host = IPAddress::IPv4.new "10.1.1.1/32"
136
+
137
+ === Handling the IPv4 address
151
138
 
152
139
  Once created, you can obtain the attributes for an IPv4 object:
153
140
 
@@ -182,7 +169,7 @@ use IPv4#to_string
182
169
  ip.to_string
183
170
  #=> "172.16.10.l/24"
184
171
 
185
- ==== Changing netmask
172
+ === Changing netmask
186
173
 
187
174
  You can set a new prefix (netmask) after creating an IPv4
188
175
  object. For example:
@@ -200,7 +187,7 @@ using the IPv4#netmask= method
200
187
  ip.to_string
201
188
  #=> "172.16.10.1/30"
202
189
 
203
- ==== Working with networks, broadcasts and addresses
190
+ === Working with networks, broadcasts and addresses
204
191
 
205
192
  Some very important topics in dealing with IP addresses are the
206
193
  concepts of +network+ and +broadcast+, as well as the addresses
@@ -221,7 +208,7 @@ This is very important because, for instance, IP "172.16.10.1/16" is
221
208
  very different to the previous one, belonging to the very different
222
209
  network "172.16.0.0/16".
223
210
 
224
- ===== Networks
211
+ ==== Networks
225
212
 
226
213
  With IPAddress it's very easy to calculate the network for an IP
227
214
  address:
@@ -251,7 +238,7 @@ network or not:
251
238
  ip2.network?
252
239
  #=> true
253
240
 
254
- ===== Broadcast
241
+ ==== Broadcast
255
242
 
256
243
  The broadcast address is the contrary than the network number: where
257
244
  the network number has all zeroes in the host portion, the broadcast
@@ -272,7 +259,7 @@ address:
272
259
  bcast.to_string
273
260
  #=> "172.16.10.255/24"
274
261
 
275
- ===== Addresses, ranges and iterators
262
+ ==== Addresses, ranges and iterators
276
263
 
277
264
  So we see that the netmask essentially specifies a range for IP
278
265
  addresses that are included in a network: all the addresses between
@@ -310,7 +297,7 @@ respectively the first and the last host address in the range
310
297
  ip.last.to_string
311
298
  #=> "172.16.10.254/24"
312
299
 
313
- ==== IP special formats
300
+ === IP special formats
314
301
 
315
302
  The IPAddress library provides a complete set of methods to access an
316
303
  IPv4 address in special formats, such as binary, 32 bits unsigned int,
@@ -352,6 +339,53 @@ suitable to use in IPv4-IPv6 mapped addresses:
352
339
  ip.to_ipv6
353
340
  #=> "ac10:0a01"
354
341
 
342
+ === Classful networks
343
+
344
+ IPAddress allows you to create and manipulate objects using the old
345
+ and deprecated (but apparently still popular) classful networks concept.
346
+
347
+ Classful networks and addresses don't have a prefix: their subnet mask
348
+ is univocally identified by their address, and therefore diveded in classes.
349
+ As per RFC 791, these classes are:
350
+
351
+ * Class A, from 0.0.0.0 to 127.255.255.255
352
+ * Class B, from 128.0.0.0 to 191.255.255.255
353
+ * Class C, from 192.0.0.0 to 255.255.255.255
354
+
355
+ Since classful networks here are only considered to calculate the default
356
+ prefix number, classes D and E are not considered.
357
+
358
+ To create a classful IP and prefix from an IP address, use the
359
+ IPv4::parse_classful method:
360
+
361
+ # classful ip
362
+ ip = IPAddress::IPv4::parse_classful "10.1.1.1"
363
+
364
+ ip.prefix
365
+ #=> 8
366
+
367
+ The method automatically created a new IPv4 object and assigned it
368
+ the correct prefix.
369
+
370
+ You can easily check which CLASSFUL network an IPv4 object belongs:
371
+
372
+ ip = IPAddress("10.0.0.1/24")
373
+ ip.a?
374
+ #=> true
375
+
376
+ ip = IPAddress("172.16.10.1/24")
377
+ ip.b?
378
+ #=> true
379
+
380
+ ip = IPAddress("192.168.1.1/30")
381
+ ip.c?
382
+ #=> true
383
+
384
+ Remember that these methods are only checking the address portion of an IP, and are
385
+ indipendent from its prefix, as classful networks have no concept of prefix.
386
+
387
+ For more information on CLASSFUL networks visit the
388
+ {Wikipedia page}[http://en.wikipedia.org/wiki/Classful_network]
355
389
 
356
390
  === Network design with IPAddress
357
391
 
data/Rakefile CHANGED
@@ -61,7 +61,7 @@ end
61
61
 
62
62
  desc "Open an irb session preloaded with this library"
63
63
  task :console do
64
- sh "irb1.9 -rubygems -I lib -r ipaddress.rb"
64
+ sh "irb -rubygems -I lib -r ipaddress.rb"
65
65
  end
66
66
 
67
67
  desc "Look for TODO and FIXME tags in the code"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
@@ -46,11 +46,7 @@ module IPAddress
46
46
  when /:.+\./
47
47
  IPAddress::IPv6::Mapped.new(str)
48
48
  else
49
- begin
50
- IPAddress::IPv4.new(str)
51
- rescue ArgumentError
52
- IPAddress::IPv6.new(str)
53
- end
49
+ IPAddress::IPv4.new(str) rescue IPAddress::IPv6.new(str)
54
50
  end
55
51
  end
56
52
 
@@ -1,14 +1,14 @@
1
- class << Math
1
+ class << Math # :nodoc:
2
2
  def log2(n); log(n) / log(2); end
3
3
  end
4
4
 
5
5
  if RUBY_VERSION =~ /1\.8/
6
- class Hash
6
+ class Hash # :nodoc:
7
7
  alias :key :index
8
8
  end
9
9
  end
10
10
 
11
- class Integer
11
+ class Integer # :nodoc:
12
12
  def power_of_2?
13
13
  Math::log2(self).to_i == Math::log2(self)
14
14
  end
@@ -42,30 +42,23 @@ module IPAddress;
42
42
  #
43
43
  # An IPv4 address can be expressed in any of the following forms:
44
44
  #
45
- # * "10.1.1.1/24": ip address and prefix. This is the common and
46
- # suggested way to create an object .
47
- # * "10.1.1.1/255.255.255.0": ip address and netmask. Although
48
- # convenient sometimes, this format is less clear than the previous
49
- # one.
50
- # * "10.1.1.1": if the address alone is specified, the prefix will be
51
- # assigned using the classful boundaries. In this case, the
52
- # prefix would be /8, a 255.0.0.0 netmask.
53
- #
54
- # It is advisable to use the syntactic shortcut provided with the
55
- # IPAddress() method, as in all the examples below.
56
- #
45
+ # * "10.1.1.1/24": ip +address+ and +prefix+. This is the common and
46
+ # suggested way to create an object .
47
+ # * "10.1.1.1/255.255.255.0": ip +address+ and +netmask+. Although
48
+ # convenient sometimes, this format is less clear than the previous
49
+ # one.
50
+ # * "10.1.1.1": if the address alone is specified, the prefix will be
51
+ # set as default 32, also known as the host prefix
52
+ #
57
53
  # Examples:
58
54
  #
59
- # # These two methods return the same object
55
+ # # These two are the same
60
56
  # ip = IPAddress::IPv4.new("10.0.0.1/24")
61
57
  # ip = IPAddress("10.0.0.1/24")
62
58
  #
63
- # # These three are the same
64
- # IPAddress("10.0.0.1/8")
65
- # IPAddress("10.0.0.1/255.0.0.0")
66
- # IPAddress("10.0.0.1")
67
- # #=> #<IPAddress::IPv4:0xb7b1a438
68
- # @octets=[10, 0, 0, 1], @address="10.0.0.1", @prefix=8>
59
+ # # These two are the same
60
+ # IPAddress::IPv4.new "10.0.0.1/8"
61
+ # IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
69
62
  #
70
63
  def initialize(str)
71
64
  ip, netmask = str.split("/")
@@ -88,12 +81,14 @@ module IPAddress;
88
81
  raise ArgumentError, "Invalid netmask #{netmask}"
89
82
  end
90
83
  else # netmask is nil, reverting to defaul classful mask
91
- @prefix = prefix_from_ip(@address)
84
+ @prefix = Prefix32.new(32)
92
85
  end
93
86
 
94
87
  # Array formed with the IP octets
95
88
  @octets = @address.split(".").map{|i| i.to_i}
96
-
89
+ # 32 bits interger containing the address
90
+ @u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3])
91
+
97
92
  end # def initialize
98
93
 
99
94
  #
@@ -185,7 +180,6 @@ module IPAddress;
185
180
  "#@address/#@prefix"
186
181
  end
187
182
 
188
-
189
183
  #
190
184
  # Returns the prefix as a string in IP format
191
185
  #
@@ -227,14 +221,15 @@ module IPAddress;
227
221
  #
228
222
  # ip = IPAddress("10.0.0.0/8")
229
223
  #
230
- # ip.to_u32
224
+ # ip.to_i
231
225
  # #=> 167772160
232
226
  #
233
- def to_u32
234
- data.unpack("N").first
227
+ def u32
228
+ @u32
235
229
  end
236
- alias_method :to_i, :to_u32
237
-
230
+ alias_method :to_i, :u32
231
+ alias_method :to_u32, :u32
232
+
238
233
  #
239
234
  # Returns the address portion of an IPv4 object
240
235
  # in a network byte order format.
@@ -255,7 +250,7 @@ module IPAddress;
255
250
  # a.puts binary_data
256
251
  #
257
252
  def data
258
- @octets.pack("C4")
253
+ [@u32].pack("N")
259
254
  end
260
255
 
261
256
  #
@@ -316,7 +311,7 @@ module IPAddress;
316
311
  # #=> true
317
312
  #
318
313
  def network?
319
- to_u32 | @prefix.to_u32 == @prefix.to_u32
314
+ @u32 | @prefix.to_u32 == @prefix.to_u32
320
315
  end
321
316
 
322
317
  #
@@ -477,7 +472,7 @@ module IPAddress;
477
472
  # #=> 8
478
473
  #
479
474
  def size
480
- broadcast_u32 - network_u32 + 1
475
+ 2 ** @prefix.host_prefix
481
476
  end
482
477
 
483
478
  #
@@ -507,7 +502,7 @@ module IPAddress;
507
502
  # #=> 167772160
508
503
  #
509
504
  def network_u32
510
- to_u32 & @prefix.to_u32
505
+ @u32 & @prefix.to_u32
511
506
  end
512
507
 
513
508
  #
@@ -519,7 +514,7 @@ module IPAddress;
519
514
  # #=> 167772167
520
515
  #
521
516
  def broadcast_u32
522
- [to_u32 | ~@prefix.to_u32].pack("N").unpack("N").first
517
+ network_u32 + size - 1
523
518
  end
524
519
 
525
520
  #
@@ -539,7 +534,23 @@ module IPAddress;
539
534
  # #=> false
540
535
  #
541
536
  def include?(oth)
542
- @prefix <= oth.prefix and network_u32 == self.class.new(oth.address+"/#@prefix").network_u32
537
+ @prefix <= oth.prefix and network_u32 == (oth.to_u32 & @prefix.to_u32)
538
+ end
539
+
540
+ #
541
+ # Checks if an IPv4 address objects belongs
542
+ # to a private network RFC1918
543
+ #
544
+ # Example:
545
+ #
546
+ # ip = IPAddress "10.1.1.1/24"
547
+ # ip.private?
548
+ # #=> true
549
+ #
550
+ def private?
551
+ [self.class.new("10.0.0.0/8"),
552
+ self.class.new("172.16.0.0/12"),
553
+ self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
543
554
  end
544
555
 
545
556
  #
@@ -589,7 +600,7 @@ module IPAddress;
589
600
  # Returns an array of IPAddress objects
590
601
  #
591
602
  def subnet(subnets=2)
592
- unless (1..(2**(32-prefix.to_i))).include? subnets
603
+ unless (1..(2**@prefix.host_prefix)).include? subnets
593
604
  raise ArgumentError, "Value #{subnets} out of range"
594
605
  end
595
606
  calculate_subnets(subnets)
@@ -746,13 +757,8 @@ module IPAddress;
746
757
  # ip.to_string
747
758
  # #=> "10.0.0.0/8"
748
759
  #
749
- def self.parse_u32(u32, prefix=nil)
750
- ip = [u32].pack("N").unpack("C4").join(".")
751
- if prefix
752
- self.new(ip+"/#{prefix}")
753
- else
754
- self.new(ip)
755
- end
760
+ def self.parse_u32(u32, prefix=32)
761
+ self.new([u32].pack("N").unpack("C4").join(".")+"/#{prefix}")
756
762
  end
757
763
 
758
764
  #
@@ -768,12 +774,12 @@ module IPAddress;
768
774
  # ip.to_string
769
775
  # #=> "172.16.10.1/24"
770
776
  #
771
- def self.parse_data(str)
772
- self.new str.unpack("C4").join(".")
777
+ def self.parse_data(str, prefix=32)
778
+ self.new(str.unpack("C4").join(".")+"/#{prefix}")
773
779
  end
774
780
 
775
781
  #
776
- # Exctract an IPv4 address from a string and
782
+ # Extract an IPv4 address from a string and
777
783
  # returns a new object
778
784
  #
779
785
  # Example:
@@ -872,20 +878,44 @@ module IPAddress;
872
878
  end
873
879
  end
874
880
 
881
+ #
882
+ # Creates a new IPv4 address object by parsing the
883
+ # address in a classful way.
884
+ #
885
+ # Classful addresses have a fixed netmask based on the
886
+ # class they belong to:
887
+ #
888
+ # * Class A, from 0.0.0.0 to 127.255.255.255
889
+ # * Class B, from 128.0.0.0 to 191.255.255.255
890
+ # * Class C, D and E, from 192.0.0.0 to 255.255.255.254
891
+ #
892
+ # Note that classes C, D and E will all have a default
893
+ # prefix of /24 or 255.255.255.0
894
+ #
895
+ # Example:
896
+ #
897
+ # ip = IPAddress::IPv4.parse_classful "10.0.0.1"
898
+ #
899
+ # ip.netmask
900
+ # #=> "255.0.0.0"
901
+ # ip.a?
902
+ # #=> true
903
+ #
904
+ def self.parse_classful(ip)
905
+ if IPAddress.valid_ipv4?(ip)
906
+ address = ip.strip
907
+ else
908
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
909
+ end
910
+ prefix = CLASSFUL.find{|h,k| h === ("%.8b" % address.to_i)}.last
911
+ self.new "#{address}/#{prefix}"
912
+ end
913
+
875
914
  #
876
915
  # private methods
877
916
  #
878
917
  private
879
918
 
880
- def bits_from_address(ip)
881
- ip.split(".").map{|i| i.to_i}.pack("C4").unpack("B*").first
882
- end
883
-
884
- def prefix_from_ip(ip)
885
- bits = bits_from_address(ip)
886
- CLASSFUL.each {|reg,prefix| return Prefix32.new(prefix) if bits =~ reg}
887
- end
888
-
889
919
  def calculate_subnets(subnets)
890
920
  po2 = subnets.closest_power_of_2
891
921
  new_prefix = @prefix + Math::log2(po2).to_i
@@ -88,6 +88,10 @@ module IPAddress;
88
88
  #
89
89
  def initialize(str)
90
90
  ip, netmask = str.split("/")
91
+
92
+ if str =~ /:.+\./
93
+ raise ArgumentError, "Please use #{self.class}::Mapped for IPv4 mapped addresses"
94
+ end
91
95
 
92
96
  if IPAddress.valid_ipv6?(ip)
93
97
  @groups = self.class.groups(ip)
@@ -101,7 +105,6 @@ module IPAddress;
101
105
 
102
106
  end # def initialize
103
107
 
104
-
105
108
  #
106
109
  # Returns the IPv6 address in uncompressed form:
107
110
  #
@@ -303,7 +306,7 @@ module IPAddress;
303
306
  # Returns the IPv6 address in a DNS reverse lookup
304
307
  # string, as per RFC3172 and RFC2874.
305
308
  #
306
- # ip6 = IPAddress "3ffe:505:2::f")
309
+ # ip6 = IPAddress "3ffe:505:2::f"
307
310
  #
308
311
  # ip6.reverse
309
312
  # #=> "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
@@ -312,7 +315,36 @@ module IPAddress;
312
315
  to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
313
316
  end
314
317
  alias_method :arpa, :reverse
315
-
318
+
319
+ #
320
+ # Returns the network number in Unsigned 128bits format
321
+ #
322
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
323
+ #
324
+ # ip6.network_u128
325
+ # #=> 42540766411282592856903984951653826560
326
+ #
327
+ def network_u128
328
+ to_u128 & @prefix.to_u128
329
+ end
330
+
331
+ #
332
+ # Checks whether a subnet includes the given IP address.
333
+ #
334
+ # Example:
335
+ #
336
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
337
+ # addr = IPAddress "2001:db8::8:800:200c:1/128"
338
+ #
339
+ # ip6.include? addr
340
+ # #=> true
341
+ #
342
+ # ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
343
+ # #=> false
344
+ #
345
+ def include?(oth)
346
+ @prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
347
+ end
316
348
 
317
349
  #
318
350
  # Compressed form of the IPv6 address
@@ -350,7 +382,7 @@ module IPAddress;
350
382
  # See IPAddress::IPv6::Mapped for more information
351
383
  #
352
384
  def mapped?
353
- false
385
+ to_u128 >> 32 == 0xffff
354
386
  end
355
387
 
356
388
  #
@@ -674,13 +706,29 @@ module IPAddress;
674
706
  attr_reader :ipv4
675
707
 
676
708
  #
677
- # Creates a new IPv6 unspecified address
709
+ # Creates a new IPv6 IPv4-mapped address
678
710
  #
679
711
  # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
680
712
  #
713
+ # ipv6.ipv4.class
714
+ # #=> IPAddress::IPv4
715
+ #
716
+ # An IPv6 IPv4-mapped address can also be created using the
717
+ # IPv6 only format of the address:
718
+ #
719
+ # ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403"
720
+ #
721
+ # ip6.to_s
722
+ # #=> "::ffff:13.1.68.3"
723
+ #
681
724
  def initialize(str)
682
725
  string, netmask = str.split("/")
683
- @ipv4 = IPAddress::IPv4.extract(string)
726
+ if string =~ /\./ # IPv4 in dotted decimal form
727
+ @ipv4 = IPAddress::IPv4.extract(string)
728
+ else # IPv4 in hex form
729
+ groups = IPAddress::IPv6.groups(string)
730
+ @ipv4 = IPAddress::IPv4.parse_u32((groups[-2]<< 16)+groups[-1])
731
+ end
684
732
  super("::ffff:#{@ipv4.to_ipv6}/#{netmask}")
685
733
  end
686
734
 
@@ -688,11 +736,10 @@ module IPAddress;
688
736
  # Similar to IPv6#to_s, but prints out the IPv4 address
689
737
  # in dotted decimal format
690
738
  #
691
- #
692
739
  # ip6 = IPAddress "::ffff:172.16.10.1/128"
693
740
  #
694
741
  # ip6.to_s
695
- # #=> "::FFFF:172.16.10.1"
742
+ # #=> "::ffff:172.16.10.1"
696
743
  #
697
744
  def to_s
698
745
  "::ffff:#{@ipv4.address}"
@@ -705,8 +752,8 @@ module IPAddress;
705
752
  #
706
753
  # ip6 = IPAddress "::ffff:172.16.10.1/128"
707
754
  #
708
- # ip6.to_s
709
- # #=> "::FFFF:172.16.10.1/128"
755
+ # ip6.to_string
756
+ # #=> "::ffff:172.16.10.1/128"
710
757
  #
711
758
  def to_string
712
759
  "::ffff:#{@ipv4.address}/#@prefix"
@@ -83,6 +83,8 @@ module IPAddress
83
83
 
84
84
  class Prefix32 < Prefix
85
85
 
86
+ IN4MASK = 0xffffffff
87
+
86
88
  #
87
89
  # Creates a new prefix object for 32 bits IPv4 addresses
88
90
  #
@@ -91,11 +93,24 @@ module IPAddress
91
93
  #
92
94
  def initialize(num)
93
95
  unless (1..32).include? num
94
- raise ArgumentError, "Prefix must be in range 1..128, got: #{num}"
96
+ raise ArgumentError, "Prefix must be in range 1..32, got: #{num}"
95
97
  end
96
98
  super(num)
97
99
  end
98
100
 
101
+ #
102
+ # Returns the length of the host portion
103
+ # of a netmask.
104
+ #
105
+ # prefix = Prefix32.new 24
106
+ #
107
+ # prefix.host_prefix
108
+ # #=> 8
109
+ #
110
+ def host_prefix
111
+ 32 - @prefix
112
+ end
113
+
99
114
  #
100
115
  # Transforms the prefix into a string of bits
101
116
  # representing the netmask
@@ -106,7 +121,7 @@ module IPAddress
106
121
  # #=> "11111111111111111111111100000000"
107
122
  #
108
123
  def bits
109
- "1" * @prefix + "0" * (32 - @prefix)
124
+ to_u32.to_s(2)
110
125
  end
111
126
 
112
127
  #
@@ -145,7 +160,7 @@ module IPAddress
145
160
  # #=> 4294967040
146
161
  #
147
162
  def to_u32
148
- [bits].pack("B*").unpack("N").first
163
+ (IN4MASK >> host_prefix) << host_prefix
149
164
  end
150
165
 
151
166
  #
@@ -185,7 +200,7 @@ module IPAddress
185
200
  def self.parse_netmask(netmask)
186
201
  octets = netmask.split(".").map{|i| i.to_i}
187
202
  num = octets.pack("C"*octets.size).unpack("B*").first.count "1"
188
- return IPAddress::Prefix.new(num)
203
+ return self.new(num)
189
204
  end
190
205
 
191
206
  end # class Prefix32 < Prefix
@@ -229,7 +244,7 @@ module IPAddress
229
244
  # #=> 340282366920938463444927863358058659840
230
245
  #
231
246
  def to_u128
232
- eval "0b#{bits}.to_i"
247
+ bits.to_i(2)
233
248
  end
234
249
 
235
250
  end # class Prefix123 < Prefix
@@ -6,8 +6,8 @@ class IPv4Test < Test::Unit::TestCase
6
6
  @klass = IPAddress::IPv4
7
7
 
8
8
  @valid_ipv4 = {
9
- "10.0.0.0" => ["10.0.0.0", 8],
10
- "10.0.0.1" => ["10.0.0.1", 8],
9
+ "10.0.0.0" => ["10.0.0.0", 32],
10
+ "10.0.0.1" => ["10.0.0.1", 32],
11
11
  "10.0.0.1/24" => ["10.0.0.1", 24],
12
12
  "10.0.0.1/255.255.255.0" => ["10.0.0.1", 24]}
13
13
 
@@ -50,6 +50,11 @@ class IPv4Test < Test::Unit::TestCase
50
50
  @class_a = @klass.new("10.0.0.1/8")
51
51
  @class_b = @klass.new("172.16.0.1/16")
52
52
  @class_c = @klass.new("192.168.0.1/24")
53
+
54
+ @classful = {
55
+ "10.1.1.1" => 8,
56
+ "150.1.1.1" => 16,
57
+ "200.1.1.1" => 24 }
53
58
 
54
59
  end
55
60
 
@@ -80,6 +85,7 @@ class IPv4Test < Test::Unit::TestCase
80
85
  end
81
86
  ip = @klass.new("10.10.0.0")
82
87
  assert_instance_of IPAddress::Prefix32, ip.prefix
88
+ assert_equal 32, ip.prefix.to_i
83
89
  end
84
90
 
85
91
  def test_attributes
@@ -231,6 +237,21 @@ class IPv4Test < Test::Unit::TestCase
231
237
  assert_equal false, ip.include?(@klass.new("13.16.0.0/32"))
232
238
  end
233
239
 
240
+ def test_method_private?
241
+ assert_equal true, @klass.new("192.168.10.50/24").private?
242
+ assert_equal true, @klass.new("192.168.10.50/16").private?
243
+ assert_equal true, @klass.new("172.16.77.40/24").private?
244
+ assert_equal true, @klass.new("172.16.10.50/14").private?
245
+ assert_equal true, @klass.new("10.10.10.10/10").private?
246
+ assert_equal true, @klass.new("10.0.0.0/8").private?
247
+ assert_equal false, @klass.new("192.168.10.50/12").private?
248
+ assert_equal false, @klass.new("3.3.3.3").private?
249
+ assert_equal false, @klass.new("10.0.0.0/7").private?
250
+ assert_equal false, @klass.new("172.32.0.0/12").private?
251
+ assert_equal false, @klass.new("172.16.0.0/11").private?
252
+ assert_equal false, @klass.new("192.0.0.2/24").private?
253
+ end
254
+
234
255
  def test_method_octet
235
256
  assert_equal 172, @ip[0]
236
257
  assert_equal 16, @ip[1]
@@ -438,7 +459,16 @@ class IPv4Test < Test::Unit::TestCase
438
459
  ip = @klass.parse_data "\254\020\n\001"
439
460
  assert_instance_of @klass, ip
440
461
  assert_equal "172.16.10.1", ip.address
441
- assert_equal "172.16.10.1/16", ip.to_string
462
+ assert_equal "172.16.10.1/32", ip.to_string
463
+ end
464
+
465
+ def test_classmethod_parse_classful
466
+ @classful.each do |ip,prefix|
467
+ res = @klass.parse_classful(ip)
468
+ assert_equal prefix, res.prefix
469
+ assert_equal "#{ip}/#{prefix}", res.to_string
470
+ end
471
+ assert_raise(ArgumentError){ @klass.parse_classful("192.168.256.257") }
442
472
  end
443
473
 
444
474
  end # class IPv4Test
@@ -39,7 +39,6 @@ class IPv6Test < Test::Unit::TestCase
39
39
  @hex = "20010db80000000000080800200c417a"
40
40
  end
41
41
 
42
-
43
42
  def test_attribute_address
44
43
  addr = "2001:0db8:0000:0000:0008:0800:200c:417a"
45
44
  assert_equal addr, @ip.address
@@ -54,6 +53,10 @@ class IPv6Test < Test::Unit::TestCase
54
53
  assert_raise(ArgumentError) {@klass.new ip}
55
54
  end
56
55
  assert_equal 64, @ip.prefix
56
+
57
+ assert_raise(ArgumentError) {
58
+ @klass.new "::10.1.1.1"
59
+ }
57
60
  end
58
61
 
59
62
  def test_attribute_groups
@@ -88,6 +91,8 @@ class IPv6Test < Test::Unit::TestCase
88
91
 
89
92
  def test_method_mapped?
90
93
  assert_equal false, @ip.mapped?
94
+ ip6 = @klass.new "::ffff:1234:5678"
95
+ assert_equal true, ip6.mapped?
91
96
  end
92
97
 
93
98
  def test_method_literal
@@ -106,6 +111,29 @@ class IPv6Test < Test::Unit::TestCase
106
111
  assert_equal false, @ip.network?
107
112
  end
108
113
 
114
+ def test_method_network_u128
115
+ assert_equal 42540766411282592856903984951653826560, @ip.network_u128
116
+ end
117
+
118
+ def test_method_include?
119
+ assert_equal true, @ip.include?(@ip)
120
+ # test prefix on same address
121
+ included = @klass.new "2001:db8::8:800:200c:417a/128"
122
+ not_included = @klass.new "2001:db8::8:800:200c:417a/46"
123
+ assert_equal true, @ip.include?(included)
124
+ assert_equal false, @ip.include?(not_included)
125
+ # test address on same prefix
126
+ included = @klass.new "2001:db8::8:800:200c:0/64"
127
+ not_included = @klass.new "2001:db8:1::8:800:200c:417a/64"
128
+ assert_equal true, @ip.include?(included)
129
+ assert_equal false, @ip.include?(not_included)
130
+ # general test
131
+ included = @klass.new "2001:db8::8:800:200c:1/128"
132
+ not_included = @klass.new "2001:db8:1::8:800:200c:417a/76"
133
+ assert_equal true, @ip.include?(included)
134
+ assert_equal false, @ip.include?(not_included)
135
+ end
136
+
109
137
  def test_method_to_hex
110
138
  assert_equal @hex, @ip.to_hex
111
139
  end
@@ -128,20 +156,11 @@ class IPv6Test < Test::Unit::TestCase
128
156
  assert_equal str, @ip.data
129
157
  end
130
158
 
131
-
132
159
  def test_method_reverse
133
160
  str = "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
134
161
  assert_equal str, @klass.new("3ffe:505:2::f").reverse
135
162
  end
136
163
 
137
- # def test_ip6_int
138
- # assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int", IPAddress("3ffe:505:2::f").ip6_int)
139
- # assert_raises(ArgumentError) {
140
- # IPAddress("192.168.2.1").ip6_int
141
- # }
142
- # end
143
-
144
-
145
164
  def test_method_compressed
146
165
  assert_equal "1:1:1::1", @klass.new("1:1:1:0:0:0:0:1").compressed
147
166
  assert_equal "1:0:1::1", @klass.new("1:0:1:0:0:0:0:1").compressed
@@ -287,6 +306,15 @@ class IPv6MappedTest < Test::Unit::TestCase
287
306
  @valid_mapped = {'::13.1.68.3' => 281470899930115,
288
307
  '0:0:0:0:0:ffff:129.144.52.38' => 281472855454758,
289
308
  '::ffff:129.144.52.38' => 281472855454758}
309
+
310
+ @valid_mapped_ipv6 = {'::0d01:4403' => 281470899930115,
311
+ '0:0:0:0:0:ffff:8190:3426' => 281472855454758,
312
+ '::ffff:8190:3426' => 281472855454758}
313
+
314
+ @valid_mapped_ipv6_conversion = {'::0d01:4403' => "13.1.68.3",
315
+ '0:0:0:0:0:ffff:8190:3426' => "129.144.52.38",
316
+ '::ffff:8190:3426' => "129.144.52.38"}
317
+
290
318
  end
291
319
 
292
320
  def test_initialize
@@ -296,6 +324,16 @@ class IPv6MappedTest < Test::Unit::TestCase
296
324
  assert_nothing_raised {@klass.new ip}
297
325
  assert_equal u128, @klass.new(ip).to_u128
298
326
  end
327
+ @valid_mapped_ipv6.each do |ip, u128|
328
+ assert_nothing_raised {@klass.new ip}
329
+ assert_equal u128, @klass.new(ip).to_u128
330
+ end
331
+ end
332
+
333
+ def test_mapped_from_ipv6_conversion
334
+ @valid_mapped_ipv6_conversion.each do |ip6,ip4|
335
+ assert_equal ip4, @klass.new(ip6).ipv4.to_s
336
+ end
299
337
  end
300
338
 
301
339
  def test_attributes
@@ -41,6 +41,7 @@ class Prefix32Test < Test::Unit::TestCase
41
41
  @prefix_hash.each do |netmask, num|
42
42
  prefix = @klass.parse_netmask(netmask)
43
43
  assert_equal num, prefix.prefix
44
+ assert_instance_of @klass, prefix
44
45
  end
45
46
  end
46
47
 
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ipaddress
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ hash: 3
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 7
9
+ - 0
10
+ version: 0.7.0
5
11
  platform: ruby
6
12
  authors:
7
13
  - Marco Ceresa
@@ -9,7 +15,7 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-07-19 00:00:00 +02:00
18
+ date: 2010-09-08 00:00:00 +02:00
13
19
  default_executable:
14
20
  dependencies: []
15
21
 
@@ -51,21 +57,27 @@ rdoc_options:
51
57
  require_paths:
52
58
  - lib
53
59
  required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
54
61
  requirements:
55
62
  - - ">="
56
63
  - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
57
67
  version: "0"
58
- version:
59
68
  required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
60
70
  requirements:
61
71
  - - ">="
62
72
  - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
63
76
  version: "0"
64
- version:
65
77
  requirements: []
66
78
 
67
79
  rubyforge_project:
68
- rubygems_version: 1.3.5
80
+ rubygems_version: 1.3.7
69
81
  signing_key:
70
82
  specification_version: 3
71
83
  summary: IPv4/IPv6 addresses manipulation library