ipaddress-0.8.3/ 0000755 0000041 0000041 00000000000 12666647272 013552 5 ustar www-data www-data ipaddress-0.8.3/Rakefile 0000644 0000041 0000041 00000002610 12666647272 015216 0 ustar www-data www-data require 'rubygems'
require 'rake'
require 'rake/clean'
require "bundler/gem_tasks"
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
begin
require 'rcov/rcovtask'
Rcov::RcovTask.new do |test|
test.libs << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
rescue LoadError
task :rcov do
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
end
end
task :default => :test
require 'rdoc/task'
Rake::RDocTask.new do |rdoc|
if File.exist?('VERSION.yml')
config = YAML.load(File.read('VERSION.yml'))
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
else
version = ""
end
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "ipaddress #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
desc "Open an irb session preloaded with this library"
task :console do
sh "irb -rubygems -I lib -r ipaddress.rb"
end
desc "Look for TODO and FIXME tags in the code"
task :todo do
def egrep(pattern)
Dir['**/*.rb'].each do |fn|
count = 0
open(fn) do |f|
while line = f.gets
count += 1
if line =~ pattern
puts "#{fn}:#{count}:#{line}"
end
end
end
end
end
egrep /(FIXME|TODO|TBD)/
end
ipaddress-0.8.3/Gemfile 0000644 0000041 0000041 00000000047 12666647272 015046 0 ustar www-data www-data source "https://rubygems.org"
gemspec
ipaddress-0.8.3/CHANGELOG.rdoc 0000644 0000041 0000041 00000007675 12666647272 015731 0 ustar www-data www-data == ipaddress 0.8.2
CHANGED:: merged bundler branch to cleanup gemspec and Rakefiles
FIXED:: IPAddress::IPv4.split handling (Issue #40)
NEW:: Added #[]= method to IPv4/6 classes to add octet writing support. (Issue #24)
NEW:: IPV4#multicast?
NEW:: IPV4#loopback?
NEW:: IPV4#to()
== ipaddress 0.8.1
CHANGED:: ipaddress now uses minitest for testing, all tests passing
== ipaddress 0.8.0
CHANGED:: ipaddress now uses this[https://gist.github.com/cpetschnig/294476] regexp to validate IPv6 addresses. Thanks to Christoph Petschnig for his regexp and to Bronislav Robenek for fixing this.
CHANGED:: Removed extension methods and extension directory to facilitate integration with the stdlib
CHANGED:: Reworked IPv4#<=>, now intuitively sorts objects based on the prefix
CHANGED:: IPv4#supernet now returns "0.0.0.0/0" if supernetting with a prefix less than 1
CHANGED:: IPv4#subnet now accept a new prefix instead of number of subnets (as per RFC3531)
NEW:: IPv6#network
NEW:: Prefix128#host_prefix
NEW:: IPv6#broadcast_u128
NEW:: IPv6#each
NEW:: IPv6#<=>
NEW:: IPv4#split
NEW:: IP#ntoa
== ipaddress 0.7.5
CHANGED:: IPAddress::IPv4#each_host to improve speed
FIXED:: IPAddress::IPv4::summarize bug (summarization should now work properly)
NEW:: IPAddress::IPv4#include_all?
NEW:: #ipv4? and #ipv6?
== ipaddress 0.7.0
NEW:: IPAddress::IPv6#include?
NEW:: IPAddress::IPv6#network_u128
NEW:: Modified IPAddress::IPv6::Mapped to accept IPv4 mapped addresses in IPv6 format
NEW:: IPAddress::IPv4#private?
NEW:: IPAddress::IPv4::parse_classful
== ipaddress 0.6.0
=== API changes
* IPv4#to_s now returns the address portion only,
to retain compatibility with IPAddr. Example:
IPAddress("172.16.10.1/24").to_s
#=> "172.16.10.1" # ipaddress 0.6.0
IPAddress("172.16.10.1/24").to_s
#=> "172.16.10.1/24" # ipaddress 0.5.0
* IPv6#to_s now returns the address portion only,
to retain compatibility with IPAddr. Example:
IPAddress "2001:db8::8:800:200c:417a/64".to_s
#=> "2001:db8::8:800:200c:417a" # ipaddress 0.6.0
IPAddress "2001:db8::8:800:200c:417a/64".to_s
#=> "2001:db8::8:800:200c:417a/64" # ipaddress 0.6.0
* IPv6::Unspecified#to_s, IPv6::Loopback and
IPv6::Mapped#to_s now return the address portion only,
to retain compatibility with IPAddr.
* IPv4::summarize now returns an array even if the
result is a single subnet, to keep consistency
and avoid confusion
=== New methods
* IPv4#to_string and IPv6#to_string: print the address
with the prefix portion, like the #to_s method in
ipaddress 0.5.0
* IPAddress::parse, for those who don't like the wrapper
method IPAddress()
* IPv6#to_string_uncompressed, returns a string with the
uncompressed IPv6 and the prefix
* IPv6::Mapped#to_string, returns the IPv6 Mapped address
with IPv4 notation and the prefix
* IPv6#reverse, returns the ip6.arpa DNS reverse lookup
string
* IPv4#arpa and IPv6#arpa, alias of the respective #reverse
methods
* Prefix#+, Prefix#-
=== Library structure
* Moved all the IPAddress module methods from
lib/ipaddress/ipbase.rb to lib/ipaddress.rb
* Removed IPBase superclass
* IPv4 and IPv6 classes no longer inherit from IPBase
* Removed lib/ipaddress/ipbase.rb
* Removed test/ipaddress/ipbase_test.rb
=== Minor fixes
* Replaced Ruby 1.9 deprecated Hash#index with Hash#key
* Removed require ruby-prof from tests which was causing
users to install ruby-prof or manually remove the line
* Removed "must" method from tests, replaced by normal
Test::Unit methods
* Removed duplicate Jeweler entry in Rakefile
* Made Integer#closest_power_of_2 more general by adding
an optional limit parameter
* Fixed summarization algorithm (thanks to nicolas fevrier)
* Fixed bug in prefix_from_ip (thanks to jdpace)
=== Documentation
* Normalized README rdoc headers
* Added documentation for IPAddress::Prefix
* Added documentation for IPAddress::IPv4 and
IPAddress::IPv6
* Fixed formatting
* Fixed lots of typos
ipaddress-0.8.3/LICENSE.txt 0000644 0000041 0000041 00000002045 12666647272 015376 0 ustar www-data www-data Copyright (c) 2009-2015 Marco Ceresa
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ipaddress-0.8.3/README.rdoc 0000644 0000041 0000041 00000067563 12666647272 015401 0 ustar www-data www-data == IPAddress 1.0 is currently under development and will be released soon! Stay tuned!
---
= IPAddress
IPAddress is a Ruby library designed to make the use of IPv4 and IPv6
addresses simple, powerful and enjoyable. It provides a complete set of
methods to handle IP addresses for any need, from simple scripting to
full network design.
IPAddress is written with a full OO interface, and its code is easy to
read, maintain and extend. The documentation is full of examples, to
let you start being productive immediately.
This document provides a brief introduction to the library and
examples of typical usage.
== Requirements
* Ruby >= 1.8.7 (not tested with previous versions)
* Ruby 1.9.2 or later is strongly recommended
IPAddress 0.8.2 has been tested on:
* ruby-1.8.7-p334 [ i386 ]
* ree-1.8.7-2011.03 [ i386 ]
* rbx-head [ ]
* jruby-1.6.1 [ linux-i386-java ]
* ruby-1.9.1-p431 [ i386 ]
* ruby-1.9.2-p180 [ i386 ]
* ruby-2.0.0-p353 [ x86_64-darwin14.0.0 ]
* ruby-2.1.3-p242 [ x86_64-darwin14.0.0 ]
If you want to collaborate feel
free to send a small report to my email address, or
{join the discussion}[http://groups.google.com/group/ruby-ipaddress].
== Installation
Install the library using rubygems
$ gem install ipaddress
You can then use it in your programs:
require 'rubygems' # optional
require 'ipaddress'
Another way would be to clone the git repository
$ git clone git://github.com/bluemonk/ipaddress.git
And then install the library
$ cd ipaddress
ipaddress$ rake install
== Documentation
The code is fully documented with RDoc. You can generate the
documentation with Rake:
ipaddress$ rake rdoc
The latest documentation can be found online at
{this address}[http://rubydoc.info/gems/ipaddress/0.8.0/frames]
== IPv4
Class `IPAddress::IPv4` is used to handle IPv4 type addresses. IPAddress
is similar to other IP Addresses libraries, like Ruby's own
IPAddr. However it works slightly different, as we will see.
=== Create a new IPv4 address
The usual way to express an IP Address is using its dotted decimal
form, such as `172.16.10.1`, and a prefix, such as `24`, separated by a
slash.
172.16.10.1/24
To create a new IPv4 object, you can use IPv4 own class
ip = IPAddress::IPv4.new "172.16.10.1/24"
or, in a easier way, using the IPAddress parse method
ip = IPAddress.parse "172.16.10.1/24"
which accepts and parses any kind of IP (uint32, IPv4, IPV6 and
IPv4 IPv6 Mapped addresses).
If you like syntactic sugar, you can use the wrapper method
`IPAddress()`, which is built around `IPAddress::parse`:
ip = IPAddress "172.16.10.1/24"
You can specify an IPv4 address in any of two ways:
IPAddress "172.16.10.1/24"
IPAddress "172.16.10.1/255.255.255.0"
In this example, prefix `/24` and netmask `255.255.255.0` are the same and
you have the flexibility to use either one of them.
If you don't explicitly specify the prefix (or the subnet mask),
IPAddress thinks you're dealing with host addresses and not with
networks. Therefore, the default prefix will be `/32`, or
`255.255.255.255`. For example:
# let's declare an host address
host = IPAddress::IPv4.new "10.1.1.1"
puts host.to_string
#=> "10.1.1.1/32"
The new created object has prefix `/32`, which is the same
as we created the following:
host = IPAddress::IPv4.new "10.1.1.1/32"
You can also pass a `uint32` to obtain an `IPAddress::IPv4` object:
# Create host object
ip = IPAddress 167837953
puts ip.to_string
#=> "10.1.1.1/32"
=== Handling the IPv4 address
Once created, you can obtain the attributes for an IPv4 object:
ip = IPAddress("172.16.10.1/24")
ip.address
#=> "172.16.10.1"
ip.prefix
#=> 24
In case you need to retrieve the netmask in IPv4 format, you can use
the `IPv4#netmask` method:
ip.netmask
#=> "255.255.255.0"
A special attribute, `IPv4#octets`, is available to get the four
decimal octets from the IP address:
ip.octets
#=> [172,16,10,1]
Shortcut method `IPv4#[]`, provides access to a given octet whithin the
range:
ip[1]
#=> 16
If you need to print out the IPv4 address in a canonical form, you can
use `IPv4#to_string`:
ip.to_string
#=> "172.16.10.l/24"
=== Changing netmask
You can set a new prefix (netmask) after creating an IPv4
object. For example:
ip.prefix = 25
ip.to_string
#=> "172.16.10.l/25"
If you need to use a netmask in IPv4 format, you can achive so by
using the `IPv4#netmask=` method:
ip.netmask = "255.255.255.252"
ip.to_string
#=> "172.16.10.1/30"
=== Working with networks, broadcasts and addresses
Some very important topics in dealing with IP addresses are the
concepts of +network+ and +broadcast+, as well as the addresses
included in a range.
When you specify an IPv4 address such as `172.16.10.1/24`, you are
actually handling two different information:
* The IP address itself, "172.16.10.1"
* The subnet mask which indicates the network
The network number is the IP which has all zeroes in the host
portion. In our example, because the prefix is 24, we identify our
network number to have the last 8 (32-24) bits all zeroes. Thus, IP
address `172.16.10.1/24` belongs to network `172.16.10.0/24`.
This is very important because, for instance, IP `172.16.10.1/16` is
very different to the previous one, belonging to the very different
network `172.16.0.0/16`.
==== Networks
With IPAddress it's very easy to calculate the network for an IP
address:
ip = IPAddress "172.16.10.1/24"
net = ip.network
#=> #
net.to_string
#=> "172.16.10.0/24"
Method IPv4#network creates a new IPv4 object from the network
number, calculated after the original object. We want to outline here
that the network address is a perfect legitimate IPv4 address, which
just happen to have all zeroes in the host portion.
You can use method `IPv4#network?` to check whether an IP address is a
network or not:
ip1 = IPAddress "172.16.10.1/24"
ip2 = IPAddress "172.16.10.4/30"
ip1.network?
#=> false
ip2.network?
#=> true
==== Broadcast
The broadcast address is the contrary than the network number: where
the network number has all zeroes in the host portion, the broadcast
address has all one's. For example, ip `172.16.10.1/24` has broadcast
`172.16.10.255/24`, where ip `172.16.10.1/16` has broadcast
`172.16.255.255/16`.
Method `IPv4#broadcast` has the same behavior as is `#network`
counterpart: it creates a new IPv4 object to handle the broadcast
address:
ip = IPAddress "172.16.10.1/24"
bcast = ip.broadcast
#=> #
bcast.to_string
#=> "172.16.10.255/24"
==== Addresses, ranges and iterators
So we see that the netmask essentially specifies a range for IP
addresses that are included in a network: all the addresses between
the network number and the broadcast. IPAddress has many methods to
iterate between those addresses. Let's start with `IPv4#each`, which
iterates over all addresses in a range
ip = IPAddress "172.16.10.1/24"
ip.each do |addr|
puts addr
end
It is important to note that it doesn't matter if the original IP is a
host IP or a network number (or a broadcast address): the #each method
only considers the range that the original IP specifies.
If you only want to iterate over hosts IP, use the `IPv4#each_host`
method:
ip = IPAddress "172.16.10.1/24"
ip.each_host do |host|
puts host
end
Methods `IPv4#first` and `IPv4#last` return a new object containing
respectively the first and the last host address in the range
ip = IPAddress "172.16.10.100/24"
ip.first.to_string
#=> "172.16.10.1/24"
ip.last.to_string
#=> "172.16.10.254/24"
Checking if an address is loopback is easy with the `IPv4#loopback?`
method:
ip = IPAddress "127.0.0.1"
ip.loopback?
#=> true
Checking if an address is in the multicast range can be done using the `IPv4#multicast?`
method:
ip = IPAddress "224.0.0.1/32"
ip.multicast?
#=> true
The ability to generate a range also exists by using the `IPv4#to()` method. This allows you to create a subnet agnostic range based off a fixed amount.
ip = IPAddress "172.16.10.100/24"
ip.to('172.16.10.110')
#=> ["172.16.10.100", ..., "172.16.10.110"]
=== IP special formats
The IPAddress library provides a complete set of methods to access an
IPv4 address in special formats, such as binary, 32 bits unsigned int,
data and hexadecimal.
Let's take the following IPv4 as an example:
ip = IPAddress "172.16.10.1/24"
ip.address
#=> "172.16.10.1"
The first thing to highlight here is that all these conversion methods
only take into consideration the address portion of an IPv4 object and
not the prefix (netmask).
So, to express the address in binary format, use the `IPv4#bits` method:
ip.bits
#=> "10101100000100000000101000000001"
To calculate the 32 bits unsigned int format of the ip address, use
the `IPv4#to_u32` method
ip.to_u32
#=> 2886732289
This method is the equivalent of the Unix call `pton()`, expressing an
IP address in the so called +network byte order+ notation. However, if
you want to transmit your IP over a network socket, you might need to
transform it in data format using the `IPv4#data` method:
ip.data
#=> "\254\020\n\001"
Also, you can transform an IPv4 address into a format which is
suitable to use in IPv4-IPv6 mapped addresses:
ip.to_ipv6
#=> "ac10:0a01"
Finally, much like `IPv4#to_ipv6` you can use to `IPv4#to_h` method to return a non-semicolon delineated string (useful with pcap/byte level usage):
ip.to_h
#=> "ac100a01"
=== Classful networks
IPAddress allows you to create and manipulate objects using the old
and deprecated (but apparently still popular) classful networks concept.
Classful networks and addresses don't have a prefix: their subnet mask
is univocally identified by their address, and therefore divided in classes.
As per RFC 791, these classes are:
* Class A, from 0.0.0.0 to 127.255.255.255
* Class B, from 128.0.0.0 to 191.255.255.255
* Class C, from 192.0.0.0 to 255.255.255.255
Since classful networks here are only considered to calculate the default
prefix number, classes D and E are not considered.
To create a classful IP and prefix from an IP address, use the
IPv4::parse_classful method:
# classful ip
ip = IPAddress::IPv4::parse_classful "10.1.1.1"
ip.prefix
#=> 8
The method automatically created a new IPv4 object and assigned it
the correct prefix.
You can easily check which CLASSFUL network an IPv4 object belongs:
ip = IPAddress("10.0.0.1/24")
ip.a?
#=> true
ip = IPAddress("172.16.10.1/24")
ip.b?
#=> true
ip = IPAddress("192.168.1.1/30")
ip.c?
#=> true
Remember that these methods are only checking the address portion of an IP, and are
independent from its prefix, as classful networks have no concept of prefix.
For more information on CLASSFUL networks visit the
{Wikipedia page}[http://en.wikipedia.org/wiki/Classful_network]
=== Network design with IPAddress
IPAddress includes a lot of useful methods to manipulate IPv4 and IPv6
networks and do some basic network design.
==== Subnetting
The process of subnetting is the division of a network into smaller
(in terms of hosts capacity) networks, called subnets, so that they
all share a common root, which is the starting network.
For example, if you have network "172.16.10.0/24", we can subnet it
into 4 smaller subnets. The new prefix will be /26, because 4 is 2^2
and therefore we add 2 bits to the network prefix (24+2=26).
Subnetting is easy with IPAddress. You actually have two options:
* IPv4#subnet: specify a new prefix
* IPv4#split: tell IPAddress how many subnets you want to create.
Let's examine `IPv4#subnet` first. Say you have network "172.16.10.0/24"
and you want to subnet it into /26 networks. With IPAddress it's very
easy:
network = IPAddress "172.16.10.0/24"
subnets = network.subnet(26)
subnets.map{|i| i.to_string}
#=> ["172.16.10.0/26",
"172.16.10.64/26",
"172.16.10.128/26",
"172.16.10.192/26"]
As you can see, an Array has been created, containing 4 new IPv4 objects
representing the new subnets.
Another way to create subnets is to tell IPAddress how many subnets you'd
like to have, and letting the library calculate the new prefix for you.
Let's see how it works, using `IPv4#split` method. Say you want 4 new subnets:
network = IPAddress("172.16.10.0/24")
subnets = network.split(4)
subnets.map{|i| i.to_string}
#=> ["172.16.10.0/26",
"172.16.10.64/26",
"172.16.10.128/26",
"172.16.10.192/26"]
Hey, that's the same result as before! This actually makes sense, as the
two operations are complementary. When you use `IPv4#subnet` with the new
prefix, IPAddress will always create a number of subnets that is a power
of two. This is equivalent to use IPv4#split with a power of 2.
Where `IPv4#split` really shines is with the so called "uneven subnetting".
You are not limited to split a network into a power-of-two numbers of
subnets: IPAddress lets you create any number of subnets, and it will
try to organize the new created network in the best possible way, making
an efficient allocation of the space.
An example here is worth a thousand words. Let's use the same network
as the previous examples:
network = IPAddress("172.16.10.0/24")
How do we split this network into 3 subnets? Very easy:
subnets = network.split(3)
subnets.map{|i| i.to_string}
#=> ["172.16.10.0/26",
"172.16.10.64/26",
"172.16.10.128/25"]
As you can see, IPAddress tried to perform a good allocation by filling up
all the address space from the original network. There is no point in splitting
a network into 3 subnets like `172.16.10.0/26`, `172.16.10.64/26` and
`172.16.10.128/26`, as you would end up having `172.16.10.192/26` wasted (plus,
I suppose I wouldn't need a Ruby library to perform un-efficient IP
allocation, as I do that myself very well ;) ).
We can go even further and split into 11 subnets:
network.split(11)
#=> ["172.16.10.0/28", "172.16.10.16/28", "172.16.10.32/28",
"172.16.10.48/28", "172.16.10.64/28", "172.16.10.80/28",
"172.16.10.96/28", "172.16.10.112/28", "172.16.10.128/27",
"172.16.10.160/27", "172.16.10.192/26"]
As you can see, most of the networks are `/28`, with a few `/27` and one
`/26` to fill up the remaining space.
==== Summarization
Summarization (or aggregation) is the process when two or more
networks are taken together to check if a supernet, including
all and only these networks, exists. If it exists then this supernet
is called the summarized (or aggregated) network.
It is very important to understand that summarization can only
occur if there are no holes in the aggregated network, or, in
other words, if the given networks fill completely the address space
of the supernet. So the two rules are:
1) The aggregate network must contain +all+ the IP addresses of the
original networks;
2) The aggregate network must contain +only+ the IP addresses of the
original networks;
A few examples will help clarify the above. Let's consider for
instance the following two networks:
ip1 = IPAddress("172.16.10.0/24")
ip2 = IPAddress("172.16.11.0/24")
These two networks can be expressed using only one IP address
network if we change the prefix. Let Ruby do the work:
IPAddress::IPv4::summarize(ip1,ip2).map(&:to_string)
#=> "172.16.10.0/23"
We note how the network `172.16.10.0/23` includes all the
addresses specified in the above networks, and (more important) includes
ONLY those addresses.
If we summarized +ip1+ and +ip2+ with the following network:
"172.16.0.0/16"
we would have satisfied rule #1 above, but not rule #2. So
"172.16.0.0/16"
is not an aggregate network for +ip1+ and +ip2+.
If it's not possible to compute a single aggregated network for
all the original networks, the method returns an array with all the
aggregate networks found. For example, the following four networks can be
aggregated in a single `/22`:
ip1 = IPAddress("10.0.0.1/24")
ip2 = IPAddress("10.0.1.1/24")
ip3 = IPAddress("10.0.2.1/24")
ip4 = IPAddress("10.0.3.1/24")
IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
#=> ["10.0.0.0/22"]
But the following networks can't be summarized in a single
network:
ip1 = IPAddress("10.0.1.1/24")
ip2 = IPAddress("10.0.2.1/24")
ip3 = IPAddress("10.0.3.1/24")
ip4 = IPAddress("10.0.4.1/24")
IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
#=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
In this case, the two summarizables networks have been aggregated into
a single `/23`, while the other two networks have been left untouched.
==== Supernetting
Supernetting is a different operation than aggregation, as it only
works on a single network and returns a new single IPv4 object,
representing the supernet.
Supernetting is similar to subnetting, except that you getting as a
result a network with a smaller prefix (bigger host space). For
example, given the network
ip = IPAddress("172.16.10.0/24")
you can supernet it with a new /23 prefix
ip.supernet(23).to_string
#=> "172.16.10.0/23"
However if you supernet it with a `/22` prefix, the network address will
change:
ip.supernet(22).to_string
#=> "172.16.8.0/22"
This is because `172.16.10.0/22` is not a network anymore, but an host
address.
== IPv6
IPAddress is not only fantastic for IPv4 addresses, it's also great to
handle IPv6 addresses family! Let's discover together how to use it in
our projects.
=== IPv6 addresses
IPv6 addresses are 128 bits long, in contrast with IPv4 addresses
which are only 32 bits long. An IPv6 address is generally written as
eight groups of four hexadecimal digits, each group representing 16
bits or two octet. For example, the following is a valid IPv6
address:
2001:0db8:0000:0000:0008:0800:200c:417a
Letters in an IPv6 address are usually written downcase, as per
RFC. You can create a new IPv6 object using uppercase letters, but
they will be converted.
==== Compression
Since IPv6 addresses are very long to write, there are some
simplifications and compressions that you can use to shorten them.
* Leading zeroes: all the leading zeroes within a group can be
omitted: "0008" would become "8"
* A string of consecutive zeroes can be replaced by the string
"::". This can be only applied once.
Using compression, the IPv6 address written above can be shorten into
the following, equivalent, address
2001:db8::8:800:200c:417a
This short version is often used in human representation.
==== Network Mask
As we used to do with IPv4 addresses, an IPv6 address can be written
using the prefix notation to specify the subnet mask:
2001:db8::8:800:200c:417a/64
The /64 part means that the first 64 bits of the address are
representing the network portion, and the last 64 bits are the host
portion.
=== Using IPAddress with IPv6 addresses
All the IPv6 representations we've just seen are perfectly fine when
you want to create a new IPv6 address:
ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200C:417A"
ip6 = IPAddress "2001:db8:0:0:8:800:200C:417A"
ip6 = IPAddress "2001:db8:8:800:200C:417A"
All three are giving out the same IPv6 object. The default subnet mask
for an IPv6 is 128, as IPv6 addresses don't have classes like IPv4
addresses. If you want a different mask, you can go ahead and explicit
it:
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
Access the address portion and the prefix by using the respective
methods:
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
ip6.address
#=> "2001:0db8:0000:0000:0008:0800:200c:417a"
ip6.prefix
#=> 64
A compressed version of the IPv6 address can be obtained with the
`IPv6#compressed` method:
ip6 = IPAddress "2001:0db8:0000:0000:0008:200c:417a:00ab/64"
ip6.compressed
#=> "2001:db8::8:800:200c:417a"
=== Handling the IPv6 address
Accessing the groups that form an IPv6 address is very easy with the
`IPv6#groups` method:
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
ip6.groups
#=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
As with IPv4 addresses, each individual group can be accessed using
the `IPv6#[]` shortcut method:
ip6[0]
#=> 8193
ip6[1]
#=> 3512
ip6[2]
#=> 0
ip6[3]
#=> 0
Note that each 16 bits group is expressed in its decimal form. You can
also obtain the groups into hexadecimal format using the `IPv6#hexs`
method:
ip6.hexs
#=> => ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
A few other methods are available to transform an IPv6 address into
decimal representation, with `IPv6.to_i`
ip6.to_i
#=> 42540766411282592856906245548098208122
or to hexadecimal representation
ip6.to_hex
#=> "20010db80000000000080800200c417a"
To print out an IPv6 address in human readable form, use the `IPv6#to_s`, `IPv6#to_string`
and `IPv6#to_string_uncompressed` methods
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
ip6.to_string
#=> "2001:db8::8:800:200c:417a/96"
ip6.to_string_uncompressed
#=> "2001:0db8:0000:0000:0008:0800:200c:417a/96"
As you can see, `IPv6.to_string` prints out the compressed form, while
`IPv6.to_string_uncompressed` uses the expanded version.
==== Compressing and uncompressing
If you have a string representing an IPv6 address, you can easily
compress it and uncompress it using the two class methods IPv6::expand
and IPv6::compress.
For example, let's say you have the following uncompressed IPv6
address:
ip6str = "2001:0DB8:0000:CD30:0000:0000:0000:0000"
Here is the compressed version:
IPAddress::IPv6.compress ip6str
#=> "2001:db8:0:cd30::"
The other way works as well:
ip6str = "2001:db8:0:cd30::"
IPAddress::IPv6.expand ip6str
#=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
These methods can be used when you don't want to create a new object
just for expanding or compressing an address (although a new object is
actually created internally).
=== New IPv6 address from other formats
You can create a new IPv6 address from different formats than just a
string representing the colon-hex groups.
For instance, if you have a data stream, you can use `IPv6::parse_data`,
like in the following example:
data = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
ip6 = IPAddress::IPv6::parse_data data
ip6.prefix = 64
ip6.to_string
#=> "2001:db8::8:800:200c:417a/64"
A new IPv6 address can also be created from an unsigned 128 bits
integer:
u128 = 42540766411282592856906245548098208122
ip6 = IPAddress::IPv6::parse_u128 u128
ip6.prefix = 64
ip6.to_string
#=>"2001:db8::8:800:200c:417a/64"
Finally, a new IPv6 address can be created from an hex string:
hex = "20010db80000000000080800200c417a"
ip6 = IPAddress::IPv6::parse_hex hex
ip6.prefix = 64
ip6.to_string
#=> "2001:db8::8:800:200c:417a/64"
=== Special IPv6 addresses
Some IPv6 have a special meaning and are expressed in a special form,
quite different than an usual IPv6 address. IPAddress has built-in
support for unspecified, loopback and mapped IPv6 addresses.
==== Unspecified address
The address with all zero bits is called the +unspecified+ address
(corresponding to `0.0.0.0` in IPv4). It should be something like this:
0000:0000:0000:0000:0000:0000:0000:0000
but, with the use of compression, it is usually written as just two
colons:
::
or, specifying the netmask:
::/128
With IPAddress, create a new unspecified IPv6 address using its own
subclass:
ip = IPAddress::IPv6::Unspecified.new
ip.to_string
#=> "::/128"
You can easily check if an IPv6 object is an unspecified address by
using the IPv6#unspecified? method
ip.unspecified?
#=> true
An unspecified IPv6 address can also be created with the wrapper
method, like we've seen before
ip = IPAddress "::"
ip.unspecified?
#=> true
This address must never be assigned to an interface and is to be used
only in software before the application has learned its host's source
address appropriate for a pending connection. Routers must not forward
packets with the unspecified address.
==== Loopback address
The loopback address is a unicast localhost address. If an
application in a host sends packets to this address, the IPv6 stack
will loop these packets back on the same virtual interface.
Loopback addresses are expressed in the following form:
::1
or, with their appropriate prefix,
::1/128
As for the unspecified addresses, IPv6 loopbacks can be created with
IPAddress calling their own class:
ip = IPAddress::IPv6::Loopback.new
ip.to_string
#=> "::1/128"
or by using the wrapper:
ip = IPAddress "::1"
ip.to_string
#=> "::1/128"
Checking if an address is loopback is easy with the `IPv6#loopback?`
method:
ip.loopback?
#=> true
The IPv6 loopback address corresponds to `127.0.0.1` in IPv4.
==== Mapped address
It is usually identified as a IPv4 mapped IPv6 address, a particular
IPv6 address which aids the transition from IPv4 to IPv6. The
structure of the address is
::ffff:w.y.x.z
where `w.x.y.z` is a normal IPv4 address. For example, the following is
a mapped IPv6 address:
::ffff:192.168.100.1
IPAddress is very powerful in handling mapped IPv6 addresses, as the
IPv4 portion is stored internally as a normal IPv4 object. Let's have
a look at some examples. To create a new mapped address, just use the
class builder itself
ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
or just use the wrapper method
ip6 = IPAddress "::ffff:172.16.10.1/128"
Let's check it's really a mapped address:
ip6.mapped?
#=> true
ip6.to_string
#=> "::ffff:172.16.10.1/128"
Now with the `#ipv4` attribute, we can easily access the IPv4 portion
of the mapped IPv6 address:
ip6.ipv4.address
#=> "172.16.10.1"
Internally, the IPv4 address is stored as two 16 bits
groups. Therefore all the usual methods for an IPv6 address are
working perfectly fine:
ip6.to_hex
#=> "00000000000000000000ffffac100a01"
ip6.address
#=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
A mapped IPv6 can also be created just by specify the address in the
following format:
ip6 = IPAddress "::172.16.10.1"
That is, two colons and the IPv4 address. However, as by RFC, the `ffff`
group will be automatically added at the beginning
ip6.to_string
=> "::ffff:172.16.10.1/128"
making it a mapped IPv6 compatible address.
== Why not using IPAddr?
IPAddr is the IP addresses library that comes with Ruby standard
lib. We found this library, although very well written, not very
suitable for all our needs, and not very flexible.
Some quick examples of things you can't do with IPAddr:
* store both the address and the prefix information
* quickly find the broadcast address of a network
* iterate over hosts
* perform subnetting or network aggregation
Many methods and procedures are so old that they have been
declared deprecated by the IETF, and some others have bugs in their
implementation.
Moreover, IPAddress is more robust and is already around 50% faster than IPAddr,
in addition to provide an organic API with logical separation and OO structure.
We hope that IPAddress will address all these issues and meet all your
needs in network programming.
== Community
Want to join the community?
* {IPAddress google group}[http://groups.google.com/group/ruby-ipaddress]
We've created a group to discuss about
IPAddress future development, features and provide some kind of support.
Feel free to join us and tell us what you think!
== Thanks to
Thanks to Luca Russo (vargolo) and Simone Carletti
(weppos) for all the support and technical review. Thanks to Marco Beri,
Bryan T. Richardson, Nicolas Fevrier, jdpace, Daniele Alessandri, jrdioko,
Ghislain Charrier, Pawel Krzesniak, Mark Sullivan, Leif Gensert,
Erik Ahlström, Peter Vandenberk and Steve Rawlinson for their support,
feedback and bug reports.
== Copyright
Copyright (c) 2009-2015 Marco Ceresa and Mike Mackintosh. See LICENSE for details.
ipaddress-0.8.3/.rock.yml 0000644 0000041 0000041 00000000106 12666647272 015306 0 ustar www-data www-data runtime: ruby21
doc: exec rake rdoc
build: |
rock clean
{{parent}} ipaddress-0.8.3/lib/ 0000755 0000041 0000041 00000000000 12666647272 014320 5 ustar www-data www-data ipaddress-0.8.3/lib/ipaddress.rb 0000644 0000041 0000041 00000013506 12666647272 016630 0 ustar www-data www-data #
# = IPAddress
#
# A ruby library to manipulate IPv4 and IPv6 addresses
#
#
# Package:: IPAddress
# Author:: Marco Ceresa
# License:: Ruby License
#
#--
#
#++
require 'ipaddress/ipv4'
require 'ipaddress/ipv6'
require 'ipaddress/mongoid' if defined?(Mongoid)
module IPAddress
NAME = "IPAddress"
GEM = "ipaddress"
AUTHORS = ["Marco Ceresa "]
#
# Parse the argument string to create a new
# IPv4, IPv6 or Mapped IP object
#
# ip = IPAddress.parse 167837953 # 10.1.1.1
# ip = IPAddress.parse "172.16.10.1/24"
# ip6 = IPAddress.parse "2001:db8::8:800:200c:417a/64"
# ip_mapped = IPAddress.parse "::ffff:172.16.10.1/128"
#
# All the object created will be instances of the
# correct class:
#
# ip.class
# #=> IPAddress::IPv4
# ip6.class
# #=> IPAddress::IPv6
# ip_mapped.class
# #=> IPAddress::IPv6::Mapped
#
def IPAddress::parse(str)
# Check if an int was passed
if str.kind_of? Integer
return IPAddress::IPv4.new(ntoa(str))
end
case str
when /:.+\./
IPAddress::IPv6::Mapped.new(str)
when /\./
IPAddress::IPv4.new(str)
when /:/
IPAddress::IPv6.new(str)
else
raise ArgumentError, "Unknown IP Address #{str}"
end
end
#
# Converts a unit32 to IPv4
#
# IPAddress::ntoa(167837953)
# #-> "10.1.1.1"
#
def self.ntoa(uint)
unless(uint.is_a? Numeric and uint <= 0xffffffff and uint >= 0)
raise(::ArgumentError, "not a long integer: #{uint.inspect}")
end
ret = []
4.times do
ret.unshift(uint & 0xff)
uint >>= 8
end
ret.join('.')
end
#
# True if the object is an IPv4 address
#
# ip = IPAddress("192.168.10.100/24")
#
# ip.ipv4?
# #-> true
#
def ipv4?
self.kind_of? IPAddress::IPv4
end
#
# True if the object is an IPv6 address
#
# ip = IPAddress("192.168.10.100/24")
#
# ip.ipv6?
# #-> false
#
def ipv6?
self.kind_of? IPAddress::IPv6
end
#
# Checks if the given string is a valid IP address,
# either IPv4 or IPv6
#
# Example:
#
# IPAddress::valid? "2002::1"
# #=> true
#
# IPAddress::valid? "10.0.0.256"
# #=> false
#
def self.valid?(addr)
valid_ipv4?(addr) || valid_ipv6?(addr)
end
#
# Checks if the given string is a valid IPv4 address
#
# Example:
#
# IPAddress::valid_ipv4? "2002::1"
# #=> false
#
# IPAddress::valid_ipv4? "172.16.10.1"
# #=> true
#
def self.valid_ipv4?(addr)
if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
return $~.captures.all? {|i| i.to_i < 256}
end
false
end
#
# Checks if the argument is a valid IPv4 netmask
# expressed in dotted decimal format.
#
# IPAddress.valid_ipv4_netmask? "255.255.0.0"
# #=> true
#
def self.valid_ipv4_netmask?(addr)
arr = addr.split(".").map{|i| i.to_i}.pack("CCCC").unpack("B*").first.scan(/01/)
arr.empty? && valid_ipv4?(addr)
rescue
return false
end
#
# Checks if the given string is a valid IPv6 address
#
# Example:
#
# IPAddress::valid_ipv6? "2002::1"
# #=> true
#
# IPAddress::valid_ipv6? "2002::DEAD::BEEF"
# #=> false
#
def self.valid_ipv6?(addr)
# https://gist.github.com/cpetschnig/294476
# http://forums.intermapper.com/viewtopic.php?t=452
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
false
end
#
# Deprecate method
#
def self.deprecate(message = nil) # :nodoc:
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
warn("DEPRECATION WARNING: #{message}")
end
end # module IPAddress
#
# IPAddress is a wrapper method built around
# IPAddress's library classes. Its purpouse is to
# make you indipendent from the type of IP address
# you're going to use.
#
# For example, instead of creating the three types
# of IP addresses using their own contructors
#
# ip = IPAddress::IPv4.new "172.16.10.1/24"
# ip6 = IPAddress::IPv6.new "2001:db8::8:800:200c:417a/64"
# ip_mapped = IPAddress::IPv6::Mapped "::ffff:172.16.10.1/128"
#
# you can just use the IPAddress wrapper:
#
# ip = IPAddress "172.16.10.1/24"
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
# ip_mapped = IPAddress "::ffff:172.16.10.1/128"
#
# All the object created will be instances of the
# correct class:
#
# ip.class
# #=> IPAddress::IPv4
# ip6.class
# #=> IPAddress::IPv6
# ip_mapped.class
# #=> IPAddress::IPv6::Mapped
#
def IPAddress(str)
IPAddress::parse str
end
#
# Compatibility with Ruby 1.8
#
if RUBY_VERSION =~ /^1\.8/
class Hash # :nodoc:
alias :key :index
end
module Math # :nodoc:
def Math.log2(n)
log(n) / log(2)
end
end
end
ipaddress-0.8.3/lib/ipaddress/ 0000755 0000041 0000041 00000000000 12666647272 016276 5 ustar www-data www-data ipaddress-0.8.3/lib/ipaddress/prefix.rb 0000644 0000041 0000041 00000012210 12666647272 020114 0 ustar www-data www-data module IPAddress
#
# =NAME
#
# IPAddress::Prefix
#
# =SYNOPSIS
#
# Parent class for Prefix32 and Prefix128
#
# =DESCRIPTION
#
# IPAddress::Prefix is the parent class for IPAddress::Prefix32
# and IPAddress::Prefix128, defining some modules in common for
# both the subclasses.
#
# IPAddress::Prefix shouldn't be accesses directly, unless
# for particular needs.
#
class Prefix
include Comparable
attr_reader :prefix
#
# Creates a new general prefix
#
def initialize(num)
@prefix = num.to_i
end
#
# Returns a string with the prefix
#
def to_s
"#@prefix"
end
alias_method :inspect, :to_s
#
# Returns the prefix
#
def to_i
@prefix
end
#
# Compare the prefix
#
def <=>(oth)
@prefix <=> oth.to_i
end
#
# Sums two prefixes or a prefix to a
# number, returns a Fixnum
#
def +(oth)
if oth.is_a? Fixnum
self.prefix + oth
else
self.prefix + oth.prefix
end
end
#
# Returns the difference between two
# prefixes, or a prefix and a number,
# as a Fixnum
#
def -(oth)
if oth.is_a? Fixnum
self.prefix - oth
else
(self.prefix - oth.prefix).abs
end
end
end # class Prefix
class Prefix32 < Prefix
IN4MASK = 0xffffffff
#
# Creates a new prefix object for 32 bits IPv4 addresses
#
# prefix = IPAddress::Prefix32.new 24
# #=> 24
#
def initialize(num)
unless (0..32).include? num
raise ArgumentError, "Prefix must be in range 0..32, got: #{num}"
end
super(num)
end
#
# Returns the length of the host portion
# of a netmask.
#
# prefix = Prefix32.new 24
#
# prefix.host_prefix
# #=> 8
#
def host_prefix
32 - @prefix
end
#
# Transforms the prefix into a string of bits
# representing the netmask
#
# prefix = IPAddress::Prefix32.new 24
#
# prefix.bits
# #=> "11111111111111111111111100000000"
#
def bits
"%.32b" % to_u32
end
#
# Gives the prefix in IPv4 dotted decimal format,
# i.e. the canonical netmask we're all used to
#
# prefix = IPAddress::Prefix32.new 24
#
# prefix.to_ip
# #=> "255.255.255.0"
#
def to_ip
[bits].pack("B*").unpack("CCCC").join(".")
end
#
# An array of octets of the IPv4 dotted decimal
# format
#
# prefix = IPAddress::Prefix32.new 24
#
# prefix.octets
# #=> [255, 255, 255, 0]
#
def octets
to_ip.split(".").map{|i| i.to_i}
end
#
# Unsigned 32 bits decimal number representing
# the prefix
#
# prefix = IPAddress::Prefix32.new 24
#
# prefix.to_u32
# #=> 4294967040
#
def to_u32
(IN4MASK >> host_prefix) << host_prefix
end
#
# Shortcut for the octecs in the dotted decimal
# representation
#
# prefix = IPAddress::Prefix32.new 24
#
# prefix[2]
# #=> 255
#
def [](index)
octets[index]
end
#
# The hostmask is the contrary of the subnet mask,
# as it shows the bits that can change within the
# hosts
#
# prefix = IPAddress::Prefix32.new 24
#
# prefix.hostmask
# #=> "0.0.0.255"
#
def hostmask
[~to_u32].pack("N").unpack("CCCC").join(".")
end
#
# Creates a new prefix by parsing a netmask in
# dotted decimal form
#
# prefix = IPAddress::Prefix32::parse_netmask "255.255.255.0"
# #=> 24
#
def self.parse_netmask(netmask)
octets = netmask.split(".").map{|i| i.to_i}
num = octets.pack("C"*octets.size).unpack("B*").first.count "1"
return self.new(num)
end
end # class Prefix32 < Prefix
class Prefix128 < Prefix
#
# Creates a new prefix object for 128 bits IPv6 addresses
#
# prefix = IPAddress::Prefix128.new 64
# #=> 64
#
def initialize(num=128)
unless (0..128).include? num.to_i
raise ArgumentError, "Prefix must be in range 0..128, got: #{num}"
end
super(num.to_i)
end
#
# Transforms the prefix into a string of bits
# representing the netmask
#
# prefix = IPAddress::Prefix128.new 64
#
# prefix.bits
# #=> "1111111111111111111111111111111111111111111111111111111111111111"
# "0000000000000000000000000000000000000000000000000000000000000000"
#
def bits
"1" * @prefix + "0" * (128 - @prefix)
end
#
# Unsigned 128 bits decimal number representing
# the prefix
#
# prefix = IPAddress::Prefix128.new 64
#
# prefix.to_u128
# #=> 340282366920938463444927863358058659840
#
def to_u128
bits.to_i(2)
end
#
# Returns the length of the host portion
# of a netmask.
#
# prefix = Prefix128.new 96
#
# prefix.host_prefix
# #=> 32
#
def host_prefix
128 - @prefix
end
end # class Prefix123 < Prefix
end # module IPAddress
ipaddress-0.8.3/lib/ipaddress/version.rb 0000644 0000041 0000041 00000000051 12666647272 020304 0 ustar www-data www-data module Ipaddress
VERSION = "0.8.3"
end
ipaddress-0.8.3/lib/ipaddress/mongoid.rb 0000644 0000041 0000041 00000003176 12666647272 020266 0 ustar www-data www-data module IPAddress
#
# Mongoid field serialization
#
# IPAddress objects are converted to String
#
# IPAddress.mongoize IPAddress.parse("172.16.10.1")
# #=> "172.16.10.1"
#
# Prefix will be removed from host adresses
#
# IPAddress.mongoize "172.16.10.1/32"
# #=> "172.16.10.1"
#
# Prefix will be kept for network addresses
#
# IPAddress.mongoize "172.16.10.1/24"
# #=> "172.16.10.1/24"
#
# IPv6 addresses will be stored uncompressed to ease DB search and sorting
#
# IPAddress.mongoize "2001:db8::8:800:200c:417a"
# #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
# IPAddress.mongoize "2001:db8::8:800:200c:417a/64"
# #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
#
# Invalid addresses will be serialized as nil
#
# IPAddress.mongoize "invalid"
# #=> nil
# IPAddress.mongoize ""
# #=> nil
# IPAddress.mongoize 1
# #=> nil
# IPAddress.mongoize nil
# #=> nil
#
def self.mongoize(ipaddress)
ipaddress = self.parse(ipaddress) unless ipaddress.is_a?(IPAddress)
if ipaddress.bits.length == ipaddress.prefix
ipaddress.address
elsif ipaddress.is_a?(IPAddress::IPv6)
ipaddress.to_string_uncompressed
else
ipaddress.to_string
end
rescue ArgumentError
nil
end
#
# Mongoid field deserialization
#
def self.demongoize(string)
parse(string)
rescue ArgumentError
nil
end
#
# Delegates to IPAddress.mongoize
#
def self.evolve(ipaddress)
mongoize(ipaddress)
end
#
# Sends self object to IPAddress#mongoize
#
def mongoize
IPAddress.mongoize(self)
end
end ipaddress-0.8.3/lib/ipaddress/ipv4.rb 0000644 0000041 0000041 00000066564 12666647272 017526 0 ustar www-data www-data require 'ipaddress/prefix'
module IPAddress;
#
# =Name
#
# IPAddress::IPv4 - IP version 4 address manipulation library
#
# =Synopsis
#
# require 'ipaddress'
#
# =Description
#
# Class IPAddress::IPv4 is used to handle IPv4 type addresses.
#
class IPv4
include IPAddress
include Enumerable
include Comparable
#
# This Hash contains the prefix values for Classful networks
#
# Note that classes C, D and E will all have a default
# prefix of /24 or 255.255.255.0
#
CLASSFUL = {
/^0../ => 8, # Class A, from 0.0.0.0 to 127.255.255.255
/^10./ => 16, # Class B, from 128.0.0.0 to 191.255.255.255
/^110/ => 24 # Class C, D and E, from 192.0.0.0 to 255.255.255.254
}
#
# Regular expression to match an IPv4 address
#
REGEXP = Regexp.new(/((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/)
#
# Creates a new IPv4 address object.
#
# An IPv4 address can be expressed in any of the following forms:
#
# * "10.1.1.1/24": ip +address+ and +prefix+. This is the common and
# suggested way to create an object .
# * "10.1.1.1/255.255.255.0": ip +address+ and +netmask+. Although
# convenient sometimes, this format is less clear than the previous
# one.
# * "10.1.1.1": if the address alone is specified, the prefix will be
# set as default 32, also known as the host prefix
#
# Examples:
#
# # These two are the same
# ip = IPAddress::IPv4.new("10.0.0.1/24")
# ip = IPAddress("10.0.0.1/24")
#
# # These two are the same
# IPAddress::IPv4.new "10.0.0.1/8"
# IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
#
def initialize(str)
ip, netmask = str.split("/")
# Check the ip and remove white space
if IPAddress.valid_ipv4?(ip)
@address = ip.strip
else
raise ArgumentError, "Invalid IP #{ip.inspect}"
end
# Check the netmask
if netmask # netmask is defined
netmask.strip!
if netmask =~ /^\d{1,2}$/ # netmask in cidr format
@prefix = Prefix32.new(netmask.to_i)
elsif IPAddress.valid_ipv4_netmask?(netmask) # netmask in IP format
@prefix = Prefix32.parse_netmask(netmask)
else # invalid netmask
raise ArgumentError, "Invalid netmask #{netmask}"
end
else # netmask is nil, reverting to defaul classful mask
@prefix = Prefix32.new(32)
end
# Array formed with the IP octets
@octets = @address.split(".").map{|i| i.to_i}
# 32 bits interger containing the address
@u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3])
end # def initialize
#
# Returns the address portion of the IPv4 object
# as a string.
#
# ip = IPAddress("172.16.100.4/22")
#
# ip.address
# #=> "172.16.100.4"
#
def address
@address
end
#
# Returns the prefix portion of the IPv4 object
# as a IPAddress::Prefix32 object
#
# ip = IPAddress("172.16.100.4/22")
#
# ip.prefix
# #=> 22
#
# ip.prefix.class
# #=> IPAddress::Prefix32
#
def prefix
@prefix
end
#
# Set a new prefix number for the object
#
# This is useful if you want to change the prefix
# to an object created with IPv4::parse_u32 or
# if the object was created using the classful
# mask.
#
# ip = IPAddress("172.16.100.4")
#
# puts ip
# #=> 172.16.100.4/16
#
# ip.prefix = 22
#
# puts ip
# #=> 172.16.100.4/22
#
def prefix=(num)
@prefix = Prefix32.new(num)
end
#
# Returns the address as an array of decimal values
#
# ip = IPAddress("172.16.100.4")
#
# ip.octets
# #=> [172, 16, 100, 4]
#
def octets
@octets
end
#
# Returns a string with the address portion of
# the IPv4 object
#
# ip = IPAddress("172.16.100.4/22")
#
# ip.to_s
# #=> "172.16.100.4"
#
def to_s
@address
end
#
# Returns a string with the IP address in canonical
# form.
#
# ip = IPAddress("172.16.100.4/22")
#
# ip.to_string
# #=> "172.16.100.4/22"
#
def to_string
"#@address/#@prefix"
end
#
# Returns the prefix as a string in IP format
#
# ip = IPAddress("172.16.100.4/22")
#
# ip.netmask
# #=> "255.255.252.0"
#
def netmask
@prefix.to_ip
end
#
# Like IPv4#prefix=, this method allow you to
# change the prefix / netmask of an IP address
# object.
#
# ip = IPAddress("172.16.100.4")
#
# puts ip
# #=> 172.16.100.4/16
#
# ip.netmask = "255.255.252.0"
#
# puts ip
# #=> 172.16.100.4/22
#
def netmask=(addr)
@prefix = Prefix32.parse_netmask(addr)
end
#
# Returns the address portion in unsigned
# 32 bits integer format.
#
# This method is identical to the C function
# inet_pton to create a 32 bits address family
# structure.
#
# ip = IPAddress("10.0.0.0/8")
#
# ip.to_i
# #=> 167772160
#
def u32
@u32
end
alias_method :to_i, :u32
alias_method :to_u32, :u32
#
# Returns the address portion in
# hex
#
# ip = IPAddress("10.0.0.0")
#
# ip.to_h
# #=> 0a000000
#
def hex(space=true)
"%.4x%.4x" % [to_u32].pack("N").unpack("nn")
end
alias_method :to_h, :hex
alias_method :to_hex, :hex
#
# Returns the address portion of an IPv4 object
# in a network byte order format.
#
# ip = IPAddress("172.16.10.1/24")
#
# ip.data
# #=> "\254\020\n\001"
#
# It is usually used to include an IP address
# in a data packet to be sent over a socket
#
# a = Socket.open(params) # socket details here
# ip = IPAddress("10.1.1.0/24")
# binary_data = ["Address: "].pack("a*") + ip.data
#
# # Send binary data
# a.puts binary_data
#
def data
[@u32].pack("N")
end
#
# Returns the octet specified by index
#
# ip = IPAddress("172.16.100.50/24")
#
# ip[0]
# #=> 172
# ip[1]
# #=> 16
# ip[2]
# #=> 100
# ip[3]
# #=> 50
#
def [](index)
@octets[index]
end
alias_method :octet, :[]
#
# Updated the octet specified at index
#
# ip = IPAddress("172.16.100.50/24")
# ip[2] = 200
#
# #=> # @prefix=32, @octets=[172, 16, 200, 1], @u32=2886780929>
#
def []=(index, value)
@octets[index] = value.to_i
initialize("#{@octets.join('.')}/#{prefix}")
end
alias_method :octet=, :[]=
#
# Returns the address portion of an IP in binary format,
# as a string containing a sequence of 0 and 1
#
# ip = IPAddress("127.0.0.1")
#
# ip.bits
# #=> "01111111000000000000000000000001"
#
def bits
data.unpack("B*").first
end
#
# Returns the broadcast address for the given IP.
#
# ip = IPAddress("172.16.10.64/24")
#
# ip.broadcast.to_s
# #=> "172.16.10.255"
#
def broadcast
case
when prefix <= 30
self.class.parse_u32(broadcast_u32, @prefix)
when prefix == 31
self.class.parse_u32(-1, @prefix)
when prefix == 32
return self
end
end
#
# Checks if the IP address is actually a network
#
# ip = IPAddress("172.16.10.64/24")
#
# ip.network?
# #=> false
#
# ip = IPAddress("172.16.10.64/26")
#
# ip.network?
# #=> true
#
def network?
(@prefix < 32) && (@u32 | @prefix.to_u32 == @prefix.to_u32)
end
#
# Returns a new IPv4 object with the network number
# for the given IP.
#
# ip = IPAddress("172.16.10.64/24")
#
# ip.network.to_s
# #=> "172.16.10.0"
#
def network
self.class.parse_u32(network_u32, @prefix)
end
#
# Returns a new IPv4 object with the
# first host IP address in the range.
#
# Example: given the 192.168.100.0/24 network, the first
# host IP address is 192.168.100.1.
#
# ip = IPAddress("192.168.100.0/24")
#
# ip.first.to_s
# #=> "192.168.100.1"
#
# The object IP doesn't need to be a network: the method
# automatically gets the network number from it
#
# ip = IPAddress("192.168.100.50/24")
#
# ip.first.to_s
# #=> "192.168.100.1"
#
def first
case
when prefix <= 30
self.class.parse_u32(network_u32+1, @prefix)
when prefix == 31
self.class.parse_u32(network_u32, @prefix)
when prefix == 32
return self
end
end
#
# Like its sibling method IPv4#first, this method
# returns a new IPv4 object with the
# last host IP address in the range.
#
# Example: given the 192.168.100.0/24 network, the last
# host IP address is 192.168.100.254
#
# ip = IPAddress("192.168.100.0/24")
#
# ip.last.to_s
# #=> "192.168.100.254"
#
# The object IP doesn't need to be a network: the method
# automatically gets the network number from it
#
# ip = IPAddress("192.168.100.50/24")
#
# ip.last.to_s
# #=> "192.168.100.254"
#
def last
case
when prefix <= 30
self.class.parse_u32(broadcast_u32-1, @prefix)
when prefix == 31
self.class.parse_u32(broadcast_u32, @prefix)
when prefix == 32
return self
end
end
#
# Iterates over all the hosts IP addresses for the given
# network (or IP address).
#
# ip = IPAddress("10.0.0.1/29")
#
# ip.each_host do |i|
# p i.to_s
# end
# #=> "10.0.0.1"
# #=> "10.0.0.2"
# #=> "10.0.0.3"
# #=> "10.0.0.4"
# #=> "10.0.0.5"
# #=> "10.0.0.6"
#
def each_host
(network_u32+1..broadcast_u32-1).each do |i|
yield self.class.parse_u32(i, @prefix)
end
end
#
# Iterates over all the IP addresses for the given
# network (or IP address).
#
# The object yielded is a new IPv4 object created
# from the iteration.
#
# ip = IPAddress("10.0.0.1/29")
#
# ip.each do |i|
# p i.address
# end
# #=> "10.0.0.0"
# #=> "10.0.0.1"
# #=> "10.0.0.2"
# #=> "10.0.0.3"
# #=> "10.0.0.4"
# #=> "10.0.0.5"
# #=> "10.0.0.6"
# #=> "10.0.0.7"
#
def each
(network_u32..broadcast_u32).each do |i|
yield self.class.parse_u32(i, @prefix)
end
end
#
# Spaceship operator to compare IPv4 objects
#
# Comparing IPv4 addresses is useful to ordinate
# them into lists that match our intuitive
# perception of ordered IP addresses.
#
# The first comparison criteria is the u32 value.
# For example, 10.100.100.1 will be considered
# to be less than 172.16.0.1, because, in a ordered list,
# we expect 10.100.100.1 to come before 172.16.0.1.
#
# The second criteria, in case two IPv4 objects
# have identical addresses, is the prefix. An higher
# prefix will be considered greater than a lower
# prefix. This is because we expect to see
# 10.100.100.0/24 come before 10.100.100.0/25.
#
# Example:
#
# ip1 = IPAddress "10.100.100.1/8"
# ip2 = IPAddress "172.16.0.1/16"
# ip3 = IPAddress "10.100.100.1/16"
#
# ip1 < ip2
# #=> true
# ip1 > ip3
# #=> false
#
# [ip1,ip2,ip3].sort.map{|i| i.to_string}
# #=> ["10.100.100.1/8","10.100.100.1/16","172.16.0.1/16"]
#
def <=>(oth)
return prefix <=> oth.prefix if to_u32 == oth.to_u32
to_u32 <=> oth.to_u32
end
#
# Returns the number of IP addresses included
# in the network. It also counts the network
# address and the broadcast address.
#
# ip = IPAddress("10.0.0.1/29")
#
# ip.size
# #=> 8
#
def size
2 ** @prefix.host_prefix
end
#
# Returns an array with the IP addresses of
# all the hosts in the network.
#
# ip = IPAddress("10.0.0.1/29")
#
# ip.hosts.map {|i| i.address}
# #=> ["10.0.0.1",
# #=> "10.0.0.2",
# #=> "10.0.0.3",
# #=> "10.0.0.4",
# #=> "10.0.0.5",
# #=> "10.0.0.6"]
#
def hosts
to_a[1..-2]
end
#
# Returns the network number in Unsigned 32bits format
#
# ip = IPAddress("10.0.0.1/29")
#
# ip.network_u32
# #=> 167772160
#
def network_u32
@u32 & @prefix.to_u32
end
#
# Returns the broadcast address in Unsigned 32bits format
#
# ip = IPaddress("10.0.0.1/29")
#
# ip.broadcast_u32
# #=> 167772167
#
def broadcast_u32
network_u32 + size - 1
end
#
# Checks whether a subnet includes the given IP address.
#
# Accepts an IPAddress::IPv4 object.
#
# ip = IPAddress("192.168.10.100/24")
#
# addr = IPAddress("192.168.10.102/24")
#
# ip.include? addr
# #=> true
#
# ip.include? IPAddress("172.16.0.48/16")
# #=> false
#
def include?(oth)
@prefix <= oth.prefix and network_u32 == (oth.to_u32 & @prefix.to_u32)
end
#
# Checks whether a subnet includes all the
# given IPv4 objects.
#
# ip = IPAddress("192.168.10.100/24")
#
# addr1 = IPAddress("192.168.10.102/24")
# addr2 = IPAddress("192.168.10.103/24")
#
# ip.include_all?(addr1,addr2)
# #=> true
#
def include_all?(*others)
others.all? {|oth| include?(oth)}
end
#
# Checks if an IPv4 address objects belongs
# to a private network RFC1918
#
# Example:
#
# ip = IPAddress "10.1.1.1/24"
# ip.private?
# #=> true
#
def private?
[self.class.new("10.0.0.0/8"),
self.class.new("172.16.0.0/12"),
self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
end
#
# Checks if an IPv4 address objects belongs
# to a multicast network RFC3171
#
# Example:
#
# ip = IPAddress "224.0.0.0/4"
# ip.multicast?
# #=> true
#
def multicast?
[self.class.new("224.0.0.0/4")].any? {|i| i.include? self}
end
#
# Checks if an IPv4 address objects belongs
# to a multicast network RFC3171
#
# Example:
#
# ip = IPAddress "224.0.0.0/4"
# ip.multicast?
# #=> true
#
def multicast?
[self.class.new("224.0.0.0/4")].any? {|i| i.include? self}
end
#
# Checks if an IPv4 address objects belongs
# to a loopback network RFC1122
#
# Example:
#
# ip = IPAddress "127.0.0.1"
# ip.loopback?
# #=> true
#
def loopback?
[self.class.new("127.0.0.0/8")].any? {|i| i.include? self}
end
#
# Returns the IP address in in-addr.arpa format
# for DNS lookups
#
# ip = IPAddress("172.16.100.50/24")
#
# ip.reverse
# #=> "50.100.16.172.in-addr.arpa"
#
def reverse
@octets.reverse.join(".") + ".in-addr.arpa"
end
alias_method :arpa, :reverse
#
# Return a list of IP's between @address
# and the supplied IP
#
# ip = IPAddress("172.16.100.51/32")
#
# ip.to("172.16.100.100")
# #=> ["172.16.100.51",
# #=> "172.16.100.52",
# #=> ...
# #=> "172.16.100.99",
# #=> "172.16.100.100"]
#
def to(e)
unless e.is_a? IPAddress::IPv4
e = IPv4.new(e)
end
Range.new(@u32, e.to_u32).map{|i| IPAddress.ntoa(i) }
end
#
# Splits a network into different subnets
#
# If the IP Address is a network, it can be divided into
# multiple networks. If +self+ is not a network, this
# method will calculate the network from the IP and then
# subnet it.
#
# If +subnets+ is an power of two number, the resulting
# networks will be divided evenly from the supernet.
#
# network = IPAddress("172.16.10.0/24")
#
# network / 4 # implies map{|i| i.to_string}
# #=> ["172.16.10.0/26",
# #=> "172.16.10.64/26",
# #=> "172.16.10.128/26",
# #=> "172.16.10.192/26"]
#
# If +num+ is any other number, the supernet will be
# divided into some networks with a even number of hosts and
# other networks with the remaining addresses.
#
# network = IPAddress("172.16.10.0/24")
#
# network / 3 # implies map{|i| i.to_string}
# #=> ["172.16.10.0/26",
# #=> "172.16.10.64/26",
# #=> "172.16.10.128/25"]
#
# Returns an array of IPv4 objects
#
def split(subnets=2)
unless (1..(2**@prefix.host_prefix)).include? subnets
raise ArgumentError, "Value #{subnets} out of range"
end
networks = subnet(newprefix(subnets))
until networks.size == subnets
networks = sum_first_found(networks)
end
return networks
end
alias_method :/, :split
#
# Returns a new IPv4 object from the supernetting
# of the instance network.
#
# Supernetting is similar to subnetting, except
# that you getting as a result a network with a
# smaller prefix (bigger host space). For example,
# given the network
#
# ip = IPAddress("172.16.10.0/24")
#
# you can supernet it with a new /23 prefix
#
# ip.supernet(23).to_string
# #=> "172.16.10.0/23"
#
# However if you supernet it with a /22 prefix, the
# network address will change:
#
# ip.supernet(22).to_string
# #=> "172.16.8.0/22"
#
# If +new_prefix+ is less than 1, returns 0.0.0.0/0
#
def supernet(new_prefix)
raise ArgumentError, "New prefix must be smaller than existing prefix" if new_prefix >= @prefix.to_i
return self.class.new("0.0.0.0/0") if new_prefix < 1
return self.class.new(@address+"/#{new_prefix}").network
end
#
# This method implements the subnetting function
# similar to the one described in RFC3531.
#
# By specifying a new prefix, the method calculates
# the network number for the given IPv4 object
# and calculates the subnets associated to the new
# prefix.
#
# For example, given the following network:
#
# ip = IPAddress "172.16.10.0/24"
#
# we can calculate the subnets with a /26 prefix
#
# ip.subnets(26).map{&:to_string)
# #=> ["172.16.10.0/26", "172.16.10.64/26",
# "172.16.10.128/26", "172.16.10.192/26"]
#
# The resulting number of subnets will of course always be
# a power of two.
#
def subnet(subprefix)
unless ((@prefix.to_i)..32).include? subprefix
raise ArgumentError, "New prefix must be between #@prefix and 32"
end
Array.new(2**(subprefix-@prefix.to_i)) do |i|
self.class.parse_u32(network_u32+(i*(2**(32-subprefix))), subprefix)
end
end
#
# Returns the difference between two IP addresses
# in unsigned int 32 bits format
#
# Example:
#
# ip1 = IPAddress("172.16.10.0/24")
# ip2 = IPAddress("172.16.11.0/24")
#
# puts ip1 - ip2
# #=> 256
#
def -(oth)
return (to_u32 - oth.to_u32).abs
end
#
# Returns a new IPv4 object which is the result
# of the summarization, if possible, of the two
# objects
#
# Example:
#
# ip1 = IPAddress("172.16.10.1/24")
# ip2 = IPAddress("172.16.11.2/24")
#
# p (ip1 + ip2).map {|i| i.to_string}
# #=> ["172.16.10.0/23"]
#
# If the networks are not contiguous, returns
# the two network numbers from the objects
#
# ip1 = IPAddress("10.0.0.1/24")
# ip2 = IPAddress("10.0.2.1/24")
#
# p (ip1 + ip2).map {|i| i.to_string}
# #=> ["10.0.0.0/24","10.0.2.0/24"]
#
def +(oth)
aggregate(*[self,oth].sort.map{|i| i.network})
end
#
# Checks whether the ip address belongs to a
# RFC 791 CLASS A network, no matter
# what the subnet mask is.
#
# Example:
#
# ip = IPAddress("10.0.0.1/24")
#
# ip.a?
# #=> true
#
def a?
CLASSFUL.key(8) === bits
end
#
# Checks whether the ip address belongs to a
# RFC 791 CLASS B network, no matter
# what the subnet mask is.
#
# Example:
#
# ip = IPAddress("172.16.10.1/24")
#
# ip.b?
# #=> true
#
def b?
CLASSFUL.key(16) === bits
end
#
# Checks whether the ip address belongs to a
# RFC 791 CLASS C network, no matter
# what the subnet mask is.
#
# Example:
#
# ip = IPAddress("192.168.1.1/30")
#
# ip.c?
# #=> true
#
def c?
CLASSFUL.key(24) === bits
end
#
# Return the ip address in a format compatible
# with the IPv6 Mapped IPv4 addresses
#
# Example:
#
# ip = IPAddress("172.16.10.1/24")
#
# ip.to_ipv6
# #=> "ac10:0a01"
#
def to_ipv6
"%.4x:%.4x" % [to_u32].pack("N").unpack("nn")
end
#
# Creates a new IPv4 object from an
# unsigned 32bits integer.
#
# ip = IPAddress::IPv4::parse_u32(167772160)
#
# ip.prefix = 8
# ip.to_string
# #=> "10.0.0.0/8"
#
# The +prefix+ parameter is optional:
#
# ip = IPAddress::IPv4::parse_u32(167772160, 8)
#
# ip.to_string
# #=> "10.0.0.0/8"
#
def self.parse_u32(u32, prefix=32)
self.new([u32].pack("N").unpack("C4").join(".")+"/#{prefix}")
end
#
# Creates a new IPv4 object from binary data,
# like the one you get from a network stream.
#
# For example, on a network stream the IP 172.16.0.1
# is represented with the binary "\254\020\n\001".
#
# ip = IPAddress::IPv4::parse_data "\254\020\n\001"
# ip.prefix = 24
#
# ip.to_string
# #=> "172.16.10.1/24"
#
def self.parse_data(str, prefix=32)
self.new(str.unpack("C4").join(".")+"/#{prefix}")
end
#
# Extract an IPv4 address from a string and
# returns a new object
#
# Example:
#
# str = "foobar172.16.10.1barbaz"
# ip = IPAddress::IPv4::extract str
#
# ip.to_s
# #=> "172.16.10.1"
#
def self.extract(str)
self.new REGEXP.match(str).to_s
end
#
# Summarization (or aggregation) is the process when two or more
# networks are taken together to check if a supernet, including all
# and only these networks, exists. If it exists then this supernet
# is called the summarized (or aggregated) network.
#
# It is very important to understand that summarization can only
# occur if there are no holes in the aggregated network, or, in other
# words, if the given networks fill completely the address space
# of the supernet. So the two rules are:
#
# 1) The aggregate network must contain +all+ the IP addresses of the
# original networks;
# 2) The aggregate network must contain +only+ the IP addresses of the
# original networks;
#
# A few examples will help clarify the above. Let's consider for
# instance the following two networks:
#
# ip1 = IPAddress("172.16.10.0/24")
# ip2 = IPAddress("172.16.11.0/24")
#
# These two networks can be expressed using only one IP address
# network if we change the prefix. Let Ruby do the work:
#
# IPAddress::IPv4::summarize(ip1,ip2).to_s
# #=> "172.16.10.0/23"
#
# We note how the network "172.16.10.0/23" includes all the addresses
# specified in the above networks, and (more important) includes
# ONLY those addresses.
#
# If we summarized +ip1+ and +ip2+ with the following network:
#
# "172.16.0.0/16"
#
# we would have satisfied rule #1 above, but not rule #2. So "172.16.0.0/16"
# is not an aggregate network for +ip1+ and +ip2+.
#
# If it's not possible to compute a single aggregated network for all the
# original networks, the method returns an array with all the aggregate
# networks found. For example, the following four networks can be
# aggregated in a single /22:
#
# ip1 = IPAddress("10.0.0.1/24")
# ip2 = IPAddress("10.0.1.1/24")
# ip3 = IPAddress("10.0.2.1/24")
# ip4 = IPAddress("10.0.3.1/24")
#
# IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_string
# #=> "10.0.0.0/22",
#
# But the following networks can't be summarized in a single network:
#
# ip1 = IPAddress("10.0.1.1/24")
# ip2 = IPAddress("10.0.2.1/24")
# ip3 = IPAddress("10.0.3.1/24")
# ip4 = IPAddress("10.0.4.1/24")
#
# IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
# #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
#
def self.summarize(*args)
# one network? no need to summarize
return [args.first.network] if args.size == 1
i = 0
result = args.dup.sort.map{|ip| ip.network}
while i < result.size-1
sum = result[i] + result[i+1]
result[i..i+1] = sum.first if sum.size == 1
i += 1
end
result.flatten!
if result.size == args.size
# nothing more to summarize
return result
else
# keep on summarizing
return self.summarize(*result)
end
end
#
# Creates a new IPv4 address object by parsing the
# address in a classful way.
#
# Classful addresses have a fixed netmask based on the
# class they belong to:
#
# * Class A, from 0.0.0.0 to 127.255.255.255
# * Class B, from 128.0.0.0 to 191.255.255.255
# * Class C, D and E, from 192.0.0.0 to 255.255.255.254
#
# Example:
#
# ip = IPAddress::IPv4.parse_classful "10.0.0.1"
#
# ip.netmask
# #=> "255.0.0.0"
# ip.a?
# #=> true
#
# Note that classes C, D and E will all have a default
# prefix of /24 or 255.255.255.0
#
def self.parse_classful(ip)
if IPAddress.valid_ipv4?(ip)
address = ip.strip
else
raise ArgumentError, "Invalid IP #{ip.inspect}"
end
prefix = CLASSFUL.find{|h,k| h === ("%.8b" % address.to_i)}.last
self.new "#{address}/#{prefix}"
end
#
# private methods
#
private
# Tweaked to remove the #upto(32)
def newprefix(num)
return @prefix + (Math::log2(num).ceil )
end
def sum_first_found(arr)
dup = arr.dup.reverse
dup.each_with_index do |obj,i|
a = [self.class.summarize(obj,dup[i+1])].flatten
if a.size == 1
dup[i..i+1] = a
return dup.reverse
end
end
return dup.reverse
end
def aggregate(ip1,ip2)
return [ip1] if ip1.include? ip2
snet = ip1.supernet(ip1.prefix-1)
if snet.include_all?(ip1, ip2) && ((ip1.size + ip2.size) == snet.size)
return [snet]
else
return [ip1, ip2]
end
end
end # class IPv4
end # module IPAddress
ipaddress-0.8.3/lib/ipaddress/ipv6.rb 0000644 0000041 0000041 00000054624 12666647272 017522 0 ustar www-data www-data require 'ipaddress/prefix'
module IPAddress;
#
# =Name
#
# IPAddress::IPv6 - IP version 6 address manipulation library
#
# =Synopsis
#
# require 'ipaddress'
#
# =Description
#
# Class IPAddress::IPv6 is used to handle IPv6 type addresses.
#
# == IPv6 addresses
#
# IPv6 addresses are 128 bits long, in contrast with IPv4 addresses
# which are only 32 bits long. An IPv6 address is generally written as
# eight groups of four hexadecimal digits, each group representing 16
# bits or two octect. For example, the following is a valid IPv6
# address:
#
# 2001:0db8:0000:0000:0008:0800:200c:417a
#
# Letters in an IPv6 address are usually written downcase, as per
# RFC. You can create a new IPv6 object using uppercase letters, but
# they will be converted.
#
# === Compression
#
# Since IPv6 addresses are very long to write, there are some
# semplifications and compressions that you can use to shorten them.
#
# * Leading zeroes: all the leading zeroes within a group can be
# omitted: "0008" would become "8"
#
# * A string of consecutive zeroes can be replaced by the string
# "::". This can be only applied once.
#
# Using compression, the IPv6 address written above can be shorten into
# the following, equivalent, address
#
# 2001:db8::8:800:200c:417a
#
# This short version is often used in human representation.
#
# === Network Mask
#
# As we used to do with IPv4 addresses, an IPv6 address can be written
# using the prefix notation to specify the subnet mask:
#
# 2001:db8::8:800:200c:417a/64
#
# The /64 part means that the first 64 bits of the address are
# representing the network portion, and the last 64 bits are the host
# portion.
#
#
class IPv6
include IPAddress
include Enumerable
include Comparable
#
# Format string to pretty print IPv6 addresses
#
IN6FORMAT = ("%.4x:"*8).chop
#
# Creates a new IPv6 address object.
#
# An IPv6 address can be expressed in any of the following forms:
#
# * "2001:0db8:0000:0000:0008:0800:200C:417A": IPv6 address with no compression
# * "2001:db8:0:0:8:800:200C:417A": IPv6 address with leading zeros compression
# * "2001:db8::8:800:200C:417A": IPv6 address with full compression
#
# In all these 3 cases, a new IPv6 address object will be created, using the default
# subnet mask /128
#
# You can also specify the subnet mask as with IPv4 addresses:
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
def initialize(str)
ip, netmask = str.split("/")
if str =~ /:.+\./
raise ArgumentError, "Please use #{self.class}::Mapped for IPv4 mapped addresses"
end
if IPAddress.valid_ipv6?(ip)
@groups = self.class.groups(ip)
@address = IN6FORMAT % @groups
@compressed = compress_address
else
raise ArgumentError, "Invalid IP #{ip.inspect}"
end
@prefix = Prefix128.new(netmask ? netmask : 128)
end # def initialize
#
# Returns the IPv6 address in uncompressed form:
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.address
# #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
#
def address
@address
end
#
# Returns an array with the 16 bits groups in decimal
# format:
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.groups
# #=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
#
def groups
@groups
end
#
# Returns an instance of the prefix object
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.prefix
# #=> 64
#
def prefix
@prefix
end
#
# Set a new prefix number for the object
#
# This is useful if you want to change the prefix
# to an object created with IPv6::parse_u128 or
# if the object was created using the default prefix
# of 128 bits.
#
# ip6 = IPAddress("2001:db8::8:800:200c:417a")
#
# puts ip6.to_string
# #=> "2001:db8::8:800:200c:417a/128"
#
# ip6.prefix = 64
# puts ip6.to_string
# #=> "2001:db8::8:800:200c:417a/64"
#
def prefix=(num)
@prefix = Prefix128.new(num)
end
#
# Unlike its counterpart IPv6#to_string method, IPv6#to_string_uncompressed
# returns the whole IPv6 address and prefix in an uncompressed form
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.to_string_uncompressed
# #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
#
def to_string_uncompressed
"#@address/#@prefix"
end
#
# Returns the IPv6 address in a human readable form,
# using the compressed address.
#
# ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200c:417a/64"
#
# ip6.to_string
# #=> "2001:db8::8:800:200c:417a/64"
#
def to_string
"#@compressed/#@prefix"
end
#
# Returns the IPv6 address in a human readable form,
# using the compressed address.
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.to_s
# #=> "2001:db8::8:800:200c:417a"
#
def to_s
@compressed
end
#
# Returns a decimal format (unsigned 128 bit) of the
# IPv6 address
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.to_i
# #=> 42540766411282592856906245548098208122
#
def to_i
to_hex.hex
end
alias_method :to_u128, :to_i
#
# True if the IPv6 address is a network
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.network?
# #=> false
#
# ip6 = IPAddress "2001:db8:8:800::/64"
#
# ip6.network?
# #=> true
#
def network?
to_u128 | @prefix.to_u128 == @prefix.to_u128
end
#
# Returns the 16-bits value specified by index
#
# ip = IPAddress("2001:db8::8:800:200c:417a/64")
#
# ip[0]
# #=> 8193
# ip[1]
# #=> 3512
# ip[2]
# #=> 0
# ip[3]
# #=> 0
#
def [](index)
@groups[index]
end
alias_method :group, :[]
#
# Updated the octet specified at index
#
def []=(index, value)
@groups[index] = value
initialize("#{IN6FORMAT % @groups}/#{prefix}")
end
alias_method :group=, :[]=
#
# Returns a Base16 number representing the IPv6
# address
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.to_hex
# #=> "20010db80000000000080800200c417a"
#
def to_hex
hexs.join("")
end
# Returns the address portion of an IPv6 object
# in a network byte order format.
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.data
# #=> " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
#
# It is usually used to include an IP address
# in a data packet to be sent over a socket
#
# a = Socket.open(params) # socket details here
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
# binary_data = ["Address: "].pack("a*") + ip.data
#
# # Send binary data
# a.puts binary_data
#
def data
@groups.pack("n8")
end
#
# Returns an array of the 16 bits groups in hexdecimal
# format:
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.hexs
# #=> ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
#
# Not to be confused with the similar IPv6#to_hex method.
#
def hexs
@address.split(":")
end
#
# Returns the IPv6 address in a DNS reverse lookup
# string, as per RFC3172 and RFC2874.
#
# ip6 = IPAddress "3ffe:505:2::f"
#
# ip6.reverse
# #=> "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"
#
def reverse
to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
end
alias_method :arpa, :reverse
#
# Returns the network number in Unsigned 128bits format
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.network_u128
# #=> 42540766411282592856903984951653826560
#
def network_u128
to_u128 & @prefix.to_u128
end
#
# Returns the broadcast address in Unsigned 128bits format
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.broadcast_u128
# #=> 42540766411282592875350729025363378175
#
# Please note that there is no Broadcast concept in IPv6
# addresses as in IPv4 addresses, and this method is just
# an helper to other functions.
#
def broadcast_u128
network_u128 + size - 1
end
#
# Returns the number of IP addresses included
# in the network. It also counts the network
# address and the broadcast address.
#
# ip6 = IPAddress("2001:db8::8:800:200c:417a/64")
#
# ip6.size
# #=> 18446744073709551616
#
def size
2 ** @prefix.host_prefix
end
#
# Checks whether a subnet includes the given IP address.
#
# Example:
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
# addr = IPAddress "2001:db8::8:800:200c:1/128"
#
# ip6.include? addr
# #=> true
#
# ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
# #=> false
#
def include?(oth)
@prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
end
#
# Compressed form of the IPv6 address
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.compressed
# #=> "2001:db8::8:800:200c:417a"
#
def compressed
@compressed
end
#
# Returns true if the address is an unspecified address
#
# See IPAddress::IPv6::Unspecified for more information
#
def unspecified?
@prefix == 128 and @compressed == "::"
end
#
# Returns true if the address is a loopback address
#
# See IPAddress::IPv6::Loopback for more information
#
def loopback?
@prefix == 128 and @compressed == "::1"
end
#
# Returns true if the address is a mapped address
#
# See IPAddress::IPv6::Mapped for more information
#
def mapped?
to_u128 >> 32 == 0xffff
end
#
# Iterates over all the IP addresses for the given
# network (or IP address).
#
# The object yielded is a new IPv6 object created
# from the iteration.
#
# ip6 = IPAddress("2001:db8::4/125")
#
# ip6.each do |i|
# p i.compressed
# end
# #=> "2001:db8::"
# #=> "2001:db8::1"
# #=> "2001:db8::2"
# #=> "2001:db8::3"
# #=> "2001:db8::4"
# #=> "2001:db8::5"
# #=> "2001:db8::6"
# #=> "2001:db8::7"
#
# WARNING: if the host portion is very large, this method
# can be very slow and possibly hang your system!
#
def each
(network_u128..broadcast_u128).each do |i|
yield self.class.parse_u128(i, @prefix)
end
end
#
# Spaceship operator to compare IPv6 objects
#
# Comparing IPv6 addresses is useful to ordinate
# them into lists that match our intuitive
# perception of ordered IP addresses.
#
# The first comparison criteria is the u128 value.
# For example, 2001:db8:1::1 will be considered
# to be less than 2001:db8:2::1, because, in a ordered list,
# we expect 2001:db8:1::1 to come before 2001:db8:2::1.
#
# The second criteria, in case two IPv6 objects
# have identical addresses, is the prefix. An higher
# prefix will be considered greater than a lower
# prefix. This is because we expect to see
# 2001:db8:1::1/64 come before 2001:db8:1::1/65
#
# Example:
#
# ip1 = IPAddress "2001:db8:1::1/64"
# ip2 = IPAddress "2001:db8:2::1/64"
# ip3 = IPAddress "2001:db8:1::1/65"
#
# ip1 < ip2
# #=> true
# ip1 < ip3
# #=> false
#
# [ip1,ip2,ip3].sort.map{|i| i.to_string}
# #=> ["2001:db8:1::1/64","2001:db8:1::1/65","2001:db8:2::1/64"]
#
def <=>(oth)
return prefix <=> oth.prefix if to_u128 == oth.to_u128
to_u128 <=> oth.to_u128
end
#
# Returns the address portion of an IP in binary format,
# as a string containing a sequence of 0 and 1
#
# ip6 = IPAddress("2001:db8::8:800:200c:417a")
#
# ip6.bits
# #=> "0010000000000001000011011011100000 [...] "
#
def bits
data.unpack("B*").first
end
#
# Expands an IPv6 address in the canocical form
#
# IPAddress::IPv6.expand "2001:0DB8:0:CD30::"
# #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
#
def self.expand(str)
self.new(str).address
end
#
# Compress an IPv6 address in its compressed form
#
# IPAddress::IPv6.compress "2001:0DB8:0000:CD30:0000:0000:0000:0000"
# #=> "2001:db8:0:cd30::"
#
def self.compress(str)
self.new(str).compressed
end
#
# Literal version of the IPv6 address
#
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
# ip6.literal
# #=> "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
#
def literal
@address.gsub(":","-") + ".ipv6-literal.net"
end
#
# Returns a new IPv6 object with the network number
# for the given IP.
#
# ip = IPAddress "2001:db8:1:1:1:1:1:1/32"
#
# ip.network.to_string
# #=> "2001:db8::/32"
#
def network
self.class.parse_u128(network_u128, @prefix)
end
#
# Extract 16 bits groups from a string
#
def self.groups(str)
l, r = if str =~ /^(.*)::(.*)$/
[$1,$2].map {|i| i.split ":"}
else
[str.split(":"),[]]
end
(l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
end
#
# Creates a new IPv6 object from binary data,
# like the one you get from a network stream.
#
# For example, on a network stream the IP
#
# "2001:db8::8:800:200c:417a"
#
# is represented with the binary data
#
# " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
#
# With that data you can create a new IPv6 object:
#
# ip6 = IPAddress::IPv6::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
# ip6.prefix = 64
#
# ip6.to_s
# #=> "2001:db8::8:800:200c:417a/64"
#
def self.parse_data(str)
self.new(IN6FORMAT % str.unpack("n8"))
end
#
# Creates a new IPv6 object from an
# unsigned 128 bits integer.
#
# ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122)
# ip6.prefix = 64
#
# ip6.to_string
# #=> "2001:db8::8:800:200c:417a/64"
#
# The +prefix+ parameter is optional:
#
# ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122, 64)
#
# ip6.to_string
# #=> "2001:db8::8:800:200c:417a/64"
#
def self.parse_u128(u128, prefix=128)
str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff}
self.new(str + "/#{prefix}")
end
#
# Creates a new IPv6 object from a number expressed in
# hexdecimal format:
#
# ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a")
# ip6.prefix = 64
#
# ip6.to_string
# #=> "2001:db8::8:800:200c:417a/64"
#
# The +prefix+ parameter is optional:
#
# ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a", 64)
#
# ip6.to_string
# #=> "2001:db8::8:800:200c:417a/64"
#
def self.parse_hex(hex, prefix=128)
self.parse_u128(hex.hex, prefix)
end
private
def compress_address
str = @groups.map{|i| i.to_s 16}.join ":"
loop do
break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
break if str.sub!(/\b0:0:0:0:0\b/, ':')
break if str.sub!(/\b0:0:0:0\b/, ':')
break if str.sub!(/\b0:0:0\b/, ':')
break if str.sub!(/\b0:0\b/, ':')
break
end
str.sub(/:{3,}/, '::')
end
end # class IPv6
#
# The address with all zero bits is called the +unspecified+ address
# (corresponding to 0.0.0.0 in IPv4). It should be something like this:
#
# 0000:0000:0000:0000:0000:0000:0000:0000
#
# but, with the use of compression, it is usually written as just two
# colons:
#
# ::
#
# or, specifying the netmask:
#
# ::/128
#
# With IPAddress, create a new unspecified IPv6 address using its own
# subclass:
#
# ip = IPAddress::IPv6::Unspecified.new
#
# ip.to_s
# #=> => "::/128"
#
# You can easily check if an IPv6 object is an unspecified address by
# using the IPv6#unspecified? method
#
# ip.unspecified?
# #=> true
#
# An unspecified IPv6 address can also be created with the wrapper
# method, like we've seen before
#
# ip = IPAddress "::"
#
# ip.unspecified?
# #=> true
#
# This address must never be assigned to an interface and is to be used
# only in software before the application has learned its host's source
# address appropriate for a pending connection. Routers must not forward
# packets with the unspecified address.
#
class IPAddress::IPv6::Unspecified < IPAddress::IPv6
#
# Creates a new IPv6 unspecified address
#
# ip = IPAddress::IPv6::Unspecified.new
#
# ip.to_s
# #=> => "::/128"
#
def initialize
@address = ("0000:"*8).chop
@groups = Array.new(8,0)
@prefix = Prefix128.new(128)
@compressed = compress_address
end
end # class IPv6::Unspecified
#
# The loopback address is a unicast localhost address. If an
# application in a host sends packets to this address, the IPv6 stack
# will loop these packets back on the same virtual interface.
#
# Loopback addresses are expressed in the following form:
#
# ::1
#
# or, with their appropriate prefix,
#
# ::1/128
#
# As for the unspecified addresses, IPv6 loopbacks can be created with
# IPAddress calling their own class:
#
# ip = IPAddress::IPv6::Loopback.new
#
# ip.to_string
# #=> "::1/128"
#
# or by using the wrapper:
#
# ip = IPAddress "::1"
#
# ip.to_string
# #=> "::1/128"
#
# Checking if an address is loopback is easy with the IPv6#loopback?
# method:
#
# ip.loopback?
# #=> true
#
# The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
#
class IPAddress::IPv6::Loopback < IPAddress::IPv6
#
# Creates a new IPv6 unspecified address
#
# ip = IPAddress::IPv6::Loopback.new
#
# ip.to_string
# #=> "::1/128"
#
def initialize
@address = ("0000:"*7)+"0001"
@groups = Array.new(7,0).push(1)
@prefix = Prefix128.new(128)
@compressed = compress_address
end
end # class IPv6::Loopback
#
# It is usually identified as a IPv4 mapped IPv6 address, a particular
# IPv6 address which aids the transition from IPv4 to IPv6. The
# structure of the address is
#
# ::ffff:w.y.x.z
#
# where w.x.y.z is a normal IPv4 address. For example, the following is
# a mapped IPv6 address:
#
# ::ffff:192.168.100.1
#
# IPAddress is very powerful in handling mapped IPv6 addresses, as the
# IPv4 portion is stored internally as a normal IPv4 object. Let's have
# a look at some examples. To create a new mapped address, just use the
# class builder itself
#
# ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
#
# or just use the wrapper method
#
# ip6 = IPAddress "::ffff:172.16.10.1/128"
#
# Let's check it's really a mapped address:
#
# ip6.mapped?
# #=> true
#
# ip6.to_string
# #=> "::FFFF:172.16.10.1/128"
#
# Now with the +ipv4+ attribute, we can easily access the IPv4 portion
# of the mapped IPv6 address:
#
# ip6.ipv4.address
# #=> "172.16.10.1"
#
# Internally, the IPv4 address is stored as two 16 bits
# groups. Therefore all the usual methods for an IPv6 address are
# working perfectly fine:
#
# ip6.to_hex
# #=> "00000000000000000000ffffac100a01"
#
# ip6.address
# #=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
#
# A mapped IPv6 can also be created just by specify the address in the
# following format:
#
# ip6 = IPAddress "::172.16.10.1"
#
# That is, two colons and the IPv4 address. However, as by RFC, the ffff
# group will be automatically added at the beginning
#
# ip6.to_string
# => "::ffff:172.16.10.1/128"
#
# making it a mapped IPv6 compatible address.
#
class IPAddress::IPv6::Mapped < IPAddress::IPv6
# Access the internal IPv4 address
attr_reader :ipv4
#
# Creates a new IPv6 IPv4-mapped address
#
# ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
#
# ipv6.ipv4.class
# #=> IPAddress::IPv4
#
# An IPv6 IPv4-mapped address can also be created using the
# IPv6 only format of the address:
#
# ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403"
#
# ip6.to_string
# #=> "::ffff:13.1.68.3"
#
def initialize(str)
string, netmask = str.split("/")
if string =~ /\./ # IPv4 in dotted decimal form
@ipv4 = IPAddress::IPv4.extract(string)
else # IPv4 in hex form
groups = IPAddress::IPv6.groups(string)
@ipv4 = IPAddress::IPv4.parse_u32((groups[-2]<< 16)+groups[-1])
end
super("::ffff:#{@ipv4.to_ipv6}/#{netmask}")
end
#
# Similar to IPv6#to_s, but prints out the IPv4 address
# in dotted decimal format
#
# ip6 = IPAddress "::ffff:172.16.10.1/128"
#
# ip6.to_s
# #=> "::ffff:172.16.10.1"
#
def to_s
"::ffff:#{@ipv4.address}"
end
#
# Similar to IPv6#to_string, but prints out the IPv4 address
# in dotted decimal format
#
#
# ip6 = IPAddress "::ffff:172.16.10.1/128"
#
# ip6.to_string
# #=> "::ffff:172.16.10.1/128"
#
def to_string
"::ffff:#{@ipv4.address}/#@prefix"
end
#
# Checks if the IPv6 address is IPv4 mapped
#
# ip6 = IPAddress "::ffff:172.16.10.1/128"
#
# ip6.mapped?
# #=> true
#
def mapped?
true
end
end # class IPv6::Mapped
end # module IPAddress
ipaddress-0.8.3/test/ 0000755 0000041 0000041 00000000000 12666647272 014531 5 ustar www-data www-data ipaddress-0.8.3/test/ipaddress_test.rb 0000644 0000041 0000041 00000005350 12666647272 020076 0 ustar www-data www-data require 'test_helper'
class IPAddressTest < Minitest::Test
def setup
@valid_ipv4 = "172.16.10.1/24"
@valid_ipv6 = "2001:db8::8:800:200c:417a/64"
@valid_mapped = "::13.1.68.3"
@invalid_ipv4 = "10.0.0.256"
@invalid_ipv6 = ":1:2:3:4:5:6:7"
@invalid_mapped = "::1:2.3.4"
@valid_ipv4_uint32 = [4294967295, # 255.255.255.255
167772160, # 10.0.0.0
3232235520, # 192.168.0.0
0]
@invalid_ipv4_uint32 = [4294967296, # 256.0.0.0
"A294967295", # Invalid uINT
-1] # Invalid
@ipv4class = IPAddress::IPv4
@ipv6class = IPAddress::IPv6
@mappedclass = IPAddress::IPv6::Mapped
@invalid_ipv4 = ["10.0.0.256",
"10.0.0.0.0",
"10.0.0",
"10.0"]
@valid_ipv4_range = ["10.0.0.1-254",
"10.0.1-254.0",
"10.1-254.0.0"]
@method = Module.method("IPAddress")
end
def test_method_IPAddress
assert_instance_of @ipv4class, @method.call(@valid_ipv4)
assert_instance_of @ipv6class, @method.call(@valid_ipv6)
assert_instance_of @mappedclass, @method.call(@valid_mapped)
assert_raises(ArgumentError) {@method.call(@invalid_ipv4)}
assert_raises(ArgumentError) {@method.call(@invalid_ipv6)}
assert_raises(ArgumentError) {@method.call(@invalid_mapped)}
assert_instance_of @ipv4class, @method.call(@valid_ipv4_uint32[0])
assert_instance_of @ipv4class, @method.call(@valid_ipv4_uint32[1])
assert_instance_of @ipv4class, @method.call(@valid_ipv4_uint32[2])
assert_instance_of @ipv4class, @method.call(@valid_ipv4_uint32[3])
assert_raises(ArgumentError) {@method.call(@invalid_ipv4_uint32[0])}
assert_raises(ArgumentError) {@method.call(@invalid_ipv4_uint32[1])}
assert_raises(ArgumentError) {@method.call(@invalid_ipv4_uint32[2])}
end
def test_module_method_valid?
assert_equal true, IPAddress::valid?("10.0.0.1")
assert_equal true, IPAddress::valid?("10.0.0.0")
assert_equal true, IPAddress::valid?("2002::1")
assert_equal true, IPAddress::valid?("dead:beef:cafe:babe::f0ad")
assert_equal false, IPAddress::valid?("10.0.0.256")
assert_equal false, IPAddress::valid?("10.0.0.0.0")
assert_equal false, IPAddress::valid?("10.0.0")
assert_equal false, IPAddress::valid?("10.0")
assert_equal false, IPAddress::valid?("2002:::1")
assert_equal false, IPAddress::valid?("2002:516:2:200")
end
def test_module_method_valid_ipv4_netmark?
assert_equal true, IPAddress::valid_ipv4_netmask?("255.255.255.0")
assert_equal false, IPAddress::valid_ipv4_netmask?("10.0.0.1")
end
end
ipaddress-0.8.3/test/test_helper.rb 0000644 0000041 0000041 00000001470 12666647272 017376 0 ustar www-data www-data require 'rubygems'
require 'minitest/autorun'
$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'ipaddress'
if Minitest.const_defined?('Test')
# We're on Minitest 5+. Nothing to do here.
else
# Minitest 4 doesn't have Minitest::Test yet.
Minitest::Test = MiniTest::Unit::TestCase
end
module Minitest
class Test
def self.must(name, &block)
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
defined = instance_method(test_name) rescue false
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
define_method(test_name, &block)
else
define_method(test_name) do
flunk "No implementation provided for #{name}"
end
end
end
end
end
ipaddress-0.8.3/test/ipaddress/ 0000755 0000041 0000041 00000000000 12666647272 016507 5 ustar www-data www-data ipaddress-0.8.3/test/ipaddress/ipv4_test.rb 0000644 0000041 0000041 00000044557 12666647272 020774 0 ustar www-data www-data require 'test_helper'
class IPv4Test < Minitest::Test
def setup
@klass = IPAddress::IPv4
@valid_ipv4 = {
"0.0.0.0/0" => ["0.0.0.0", 0],
"10.0.0.0" => ["10.0.0.0", 32],
"10.0.0.1" => ["10.0.0.1", 32],
"10.0.0.1/24" => ["10.0.0.1", 24],
"10.0.0.1/255.255.255.0" => ["10.0.0.1", 24]}
@invalid_ipv4 = ["10.0.0.256",
"10.0.0.0.0",
"10.0.0",
"10.0"]
@valid_ipv4_range = ["10.0.0.1-254",
"10.0.1-254.0",
"10.1-254.0.0"]
@netmask_values = {
"0.0.0.0/0" => "0.0.0.0",
"10.0.0.0/8" => "255.0.0.0",
"172.16.0.0/16" => "255.255.0.0",
"192.168.0.0/24" => "255.255.255.0",
"192.168.100.4/30" => "255.255.255.252",
"192.168.12.4/32" => "255.255.255.255"}
@decimal_values ={
"0.0.0.0/0" => 0,
"10.0.0.0/8" => 167772160,
"172.16.0.0/16" => 2886729728,
"192.168.0.0/24" => 3232235520,
"192.168.100.4/30" => 3232261124}
@hex_values = {
"10.0.0.0" => "0a000000",
"172.16.5.4" => "ac100504",
"192.168.100.4" => "c0a86404",
}
@ip = @klass.new("172.16.10.1/24")
@network = @klass.new("172.16.10.0/24")
@broadcast = {
"10.0.0.0/8" => "10.255.255.255/8",
"172.16.0.0/16" => "172.16.255.255/16",
"192.168.0.0/24" => "192.168.0.255/24",
"192.168.100.4/30" => "192.168.100.7/30",
"192.168.12.3/31" => "255.255.255.255/31",
"10.0.0.1/32" => "10.0.0.1/32"}
@networks = {
"10.5.4.3/8" => "10.0.0.0/8",
"172.16.5.4/16" => "172.16.0.0/16",
"192.168.4.3/24" => "192.168.4.0/24",
"192.168.100.5/30" => "192.168.100.4/30",
"192.168.1.3/31" => "192.168.1.2/31",
"192.168.2.5/32" => "192.168.2.5/32"}
@class_a = @klass.new("10.0.0.1/8")
@class_b = @klass.new("172.16.0.1/16")
@class_c = @klass.new("192.168.0.1/24")
@classful = {
"10.1.1.1" => 8,
"150.1.1.1" => 16,
"200.1.1.1" => 24 }
@in_range = {
"10.32.0.1" => ["10.32.0.253", 253],
"192.0.0.0" => ["192.1.255.255", 131072]
}
end
def test_initialize
@valid_ipv4.keys.each do |i|
ip = @klass.new(i)
assert_instance_of @klass, ip
end
assert_instance_of IPAddress::Prefix32, @ip.prefix
assert_raises (ArgumentError) do
@klass.new
end
end
def test_initialize_format_error
@invalid_ipv4.each do |i|
assert_raises(ArgumentError) {@klass.new(i)}
end
assert_raises (ArgumentError) {@klass.new("10.0.0.0/asd")}
end
def test_initialize_without_prefix
ip = @klass.new("10.10.0.0")
assert_instance_of IPAddress::Prefix32, ip.prefix
assert_equal 32, ip.prefix.to_i
end
def test_attributes
@valid_ipv4.each do |arg,attr|
ip = @klass.new(arg)
assert_equal attr.first, ip.address
assert_equal attr.last, ip.prefix.to_i
end
end
def test_octets
ip = @klass.new("10.1.2.3/8")
assert_equal ip.octets, [10,1,2,3]
end
def test_initialize_should_require_ip
assert_raises(ArgumentError) { @klass.new }
end
def test_method_data
if RUBY_VERSION < "2.0"
assert_equal "\254\020\n\001", @ip.data
else
assert_equal "\xAC\x10\n\x01".b, @ip.data
end
end
def test_method_to_string
@valid_ipv4.each do |arg,attr|
ip = @klass.new(arg)
assert_equal attr.join("/"), ip.to_string
end
end
def test_method_to_s
@valid_ipv4.each do |arg,attr|
ip = @klass.new(arg)
assert_equal attr.first, ip.to_s
end
end
def test_netmask
@netmask_values.each do |addr,mask|
ip = @klass.new(addr)
assert_equal mask, ip.netmask
end
end
def test_method_to_u32
@decimal_values.each do |addr,int|
ip = @klass.new(addr)
assert_equal int, ip.to_u32
end
end
def test_method_to_hex
@hex_values.each do |addr,hex|
ip = @klass.new(addr)
assert_equal hex, ip.to_hex
end
end
def test_method_network?
assert_equal true, @network.network?
assert_equal false, @ip.network?
end
def test_one_address_network
network = @klass.new("172.16.10.1/32")
assert_equal false, network.network?
end
def test_method_broadcast
@broadcast.each do |addr,bcast|
ip = @klass.new(addr)
assert_instance_of @klass, ip.broadcast
assert_equal bcast, ip.broadcast.to_string
end
end
def test_method_network
@networks.each do |addr,net|
ip = @klass.new addr
assert_instance_of @klass, ip.network
assert_equal net, ip.network.to_string
end
end
def test_method_bits
ip = @klass.new("127.0.0.1")
assert_equal "01111111000000000000000000000001", ip.bits
end
def test_method_first
ip = @klass.new("192.168.100.0/24")
assert_instance_of @klass, ip.first
assert_equal "192.168.100.1", ip.first.to_s
ip = @klass.new("192.168.100.50/24")
assert_instance_of @klass, ip.first
assert_equal "192.168.100.1", ip.first.to_s
ip = @klass.new("192.168.100.50/32")
assert_instance_of @klass, ip.first
assert_equal "192.168.100.50", ip.first.to_s
ip = @klass.new("192.168.100.50/31")
assert_instance_of @klass, ip.first
assert_equal "192.168.100.50", ip.first.to_s
end
def test_method_last
ip = @klass.new("192.168.100.0/24")
assert_instance_of @klass, ip.last
assert_equal "192.168.100.254", ip.last.to_s
ip = @klass.new("192.168.100.50/24")
assert_instance_of @klass, ip.last
assert_equal "192.168.100.254", ip.last.to_s
ip = @klass.new("192.168.100.50/32")
assert_instance_of @klass, ip.last
assert_equal "192.168.100.50", ip.last.to_s
ip = @klass.new("192.168.100.50/31")
assert_instance_of @klass, ip.last
assert_equal "192.168.100.51", ip.last.to_s
end
def test_method_each_host
ip = @klass.new("10.0.0.1/29")
arr = []
ip.each_host {|i| arr << i.to_s}
expected = ["10.0.0.1","10.0.0.2","10.0.0.3",
"10.0.0.4","10.0.0.5","10.0.0.6"]
assert_equal expected, arr
end
def test_method_each
ip = @klass.new("10.0.0.1/29")
arr = []
ip.each {|i| arr << i.to_s}
expected = ["10.0.0.0","10.0.0.1","10.0.0.2",
"10.0.0.3","10.0.0.4","10.0.0.5",
"10.0.0.6","10.0.0.7"]
assert_equal expected, arr
end
def test_method_size
ip = @klass.new("10.0.0.1/29")
assert_equal 8, ip.size
end
def test_method_hosts
ip = @klass.new("10.0.0.1/29")
expected = ["10.0.0.1","10.0.0.2","10.0.0.3",
"10.0.0.4","10.0.0.5","10.0.0.6"]
assert_equal expected, ip.hosts.map {|i| i.to_s}
end
def test_method_network_u32
assert_equal 2886732288, @ip.network_u32
end
def test_method_broadcast_u32
assert_equal 2886732543, @ip.broadcast_u32
end
def test_method_include?
ip = @klass.new("192.168.10.100/24")
addr = @klass.new("192.168.10.102/24")
assert_equal true, ip.include?(addr)
assert_equal false, ip.include?(@klass.new("172.16.0.48"))
ip = @klass.new("10.0.0.0/8")
assert_equal true, ip.include?(@klass.new("10.0.0.0/9"))
assert_equal true, ip.include?(@klass.new("10.1.1.1/32"))
assert_equal true, ip.include?(@klass.new("10.1.1.1/9"))
assert_equal false, ip.include?(@klass.new("172.16.0.0/16"))
assert_equal false, ip.include?(@klass.new("10.0.0.0/7"))
assert_equal false, ip.include?(@klass.new("5.5.5.5/32"))
assert_equal false, ip.include?(@klass.new("11.0.0.0/8"))
ip = @klass.new("13.13.0.0/13")
assert_equal false, ip.include?(@klass.new("13.16.0.0/32"))
end
def test_method_include_all?
ip = @klass.new("192.168.10.100/24")
addr1 = @klass.new("192.168.10.102/24")
addr2 = @klass.new("192.168.10.103/24")
assert_equal true, ip.include_all?(addr1,addr2)
assert_equal false, ip.include_all?(addr1, @klass.new("13.16.0.0/32"))
end
def test_method_ipv4?
assert_equal true, @ip.ipv4?
end
def test_method_ipv6?
assert_equal false, @ip.ipv6?
end
def test_method_private?
assert_equal true, @klass.new("192.168.10.50/24").private?
assert_equal true, @klass.new("192.168.10.50/16").private?
assert_equal true, @klass.new("172.16.77.40/24").private?
assert_equal true, @klass.new("172.16.10.50/14").private?
assert_equal true, @klass.new("10.10.10.10/10").private?
assert_equal true, @klass.new("10.0.0.0/8").private?
assert_equal false, @klass.new("192.168.10.50/12").private?
assert_equal false, @klass.new("3.3.3.3").private?
assert_equal false, @klass.new("10.0.0.0/7").private?
assert_equal false, @klass.new("172.32.0.0/12").private?
assert_equal false, @klass.new("172.16.0.0/11").private?
assert_equal false, @klass.new("192.0.0.2/24").private?
end
def test_method_octet
assert_equal 172, @ip[0]
assert_equal 16, @ip[1]
assert_equal 10, @ip[2]
assert_equal 1, @ip[3]
end
def test_method_a?
assert_equal true, @class_a.a?
assert_equal false, @class_b.a?
assert_equal false, @class_c.a?
end
def test_method_b?
assert_equal true, @class_b.b?
assert_equal false, @class_a.b?
assert_equal false, @class_c.b?
end
def test_method_c?
assert_equal true, @class_c.c?
assert_equal false, @class_a.c?
assert_equal false, @class_b.c?
end
def test_method_to_ipv6
assert_equal "ac10:0a01", @ip.to_ipv6
end
def test_method_reverse
assert_equal "1.10.16.172.in-addr.arpa", @ip.reverse
end
def test_method_compare
ip1 = @klass.new("10.1.1.1/8")
ip2 = @klass.new("10.1.1.1/16")
ip3 = @klass.new("172.16.1.1/14")
ip4 = @klass.new("10.1.1.1/8")
# ip2 should be greater than ip1
assert_equal true, ip1 < ip2
assert_equal false, ip1 > ip2
assert_equal false, ip2 < ip1
# ip2 should be less than ip3
assert_equal true, ip2 < ip3
assert_equal false, ip2 > ip3
# ip1 should be less than ip3
assert_equal true, ip1 < ip3
assert_equal false, ip1 > ip3
assert_equal false, ip3 < ip1
# ip1 should be equal to itself
assert_equal true, ip1 == ip1
# ip1 should be equal to ip4
assert_equal true, ip1 == ip4
# test sorting
arr = ["10.1.1.1/8","10.1.1.1/16","172.16.1.1/14"]
assert_equal arr, [ip1,ip2,ip3].sort.map{|s| s.to_string}
# test same prefix
ip1 = @klass.new("10.0.0.0/24")
ip2 = @klass.new("10.0.0.0/16")
ip3 = @klass.new("10.0.0.0/8")
arr = ["10.0.0.0/8","10.0.0.0/16","10.0.0.0/24"]
assert_equal arr, [ip1,ip2,ip3].sort.map{|s| s.to_string}
end
def test_method_minus
ip1 = @klass.new("10.1.1.1/8")
ip2 = @klass.new("10.1.1.10/8")
assert_equal 9, ip2 - ip1
assert_equal 9, ip1 - ip2
end
def test_method_plus
ip1 = @klass.new("172.16.10.1/24")
ip2 = @klass.new("172.16.11.2/24")
assert_equal ["172.16.10.0/23"], (ip1+ip2).map{|i| i.to_string}
ip2 = @klass.new("172.16.12.2/24")
assert_equal [ip1.network.to_string, ip2.network.to_string],
(ip1 + ip2).map{|i| i.to_string}
ip1 = @klass.new("10.0.0.0/23")
ip2 = @klass.new("10.0.2.0/24")
assert_equal ["10.0.0.0/23","10.0.2.0/24"], (ip1+ip2).map{|i| i.to_string}
ip1 = @klass.new("10.0.0.0/23")
ip2 = @klass.new("10.0.2.0/24")
assert_equal ["10.0.0.0/23","10.0.2.0/24"], (ip2+ip1).map{|i| i.to_string}
ip1 = @klass.new("10.0.0.0/16")
ip2 = @klass.new("10.0.2.0/24")
assert_equal ["10.0.0.0/16"], (ip1+ip2).map{|i| i.to_string}
ip1 = @klass.new("10.0.0.0/23")
ip2 = @klass.new("10.1.0.0/24")
assert_equal ["10.0.0.0/23","10.1.0.0/24"], (ip1+ip2).map{|i| i.to_string}
end
def test_method_netmask_equal
ip = @klass.new("10.1.1.1/16")
assert_equal 16, ip.prefix.to_i
ip.netmask = "255.255.255.0"
assert_equal 24, ip.prefix.to_i
end
def test_method_split
assert_raises(ArgumentError) {@ip.split(0)}
assert_raises(ArgumentError) {@ip.split(257)}
assert_equal @ip.network, @ip.split(1).first
arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27",
"172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27",
"172.16.10.192/27", "172.16.10.224/27"]
assert_equal arr, @network.split(8).map {|s| s.to_string}
arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27",
"172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27",
"172.16.10.192/26"]
assert_equal arr, @network.split(7).map {|s| s.to_string}
arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27",
"172.16.10.96/27", "172.16.10.128/26", "172.16.10.192/26"]
assert_equal arr, @network.split(6).map {|s| s.to_string}
arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27",
"172.16.10.96/27", "172.16.10.128/25"]
assert_equal arr, @network.split(5).map {|s| s.to_string}
arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26",
"172.16.10.192/26"]
assert_equal arr, @network.split(4).map {|s| s.to_string}
arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/25"]
assert_equal arr, @network.split(3).map {|s| s.to_string}
arr = ["172.16.10.0/25", "172.16.10.128/25"]
assert_equal arr, @network.split(2).map {|s| s.to_string}
arr = ["172.16.10.0/24"]
assert_equal arr, @network.split(1).map {|s| s.to_string}
end
def test_method_subnet
assert_raises(ArgumentError) {@network.subnet(23)}
assert_raises(ArgumentError) {@network.subnet(33)}
arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26",
"172.16.10.192/26"]
assert_equal arr, @network.subnet(26).map {|s| s.to_string}
arr = ["172.16.10.0/25", "172.16.10.128/25"]
assert_equal arr, @network.subnet(25).map {|s| s.to_string}
arr = ["172.16.10.0/24"]
assert_equal arr, @network.subnet(24).map {|s| s.to_string}
end
def test_method_supernet
assert_raises(ArgumentError) {@ip.supernet(24)}
assert_equal "0.0.0.0/0", @ip.supernet(0).to_string
assert_equal "0.0.0.0/0", @ip.supernet(-2).to_string
assert_equal "172.16.10.0/23", @ip.supernet(23).to_string
assert_equal "172.16.8.0/22", @ip.supernet(22).to_string
end
def test_classmethod_parse_u32
@decimal_values.each do |addr,int|
ip = @klass.parse_u32(int)
ip.prefix = addr.split("/").last.to_i
assert_equal ip.to_string, addr
end
end
def test_classhmethod_extract
str = "foobar172.16.10.1barbaz"
assert_equal "172.16.10.1", @klass.extract(str).to_s
end
def test_classmethod_summarize
# Should return self if only one network given
assert_equal [@ip.network], @klass.summarize(@ip)
# Summarize homogeneous networks
ip1 = @klass.new("172.16.10.1/24")
ip2 = @klass.new("172.16.11.2/24")
assert_equal ["172.16.10.0/23"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
ip1 = @klass.new("10.0.0.1/24")
ip2 = @klass.new("10.0.1.1/24")
ip3 = @klass.new("10.0.2.1/24")
ip4 = @klass.new("10.0.3.1/24")
assert_equal ["10.0.0.0/22"], @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
assert_equal ["10.0.0.0/22"], @klass.summarize(ip4,ip3,ip2,ip1).map{|i| i.to_string}
# Summarize non homogeneous networks
ip1 = @klass.new("10.0.0.0/23")
ip2 = @klass.new("10.0.2.0/24")
assert_equal ["10.0.0.0/23","10.0.2.0/24"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
ip1 = @klass.new("10.0.0.0/16")
ip2 = @klass.new("10.0.2.0/24")
assert_equal ["10.0.0.0/16"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
ip1 = @klass.new("10.0.0.0/23")
ip2 = @klass.new("10.1.0.0/24")
assert_equal ["10.0.0.0/23","10.1.0.0/24"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
ip1 = @klass.new("10.0.0.0/23")
ip2 = @klass.new("10.0.2.0/23")
ip3 = @klass.new("10.0.4.0/24")
ip4 = @klass.new("10.0.6.0/24")
assert_equal ["10.0.0.0/22","10.0.4.0/24","10.0.6.0/24"],
@klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
ip1 = @klass.new("10.0.1.1/24")
ip2 = @klass.new("10.0.2.1/24")
ip3 = @klass.new("10.0.3.1/24")
ip4 = @klass.new("10.0.4.1/24")
result = ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
assert_equal result, @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
assert_equal result, @klass.summarize(ip4,ip3,ip2,ip1).map{|i| i.to_string}
ip1 = @klass.new("10.0.1.1/24")
ip2 = @klass.new("10.10.2.1/24")
ip3 = @klass.new("172.16.0.1/24")
ip4 = @klass.new("172.16.1.1/24")
result = ["10.0.1.0/24","10.10.2.0/24","172.16.0.0/23"]
assert_equal result, @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
ips = [@klass.new("10.0.0.12/30"),
@klass.new("10.0.100.0/24")]
result = ["10.0.0.12/30", "10.0.100.0/24"]
assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
ips = [@klass.new("172.16.0.0/31"),
@klass.new("10.10.2.1/32")]
result = ["10.10.2.1/32", "172.16.0.0/31"]
assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
ips = [@klass.new("172.16.0.0/32"),
@klass.new("10.10.2.1/32")]
result = ["10.10.2.1/32", "172.16.0.0/32"]
assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
end
def test_classmethod_parse_data
ip = @klass.parse_data "\254\020\n\001"
assert_instance_of @klass, ip
assert_equal "172.16.10.1", ip.address
assert_equal "172.16.10.1/32", ip.to_string
end
def test_classmethod_parse_classful
@classful.each do |ip,prefix|
res = @klass.parse_classful(ip)
assert_equal prefix, res.prefix
assert_equal "#{ip}/#{prefix}", res.to_string
end
assert_raises(ArgumentError){ @klass.parse_classful("192.168.256.257") }
end
def test_network_split
@classful.each do |ip,net|
x = @klass.new("#{ip}/#{net}")
assert_equal x.split(1).length, 1
assert_equal x.split(2).length, 2
assert_equal x.split(32).length, 32
assert_equal x.split(256).length, 256
end
end
def test_in_range
@in_range.each do |s,d|
ip = @klass.new(s)
assert_equal ip.to(d[0]).length, d[1]
end
end
def test_octect_updates
ip = @klass.new("10.0.1.15/32")
ip[1] = 15
assert_equal "10.15.1.15/32", ip.to_string
ip = @klass.new("172.16.100.1")
ip[3] = 200
assert_equal "172.16.100.200/32", ip.to_string
ip = @klass.new("192.168.199.0/24")
ip[2] = 200
assert_equal "192.168.200.0/24", ip.to_string
end
end # class IPv4Test
ipaddress-0.8.3/test/ipaddress/mongoid_test.rb 0000644 0000041 0000041 00000005717 12666647272 021541 0 ustar www-data www-data require 'test_helper'
require 'ipaddress/mongoid'
class MongoidTest < Minitest::Test
def setup
@valid_host4 = "172.16.10.1"
@valid_host6 = "2001:0db8:0000:0000:0008:0800:200c:417a"
@valid_host6_compressed = IPAddress::IPv6.compress(@valid_host6)
@valid_network4 = "#{@valid_host4}/24"
@valid_network6 = "#{@valid_host6}/96"
@valid_network6_compressed = "#{@valid_host6_compressed}/96"
@host4 = IPAddress.parse(@valid_host4)
@host6 = IPAddress.parse(@valid_host6)
@network4 = IPAddress.parse(@valid_network4)
@network6 = IPAddress.parse(@valid_network6)
@invalid_values = [nil, "", "invalid"]
end
def test_mongoize
# Instance method should be delegated to class method
assert_equal @host4.mongoize, IPAddress.mongoize(@host4)
assert_equal @network4.mongoize, IPAddress.mongoize(@network4)
# Hosts addresses should be stored without prefix
assert_equal @valid_host4, IPAddress.mongoize(@host4)
assert_equal @valid_host6, IPAddress.mongoize(@host6)
assert_equal @valid_host4, IPAddress.mongoize("#{@host4}/32")
assert_equal @valid_host6, IPAddress.mongoize("#{@host6}/128")
# Network addresses should be stored with their prefix
assert_equal @valid_network4, IPAddress.mongoize(@network4)
assert_equal @valid_network6, IPAddress.mongoize(@network6)
# IPv6 addresses should always be stored uncompressed
assert_equal @valid_host6, IPAddress.mongoize(@valid_host6_compressed)
assert_equal @valid_network6, IPAddress.mongoize(@valid_network6_compressed)
@invalid_values.each do |invalid_value|
# Invalid addresses should serialize to nil
assert_equal nil, IPAddress.mongoize(invalid_value)
end
end
def test_demongoize
# Valid stored values should be loaded with expected IPAddress type
assert_instance_of IPAddress::IPv4, IPAddress.demongoize(@valid_host4)
assert_instance_of IPAddress::IPv6, IPAddress.demongoize(@valid_host6)
assert_instance_of IPAddress::IPv4, IPAddress.demongoize(@valid_network4)
assert_instance_of IPAddress::IPv6, IPAddress.demongoize(@valid_network6)
# Valid stored values should be loaded as the original IPAddress object
assert_equal @host4, IPAddress.demongoize(@valid_host4)
assert_equal @host6, IPAddress.demongoize(@valid_host6)
assert_equal @network4, IPAddress.demongoize(@valid_network4)
assert_equal @network6, IPAddress.demongoize(@valid_network6)
@invalid_values.each do |invalid_value|
# Invalid stored value should be loaded as nil
assert_equal nil, IPAddress.demongoize(invalid_value)
end
end
def test_evolve
# evolve should delegate to mongoize
assert_equal IPAddress.mongoize(@valid_host4), IPAddress.evolve(@valid_host4)
assert_equal IPAddress.mongoize(@valid_network4), IPAddress.evolve(@valid_network4)
end
end ipaddress-0.8.3/test/ipaddress/prefix_test.rb 0000644 0000041 0000041 00000006360 12666647272 021375 0 ustar www-data www-data require 'test_helper'
class Prefix32Test < Minitest::Test
def setup
@netmask0 = "0.0.0.0"
@netmask8 = "255.0.0.0"
@netmask16 = "255.255.0.0"
@netmask24 = "255.255.255.0"
@netmask30 = "255.255.255.252"
@netmasks = [@netmask0,@netmask8,@netmask16,@netmask24,@netmask30]
@prefix_hash = {
"0.0.0.0" => 0,
"255.0.0.0" => 8,
"255.255.0.0" => 16,
"255.255.255.0" => 24,
"255.255.255.252" => 30}
@octets_hash = {
[0,0,0,0] => 0,
[255,0,0,0] => 8,
[255,255,0,0] => 16,
[255,255,255,0] => 24,
[255,255,255,252] => 30}
@u32_hash = {
0 => 0,
8 => 4278190080,
16 => 4294901760,
24 => 4294967040,
30 => 4294967292}
@klass = IPAddress::Prefix32
end
def test_attributes
@prefix_hash.values.each do |num|
prefix = @klass.new(num)
assert_equal num, prefix.prefix
end
end
def test_parse_netmask
@prefix_hash.each do |netmask, num|
prefix = @klass.parse_netmask(netmask)
assert_equal num, prefix.prefix
assert_instance_of @klass, prefix
end
end
def test_method_to_ip
@prefix_hash.each do |netmask, num|
prefix = @klass.new(num)
assert_equal netmask, prefix.to_ip
end
end
def test_method_to_s
prefix = @klass.new(8)
assert_equal "8", prefix.to_s
end
def test_method_bits
prefix = @klass.new(16)
str = "1"*16 + "0"*16
assert_equal str, prefix.bits
end
def test_method_to_u32
@u32_hash.each do |num,u32|
assert_equal u32, @klass.new(num).to_u32
end
end
def test_method_plus
p1 = @klass.new 8
p2 = @klass.new 10
assert_equal 18, p1+p2
assert_equal 12, p1+4
end
def test_method_minus
p1 = @klass.new 8
p2 = @klass.new 24
assert_equal 16, p1-p2
assert_equal 16, p2-p1
assert_equal 20, p2-4
end
def test_initialize
assert_raises (ArgumentError) do
@klass.new 33
end
assert_instance_of @klass, @klass.new(8)
end
def test_method_octets
@octets_hash.each do |arr,pref|
prefix = @klass.new(pref)
assert_equal prefix.octets, arr
end
end
def test_method_brackets
@octets_hash.each do |arr,pref|
prefix = @klass.new(pref)
arr.each_with_index do |oct,index|
assert_equal prefix[index], oct
end
end
end
def test_method_hostmask
prefix = @klass.new(8)
assert_equal "0.255.255.255", prefix.hostmask
end
end # class Prefix32Test
class Prefix128Test < Minitest::Test
def setup
@u128_hash = {
32 => 340282366841710300949110269838224261120,
64 => 340282366920938463444927863358058659840,
96 => 340282366920938463463374607427473244160,
126 => 340282366920938463463374607431768211452}
@klass = IPAddress::Prefix128
end
def test_initialize
assert_raises (ArgumentError) do
@klass.new 129
end
assert_instance_of @klass, @klass.new(64)
end
def test_method_bits
prefix = @klass.new(64)
str = "1"*64 + "0"*64
assert_equal str, prefix.bits
end
def test_method_to_u32
@u128_hash.each do |num,u128|
assert_equal u128, @klass.new(num).to_u128
end
end
end # class Prefix128Test
ipaddress-0.8.3/test/ipaddress/ipv6_test.rb 0000644 0000041 0000041 00000030367 12666647272 020770 0 ustar www-data www-data require 'test_helper'
class IPv6Test < Minitest::Test
def setup
@klass = IPAddress::IPv6
@compress_addr = {
"2001:db8:0000:0000:0008:0800:200c:417a" => "2001:db8::8:800:200c:417a",
"2001:db8:0:0:8:800:200c:417a" => "2001:db8::8:800:200c:417a",
"ff01:0:0:0:0:0:0:101" => "ff01::101",
"0:0:0:0:0:0:0:1" => "::1",
"0:0:0:0:0:0:0:0" => "::"}
@valid_ipv6 = { # Kindly taken from the python IPy library
"FEDC:BA98:7654:3210:FEDC:BA98:7654:3210" => 338770000845734292534325025077361652240,
"1080:0000:0000:0000:0008:0800:200C:417A" => 21932261930451111902915077091070067066,
"1080:0:0:0:8:800:200C:417A" => 21932261930451111902915077091070067066,
"1080:0::8:800:200C:417A" => 21932261930451111902915077091070067066,
"1080::8:800:200C:417A" => 21932261930451111902915077091070067066,
"FF01:0:0:0:0:0:0:43" => 338958331222012082418099330867817087043,
"FF01:0:0::0:0:43" => 338958331222012082418099330867817087043,
"FF01::43" => 338958331222012082418099330867817087043,
"0:0:0:0:0:0:0:1" => 1,
"0:0:0::0:0:1" => 1,
"::1" => 1,
"0:0:0:0:0:0:0:0" => 0,
"0:0:0::0:0:0" => 0,
"::" => 0,
"1080:0:0:0:8:800:200C:417A" => 21932261930451111902915077091070067066,
"1080::8:800:200C:417A" => 21932261930451111902915077091070067066}
@invalid_ipv6 = [":1:2:3:4:5:6:7",
":1:2:3:4:5:6:7",
"2002:516:2:200",
"dd"]
@networks = {
"2001:db8:1:1:1:1:1:1/32" => "2001:db8::/32",
"2001:db8:1:1:1:1:1::/32" => "2001:db8::/32",
"2001:db8::1/64" => "2001:db8::/64"}
@ip = @klass.new "2001:db8::8:800:200c:417a/64"
@network = @klass.new "2001:db8:8:800::/64"
@arr = [8193,3512,0,0,8,2048,8204,16762]
@hex = "20010db80000000000080800200c417a"
end
def test_attribute_address
addr = "2001:0db8:0000:0000:0008:0800:200c:417a"
assert_equal addr, @ip.address
end
def test_initialize
assert_instance_of @klass, @ip
@invalid_ipv6.each do |ip|
assert_raises(ArgumentError) {@klass.new ip}
end
assert_equal 64, @ip.prefix
assert_raises(ArgumentError) {
@klass.new "::10.1.1.1"
}
end
def test_attribute_groups
assert_equal @arr, @ip.groups
end
def test_method_hexs
arr = "2001:0db8:0000:0000:0008:0800:200c:417a".split(":")
assert_equal arr, @ip.hexs
end
def test_method_to_i
@valid_ipv6.each do |ip,num|
assert_equal num, @klass.new(ip).to_i
end
end
def test_method_bits
bits = "0010000000000001000011011011100000000000000000000" +
"000000000000000000000000000100000001000000000000010000" +
"0000011000100000101111010"
assert_equal bits, @ip.bits
end
def test_method_prefix=()
ip = @klass.new "2001:db8::8:800:200c:417a"
assert_equal 128, ip.prefix
ip.prefix = 64
assert_equal 64, ip.prefix
assert_equal "2001:db8::8:800:200c:417a/64", ip.to_string
end
def test_method_mapped?
assert_equal false, @ip.mapped?
ip6 = @klass.new "::ffff:1234:5678"
assert_equal true, ip6.mapped?
end
def test_method_literal
str = "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
assert_equal str, @ip.literal
end
def test_method_group
@arr.each_with_index do |val,index|
assert_equal val, @ip[index]
end
end
def test_method_ipv4?
assert_equal false, @ip.ipv4?
end
def test_method_ipv6?
assert_equal true, @ip.ipv6?
end
def test_method_network?
assert_equal true, @network.network?
assert_equal false, @ip.network?
end
def test_method_network_u128
assert_equal 42540766411282592856903984951653826560, @ip.network_u128
end
def test_method_broadcast_u128
assert_equal 42540766411282592875350729025363378175, @ip.broadcast_u128
end
def test_method_size
ip = @klass.new("2001:db8::8:800:200c:417a/64")
assert_equal 2**64, ip.size
ip = @klass.new("2001:db8::8:800:200c:417a/32")
assert_equal 2**96, ip.size
ip = @klass.new("2001:db8::8:800:200c:417a/120")
assert_equal 2**8, ip.size
ip = @klass.new("2001:db8::8:800:200c:417a/124")
assert_equal 2**4, ip.size
end
def test_method_include?
assert_equal true, @ip.include?(@ip)
# test prefix on same address
included = @klass.new "2001:db8::8:800:200c:417a/128"
not_included = @klass.new "2001:db8::8:800:200c:417a/46"
assert_equal true, @ip.include?(included)
assert_equal false, @ip.include?(not_included)
# test address on same prefix
included = @klass.new "2001:db8::8:800:200c:0/64"
not_included = @klass.new "2001:db8:1::8:800:200c:417a/64"
assert_equal true, @ip.include?(included)
assert_equal false, @ip.include?(not_included)
# general test
included = @klass.new "2001:db8::8:800:200c:1/128"
not_included = @klass.new "2001:db8:1::8:800:200c:417a/76"
assert_equal true, @ip.include?(included)
assert_equal false, @ip.include?(not_included)
end
def test_method_to_hex
assert_equal @hex, @ip.to_hex
end
def test_method_to_s
assert_equal "2001:db8::8:800:200c:417a", @ip.to_s
end
def test_method_to_string
assert_equal "2001:db8::8:800:200c:417a/64", @ip.to_string
end
def test_method_to_string_uncompressed
str = "2001:0db8:0000:0000:0008:0800:200c:417a/64"
assert_equal str, @ip.to_string_uncompressed
end
def test_method_data
if RUBY_VERSION < "2.0"
str = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
else
str = " \x01\r\xB8\x00\x00\x00\x00\x00\b\b\x00 \fAz".b
end
assert_equal str, @ip.data
end
def test_method_reverse
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"
assert_equal str, @klass.new("3ffe:505:2::f").reverse
end
def test_method_compressed
assert_equal "1:1:1::1", @klass.new("1:1:1:0:0:0:0:1").compressed
assert_equal "1:0:1::1", @klass.new("1:0:1:0:0:0:0:1").compressed
assert_equal "1:0:0:1::1", @klass.new("1:0:0:1:0:0:0:1").compressed
assert_equal "1::1:0:0:1", @klass.new("1:0:0:0:1:0:0:1").compressed
assert_equal "1::1", @klass.new("1:0:0:0:0:0:0:1").compressed
end
def test_method_unspecified?
assert_equal true, @klass.new("::").unspecified?
assert_equal false, @ip.unspecified?
end
def test_method_loopback?
assert_equal true, @klass.new("::1").loopback?
assert_equal false, @ip.loopback?
end
def test_method_network
@networks.each do |addr,net|
ip = @klass.new addr
assert_instance_of @klass, ip.network
assert_equal net, ip.network.to_string
end
end
def test_method_each
ip = @klass.new("2001:db8::4/125")
arr = []
ip.each {|i| arr << i.compressed}
expected = ["2001:db8::","2001:db8::1","2001:db8::2",
"2001:db8::3","2001:db8::4","2001:db8::5",
"2001:db8::6","2001:db8::7"]
assert_equal expected, arr
end
def test_method_compare
ip1 = @klass.new("2001:db8:1::1/64")
ip2 = @klass.new("2001:db8:2::1/64")
ip3 = @klass.new("2001:db8:1::2/64")
ip4 = @klass.new("2001:db8:1::1/65")
# ip2 should be greater than ip1
assert_equal true, ip2 > ip1
assert_equal false, ip1 > ip2
assert_equal false, ip2 < ip1
# ip3 should be less than ip2
assert_equal true, ip2 > ip3
assert_equal false, ip2 < ip3
# ip1 should be less than ip3
assert_equal true, ip1 < ip3
assert_equal false, ip1 > ip3
assert_equal false, ip3 < ip1
# ip1 should be equal to itself
assert_equal true, ip1 == ip1
# ip4 should be greater than ip1
assert_equal true, ip1 < ip4
assert_equal false, ip1 > ip4
# test sorting
arr = ["2001:db8:1::1/64","2001:db8:1::1/65",
"2001:db8:1::2/64","2001:db8:2::1/64"]
assert_equal arr, [ip1,ip2,ip3,ip4].sort.map{|s| s.to_string}
end
def test_classmethod_expand
compressed = "2001:db8:0:cd30::"
expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000"
assert_equal expanded, @klass.expand(compressed)
refute_equal expanded, @klass.expand("2001:0db8:0::cd3")
refute_equal expanded, @klass.expand("2001:0db8::cd30")
refute_equal expanded, @klass.expand("2001:0db8::cd3")
end
def test_classmethod_compress
compressed = "2001:db8:0:cd30::"
expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000"
assert_equal compressed, @klass.compress(expanded)
refute_equal compressed, @klass.compress("2001:0db8:0::cd3")
refute_equal compressed, @klass.compress("2001:0db8::cd30")
refute_equal compressed, @klass.compress("2001:0db8::cd3")
end
def test_classmethod_parse_data
str = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
ip = @klass.parse_data str
assert_instance_of @klass, ip
assert_equal "2001:0db8:0000:0000:0008:0800:200c:417a", ip.address
assert_equal "2001:db8::8:800:200c:417a/128", ip.to_string
end
def test_classhmethod_parse_u128
@valid_ipv6.each do |ip,num|
assert_equal @klass.new(ip).to_s, @klass.parse_u128(num).to_s
end
end
def test_classmethod_parse_hex
assert_equal @ip.to_s, @klass.parse_hex(@hex,64).to_s
end
def test_group_updates
ip = @klass.new("2001:db8::8:800:200c:417a/64")
ip[2] = '1234'
assert_equal "2001:db8:4d2:0:8:800:200c:417a/64", ip.to_string
end
end # class IPv6Test
class IPv6UnspecifiedTest < Minitest::Test
def setup
@klass = IPAddress::IPv6::Unspecified
@ip = @klass.new
@s = "::"
@str = "::/128"
@string = "0000:0000:0000:0000:0000:0000:0000:0000/128"
@u128 = 0
@address = "::"
end
def test_initialize
assert_instance_of @klass, @ip
end
def test_attributes
assert_equal @address, @ip.compressed
assert_equal 128, @ip.prefix
assert_equal true, @ip.unspecified?
assert_equal @s, @ip.to_s
assert_equal @str, @ip.to_string
assert_equal @string, @ip.to_string_uncompressed
assert_equal @u128, @ip.to_u128
end
def test_method_ipv6?
assert_equal true, @ip.ipv6?
end
end # class IPv6UnspecifiedTest
class IPv6LoopbackTest < Minitest::Test
def setup
@klass = IPAddress::IPv6::Loopback
@ip = @klass.new
@s = "::1"
@str = "::1/128"
@string = "0000:0000:0000:0000:0000:0000:0000:0001/128"
@u128 = 1
@address = "::1"
end
def test_initialize
assert_instance_of @klass, @ip
end
def test_attributes
assert_equal @address, @ip.compressed
assert_equal 128, @ip.prefix
assert_equal true, @ip.loopback?
assert_equal @s, @ip.to_s
assert_equal @str, @ip.to_string
assert_equal @string, @ip.to_string_uncompressed
assert_equal @u128, @ip.to_u128
end
def test_method_ipv6?
assert_equal true, @ip.ipv6?
end
end # class IPv6LoopbackTest
class IPv6MappedTest < Minitest::Test
def setup
@klass = IPAddress::IPv6::Mapped
@ip = @klass.new("::172.16.10.1")
@s = "::ffff:172.16.10.1"
@str = "::ffff:172.16.10.1/128"
@string = "0000:0000:0000:0000:0000:ffff:ac10:0a01/128"
@u128 = 281473568475649
@address = "::ffff:ac10:a01"
@valid_mapped = {'::13.1.68.3' => 281470899930115,
'0:0:0:0:0:ffff:129.144.52.38' => 281472855454758,
'::ffff:129.144.52.38' => 281472855454758}
@valid_mapped_ipv6 = {'::0d01:4403' => 281470899930115,
'0:0:0:0:0:ffff:8190:3426' => 281472855454758,
'::ffff:8190:3426' => 281472855454758}
@valid_mapped_ipv6_conversion = {'::0d01:4403' => "13.1.68.3",
'0:0:0:0:0:ffff:8190:3426' => "129.144.52.38",
'::ffff:8190:3426' => "129.144.52.38"}
end
def test_initialize
assert_instance_of @klass, @ip
@valid_mapped.each do |ip, u128|
assert_equal u128, @klass.new(ip).to_u128
end
@valid_mapped_ipv6.each do |ip, u128|
assert_equal u128, @klass.new(ip).to_u128
end
end
def test_mapped_from_ipv6_conversion
@valid_mapped_ipv6_conversion.each do |ip6,ip4|
assert_equal ip4, @klass.new(ip6).ipv4.to_s
end
end
def test_attributes
assert_equal @address, @ip.compressed
assert_equal 128, @ip.prefix
assert_equal @s, @ip.to_s
assert_equal @str, @ip.to_string
assert_equal @string, @ip.to_string_uncompressed
assert_equal @u128, @ip.to_u128
end
def test_method_ipv6?
assert_equal true, @ip.ipv6?
end
def test_mapped?
assert_equal true, @ip.mapped?
end
end # class IPv6MappedTest
ipaddress-0.8.3/.gitignore 0000644 0000041 0000041 00000000230 12666647272 015535 0 ustar www-data www-data *.a
/.bundle/
*.bundle
/coverage/
/doc/
.DS_Store
/Gemfile.lock
ipaddr.html
mkmf.log
*.o
/pkg/
rdoc
server.rb
*.so
/spec/reports/
*.sw?
/tmp/
/_yardoc/
ipaddress-0.8.3/ipaddress.gemspec 0000644 0000041 0000041 00000002074 12666647272 017100 0 ustar www-data www-data # coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'ipaddress/version'
Gem::Specification.new do |spec|
spec.name = "ipaddress"
spec.version = Ipaddress::VERSION
spec.authors = ["bluemonk", "mikemackintosh"]
spec.email = ["ceresa@gmail.com"]
spec.summary = %q{IPv4/IPv6 address manipulation library}
spec.description = %q{IPAddress is a Ruby library designed to make manipulation
of IPv4 and IPv6 addresses both powerful and simple. It mantains
a layer of compatibility with Ruby's own IPAddr, while
addressing many of its issues.}
spec.homepage = "https://github.com/bluemonk/ipaddress"
spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0")
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler"
spec.add_development_dependency "rake"
end
ipaddress-0.8.3/.document 0000644 0000041 0000041 00000000074 12666647272 015372 0 ustar www-data www-data README.rdoc
lib/**/*.rb
bin/*
features/**/*.feature
LICENSE