diff --git a/spec/std/socket/address_spec.cr b/spec/std/socket/address_spec.cr index bc7274f44dff..500df202160a 100644 --- a/spec/std/socket/address_spec.cr +++ b/spec/std/socket/address_spec.cr @@ -128,6 +128,18 @@ describe Socket::IPAddress do Socket::IPAddress.valid_port?(65_536).should be_false end + it "#private?" do + Socket::IPAddress.new("192.168.0.1", 0).private?.should be_true + Socket::IPAddress.new("192.100.0.1", 0).private?.should be_false + Socket::IPAddress.new("172.16.0.1", 0).private?.should be_true + Socket::IPAddress.new("172.10.0.1", 0).private?.should be_false + Socket::IPAddress.new("10.0.0.1", 0).private?.should be_true + Socket::IPAddress.new("1.1.1.1", 0).private?.should be_false + Socket::IPAddress.new("fd00::1", 0).private?.should be_true + Socket::IPAddress.new("fb00::1", 0).private?.should be_false + Socket::IPAddress.new("2001:4860:4860::8888", 0).private?.should be_false + end + it "#==" do Socket::IPAddress.new("127.0.0.1", 8080).should eq Socket::IPAddress.new("127.0.0.1", 8080) Socket::IPAddress.new("127.0.0.1", 8080).hash.should eq Socket::IPAddress.new("127.0.0.1", 8080).hash diff --git a/src/socket/address.cr b/src/socket/address.cr index 7078c0e22442..298ca77e89bb 100644 --- a/src/socket/address.cr +++ b/src/socket/address.cr @@ -208,7 +208,7 @@ class Socket def loopback? : Bool case addr = @addr in LibC::InAddr - addr.s_addr & 0x00000000ff_u32 == 0x0000007f_u32 + addr.s_addr & 0x000000ff_u32 == 0x0000007f_u32 in LibC::In6Addr ipv6_addr8(addr) == StaticArray[0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 1_u8] end @@ -224,6 +224,21 @@ class Socket end end + # Returns `true` if this IP is a private address. + # + # IPv4 addresses in `10.0.0.0/8`, `172.16.0.0/12` and `192.168.0.0/16` as defined in [RFC 1918](https://tools.ietf.org/html/rfc1918) + # and IPv6 Unique Local Addresses in `fc00::/7` as defined in [RFC 4193](https://tools.ietf.org/html/rfc4193) are considered private. + def private? : Bool + case addr = @addr + in LibC::InAddr + addr.s_addr & 0x000000ff_u32 == 0x00000000a_u32 || # 10.0.0.0/8 + addr.s_addr & 0x000000f0ff_u32 == 0x0000010ac_u32 || # 172.16.0.0/12 + addr.s_addr & 0x000000ffff_u32 == 0x0000a8c0_u32 # 192.168.0.0/16 + in LibC::In6Addr + ipv6_addr8(addr)[0] & 0xfe_u8 == 0xfc_u8 + end + end + private def ipv6_addr8(addr : LibC::In6Addr) {% if flag?(:darwin) || flag?(:bsd) %} addr.__u6_addr.__u6_addr8