summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml19
-rw-r--r--CONTRIBUTING.md39
-rw-r--r--Gemfile11
-rw-r--r--README.rdoc17
-rw-r--r--Rakefile3
-rw-r--r--VERSION1
-rw-r--r--ipaddress.gemspec101
-rw-r--r--lib/ipaddress.rb132
-rw-r--r--lib/ipaddress/ipv4.rb57
-rw-r--r--lib/ipaddress/ipv6.rb68
-rw-r--r--lib/ipaddress/prefix.rb10
-rw-r--r--tasks/jeweler.rake15
-rw-r--r--test/ipaddress/ipv4_test.rb58
-rw-r--r--test/ipaddress/ipv6_test.rb90
-rw-r--r--test/ipaddress/mongoid_test.rb6
-rw-r--r--test/ipaddress/prefix_test.rb2
-rw-r--r--test/ipaddress_test.rb64
-rw-r--r--test/test_helper.rb2
19 files changed, 595 insertions, 102 deletions
diff --git a/.gitignore b/.gitignore
index 85d89b4..1c2e4bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,5 @@ server.rb
*.sw?
/tmp/
/_yardoc/
+.idea/
+**/*.gem
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..30293f6
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,19 @@
+language: ruby
+rvm:
+- 2.3
+- 2.4
+- 2.5
+- 2.6
+- 2.7
+install:
+- gem install bundler
+- gem install rake
+- bundle install
+script:
+- uname -s
+- rake test
+- codeclimate-test-reporter
+addons:
+ code_climate:
+ repo_token:
+ secure: Na2Ghl3W0IpUWAWx66V9skLie5MqiJWfn7muCSRkAYIVL/j9fR6jhhawOKlX0R6bg4byouOyLieDu9HWsv2EY5L7JweVspodRuuaJndxDQ1E5rOp0mU6vDl7kIqBeboX3AsivXDgcc8C8qvE+WD++vr3oEMg22c2/RIbt6ecJGs=
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..94f60b5
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,39 @@
+# Contributing to the IPAddress Gem
+
+[![Build Status](https://travis-ci.org/ipaddress-gem/ipaddress.svg?branch=master)](https://travis-ci.org/ipaddress-gem/ipaddress) [![Code Climate](https://codeclimate.com/github/ipaddress-gem/ipaddress/badges/gpa.svg)](https://codeclimate.com/github/ipaddress-gem/ipaddress) [![Dependency Status](https://www.versioneye.com/user/projects/57001305fcd19a0051853bde/badge.svg?style=flat)](https://www.versioneye.com/user/projects/57001305fcd19a0051853bde)
+
+This gem is run by people who have jobs. So please understand if we can't always prioritize PRs and issues.
+
+You can help by making your code submissions. We can't promise a specific turnaround time, or that your code will be incorporated but all submissions are appreciated.
+
+## Steps to Submit a Pull Request
+
+* [Fork](https://help.github.com/articles/fork-a-repo) the project on GitHub.
+* Make your feature addition or bug fix [in a feature branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches). (Include a description of your changes in the PR)
+* Push your feature branch to GitHub.
+* Send a [Pull Request](https://help.github.com/articles/using-pull-requests)
+
+## Style Guide
+
+We will require that you adhere to the [ruby-style-guide](https://github.com/bbatsov/ruby-style-guide) for your code submissions.
+
+## Test Coverage
+
+All submissions of code must include test coverage which describes intent and expected behavior. The test suite used by this gem is [Minitest](https://github.com/seattlerb/minitest)
+
+Unit tests are expected to execute quickly. We will ask you to revise any long-running tests.
+
+We intend to add [Travis CI](https://travis-ci.org/) for automatic execution of branch tests.
+
+## Versioning: Jeweler and Semantic Versioning
+
+This repo uses [semantic versioning](http://semver.org/) implemented by the [Jeweler Gem](https://github.com/technicalpickles/jeweler). Please do not update the version by editting files, instead, you may increment or modify the version using the Rake tasks added by Jeweler.
+
+```
+$ rake -T | grep version
+rake version # Displays the current version
+rake version:bump:major # Bump the major version by 1
+rake version:bump:minor # Bump the a minor version by 1
+rake version:bump:patch # Bump the patch version by 1
+rake version:write # Writes out an explicit version
+``` \ No newline at end of file
diff --git a/Gemfile b/Gemfile
index b4e2a20..e479aab 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,12 @@
source "https://rubygems.org"
-gemspec
+group :development do
+ gem 'bundler', '>= 1.0'
+ gem 'rake', '10.5.0'
+ gem 'minitest', '~> 5.8', '>= 5.8.4'
+ gem 'pry', '>= 0.10.1'
+ gem 'travis', '>= 1.8.2'
+ gem 'jeweler', '>=2.0.1'
+ gem 'codeclimate-test-reporter'
+ gem 'simplecov'
+end
diff --git a/README.rdoc b/README.rdoc
index 05698a0..7d9600a 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,7 +1,3 @@
-== <b>IPAddress 1.0 is currently under development and will be released soon! Stay tuned!</b>
-
----
-
= IPAddress
IPAddress is a Ruby library designed to make the use of IPv4 and IPv6
@@ -18,10 +14,13 @@ examples of typical usage.
== Requirements
-* Ruby >= 1.8.7 (not tested with previous versions)
-* Ruby 1.9.2 or later is strongly recommended
+* Ruby 1.9.3 or later
+
+Please refer to {Travis CI}[https://travis-ci.org/ipaddress-gem/ipaddress] for Build Tests on specific versions of Ruby.
+
+{<img src="https://travis-ci.org/ipaddress-gem/ipaddress.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/ipaddress-gem/ipaddress] {<img src="https://codeclimate.com/github/ipaddress-gem/ipaddress/badges/gpa.svg" />}[https://codeclimate.com/github/ipaddress-gem/ipaddress] {<img src="https://www.versioneye.com/user/projects/57001305fcd19a0051853bde/badge.svg?style=flat" alt="Dependency Status" />}[https://www.versioneye.com/user/projects/57001305fcd19a0051853bde]
-IPAddress 0.8.2 has been tested on:
+IPAddress 0.8.2 was manually tested on:
* ruby-1.8.7-p334 [ i386 ]
* ree-1.8.7-2011.03 [ i386 ]
@@ -32,9 +31,7 @@ IPAddress 0.8.2 has been tested on:
* 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].
+If you want to contribute, please refer to {Contributing.md}[https://github.com/ipaddress-gem/ipaddress/blob/master/CONTRIBUTING.md].
== Installation
diff --git a/Rakefile b/Rakefile
index 792f86f..220a4da 100644
--- a/Rakefile
+++ b/Rakefile
@@ -9,6 +9,7 @@ Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
+ test.warning = true
end
begin
@@ -64,3 +65,5 @@ task :todo do
end
egrep /(FIXME|TODO|TBD)/
end
+
+Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |raketask| load raketask }
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..ee94dd8
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.8.3
diff --git a/ipaddress.gemspec b/ipaddress.gemspec
index 2f66ccf..599c9bd 100644
--- a/ipaddress.gemspec
+++ b/ipaddress.gemspec
@@ -1,26 +1,83 @@
-# coding: utf-8
-lib = File.expand_path('../lib', __FILE__)
-$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
-require 'ipaddress/version'
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
+# -*- encoding: utf-8 -*-
+# stub: ipaddress 0.8.3 ruby lib
-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"
+Gem::Specification.new do |s|
+ s.name = "ipaddress"
+ s.version = "0.8.3"
- 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"]
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.require_paths = ["lib"]
+ s.authors = ["bluemonk", "mikemackintosh"]
+ s.date = "2016-03-23"
+ s.description = "IPAddress is a Ruby library designed to make manipulation\n of IPv4 and IPv6 addresses both powerful and simple. It maintains\n a layer of compatibility with Ruby's own IPAddr, while\n addressing many of its issues."
+ s.email = "ceresa@gmail.com"
+ s.extra_rdoc_files = [
+ "CHANGELOG.rdoc",
+ "LICENSE.txt",
+ "README.rdoc"
+ ]
+ s.files = [
+ ".document",
+ ".rock.yml",
+ ".travis.yml",
+ "CHANGELOG.rdoc",
+ "CONTRIBUTING.md",
+ "Gemfile",
+ "LICENSE.txt",
+ "README.rdoc",
+ "Rakefile",
+ "VERSION",
+ "ipaddress.gemspec",
+ "lib/ipaddress.rb",
+ "lib/ipaddress/ipv4.rb",
+ "lib/ipaddress/ipv6.rb",
+ "lib/ipaddress/mongoid.rb",
+ "lib/ipaddress/prefix.rb",
+ "lib/ipaddress/version.rb",
+ "tasks/jeweler.rake",
+ "test/ipaddress/ipv4_test.rb",
+ "test/ipaddress/ipv6_test.rb",
+ "test/ipaddress/mongoid_test.rb",
+ "test/ipaddress/prefix_test.rb",
+ "test/ipaddress_test.rb",
+ "test/test_helper.rb"
+ ]
+ s.homepage = "https://github.com/bluemonk/ipaddress"
+ s.licenses = ["MIT"]
+ s.rubygems_version = "2.4.6"
+ s.summary = "IPv4/IPv6 address manipulation library"
- spec.add_development_dependency "bundler"
- spec.add_development_dependency "rake"
+ if s.respond_to? :specification_version then
+ s.specification_version = 4
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_development_dependency(%q<bundler>, [">= 1.0"])
+ s.add_development_dependency(%q<rake>, [">= 0"])
+ s.add_development_dependency(%q<minitest>, [">= 5.8.4", "~> 5.8"])
+ s.add_development_dependency(%q<pry>, [">= 0.10.1"])
+ s.add_development_dependency(%q<travis>, [">= 1.8.2"])
+ s.add_development_dependency(%q<jeweler>, [">= 2.0.1"])
+ s.add_development_dependency(%q<codeclimate-test-reporter>, [">= 0"])
+ else
+ s.add_dependency(%q<bundler>, [">= 1.0"])
+ s.add_dependency(%q<rake>, [">= 0"])
+ s.add_dependency(%q<minitest>, [">= 5.8.4", "~> 5.8"])
+ s.add_dependency(%q<pry>, [">= 0.10.1"])
+ s.add_dependency(%q<travis>, [">= 1.8.2"])
+ s.add_dependency(%q<jeweler>, [">= 2.0.1"])
+ s.add_dependency(%q<codeclimate-test-reporter>, [">= 0"])
+ end
+ else
+ s.add_dependency(%q<bundler>, [">= 1.0"])
+ s.add_dependency(%q<rake>, [">= 0"])
+ s.add_dependency(%q<minitest>, [">= 5.8.4", "~> 5.8"])
+ s.add_dependency(%q<pry>, [">= 0.10.1"])
+ s.add_dependency(%q<travis>, [">= 1.8.2"])
+ s.add_dependency(%q<jeweler>, [">= 2.0.1"])
+ s.add_dependency(%q<codeclimate-test-reporter>, [">= 0"])
+ end
end
+
diff --git a/lib/ipaddress.rb b/lib/ipaddress.rb
index 19b526f..96ac9f5 100644
--- a/lib/ipaddress.rb
+++ b/lib/ipaddress.rb
@@ -26,12 +26,12 @@ module IPAddress
# 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 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
+ # All the object created will be instances of the
# correct class:
#
# ip.class
@@ -42,17 +42,17 @@ module IPAddress
# #=> IPAddress::IPv6::Mapped
#
def IPAddress::parse(str)
-
+
# Check if an int was passed
if str.kind_of? Integer
- return IPAddress::IPv4.new(ntoa(str))
+ return IPAddress::IPv4.new(ntoa(str))
end
case str
when /:.+\./
IPAddress::IPv6::Mapped.new(str)
when /\./
- IPAddress::IPv4.new(str)
+ IPAddress::IPv4.new(str)
when /:/
IPAddress::IPv6.new(str)
else
@@ -68,14 +68,14 @@ module IPAddress
#
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('.')
+ raise(::ArgumentError, "not a long integer: #{uint.inspect}")
+ end
+ ret = []
+ 4.times do
+ ret.unshift(uint & 0xff)
+ uint >>= 8
+ end
+ ret.join('.')
end
#
@@ -89,7 +89,7 @@ module IPAddress
def ipv4?
self.kind_of? IPAddress::IPv4
end
-
+
#
# True if the object is an IPv6 address
#
@@ -102,22 +102,88 @@ module IPAddress
self.kind_of? IPAddress::IPv6
end
- #
- # Checks if the given string is a valid IP address,
- # either IPv4 or IPv6
+
+ #
+ # Checks if the given string is either a valid IP, either a valid IPv4 subnet
#
# Example:
#
+ # IPAddress::valid? "10.0.0.0/24"
+ # #=> true
+ #
# IPAddress::valid? "2002::1"
# #=> true
#
- # IPAddress::valid? "10.0.0.256"
+ # IPAddress::valid? "10.0.0.256"
+ # #=> false
+ #
+ # IPAddress::valid? "10.0.0.0/999"
# #=> false
#
def self.valid?(addr)
+ valid_ip?(addr) || valid_ipv4_subnet?(addr) || valid_ipv6_subnet?(addr)
+ end
+
+ #
+ # Checks if the given string is a valid IP address,
+ # either IPv4 or IPv6
+ #
+ # Example:
+ #
+ # IPAddress::valid_ip? "2002::1"
+ # #=> true
+ #
+ # IPAddress::valid_ip? "10.0.0.256"
+ # #=> false
+ #
+ def self.valid_ip?(addr)
valid_ipv4?(addr) || valid_ipv6?(addr)
end
-
+
+ #
+ # Checks if the given string is a valid IPv4 subnet
+ #
+ # Example:
+ #
+ # IPAddress::valid_ipv4_subnet? "10.0.0.0/24"
+ # #=> true
+ #
+ # IPAddress::valid_ipv4_subnet? "10.0.0.0/255.255.255.0"
+ # #=> true
+ #
+ # IPAddress::valid_ipv4_subnet? "10.0.0.0/64"
+ # #=> false
+ #
+ def self.valid_ipv4_subnet?(addr)
+ ip, netmask = addr.split("/")
+
+ valid_ipv4?(ip) && (!(netmask =~ /\A([12]?\d|3[0-2])\z/).nil? || valid_ipv4_netmask?(netmask))
+ end
+
+ #
+ # Checks if the given string is a valid IPv6 subnet
+ #
+ # Example:
+ #
+ # IPAddress::valid_ipv6_subnet? "::/0"
+ # #=> true
+ #
+ # IPAddress::valid_ipv6_subnet? "dead:beef:cafe:babe::/64"
+ # #=> true
+ #
+ # IPAddress::valid_ipv6_subnet? "2001::1/129"
+ # #=> false
+ #
+ def self.valid_ipv6_subnet?(addr)
+ ip, netmask = addr.split("/")
+
+ netmask = Integer(netmask, 10)
+
+ valid_ipv6?(ip) && netmask >= 0 && netmask <= 128
+ rescue ArgumentError
+ false
+ end
+
#
# Checks if the given string is a valid IPv4 address
#
@@ -130,12 +196,12 @@ module IPAddress
# #=> true
#
def self.valid_ipv4?(addr)
- if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
+ if /^(0|[1-9]{1}\d{0,2})\.(0|[1-9]{1}\d{0,2})\.(0|[1-9]{1}\d{0,2})\.(0|[1-9]{1}\d{0,2})$/ =~ 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.
@@ -149,7 +215,7 @@ module IPAddress
rescue
return false
end
-
+
#
# Checks if the given string is a valid IPv6 address
#
@@ -161,35 +227,35 @@ module IPAddress
# IPAddress::valid_ipv6? "2002::DEAD::BEEF"
# #=> false
#
- def self.valid_ipv6?(addr)
+ 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
+# 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
+# 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"
+# ip_mapped = IPAddress::IPv6::Mapped "::ffff:172.16.10.1/128"
#
# you can just use the IPAddress wrapper:
#
@@ -197,7 +263,7 @@ end # module IPAddress
# 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
+# All the object created will be instances of the
# correct class:
#
# ip.class
@@ -219,8 +285,8 @@ if RUBY_VERSION =~ /^1\.8/
alias :key :index
end
module Math # :nodoc:
- def Math.log2(n)
- log(n) / log(2)
+ def Math.log2(n)
+ log(n) / log(2)
end
end
end
diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb
index 3ad2c15..82546d6 100644
--- a/lib/ipaddress/ipv4.rb
+++ b/lib/ipaddress/ipv4.rb
@@ -61,6 +61,7 @@ module IPAddress;
# IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
#
def initialize(str)
+ raise ArgumentError, "Nil IP" unless str
ip, netmask = str.split("/")
# Check the ip and remove white space
@@ -89,6 +90,7 @@ module IPAddress;
# 32 bits interger containing the address
@u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3])
+ @allocator = 0
end # def initialize
#
@@ -509,6 +511,7 @@ module IPAddress;
# #=> ["10.100.100.1/8","10.100.100.1/16","172.16.0.1/16"]
#
def <=>(oth)
+ return nil unless oth.is_a?(self.class)
return prefix <=> oth.prefix if to_u32 == oth.to_u32
to_u32 <=> oth.to_u32
end
@@ -637,30 +640,30 @@ module IPAddress;
#
# Checks if an IPv4 address objects belongs
- # to a multicast network RFC3171
+ # to a loopback network RFC1122
#
# Example:
#
- # ip = IPAddress "224.0.0.0/4"
- # ip.multicast?
+ # ip = IPAddress "127.0.0.1"
+ # ip.loopback?
# #=> true
#
- def multicast?
- [self.class.new("224.0.0.0/4")].any? {|i| i.include? self}
+ def loopback?
+ [self.class.new("127.0.0.0/8")].any? {|i| i.include? self}
end
#
# Checks if an IPv4 address objects belongs
- # to a loopback network RFC1122
+ # to a link-local network RFC3927
#
# Example:
#
- # ip = IPAddress "127.0.0.1"
- # ip.loopback?
+ # ip = IPAddress "169.254.0.1"
+ # ip.link_local?
# #=> true
- #
- def loopback?
- [self.class.new("127.0.0.0/8")].any? {|i| i.include? self}
+ #
+ def link_local?
+ [self.class.new("169.254.0.0/16")].any? {|i| i.include? self}
end
#
@@ -786,7 +789,7 @@ module IPAddress;
#
# we can calculate the subnets with a /26 prefix
#
- # ip.subnets(26).map{&:to_string)
+ # ip.subnet(26).map{&:to_string)
# #=> ["172.16.10.0/26", "172.16.10.64/26",
# "172.16.10.128/26", "172.16.10.192/26"]
#
@@ -1079,6 +1082,36 @@ module IPAddress;
end
#
+ # Allocates a new ip from the current subnet. Optional skip parameter
+ # can be used to skip addresses.
+ #
+ # Will raise StopIteration exception when all addresses have been allocated
+ #
+ # Example:
+ #
+ # ip = IPAddress("10.0.0.0/24")
+ # ip.allocate
+ # #=> "10.0.0.1/24"
+ # ip.allocate
+ # #=> "10.0.0.2/24"
+ # ip.allocate(2)
+ # #=> "10.0.0.5/24"
+ #
+ #
+ # Uses an internal @allocator which tracks the state of allocated
+ # addresses.
+ #
+ def allocate(skip=0)
+ @allocator += 1 + skip
+
+ next_ip = network_u32+@allocator
+ if next_ip > broadcast_u32+1
+ raise StopIteration
+ end
+ self.class.parse_u32(network_u32+@allocator, @prefix)
+ end
+
+ #
# private methods
#
private
diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb
index 2dda4d2..1601a01 100644
--- a/lib/ipaddress/ipv6.rb
+++ b/lib/ipaddress/ipv6.rb
@@ -87,6 +87,7 @@ module IPAddress;
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
def initialize(str)
+ raise ArgumentError, "Nil IP" unless str
ip, netmask = str.split("/")
if str =~ /:.+\./
@@ -102,6 +103,7 @@ module IPAddress;
end
@prefix = Prefix128.new(netmask ? netmask : 128)
+ @allocator = 0
end # def initialize
@@ -397,6 +399,13 @@ module IPAddress;
@compressed
end
+ #
+ # Returns true if the address is a link local address
+ #
+ def link_local?
+ @groups[0] == 0xfe80
+ end
+
#
# Returns true if the address is an unspecified address
#
@@ -415,6 +424,34 @@ module IPAddress;
@prefix == 128 and @compressed == "::1"
end
+ #
+ # Checks if an IPv6 address objects belongs
+ # to a link-local network RFC4291
+ #
+ # Example:
+ #
+ # ip = IPAddress "fe80::1"
+ # ip.link_local?
+ # #=> true
+ #
+ def link_local?
+ [self.class.new("fe80::/64")].any? {|i| i.include? self}
+ end
+
+ #
+ # Checks if an IPv6 address objects belongs
+ # to a unique-local network RFC4193
+ #
+ # Example:
+ #
+ # ip = IPAddress "fc00::1"
+ # ip.unique_local?
+ # #=> true
+ #
+ def unique_local?
+ [self.class.new("fc00::/7")].any? {|i| i.include? self}
+ end
+
#
# Returns true if the address is a mapped address
#
@@ -487,6 +524,7 @@ module IPAddress;
# #=> ["2001:db8:1::1/64","2001:db8:1::1/65","2001:db8:2::1/64"]
#
def <=>(oth)
+ return nil unless oth.is_a?(self.class)
return prefix <=> oth.prefix if to_u128 == oth.to_u128
to_u128 <=> oth.to_u128
end
@@ -628,6 +666,36 @@ module IPAddress;
def self.parse_hex(hex, prefix=128)
self.parse_u128(hex.hex, prefix)
end
+
+ #
+ # Allocates a new ip from the current subnet. Optional skip parameter
+ # can be used to skip addresses.
+ #
+ # Will raise StopIteration exception when all addresses have been allocated
+ #
+ # Example:
+ #
+ # ip = IPAddress("10.0.0.0/24")
+ # ip.allocate
+ # #=> "10.0.0.1/24"
+ # ip.allocate
+ # #=> "10.0.0.2/24"
+ # ip.allocate(2)
+ # #=> "10.0.0.5/24"
+ #
+ #
+ # Uses an internal @allocator which tracks the state of allocated
+ # addresses.
+ #
+ def allocate(skip=0)
+ @allocator += 1 + skip
+
+ next_ip = network_u128+@allocator
+ if next_ip > broadcast_u128
+ raise StopIteration
+ end
+ self.class.parse_u128(next_ip, @prefix)
+ end
private
diff --git a/lib/ipaddress/prefix.rb b/lib/ipaddress/prefix.rb
index a0e3bb0..e819800 100644
--- a/lib/ipaddress/prefix.rb
+++ b/lib/ipaddress/prefix.rb
@@ -55,10 +55,10 @@ module IPAddress
#
# Sums two prefixes or a prefix to a
- # number, returns a Fixnum
+ # number, returns a Integer
#
def +(oth)
- if oth.is_a? Fixnum
+ if oth.is_a? Integer
self.prefix + oth
else
self.prefix + oth.prefix
@@ -68,17 +68,17 @@ module IPAddress
#
# Returns the difference between two
# prefixes, or a prefix and a number,
- # as a Fixnum
+ # as a Integer
#
def -(oth)
- if oth.is_a? Fixnum
+ if oth.is_a? Integer
self.prefix - oth
else
(self.prefix - oth.prefix).abs
end
end
- end # class Prefix
+ end # class Prefix
class Prefix32 < Prefix
diff --git a/tasks/jeweler.rake b/tasks/jeweler.rake
new file mode 100644
index 0000000..eda8319
--- /dev/null
+++ b/tasks/jeweler.rake
@@ -0,0 +1,15 @@
+require 'jeweler'
+Jeweler::Tasks.new do |gem|
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
+ gem.name = "ipaddress"
+ gem.summary = %q{IPv4/IPv6 address manipulation library}
+ gem.description = %q{IPAddress is a Ruby library designed to make manipulation
+ of IPv4 and IPv6 addresses both powerful and simple. It maintains
+ a layer of compatibility with Ruby's own IPAddr, while
+ addressing many of its issues.}
+ gem.email = "ceresa@gmail.com"
+ gem.homepage = "https://github.com/bluemonk/ipaddress"
+ gem.authors = ["bluemonk", "mikemackintosh"]
+ gem.license = "MIT"
+end
+Jeweler::RubygemsDotOrgTasks.new
diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb
index 3fc808b..5e8d5a9 100644
--- a/test/ipaddress/ipv4_test.rb
+++ b/test/ipaddress/ipv4_test.rb
@@ -74,6 +74,22 @@ class IPv4Test < Minitest::Test
"10.32.0.1" => ["10.32.0.253", 253],
"192.0.0.0" => ["192.1.255.255", 131072]
}
+
+ @link_local = [
+ "169.254.0.0",
+ "169.254.255.255",
+ "169.254.12.34",
+ "169.254.0.0/16",
+ "169.254.0.0/17"]
+
+ @not_link_local = [
+ "127.0.0.1",
+ "127.0.1.1",
+ "192.168.0.100",
+ "169.255.0.0",
+ "169.254.0.0/15",
+ "0.0.0.0",
+ "255.255.255.255"]
end
@@ -83,7 +99,7 @@ class IPv4Test < Minitest::Test
assert_instance_of @klass, ip
end
assert_instance_of IPAddress::Prefix32, @ip.prefix
- assert_raises (ArgumentError) do
+ assert_raises(ArgumentError) do
@klass.new
end
end
@@ -92,6 +108,7 @@ class IPv4Test < Minitest::Test
@invalid_ipv4.each do |i|
assert_raises(ArgumentError) {@klass.new(i)}
end
+ assert_raises (ArgumentError) {@klass.new(nil)}
assert_raises (ArgumentError) {@klass.new("10.0.0.0/asd")}
end
@@ -309,6 +326,15 @@ class IPv4Test < Minitest::Test
assert_equal false, @klass.new("192.0.0.2/24").private?
end
+ def test_method_link_local?
+ @link_local.each do |addr|
+ assert_equal true, @klass.new(addr).link_local?
+ end
+ @not_link_local.each do |addr|
+ assert_equal false, @klass.new(addr).link_local?
+ end
+ end
+
def test_method_octet
assert_equal 172, @ip[0]
assert_equal 16, @ip[1]
@@ -374,6 +400,12 @@ class IPv4Test < Minitest::Test
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}
+ # compare with alien thing
+ ip1 = @klass.new('127.0.0.1')
+ ip2 = IPAddress::IPv6.new('::1')
+ not_ip = String
+ assert_equal nil, ip1 <=> ip2
+ assert_equal nil, ip1 <=> not_ip
end
def test_method_minus
@@ -597,6 +629,30 @@ class IPv4Test < Minitest::Test
assert_equal "192.168.200.0/24", ip.to_string
end
+ def test_allocate_addresses
+ ip = @klass.new("10.0.0.0/24")
+ ip1 = ip.allocate
+ ip2 = ip.allocate
+ ip3 = ip.allocate
+ assert_equal "10.0.0.1/24", ip1.to_string
+ assert_equal "10.0.0.2/24", ip2.to_string
+ assert_equal "10.0.0.3/24", ip3.to_string
+ end
+
+ def test_allocate_can_skip_addresses
+ ip = @klass.new("10.0.0.0/24")
+ ip1 = ip.allocate(2)
+ assert_equal "10.0.0.3/24", ip1.to_string
+ end
+
+ def test_allocate_will_raise_stopiteration
+ ip = @klass.new("10.0.0.0/30")
+ ip.allocate(3)
+ assert_raises (StopIteration) do
+ ip.allocate
+ end
+ end
+
end # class IPv4Test
diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb
index a1cd072..294d5fa 100644
--- a/test/ipaddress/ipv6_test.rb
+++ b/test/ipaddress/ipv6_test.rb
@@ -26,9 +26,7 @@ class IPv6Test < Minitest::Test
"::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}
+ "::" => 0}
@invalid_ipv6 = [":1:2:3:4:5:6:7",
":1:2:3:4:5:6:7",
@@ -44,6 +42,37 @@ class IPv6Test < Minitest::Test
@network = @klass.new "2001:db8:8:800::/64"
@arr = [8193,3512,0,0,8,2048,8204,16762]
@hex = "20010db80000000000080800200c417a"
+
+ @link_local = [
+ "fe80::",
+ "fe80::1",
+ "fe80::208:74ff:feda:625c",
+ "fe80::/64",
+ "fe80::/65"]
+
+ @not_link_local = [
+ "::",
+ "::1",
+ "ff80:03:02:01::",
+ "2001:db8::8:800:200c:417a",
+ "fe80::/63"]
+
+ @unique_local = [
+ "fc00::/7",
+ "fc00::/8",
+ "fd00::/8",
+ "fd12:3456:789a:1::1",
+ "fd12:3456:789a:1::/64",
+ "fc00::1"]
+
+ @not_unique_local = [
+ "fc00::/6",
+ "::",
+ "::1",
+ "fe80::",
+ "fe80::1",
+ "fe80::/64"]
+
end
def test_attribute_address
@@ -58,6 +87,7 @@ class IPv6Test < Minitest::Test
end
assert_equal 64, @ip.prefix
+ assert_raises(ArgumentError) {@klass.new nil }
assert_raises(ArgumentError) {
@klass.new "::10.1.1.1"
}
@@ -200,6 +230,12 @@ class IPv6Test < Minitest::Test
assert_equal "1::1", @klass.new("1:0:0:0:0:0:0:1").compressed
end
+ def test_method_link_local?
+ assert_equal true, @klass.new("fe80::1").link_local?
+ assert_equal true, @klass.new("fe80:ffff::1").link_local?
+ assert_equal false, @klass.new("fe81::1").link_local?
+ end
+
def test_method_unspecified?
assert_equal true, @klass.new("::").unspecified?
assert_equal false, @ip.unspecified?
@@ -210,6 +246,24 @@ class IPv6Test < Minitest::Test
assert_equal false, @ip.loopback?
end
+ def test_method_link_local?
+ @link_local.each do |addr|
+ assert_equal true, @klass.new(addr).link_local?
+ end
+ @not_link_local.each do |addr|
+ assert_equal false, @klass.new(addr).link_local?
+ end
+ end
+
+ def test_method_unique_local?
+ @unique_local.each do |addr|
+ assert_equal true, @klass.new(addr).unique_local?
+ end
+ @not_unique_local.each do |addr|
+ assert_equal false, @klass.new(addr).unique_local?
+ end
+ end
+
def test_method_network
@networks.each do |addr,net|
ip = @klass.new addr
@@ -228,6 +282,30 @@ class IPv6Test < Minitest::Test
assert_equal expected, arr
end
+ def test_allocate_addresses
+ ip = @klass.new("2001:db8::4/125")
+ ip1 = ip.allocate
+ ip2 = ip.allocate
+ ip3 = ip.allocate
+ assert_equal "2001:db8::1", ip1.compressed
+ assert_equal "2001:db8::2", ip2.compressed
+ assert_equal "2001:db8::3", ip3.compressed
+ end
+
+ def test_allocate_can_skip_addresses
+ ip = @klass.new("2001:db8::4/125")
+ ip1 = ip.allocate(2)
+ assert_equal "2001:db8::3", ip1.compressed
+ end
+
+ def test_allocate_will_raise_stopiteration
+ ip = @klass.new("2001:db8::4/125")
+ ip.allocate(6)
+ assert_raises (StopIteration) do
+ ip.allocate
+ end
+ end
+
def test_method_compare
ip1 = @klass.new("2001:db8:1::1/64")
ip2 = @klass.new("2001:db8:2::1/64")
@@ -259,6 +337,12 @@ class IPv6Test < Minitest::Test
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}
+ # compare with alien thing
+ ip1 = @klass.new('::1')
+ ip2 = IPAddress::IPv4.new('127.0.0.1')
+ not_ip = String
+ assert_equal nil, ip1 <=> ip2
+ assert_equal nil, ip1 <=> not_ip
end
def test_classmethod_expand
diff --git a/test/ipaddress/mongoid_test.rb b/test/ipaddress/mongoid_test.rb
index b463e84..4f32093 100644
--- a/test/ipaddress/mongoid_test.rb
+++ b/test/ipaddress/mongoid_test.rb
@@ -38,7 +38,7 @@ class MongoidTest < Minitest::Test
@invalid_values.each do |invalid_value|
# Invalid addresses should serialize to nil
- assert_equal nil, IPAddress.mongoize(invalid_value)
+ assert_nil IPAddress.mongoize(invalid_value)
end
end
@@ -57,7 +57,7 @@ class MongoidTest < Minitest::Test
@invalid_values.each do |invalid_value|
# Invalid stored value should be loaded as nil
- assert_equal nil, IPAddress.demongoize(invalid_value)
+ assert_nil IPAddress.demongoize(invalid_value)
end
end
@@ -67,4 +67,4 @@ class MongoidTest < Minitest::Test
assert_equal IPAddress.mongoize(@valid_network4), IPAddress.evolve(@valid_network4)
end
-end \ No newline at end of file
+end
diff --git a/test/ipaddress/prefix_test.rb b/test/ipaddress/prefix_test.rb
index 1a0e277..f0a3d8c 100644
--- a/test/ipaddress/prefix_test.rb
+++ b/test/ipaddress/prefix_test.rb
@@ -132,7 +132,7 @@ class Prefix128Test < Minitest::Test
end
def test_initialize
- assert_raises (ArgumentError) do
+ assert_raises(ArgumentError) do
@klass.new 129
end
assert_instance_of @klass, @klass.new(64)
diff --git a/test/ipaddress_test.rb b/test/ipaddress_test.rb
index 862c889..777352e 100644
--- a/test/ipaddress_test.rb
+++ b/test/ipaddress_test.rb
@@ -18,17 +18,19 @@ class IPAddressTest < Minitest::Test
@invalid_ipv4_uint32 = [4294967296, # 256.0.0.0
"A294967295", # Invalid uINT
- -1] # Invalid
+ -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"]
+ "10.0",
+ "0127.010.010.010",
+ "055.055.055.055"]
@valid_ipv4_range = ["10.0.0.1-254",
"10.0.1-254.0",
@@ -39,18 +41,18 @@ class IPAddressTest < Minitest::Test
def test_method_IPAddress
- assert_instance_of @ipv4class, @method.call(@valid_ipv4)
- assert_instance_of @ipv6class, @method.call(@valid_ipv6)
+ 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_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])}
@@ -59,6 +61,30 @@ class IPAddressTest < Minitest::Test
end
def test_module_method_valid?
+ assert_equal true, IPAddress::valid?("0.0.0.5/20")
+ assert_equal true, IPAddress::valid?("0.0.0.0/8")
+ assert_equal false, IPAddress::valid?("800.754.1.1/13")
+ assert_equal false, IPAddress::valid?("0xff/4")
+ assert_equal false, IPAddress::valid?("0xff.0xff.0xff.0xfe/20")
+ assert_equal false, IPAddress::valid?("037.05.05.01/8")
+ assert_equal false, IPAddress::valid?("0127.0.0.01/16")
+ assert_equal false, IPAddress::valid?("055.027.043.09/16")
+ # four digits fails the three digits check
+ assert_equal false, IPAddress::valid?("0255.0255.0255.01/20")
+ assert_equal false, IPAddress::valid?("013.055.0255.0216/29")
+ assert_equal false, IPAddress::valid?("013.055.025.021/29")
+ assert_equal false, IPAddress::valid?("052.015.024.020/29")
+ assert_equal true, IPAddress::valid?("10.0.0.0/24")
+ assert_equal true, IPAddress::valid?("10.0.0.0/255.255.255.0")
+ assert_equal false, IPAddress::valid?("10.0.0.0/64")
+ assert_equal false, IPAddress::valid?("10.0.0.0/255.255.255.256")
+ assert_equal true, IPAddress::valid?("::/0")
+ assert_equal true, IPAddress::valid?("2002::1/128")
+ assert_equal true, IPAddress::valid?("dead:beef:cafe:babe::/64")
+ assert_equal false, IPAddress::valid?("2002::1/129")
+ end
+
+ def test_module_method_valid_ip?
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")
@@ -69,14 +95,30 @@ class IPAddressTest < Minitest::Test
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?
+ def test_module_method_valid_ipv4_netmask?
assert_equal true, IPAddress::valid_ipv4_netmask?("255.255.255.0")
assert_equal false, IPAddress::valid_ipv4_netmask?("10.0.0.1")
end
+ def test_module_method_valid_ipv4_subnet?
+ assert_equal true, IPAddress::valid_ipv4_subnet?("10.0.0.0/255.255.255.0")
+ assert_equal true, IPAddress::valid_ipv4_subnet?("10.0.0.0/0")
+ assert_equal true, IPAddress::valid_ipv4_subnet?("10.0.0.0/32")
+ assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.0/ABC")
+ assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.1")
+ assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.0/33")
+ assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.256/24")
+ assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.0/255.255.255.256")
+ end
+
+ def test_module_method_valid_ipv6_subnet?
+ assert_equal true, IPAddress::valid_ipv6_subnet?("::/0")
+ assert_equal true, IPAddress::valid_ipv6_subnet?("2002::1/128")
+ assert_equal true, IPAddress::valid_ipv6_subnet?("dead:beef:cafe:babe::/64")
+ assert_equal false, IPAddress::valid_ipv6_subnet?("2002::1/129")
+ end
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 249ea52..4a80d3e 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,5 +1,7 @@
require 'rubygems'
require 'minitest/autorun'
+require 'simplecov'
+SimpleCov.start
$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))