From 984c8ac58bc2537a4402fd509d6e03863469ef11 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 16 Oct 2018 16:26:57 +0300 Subject: [PATCH 1/7] private networks and IsPublicAddr --- private.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 private.go diff --git a/private.go b/private.go new file mode 100644 index 0000000..e048dd5 --- /dev/null +++ b/private.go @@ -0,0 +1,73 @@ +package manet + +import ( + "net" + + ma "github.com/multiformats/go-multiaddr" +) + +// Private4 and Private6 are well-known private networks +// These are exported to allow overriding for testing +var Private4, Private6 []*net.IPNet +var privateCIDR4 = []string{ + // localhost + "127.0.0.0/8", + // private networks + "10.0.0.0/8", + "100.64.0.0/10", + "172.16.0.0/12", + "192.168.0.0/16", + // link local + "169.254.0.0/16", +} +var privateCIDR6 = []string{ + // localhost + "::1/128", + // ULA reserved + "fc00::/7", + // link local + "fe80::/10", +} + +func init() { + Private4 = parsePrivateCIDR(privateCIDR4) + Private6 = parsePrivateCIDR(privateCIDR6) +} + +func parsePrivateCIDR(cidrs []string) []*net.IPNet { + ipnets := make([]*net.IPNet, len(cidrs)) + for i, cidr := range cidrs { + _, ipnet, err := net.ParseCIDR(cidr) + if err != nil { + panic(err) + } + ipnets[i] = ipnet + } + return ipnets +} + +// IsPublicAddr retruns true if the IP part of the multiaddr is not in a private network +func IsPublicAddr(a ma.Multiaddr) bool { + ip, err := a.ValueForProtocol(ma.P_IP4) + if err == nil { + return !inAddrRange(ip, Private4) + } + + ip, err = a.ValueForProtocol(ma.P_IP6) + if err == nil { + return !inAddrRange(ip, Private6) + } + + return false +} + +func inAddrRange(s string, ipnets []*net.IPNet) bool { + ip := net.ParseIP(s) + for _, ipnet := range ipnets { + if ipnet.Contains(ip) { + return true + } + } + + return false +} From 49e7bdea20a995c90e7bd5615a28034e6b24e3fe Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 16 Oct 2018 16:45:37 +0300 Subject: [PATCH 2/7] add test --- private_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 private_test.go diff --git a/private_test.go b/private_test.go new file mode 100644 index 0000000..d93fde3 --- /dev/null +++ b/private_test.go @@ -0,0 +1,27 @@ +package manet + +import ( + "testing" + + ma "github.com/multiformats/go-multiaddr" +) + +func TestIsPublicAddr(t *testing.T) { + a, err := ma.NewMultiaddr("/ip4/192.168.1.1/tcp/80") + if err != nil { + t.Fatal(err) + } + + if IsPublicAddr(a) { + t.Fatal("192.168.1.1 is not a public address!") + } + + a, err = ma.NewMultiaddr("/ip4/1.1.1.1/tcp/80") + if err != nil { + t.Fatal(err) + } + + if !IsPublicAddr(a) { + t.Fatal("1.1.1.1 is a public address!") + } +} From 2945cfc2e647b07557049231cb04d2a1352c1052 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 16 Oct 2018 17:09:11 +0300 Subject: [PATCH 3/7] add IsPrivateAddr --- private.go | 15 +++++++++++++++ private_test.go | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/private.go b/private.go index e048dd5..fefd964 100644 --- a/private.go +++ b/private.go @@ -61,6 +61,21 @@ func IsPublicAddr(a ma.Multiaddr) bool { return false } +// IsPrivateAddr returns true if the IP part of the mutiadr is in a private network +func IsPrivateAddr(a ma.Multiaddr) bool { + ip, err := a.ValueForProtocol(ma.P_IP4) + if err == nil { + return inAddrRange(ip, Private4) + } + + ip, err = a.ValueForProtocol(ma.P_IP6) + if err == nil { + return inAddrRange(ip, Private6) + } + + return false +} + func inAddrRange(s string, ipnets []*net.IPNet) bool { ip := net.ParseIP(s) for _, ipnet := range ipnets { diff --git a/private_test.go b/private_test.go index d93fde3..ddd6485 100644 --- a/private_test.go +++ b/private_test.go @@ -16,6 +16,10 @@ func TestIsPublicAddr(t *testing.T) { t.Fatal("192.168.1.1 is not a public address!") } + if !IsPrivateAddr(a) { + t.Fatal("192.168.1.1 is a private address!") + } + a, err = ma.NewMultiaddr("/ip4/1.1.1.1/tcp/80") if err != nil { t.Fatal(err) @@ -24,4 +28,8 @@ func TestIsPublicAddr(t *testing.T) { if !IsPublicAddr(a) { t.Fatal("1.1.1.1 is a public address!") } + + if IsPrivateAddr(a) { + t.Fatal("1.1.1.1 is not a private address!") + } } From c2b139aab46332ab12f0d59810c7ce1840005de8 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 16 Oct 2018 18:10:30 +0300 Subject: [PATCH 4/7] test all known unroutable address ranges in IsPublicAddr --- private.go | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/private.go b/private.go index fefd964..3028d20 100644 --- a/private.go +++ b/private.go @@ -7,7 +7,6 @@ import ( ) // Private4 and Private6 are well-known private networks -// These are exported to allow overriding for testing var Private4, Private6 []*net.IPNet var privateCIDR4 = []string{ // localhost @@ -29,12 +28,32 @@ var privateCIDR6 = []string{ "fe80::/10", } -func init() { - Private4 = parsePrivateCIDR(privateCIDR4) - Private6 = parsePrivateCIDR(privateCIDR6) +// Unroutable4 and Unroutable6 are well known unroutable address ranges +var Unroutable4, Unroutable6 []*net.IPNet +var unroutableCIDR4 = []string{ + "0.0.0.0/8", + "192.0.0.0/26", + "192.0.2.0/24", + "192.88.99.0/24", + "198.18.0.0/15", + "198.51.100.0/24", + "203.0.113.0/24", + "224.0.0.0/4", + "240.0.0.0/4", + "255.255.255.255/32", +} +var unroutableCIDR6 = []string{ + "ff00::/8", } -func parsePrivateCIDR(cidrs []string) []*net.IPNet { +func init() { + Private4 = parseCIDR(privateCIDR4) + Private6 = parseCIDR(privateCIDR6) + Unroutable4 = parseCIDR(unroutableCIDR4) + Unroutable6 = parseCIDR(unroutableCIDR6) +} + +func parseCIDR(cidrs []string) []*net.IPNet { ipnets := make([]*net.IPNet, len(cidrs)) for i, cidr := range cidrs { _, ipnet, err := net.ParseCIDR(cidr) @@ -46,22 +65,22 @@ func parsePrivateCIDR(cidrs []string) []*net.IPNet { return ipnets } -// IsPublicAddr retruns true if the IP part of the multiaddr is not in a private network +// IsPublicAddr retruns true if the IP part of the multiaddr is a publically routable address func IsPublicAddr(a ma.Multiaddr) bool { ip, err := a.ValueForProtocol(ma.P_IP4) if err == nil { - return !inAddrRange(ip, Private4) + return !inAddrRange(ip, Private4) && !inAddrRange(ip, Unroutable4) } ip, err = a.ValueForProtocol(ma.P_IP6) if err == nil { - return !inAddrRange(ip, Private6) + return !inAddrRange(ip, Private6) && !inAddrRange(ip, Unroutable6) } return false } -// IsPrivateAddr returns true if the IP part of the mutiadr is in a private network +// IsPrivateAddr returns true if the IP part of the mutiaddr is in a private network func IsPrivateAddr(a ma.Multiaddr) bool { ip, err := a.ValueForProtocol(ma.P_IP4) if err == nil { From 7b431678970c2c633bb29b9fac6d002ae3ea3402 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 16 Oct 2018 19:09:34 +0300 Subject: [PATCH 5/7] fix typo --- private.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private.go b/private.go index 3028d20..348ae83 100644 --- a/private.go +++ b/private.go @@ -65,7 +65,7 @@ func parseCIDR(cidrs []string) []*net.IPNet { return ipnets } -// IsPublicAddr retruns true if the IP part of the multiaddr is a publically routable address +// IsPublicAddr retruns true if the IP part of the multiaddr is a publicly routable address func IsPublicAddr(a ma.Multiaddr) bool { ip, err := a.ValueForProtocol(ma.P_IP4) if err == nil { From 0425819f0ec3edbef170dc28c8d5a0638249f390 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 18 Oct 2018 13:56:20 +0100 Subject: [PATCH 6/7] use the new multiaddr utilities in IsPublicAddr/IsPrivateAddr This now means that addresses must *start* with the an IP address, we won't just pull one out of the middle. --- private.go | 57 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/private.go b/private.go index 348ae83..26e547c 100644 --- a/private.go +++ b/private.go @@ -67,36 +67,45 @@ func parseCIDR(cidrs []string) []*net.IPNet { // IsPublicAddr retruns true if the IP part of the multiaddr is a publicly routable address func IsPublicAddr(a ma.Multiaddr) bool { - ip, err := a.ValueForProtocol(ma.P_IP4) - if err == nil { - return !inAddrRange(ip, Private4) && !inAddrRange(ip, Unroutable4) - } - - ip, err = a.ValueForProtocol(ma.P_IP6) - if err == nil { - return !inAddrRange(ip, Private6) && !inAddrRange(ip, Unroutable6) - } - - return false + isPublic := false + ma.ForEach(a, func(c ma.Component) bool { + switch c.Protocol().Code { + case ma.P_IP6ZONE: + return true + default: + return false + case ma.P_IP4: + ip := net.IP(c.RawValue()) + isPublic = !inAddrRange(ip, Private4) && !inAddrRange(ip, Unroutable4) + case ma.P_IP6: + ip := net.IP(c.RawValue()) + isPublic = !inAddrRange(ip, Private6) && !inAddrRange(ip, Unroutable6) + } + return false + }) + return isPublic } // IsPrivateAddr returns true if the IP part of the mutiaddr is in a private network func IsPrivateAddr(a ma.Multiaddr) bool { - ip, err := a.ValueForProtocol(ma.P_IP4) - if err == nil { - return inAddrRange(ip, Private4) - } - - ip, err = a.ValueForProtocol(ma.P_IP6) - if err == nil { - return inAddrRange(ip, Private6) - } - - return false + isPrivate := false + ma.ForEach(a, func(c ma.Component) bool { + switch c.Protocol().Code { + case ma.P_IP6ZONE: + return true + default: + return false + case ma.P_IP4: + isPrivate = inAddrRange(net.IP(c.RawValue()), Private4) + case ma.P_IP6: + isPrivate = inAddrRange(net.IP(c.RawValue()), Private6) + } + return false + }) + return isPrivate } -func inAddrRange(s string, ipnets []*net.IPNet) bool { - ip := net.ParseIP(s) +func inAddrRange(ip net.IP, ipnets []*net.IPNet) bool { for _, ipnet := range ipnets { if ipnet.Contains(ip) { return true From 115f321e211fcc723f6035524075c7caa2d7c8a8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 18 Oct 2018 14:00:36 +0100 Subject: [PATCH 7/7] test to make sure we only consider addresses that *start* with IP addresses (would have failed with ValueForProtocol) --- private_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/private_test.go b/private_test.go index ddd6485..a4380a5 100644 --- a/private_test.go +++ b/private_test.go @@ -32,4 +32,17 @@ func TestIsPublicAddr(t *testing.T) { if IsPrivateAddr(a) { t.Fatal("1.1.1.1 is not a private address!") } + + a, err = ma.NewMultiaddr("/tcp/80/ip4/1.1.1.1") + if err != nil { + t.Fatal(err) + } + + if IsPublicAddr(a) { + t.Fatal("shouldn't consider an address that starts with /tcp/ as *public*") + } + + if IsPrivateAddr(a) { + t.Fatal("shouldn't consider an address that starts with /tcp/ as *private*") + } }