Giter Club home page Giter Club logo

ipaddr's Introduction

IPAddr

IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and IPv6 are supported.

Build Status

Installation

This library is part of the standard ruby distribution as default gem and synchronized periodically, but you can explicitly depend on this gem with version constraints as necessary, like when you need a newer version than comes with older versions of ruby.

For example, you can add this line to your application's Gemfile:

gem 'ipaddr', '~> 1.2'

And then execute:

$ bundle

Or install it yourself as:

$ gem install ipaddr

Usage

require 'ipaddr'

ipaddr1 = IPAddr.new "3ffe:505:2::1"

p ipaddr1                   #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>

p ipaddr1.to_s              #=> "3ffe:505:2::1"

ipaddr2 = ipaddr1.mask(48)  #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>

p ipaddr2.to_s              #=> "3ffe:505:2::"

ipaddr3 = IPAddr.new "192.168.2.0/24"

p ipaddr3                   #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>

Alternative

The ipaddress gem is a popular, extensive library for manipulating IP addresses with a layer of compatibility with ipaddr. If you need a richer set of features than ipaddr has, try this library instead.

Development

After checking out the repo, run bundle install to install dependencies. Then, run rake test to run the tests.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/ipaddr.

License

The gem is available as open source under the terms of the 2-Clause BSD License.

ipaddr's People

Contributors

akr avatar amatsuda avatar byroot avatar dependabot[bot] avatar drbrain avatar eban avatar esparta avatar hanazuki avatar herwinw avatar hsbt avatar ioquatix avatar jeremyevans avatar k0kubun avatar knu avatar lrandall-godaddy avatar marcandre avatar nicolasleger avatar nobu avatar nurse avatar olleolleolle avatar petergoldstein avatar rm155 avatar shyouhei avatar skipkayhil avatar sorah avatar taketo1113 avatar tenderlove avatar unak avatar y-yagi avatar znz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ipaddr's Issues

CIDR parsing ignores part after second slash

Hi.

We use IPAddr to validate user input for valid IP addresses (with or without CIDR notation) and were surprised to see, that the following cases passed validation:

IPAddr.new('192.168.0.1/24/foobar')
=> #<IPAddr: IPv4:192.168.0.0/255.255.255.0>

IPAddr.new('3ffe:505:2::1/0/foobar')
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0000/0000:0000:0000:0000:0000:0000:0000:0000>

Is this a valid case or a bug?

Allow reading unmasked address when passed a CIDR string

This behavior caught me by surprise:

> ip = IPAddr.new('1.2.3.4/16')
=> #<IPAddr: IPv4:1.2.0.0/255.255.0.0>       
> ip.to_s
=> "1.2.0.0"           

Looking at the code I believe this is happening at https://github.com/ruby/ipaddr/blob/master/lib/ipaddr.rb#L649-L651, which is a very old commit, so this behavior is clearly expected. For my use case I need the host bits unmasked, so I want to parse 1.2.3.4/16 and be able to read back something like ip.address as 1.2.3.4 and the netmask as 255.255.0.0. It looks like that's not currently possible with this gem, which surprises me. Am I overlooking a method, or is there a nuance in the CIDR spec implemented here I've forgotten?

IPAddr.new accepts certain IPv6 literals when AF_INET is specified

If the given address is an IPv6 literal enclosed in brackets and/or with a zone ID suffix, it will be unexpectedly accepted.
All the following invocations expect AddressFamilyError.

% bundle exec irb -ripaddr
irb(main):001:0> IPAddr.new('[::]', Socket::AF_INET)
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
irb(main):002:0> IPAddr.new('::%eth0', Socket::AF_INET)
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0000%eth0/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
irb(main):003:0> IPAddr.new('[::%eth0]', Socket::AF_INET)
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0000%eth0/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
irb(main):004:0> IPAddr.new('::', Socket::AF_INET)
/.../ruby/ipaddr/lib/ipaddr.rb:633:in `initialize': address family mismatch (IPAddr::AddressFamilyError)
      raise AddressFamilyError, "address family mismatch"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

`IPAddr#freeze` is broken

After freezing IPAddr instance, a lot of methods die with FrozenError: can't modify frozen IPAddr.

$ RBENV_VERSION=3.1.0-dev irb
ruby 3.1.0dev (2021-12-08T23:58:25Z master 45c5794d32) [x86_64-linux]
irb(main):001:0> require 'ipaddr'
=> true
irb(main):002:0> IPAddr.new('127.0.0.1').freeze.to_range
/usr/local/anyenv/envs/rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/ipaddr.rb:509:in `set': can't modify frozen IPAddr: #<IPAddr: IPv4:127.0.0.1/255.255.255.255> (FrozenError)
        from /usr/local/anyenv/envs/rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/ipaddr.rb:413:in `to_range'
irb(main):003:0> IPAddr.new('127.0.0.1').freeze.include?('127.0.0.1')
/usr/local/anyenv/envs/rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/ipaddr.rb:509:in `set': can't modify frozen IPAddr: #<IPAddr: IPv4:127.0.0.1/255.255.255.255> (FrozenError)
        from /usr/local/anyenv/envs/rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/ipaddr.rb:413:in `to_range'
        from /usr/local/anyenv/envs/rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/ipaddr.rb:178:in `include?'
irb(main):004:0> IPAddr.new('127.0.0.1').freeze.succ
/usr/local/anyenv/envs/rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/ipaddr.rb:509:in `set': can't modify frozen IPAddr: #<IPAddr: IPv4:127.0.0.1/255.255.255.255> (FrozenError)
        from /usr/local/anyenv/envs/rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/ipaddr.rb:377:in `succ'

It seems that clone.set() in these methods should be clone(freeze: false).set() or dup.set()

RFC6598 addresses not recognized as private

The private? function doesn't recognize the RFC6598 "shared address space", which is basically a new(ish) version of the well known 192.168/16, 172.16/12, and 10./8 spaces. The new space is 100.64.0.0/10.

A complete list of reserved address spaces can be found in the special-purpose address registries at https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml and https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml

I believe that in those two tables, any address marked with "True" in the Source and Destination columns, and "False" in the "Globally Reachable" column, should be considered private. However I'll leave that decision to others.

Wrong InvalidAddressError message

After changes from #16 and removing of

  rescue InvalidAddressError => e
    raise e.class, "#{e.message}: #{addr}"

exception no longer returns proper exception message with invalid ip address.

ruby 3.0

> IPAddr.new('192.168.0.1000')
ipaddr.rb:598:in `rescue in initialize': invalid address: 192.168.0.1000 (IPAddr::InvalidAddressError)

ruby 3.1

> IPAddr.new('192.168.0.1000')
ipaddr.rb:656:in `block in in_addr': invalid address:  (IPAddr::InvalidAddressError)

Best regards,
Georgi.

Exception on valid link-local ipv6 address

using a link-local ip address such as: fe80::302f:51e0:1313:f50d%5

causes an exception to be thrown whereas it's actually a valid address. The %5 is a reference to a scope id that the ipv6 address belongs to.

https://superuser.com/questions/99746/why-is-there-a-percent-sign-in-the-ipv6-address

I'm a noob when it comes to git but I've got a workaround that seems to work by adding the lines below (with the +) to the RE_IPV6ADDRLIKE_COMPRESSED regex.

  RE_IPV6ADDRLIKE_COMPRESSED = %r{
    \A
    ( (?: (?: [\da-f]{1,4} : )* [\da-f]{1,4} )? )
    ::
    ( (?:
      ( (?: [\da-f]{1,4} : )* )
      (?:
        [\da-f]{1,4}
      |
        (\d+) \. (\d+) \. (\d+) \. (\d+)
+      |
+        [\da-f]{1,4} %[\d]+
      )
    )? )
    \z

Regards
Martyn

== fails

Not sure if it is intended behaviour, but IPAddr.new('10.0.0.0/16') == IPAddr.new('10.0.0.0/8') is true, when these are not the same network. The test should be

def ==(value)
@addr == value.addr && @mask_addr == value.mask_addr
end
Which also means adding
attr_reader :addr
attr_reader :mask_addr

Proposal to add subnet information to the to_string method.

Thank you for the great gem.

The inspect method contains subnet information and it seemed more natural to the developer that the to_string method should also contain the same subnet information.

So how about adding subnet information to the to_string method?

【Now】

ipaddr = IPAddr.new("192.168.2.0/24")

ipaddr.inspect
=> "#<IPAddr: IPv4:192.168.2.0/255.255.255.0>"
ipaddr.to_s
=> "192.168.2.0"
ipaddr.to_string
=> "192.168.2.0"

【My Proposal】

ipaddr = IPAddr.new("192.168.2.0/24")

ipaddr.inspect
=> "#<IPAddr: IPv4:192.168.2.0/255.255.255.0>"
ipaddr.to_s
=> "192.168.2.0"
ipaddr.to_string
=> "192.168.2.0/24"

IPv6 hton layout does not match ipv6_mreq

Given this C program:

#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char * argv[])
{
        struct ipv6_mreq req;
        memset(&req, 0, sizeof(req));
	req.ipv6mr_multiaddr.s6_addr[0] = 0xFF;
	req.ipv6mr_multiaddr.s6_addr[1] = 0x02;
	req.ipv6mr_multiaddr.s6_addr[15] = 0xFB;

        uint8_t * ptr = (uint8_t *)&req;
        for(int i = 0; i < sizeof(req); i++) {
                fprintf(stderr, "\\%#02x", ptr[i]);
        }

        fprintf(stderr, "\n");
        return 0;
}

The bytes of the ipv6_mreq struct are: \0xff\0x2\00\00\00\00\00\00\00\00\00\00\00\00\00\0xfb\00\00\00\00

However, if I use this Ruby program:

require "ipaddr"

p IPAddr.new("ff02::fb").hton

The bytes are: "\xFF\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFB"

I think hton should match the ipv6_mreq layout.

IPAddr.new accepts invalid IPv6 addresses

require "ipaddr"
ip = "fe80::1213:31ff:fe67:8b94%en0"
IPAddr.new(ip)
# in ruby 3.1 and 3.2, #<IPAddr: IPv6:fe80:0000:0000:0000:1213:31ff:fe67:8b94%en0/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
# in ruby 3.0:  invalid address: fe80::1213:31ff:fe67:8b94%en0 (IPAddr::InvalidAddressError)

unsupported address family

Hello,

I do not understand the following behavior:

3.2.2 :001 > require "ipaddr"
 => true 
3.2.2 :002 > x = IPAddr.new("172.20.222.160")
 => #<IPAddr: IPv4:172.20.222.160/255.255.255.255> 
3.2.2 :003 > IPAddr.ntop(x.hton)
 => "172.20.222.160" 
3.2.2 :004 > x.hton
 => "\xAC\x14\xDE\xA0" 
3.2.2 :005 > IPAddr.ntop("\xAC\x14\xDE\xA0")
/home/marek/.rvm/rubies/ruby-3.2.2/lib/ruby/3.2.0/ipaddr.rb:120:in `ntop': unsupported address family (IPAddr::AddressFamilyError)

      raise AddressFamilyError, "unsupported address family"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	from (irb):5:in `<main>'
	from /home/marek/.rvm/gems/ruby-3.2.2/gems/irb-1.6.4/exe/irb:9:in `<top (required)>'
	from /home/marek/.rvm/gems/ruby-3.2.2/bin/irb:25:in `load'
	from /home/marek/.rvm/gems/ruby-3.2.2/bin/irb:25:in `<main>'
	from /home/marek/.rvm/gems/ruby-3.2.2/bin/ruby_executable_hooks:22:in `eval'
	from /home/marek/.rvm/gems/ruby-3.2.2/bin/ruby_executable_hooks:22:in `<main>'
3.2.2 :006 > y = x.hton
 => "\xAC\x14\xDE\xA0" 
3.2.2 :007 > IPAddr.ntop(y)
 => "172.20.222.160" 
3.2.2 :008 > y.class
 => String 
3.2.2 :001 > require "ipaddr"
 => true 
3.2.2 :002 > File.write("test", IPAddr.new("172.20.222.160").hton)
 => 4 
3.2.2 :003 > IPAddr.ntop(File.read("test"))
/home/marek/.rvm/rubies/ruby-3.2.2/lib/ruby/3.2.0/ipaddr.rb:120:in `ntop': unsupported address family (IPAddr::AddressFamilyError)

      raise AddressFamilyError, "unsupported address family"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	from (irb):3:in `<main>'
	from /home/marek/.rvm/gems/ruby-3.2.2/gems/irb-1.6.4/exe/irb:9:in `<top (required)>'
	from /home/marek/.rvm/gems/ruby-3.2.2/bin/irb:25:in `load'
	from /home/marek/.rvm/gems/ruby-3.2.2/bin/irb:25:in `<main>'
	from /home/marek/.rvm/gems/ruby-3.2.2/bin/ruby_executable_hooks:22:in `eval'
	from /home/marek/.rvm/gems/ruby-3.2.2/bin/ruby_executable_hooks:22:in `<main>'

If I try to pass the binary string as a string, an error is returned, but if I do not pass the string as a variable, it works.
This error also occurred when I tried to read an IP address from an MRT file. I then had the string displayed and tried it manually (see above). But I also got the same error.

#native results in incorrect mask, breaking #prefix

Calling native leaves the internal @mask_addr as its ipv6 mask instead of converting it to an ipv4 mask. This is most easily seen when calling prefix, but also breaks to_range and possibly other methods.

IPAddr.new('::ffff:1.2.3.4/127')
# => #<IPAddr: IPv6:0000:0000:0000:0000:0000:ffff:0102:0304/ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe> 
IPAddr.new('::ffff:1.2.3.4/127').prefix
# => 127 
IPAddr.new('::ffff:1.2.3.4/127').native
# => #<IPAddr: IPv4:1.2.3.4/255.255.255.254> 
IPAddr.new('::ffff:1.2.3.4/127').native.prefix
# => -96                <-- should be 31
IPAddr.new('1.2.3.4/31').prefix
# => 31 
IPAddr.new('::ffff:1.2.3.4/127').native.instance_variable_get :@mask_addr
# => 340282366920938463463374607431768211454       <-- root issue

Similarly, ipv4_mapped and ipv4_compat also fail to adjust @mask_addr the other direction.

IPAddr.new('1.2.3.4/31').ipv4_mapped
# => #<IPAddr: IPv6:0000:0000:0000:0000:0000:ffff:0102:0304/0000:0000:0000:0000:0000:0000:ffff:fffe> 
IPAddr.new('1.2.3.4/31').ipv4_mapped.prefix
# => 0                  <-- should be 127
IPAddr.new('1.2.3.4/31').ipv4_mapped.instance_variable_get :@mask_addr
# => 4294967294 

IPAddr.new('1.2.3.4/31').ipv4_compat
# => #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0102:0304/0000:0000:0000:0000:0000:0000:ffff:fffe> 
IPAddr.new('1.2.3.4/31').ipv4_compat.prefix
# => 0 
IPAddr.new('1.2.3.4/31').ipv4_compat.instance_variable_get :@mask_addr
# => 4294967294 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.