Skip to content

Commit d56acec

Browse files
committed
Consider IPv4-mapped IPv6 addresses link local/loopback if IPV4 address is private
Same as #57
1 parent 247459f commit d56acec

File tree

2 files changed

+29
-5
lines changed

2 files changed

+29
-5
lines changed

lib/ipaddr.rb

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,17 @@ def ipv6?
247247
end
248248

249249
# Returns true if the ipaddr is a loopback address.
250+
# Loopback IPv4 addresses in the IPv4-mapped IPv6
251+
# address range are also considered as loopback addresses.
250252
def loopback?
251253
case @family
252254
when Socket::AF_INET
253-
@addr & 0xff000000 == 0x7f000000
255+
@addr & 0xff000000 == 0x7f000000 # 127.0.0.1/8
254256
when Socket::AF_INET6
255-
@addr == 1
257+
@addr == 1 || # ::1
258+
(@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
259+
@addr & 0xff000000 == 0x7f000000 # ::ffff:127.0.0.1/8
260+
))
256261
else
257262
raise AddressFamilyError, "unsupported address family"
258263
end
@@ -282,15 +287,19 @@ def private?
282287
end
283288

284289
# Returns true if the ipaddr is a link-local address. IPv4
285-
# addresses in 169.254.0.0/16 reserved by RFC 3927 and Link-Local
290+
# addresses in 169.254.0.0/16 reserved by RFC 3927 and link-local
286291
# IPv6 Unicast Addresses in fe80::/10 reserved by RFC 4291 are
287-
# considered link-local.
292+
# considered link-local. Link-local IPv4 addresses in the
293+
# IPv4-mapped IPv6 address range are also considered link-local.
288294
def link_local?
289295
case @family
290296
when Socket::AF_INET
291297
@addr & 0xffff0000 == 0xa9fe0000 # 169.254.0.0/16
292298
when Socket::AF_INET6
293-
@addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000
299+
@addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000 || # fe80::/10
300+
(@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
301+
@addr & 0xffff0000 == 0xa9fe0000 # ::ffff:169.254.0.0/16
302+
))
294303
else
295304
raise AddressFamilyError, "unsupported address family"
296305
end

test/test_ipaddr.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,12 @@ def test_loopback?
396396
assert_equal(true, IPAddr.new('::1').loopback?)
397397
assert_equal(false, IPAddr.new('::').loopback?)
398398
assert_equal(false, IPAddr.new('3ffe:505:2::1').loopback?)
399+
400+
assert_equal(true, IPAddr.new('::ffff:127.0.0.1').loopback?)
401+
assert_equal(true, IPAddr.new('::ffff:127.127.1.1').loopback?)
402+
assert_equal(false, IPAddr.new('::ffff:0.0.0.0').loopback?)
403+
assert_equal(false, IPAddr.new('::ffff:192.168.2.0').loopback?)
404+
assert_equal(false, IPAddr.new('::ffff:255.0.0.0').loopback?)
399405
end
400406

401407
def test_private?
@@ -463,6 +469,15 @@ def test_link_local?
463469
assert_equal(false, IPAddr.new('fb84:8bf7:e905::1').link_local?)
464470

465471
assert_equal(true, IPAddr.new('fe80::dead:beef:cafe:1234').link_local?)
472+
473+
assert_equal(false, IPAddr.new('::ffff:0.0.0.0').link_local?)
474+
assert_equal(false, IPAddr.new('::ffff:127.0.0.1').link_local?)
475+
assert_equal(false, IPAddr.new('::ffff:10.0.0.0').link_local?)
476+
assert_equal(false, IPAddr.new('::ffff:172.16.0.0').link_local?)
477+
assert_equal(false, IPAddr.new('::ffff:192.168.0.0').link_local?)
478+
479+
assert_equal(true, IPAddr.new('::ffff:169.254.1.1').link_local?)
480+
assert_equal(true, IPAddr.new('::ffff:169.254.254.255').link_local?)
466481
end
467482

468483
def test_hash

0 commit comments

Comments
 (0)